@vibe-agent-toolkit/resources 0.1.38 → 0.1.39-rc.10
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 +33 -24
- package/dist/frontmatter-link-validator.d.ts +9 -9
- package/dist/frontmatter-link-validator.d.ts.map +1 -1
- package/dist/frontmatter-link-validator.js +28 -27
- package/dist/frontmatter-link-validator.js.map +1 -1
- package/dist/frontmatter-validator.d.ts +3 -2
- package/dist/frontmatter-validator.d.ts.map +1 -1
- package/dist/frontmatter-validator.js +8 -14
- package/dist/frontmatter-validator.js.map +1 -1
- package/dist/html-link-parser.d.ts +47 -0
- package/dist/html-link-parser.d.ts.map +1 -0
- package/dist/html-link-parser.js +111 -0
- package/dist/html-link-parser.js.map +1 -0
- package/dist/html-transform.d.ts +45 -0
- package/dist/html-transform.d.ts.map +1 -0
- package/dist/html-transform.js +116 -0
- package/dist/html-transform.js.map +1 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/link-parser.d.ts +26 -2
- package/dist/link-parser.d.ts.map +1 -1
- package/dist/link-parser.js +40 -6
- package/dist/link-parser.js.map +1 -1
- package/dist/link-validator.d.ts +53 -6
- package/dist/link-validator.d.ts.map +1 -1
- package/dist/link-validator.js +100 -131
- package/dist/link-validator.js.map +1 -1
- package/dist/multi-schema-validator.d.ts.map +1 -1
- package/dist/multi-schema-validator.js +6 -8
- package/dist/multi-schema-validator.js.map +1 -1
- package/dist/resource-registry.d.ts +56 -13
- package/dist/resource-registry.d.ts.map +1 -1
- package/dist/resource-registry.js +165 -66
- package/dist/resource-registry.js.map +1 -1
- package/dist/schemas/link-auth.d.ts +382 -0
- package/dist/schemas/link-auth.d.ts.map +1 -0
- package/dist/schemas/link-auth.js +124 -0
- package/dist/schemas/link-auth.js.map +1 -0
- package/dist/schemas/project-config.d.ts +3277 -171
- package/dist/schemas/project-config.d.ts.map +1 -1
- package/dist/schemas/project-config.js +68 -0
- package/dist/schemas/project-config.js.map +1 -1
- package/dist/schemas/resource-metadata.d.ts +46 -10
- package/dist/schemas/resource-metadata.d.ts.map +1 -1
- package/dist/schemas/resource-metadata.js +14 -1
- package/dist/schemas/resource-metadata.js.map +1 -1
- package/dist/schemas/validation-result.d.ts +36 -57
- package/dist/schemas/validation-result.d.ts.map +1 -1
- package/dist/schemas/validation-result.js +5 -27
- package/dist/schemas/validation-result.js.map +1 -1
- package/dist/types/resources.d.ts +1 -1
- package/dist/types/resources.d.ts.map +1 -1
- package/dist/types/resources.js.map +1 -1
- package/dist/utils.d.ts +10 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +14 -0
- package/dist/utils.js.map +1 -1
- package/package.json +4 -3
- package/src/frontmatter-link-validator.ts +28 -30
- package/src/frontmatter-validator.ts +19 -16
- package/src/html-link-parser.ts +140 -0
- package/src/html-transform.ts +157 -0
- package/src/index.ts +10 -1
- package/src/link-parser.ts +60 -11
- package/src/link-validator.ts +175 -145
- package/src/multi-schema-validator.ts +10 -8
- package/src/resource-registry.ts +205 -72
- package/src/schemas/link-auth.ts +146 -0
- package/src/schemas/project-config.ts +76 -0
- package/src/schemas/resource-metadata.ts +17 -1
- package/src/schemas/validation-result.ts +5 -29
- package/src/types/resources.ts +2 -1
- package/src/utils.ts +15 -0
package/README.md
CHANGED
|
@@ -47,7 +47,7 @@ console.log(`Errors: ${result.errorCount}, Warnings: ${result.warningCount}`);
|
|
|
47
47
|
// Show any issues
|
|
48
48
|
for (const issue of result.issues) {
|
|
49
49
|
console.log(`${issue.severity.toUpperCase()}: ${issue.message}`);
|
|
50
|
-
if (issue.line) console.log(` at ${issue.
|
|
50
|
+
if (issue.line) console.log(` at ${issue.location}:${issue.line}`);
|
|
51
51
|
}
|
|
52
52
|
```
|
|
53
53
|
|
|
@@ -546,27 +546,35 @@ interface ValidationResult {
|
|
|
546
546
|
|
|
547
547
|
A single validation issue found during link validation.
|
|
548
548
|
|
|
549
|
+
`ValidationIssue` is the unified issue shape every VAT validator emits (defined
|
|
550
|
+
in `@vibe-agent-toolkit/agent-schema`). Each issue carries a registry `code`
|
|
551
|
+
(see [Validation Codes](../../docs/validation-codes.md)) and a resolved
|
|
552
|
+
`severity`.
|
|
553
|
+
|
|
549
554
|
```typescript
|
|
550
555
|
interface ValidationIssue {
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
line?: number; // Line number where the issue occurs
|
|
554
|
-
type: string; // Issue type identifier (e.g., 'broken_file', 'broken_anchor')
|
|
555
|
-
link: string; // The problematic link
|
|
556
|
+
code: string; // Registry code, e.g. 'LINK_BROKEN_FILE', 'EXTERNAL_URL_DEAD'
|
|
557
|
+
severity: SeverityLevel; // Resolved severity (after config overrides)
|
|
556
558
|
message: string; // Human-readable description
|
|
559
|
+
location?: string; // Project-relative path of the file containing the issue
|
|
560
|
+
line?: number; // Line number where the issue occurs
|
|
561
|
+
link?: string; // The problematic link (when applicable)
|
|
562
|
+
fix?: string; // Fix hint from the code registry
|
|
563
|
+
reference?: string; // Anchor into docs/validation-codes.md
|
|
557
564
|
suggestion?: string; // Optional suggestion for fixing
|
|
558
565
|
}
|
|
559
566
|
```
|
|
560
567
|
|
|
561
|
-
###
|
|
568
|
+
### SeverityLevel
|
|
562
569
|
|
|
563
570
|
```typescript
|
|
564
|
-
type
|
|
571
|
+
type SeverityLevel = 'error' | 'warning' | 'info' | 'ignore';
|
|
565
572
|
```
|
|
566
573
|
|
|
567
574
|
- `error` - Critical issue that should block usage (e.g., broken file link)
|
|
568
|
-
- `warning` - Non-critical issue that should be addressed (e.g.,
|
|
569
|
-
- `info` - Informational message
|
|
575
|
+
- `warning` - Non-critical issue that should be addressed (e.g., dead external URL)
|
|
576
|
+
- `info` - Informational message
|
|
577
|
+
- `ignore` - Suppressed; the issue is not emitted (set via `validation.severity` config)
|
|
570
578
|
|
|
571
579
|
## Frontmatter Support
|
|
572
580
|
|
|
@@ -721,19 +729,19 @@ vat resources validate docs/ --frontmatter-schema schema.json
|
|
|
721
729
|
|
|
722
730
|
### Validation Result
|
|
723
731
|
|
|
724
|
-
Frontmatter
|
|
732
|
+
Frontmatter findings are emitted into the same unified `result.issues` array as
|
|
733
|
+
link findings — each carries a `FRONTMATTER_*` registry code (e.g.
|
|
734
|
+
`FRONTMATTER_MISSING`, `FRONTMATTER_SCHEMA_ERROR`, `FRONTMATTER_INVALID_YAML`)
|
|
735
|
+
and a resolved `severity`. There is no separate `frontmatterValidation` field.
|
|
725
736
|
|
|
726
737
|
```typescript
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
field?: string;
|
|
735
|
-
}>;
|
|
736
|
-
};
|
|
738
|
+
const result = await registry.validate();
|
|
739
|
+
|
|
740
|
+
const frontmatterIssues = result.issues.filter((i) =>
|
|
741
|
+
i.code.startsWith('FRONTMATTER_'),
|
|
742
|
+
);
|
|
743
|
+
for (const issue of frontmatterIssues) {
|
|
744
|
+
console.log(`[${issue.severity}] ${issue.code} at ${issue.location}: ${issue.message}`);
|
|
737
745
|
}
|
|
738
746
|
```
|
|
739
747
|
|
|
@@ -889,9 +897,10 @@ const warnings = result.issues.filter(i => i.severity === 'warning');
|
|
|
889
897
|
// Group by resource
|
|
890
898
|
const issuesByResource = new Map<string, ValidationIssue[]>();
|
|
891
899
|
for (const issue of result.issues) {
|
|
892
|
-
const
|
|
900
|
+
const key = issue.location ?? '(unknown)';
|
|
901
|
+
const issues = issuesByResource.get(key) ?? [];
|
|
893
902
|
issues.push(issue);
|
|
894
|
-
issuesByResource.set(
|
|
903
|
+
issuesByResource.set(key, issues);
|
|
895
904
|
}
|
|
896
905
|
|
|
897
906
|
// Show summary
|
|
@@ -926,7 +935,7 @@ async function validateDocs() {
|
|
|
926
935
|
console.error(`\nValidation failed with ${result.errorCount} errors\n`);
|
|
927
936
|
|
|
928
937
|
for (const issue of result.issues.filter(i => i.severity === 'error')) {
|
|
929
|
-
console.error(`${issue.
|
|
938
|
+
console.error(`${issue.location ?? '(unknown)'}:${issue.line ?? '?'}`);
|
|
930
939
|
console.error(` ${issue.message}`);
|
|
931
940
|
if (issue.suggestion) {
|
|
932
941
|
console.error(` Suggestion: ${issue.suggestion}`);
|
|
@@ -7,18 +7,18 @@
|
|
|
7
7
|
* frontmatter-specific type codes plus a list of external URLs the registry
|
|
8
8
|
* can fold into its existing external URL collection.
|
|
9
9
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
10
|
+
* Code mapping:
|
|
11
|
+
* LINK_BROKEN_FILE -> FRONTMATTER_LINK_BROKEN
|
|
12
|
+
* LINK_BROKEN_ANCHOR -> FRONTMATTER_ANCHOR_MISSING
|
|
13
|
+
* LINK_TO_GITIGNORED -> FRONTMATTER_LINK_TO_GITIGNORED
|
|
14
|
+
* LINK_UNKNOWN -> FRONTMATTER_UNKNOWN_LINK
|
|
15
15
|
*
|
|
16
16
|
* Skipped (no issue, no external):
|
|
17
17
|
* email (mailto:)
|
|
18
18
|
* anchor-only (validated as anchor in current file via validateLink)
|
|
19
19
|
*/
|
|
20
|
-
import { type ValidateLinkOptions } from './link-validator.js';
|
|
21
|
-
import type {
|
|
20
|
+
import { type FragmentIndex, type ValidateLinkOptions } from './link-validator.js';
|
|
21
|
+
import type { ValidationIssue } from './types.js';
|
|
22
22
|
/** A frontmatter-sourced external URL captured for downstream health checking. */
|
|
23
23
|
export interface FrontmatterExternalUrl {
|
|
24
24
|
url: string;
|
|
@@ -35,8 +35,8 @@ export interface FrontmatterLinkValidationResult {
|
|
|
35
35
|
* @param frontmatter - Parsed frontmatter (or undefined)
|
|
36
36
|
* @param schema - JSON Schema for the collection
|
|
37
37
|
* @param sourceFilePath - Absolute path to the source file
|
|
38
|
-
* @param
|
|
38
|
+
* @param fragmentsByFile - Fragment index (file path → set of valid fragments) for anchor validation
|
|
39
39
|
* @param options - Same shape as validateLink (projectRoot, gitTracker, ...)
|
|
40
40
|
*/
|
|
41
|
-
export declare function validateFrontmatterLinks(frontmatter: Record<string, unknown> | undefined, schema: object, sourceFilePath: string,
|
|
41
|
+
export declare function validateFrontmatterLinks(frontmatter: Record<string, unknown> | undefined, schema: object, sourceFilePath: string, fragmentsByFile: FragmentIndex, options?: ValidateLinkOptions): Promise<FrontmatterLinkValidationResult>;
|
|
42
42
|
//# sourceMappingURL=frontmatter-link-validator.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"frontmatter-link-validator.d.ts","sourceRoot":"","sources":["../src/frontmatter-link-validator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;
|
|
1
|
+
{"version":3,"file":"frontmatter-link-validator.d.ts","sourceRoot":"","sources":["../src/frontmatter-link-validator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAKH,OAAO,EAAgB,KAAK,aAAa,EAAE,KAAK,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAEjG,OAAO,KAAK,EAAgB,eAAe,EAAE,MAAM,YAAY,CAAC;AAUhE,kFAAkF;AAClF,MAAM,WAAW,sBAAsB;IACrC,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,+BAA+B;IAC9C,MAAM,EAAE,eAAe,EAAE,CAAC;IAC1B,YAAY,EAAE,sBAAsB,EAAE,CAAC;CACxC;AAED;;;;;;;;GAQG;AACH,wBAAsB,wBAAwB,CAC5C,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,EAChD,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,MAAM,EACtB,eAAe,EAAE,aAAa,EAC9B,OAAO,CAAC,EAAE,mBAAmB,GAC5B,OAAO,CAAC,+BAA+B,CAAC,CAoC1C"}
|
|
@@ -7,29 +7,37 @@
|
|
|
7
7
|
* frontmatter-specific type codes plus a list of external URLs the registry
|
|
8
8
|
* can fold into its existing external URL collection.
|
|
9
9
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
10
|
+
* Code mapping:
|
|
11
|
+
* LINK_BROKEN_FILE -> FRONTMATTER_LINK_BROKEN
|
|
12
|
+
* LINK_BROKEN_ANCHOR -> FRONTMATTER_ANCHOR_MISSING
|
|
13
|
+
* LINK_TO_GITIGNORED -> FRONTMATTER_LINK_TO_GITIGNORED
|
|
14
|
+
* LINK_UNKNOWN -> FRONTMATTER_UNKNOWN_LINK
|
|
15
15
|
*
|
|
16
16
|
* Skipped (no issue, no external):
|
|
17
17
|
* email (mailto:)
|
|
18
18
|
* anchor-only (validated as anchor in current file via validateLink)
|
|
19
19
|
*/
|
|
20
|
+
import { createRegistryIssue } from '@vibe-agent-toolkit/agent-schema';
|
|
20
21
|
import { classifyLink } from './link-parser.js';
|
|
21
22
|
import { validateLink } from './link-validator.js';
|
|
22
23
|
import { walkFrontmatterUriReferences } from './schema-uri-walker.js';
|
|
24
|
+
/** Map the link-level code emitted by validateLink to its frontmatter-scoped code. */
|
|
25
|
+
const LINK_CODE_TO_FRONTMATTER_CODE = {
|
|
26
|
+
LINK_BROKEN_FILE: 'FRONTMATTER_LINK_BROKEN',
|
|
27
|
+
LINK_BROKEN_ANCHOR: 'FRONTMATTER_ANCHOR_MISSING',
|
|
28
|
+
LINK_TO_GITIGNORED: 'FRONTMATTER_LINK_TO_GITIGNORED',
|
|
29
|
+
LINK_UNKNOWN: 'FRONTMATTER_UNKNOWN_LINK',
|
|
30
|
+
};
|
|
23
31
|
/**
|
|
24
32
|
* Validate every URI-family frontmatter value against the file system.
|
|
25
33
|
*
|
|
26
34
|
* @param frontmatter - Parsed frontmatter (or undefined)
|
|
27
35
|
* @param schema - JSON Schema for the collection
|
|
28
36
|
* @param sourceFilePath - Absolute path to the source file
|
|
29
|
-
* @param
|
|
37
|
+
* @param fragmentsByFile - Fragment index (file path → set of valid fragments) for anchor validation
|
|
30
38
|
* @param options - Same shape as validateLink (projectRoot, gitTracker, ...)
|
|
31
39
|
*/
|
|
32
|
-
export async function validateFrontmatterLinks(frontmatter, schema, sourceFilePath,
|
|
40
|
+
export async function validateFrontmatterLinks(frontmatter, schema, sourceFilePath, fragmentsByFile, options) {
|
|
33
41
|
if (!frontmatter)
|
|
34
42
|
return { issues: [], externalUrls: [] };
|
|
35
43
|
const captures = walkFrontmatterUriReferences(frontmatter, schema);
|
|
@@ -55,7 +63,7 @@ export async function validateFrontmatterLinks(frontmatter, schema, sourceFilePa
|
|
|
55
63
|
type: linkType,
|
|
56
64
|
line: 1, // Frontmatter per-field line numbers are post-v1.
|
|
57
65
|
};
|
|
58
|
-
const issue = await validateLink(syntheticLink, sourceFilePath,
|
|
66
|
+
const issue = await validateLink(syntheticLink, sourceFilePath, fragmentsByFile, options);
|
|
59
67
|
if (!issue)
|
|
60
68
|
continue;
|
|
61
69
|
issues.push(rewriteIssue(issue, capture.dottedPath));
|
|
@@ -63,24 +71,17 @@ export async function validateFrontmatterLinks(frontmatter, schema, sourceFilePa
|
|
|
63
71
|
return { issues, externalUrls };
|
|
64
72
|
}
|
|
65
73
|
function rewriteIssue(issue, dottedPath) {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
case 'link_to_gitignored':
|
|
79
|
-
return 'frontmatter_link_to_gitignored';
|
|
80
|
-
case 'unknown_link':
|
|
81
|
-
return 'frontmatter_unknown_link';
|
|
82
|
-
default:
|
|
83
|
-
return originalType;
|
|
84
|
-
}
|
|
74
|
+
const mappedCode = LINK_CODE_TO_FRONTMATTER_CODE[issue.code] ?? issue.code;
|
|
75
|
+
const message = `field \`${dottedPath}\`: ${issue.message}`;
|
|
76
|
+
const extras = {};
|
|
77
|
+
if (issue.location !== undefined)
|
|
78
|
+
extras.location = issue.location;
|
|
79
|
+
if (issue.line !== undefined)
|
|
80
|
+
extras.line = issue.line;
|
|
81
|
+
if (issue.link !== undefined)
|
|
82
|
+
extras.link = issue.link;
|
|
83
|
+
if (issue.suggestion !== undefined)
|
|
84
|
+
extras.suggestion = issue.suggestion;
|
|
85
|
+
return createRegistryIssue(mappedCode, message, extras);
|
|
85
86
|
}
|
|
86
87
|
//# sourceMappingURL=frontmatter-link-validator.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"frontmatter-link-validator.js","sourceRoot":"","sources":["../src/frontmatter-link-validator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,YAAY,
|
|
1
|
+
{"version":3,"file":"frontmatter-link-validator.js","sourceRoot":"","sources":["../src/frontmatter-link-validator.ts"],"names":[],"mappings":"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"}
|
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
*
|
|
13
13
|
* This is the ONLY place in the codebase that should use AJV.
|
|
14
14
|
*/
|
|
15
|
+
import { type ValidationIssue } from '@vibe-agent-toolkit/agent-schema';
|
|
15
16
|
import type { ValidationMode } from './schemas/project-config.js';
|
|
16
|
-
import type { ValidationIssue } from './schemas/validation-result.js';
|
|
17
17
|
/**
|
|
18
18
|
* Validate frontmatter against a JSON Schema.
|
|
19
19
|
*
|
|
@@ -28,6 +28,7 @@ import type { ValidationIssue } from './schemas/validation-result.js';
|
|
|
28
28
|
* @param resourcePath - File path for error reporting
|
|
29
29
|
* @param mode - Validation mode: 'strict' (default) or 'permissive'
|
|
30
30
|
* @param schemaPath - Path to schema file (for error context)
|
|
31
|
+
* @param projectRoot - Project root for computing relative issue locations
|
|
31
32
|
* @returns Array of validation issues (empty if valid)
|
|
32
33
|
*
|
|
33
34
|
* @example
|
|
@@ -46,5 +47,5 @@ import type { ValidationIssue } from './schemas/validation-result.js';
|
|
|
46
47
|
* );
|
|
47
48
|
* ```
|
|
48
49
|
*/
|
|
49
|
-
export declare function validateFrontmatter(frontmatter: Record<string, unknown> | undefined, schema: object, resourcePath: string, mode?: ValidationMode, schemaPath?: string): ValidationIssue[];
|
|
50
|
+
export declare function validateFrontmatter(frontmatter: Record<string, unknown> | undefined, schema: object, resourcePath: string, mode?: ValidationMode, schemaPath?: string, projectRoot?: string): ValidationIssue[];
|
|
50
51
|
//# sourceMappingURL=frontmatter-validator.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"frontmatter-validator.d.ts","sourceRoot":"","sources":["../src/frontmatter-validator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;
|
|
1
|
+
{"version":3,"file":"frontmatter-validator.d.ts","sourceRoot":"","sources":["../src/frontmatter-validator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAuB,KAAK,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAG7F,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAGlE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,EAChD,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,EACpB,IAAI,GAAE,cAAyB,EAC/B,UAAU,CAAC,EAAE,MAAM,EACnB,WAAW,CAAC,EAAE,MAAM,GACnB,eAAe,EAAE,CA8DnB"}
|
|
@@ -12,7 +12,9 @@
|
|
|
12
12
|
*
|
|
13
13
|
* This is the ONLY place in the codebase that should use AJV.
|
|
14
14
|
*/
|
|
15
|
+
import { createRegistryIssue } from '@vibe-agent-toolkit/agent-schema';
|
|
15
16
|
import { createAjvWithUriFormats } from './ajv-factory.js';
|
|
17
|
+
import { issueLocation } from './utils.js';
|
|
16
18
|
/**
|
|
17
19
|
* Validate frontmatter against a JSON Schema.
|
|
18
20
|
*
|
|
@@ -27,6 +29,7 @@ import { createAjvWithUriFormats } from './ajv-factory.js';
|
|
|
27
29
|
* @param resourcePath - File path for error reporting
|
|
28
30
|
* @param mode - Validation mode: 'strict' (default) or 'permissive'
|
|
29
31
|
* @param schemaPath - Path to schema file (for error context)
|
|
32
|
+
* @param projectRoot - Project root for computing relative issue locations
|
|
30
33
|
* @returns Array of validation issues (empty if valid)
|
|
31
34
|
*
|
|
32
35
|
* @example
|
|
@@ -45,7 +48,7 @@ import { createAjvWithUriFormats } from './ajv-factory.js';
|
|
|
45
48
|
* );
|
|
46
49
|
* ```
|
|
47
50
|
*/
|
|
48
|
-
export function validateFrontmatter(frontmatter, schema, resourcePath, mode = 'strict', schemaPath) {
|
|
51
|
+
export function validateFrontmatter(frontmatter, schema, resourcePath, mode = 'strict', schemaPath, projectRoot) {
|
|
49
52
|
const issues = [];
|
|
50
53
|
// In permissive mode, clone schema and set additionalProperties: true
|
|
51
54
|
let effectiveSchema = schema;
|
|
@@ -72,13 +75,7 @@ export function validateFrontmatter(frontmatter, schema, resourcePath, mode = 's
|
|
|
72
75
|
// Build context message with schema path and validation mode
|
|
73
76
|
const schemaContext = schemaPath ? ` (schema: ${schemaPath}, mode: ${mode})` : '';
|
|
74
77
|
const requiredFields = schemaRequires.join(', ');
|
|
75
|
-
issues.push({
|
|
76
|
-
resourcePath,
|
|
77
|
-
line: 1,
|
|
78
|
-
type: 'frontmatter_missing',
|
|
79
|
-
link: '',
|
|
80
|
-
message: `No frontmatter found in file. Schema requires: ${requiredFields}${schemaContext}`,
|
|
81
|
-
});
|
|
78
|
+
issues.push(createRegistryIssue('FRONTMATTER_MISSING', `No frontmatter found in file. Schema requires: ${requiredFields}${schemaContext}`, { location: issueLocation(resourcePath, projectRoot), line: 1 }));
|
|
82
79
|
}
|
|
83
80
|
return issues;
|
|
84
81
|
}
|
|
@@ -90,13 +87,10 @@ export function validateFrontmatter(frontmatter, schema, resourcePath, mode = 's
|
|
|
90
87
|
// Format validation errors with helpful messages
|
|
91
88
|
for (const error of validate.errors) {
|
|
92
89
|
const message = formatValidationError(error, frontmatter, mode, schemaPath);
|
|
93
|
-
issues.push({
|
|
94
|
-
resourcePath,
|
|
90
|
+
issues.push(createRegistryIssue('FRONTMATTER_SCHEMA_ERROR', message, {
|
|
91
|
+
location: issueLocation(resourcePath, projectRoot),
|
|
95
92
|
line: 1,
|
|
96
|
-
|
|
97
|
-
link: '',
|
|
98
|
-
message,
|
|
99
|
-
});
|
|
93
|
+
}));
|
|
100
94
|
}
|
|
101
95
|
return issues;
|
|
102
96
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"frontmatter-validator.js","sourceRoot":"","sources":["../src/frontmatter-validator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"frontmatter-validator.js","sourceRoot":"","sources":["../src/frontmatter-validator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTML resource parser.
|
|
3
|
+
*
|
|
4
|
+
* Parses local HTML files into the shared `ParseResult` shape:
|
|
5
|
+
* - `<a href>` and `<img src>` links (classified by `classifyLink`)
|
|
6
|
+
* - `id` / `name` attributes as fragment anchors
|
|
7
|
+
* - well-formedness diagnostics from parse5's `onParseError`
|
|
8
|
+
*
|
|
9
|
+
* Uses parse5 (WHATWG-conformant). The parse5 document + element walker are
|
|
10
|
+
* exported so the link rewriter (html-transform.ts) shares one parser path.
|
|
11
|
+
*
|
|
12
|
+
* Non-goal: `<base href>` is not honored — relative links are resolved against
|
|
13
|
+
* the file's own directory, not a document base.
|
|
14
|
+
*/
|
|
15
|
+
import { type DefaultTreeAdapterMap } from 'parse5';
|
|
16
|
+
import { type ParseResult } from './link-parser.js';
|
|
17
|
+
import type { HtmlParseError } from './schemas/resource-metadata.js';
|
|
18
|
+
type P5Node = DefaultTreeAdapterMap['node'];
|
|
19
|
+
type P5Element = DefaultTreeAdapterMap['element'];
|
|
20
|
+
/** A parsed parse5 document together with any well-formedness diagnostics. */
|
|
21
|
+
export interface HtmlDocument {
|
|
22
|
+
document: DefaultTreeAdapterMap['document'];
|
|
23
|
+
parseErrors: HtmlParseError[];
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Parse an HTML source string with source-location info and collect parser
|
|
27
|
+
* errors. Shared by `parseHtml` (link/anchor extraction) and `rewriteHtmlLinks`
|
|
28
|
+
* (attribute offset splicing).
|
|
29
|
+
*/
|
|
30
|
+
export declare function parseHtmlDocument(source: string): HtmlDocument;
|
|
31
|
+
/**
|
|
32
|
+
* Depth-first walk yielding every element node in the tree.
|
|
33
|
+
*
|
|
34
|
+
* Deliberate gap: a `<template>` element's content lives in its separate
|
|
35
|
+
* `content` fragment (not `childNodes`), so links inside `<template>` are not
|
|
36
|
+
* walked, and foreign-content (SVG/MathML) subtrees are not special-cased.
|
|
37
|
+
* Both are rare in the content link graph we rewrite.
|
|
38
|
+
*/
|
|
39
|
+
export declare function walkElements(node: P5Node): Generator<P5Element>;
|
|
40
|
+
/**
|
|
41
|
+
* Parse an HTML file into a `ParseResult`.
|
|
42
|
+
*
|
|
43
|
+
* @param filePath - Absolute path to the HTML file.
|
|
44
|
+
*/
|
|
45
|
+
export declare function parseHtml(filePath: string): Promise<ParseResult>;
|
|
46
|
+
export {};
|
|
47
|
+
//# sourceMappingURL=html-link-parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"html-link-parser.d.ts","sourceRoot":"","sources":["../src/html-link-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,OAAO,EAAS,KAAK,qBAAqB,EAAE,MAAM,QAAQ,CAAC;AAE3D,OAAO,EAAgB,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAClE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAGrE,KAAK,MAAM,GAAG,qBAAqB,CAAC,MAAM,CAAC,CAAC;AAC5C,KAAK,SAAS,GAAG,qBAAqB,CAAC,SAAS,CAAC,CAAC;AAGlD,8EAA8E;AAC9E,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,qBAAqB,CAAC,UAAU,CAAC,CAAC;IAC5C,WAAW,EAAE,cAAc,EAAE,CAAC;CAC/B;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,CAW9D;AAED;;;;;;;GAOG;AACH,wBAAiB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC,SAAS,CAAC,CAShE;AAuCD;;;;GAIG;AACH,wBAAsB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CA2BtE"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTML resource parser.
|
|
3
|
+
*
|
|
4
|
+
* Parses local HTML files into the shared `ParseResult` shape:
|
|
5
|
+
* - `<a href>` and `<img src>` links (classified by `classifyLink`)
|
|
6
|
+
* - `id` / `name` attributes as fragment anchors
|
|
7
|
+
* - well-formedness diagnostics from parse5's `onParseError`
|
|
8
|
+
*
|
|
9
|
+
* Uses parse5 (WHATWG-conformant). The parse5 document + element walker are
|
|
10
|
+
* exported so the link rewriter (html-transform.ts) shares one parser path.
|
|
11
|
+
*
|
|
12
|
+
* Non-goal: `<base href>` is not honored — relative links are resolved against
|
|
13
|
+
* the file's own directory, not a document base.
|
|
14
|
+
*/
|
|
15
|
+
import { readFile, stat } from 'node:fs/promises';
|
|
16
|
+
import { parse } from 'parse5';
|
|
17
|
+
import { classifyLink } from './link-parser.js';
|
|
18
|
+
/**
|
|
19
|
+
* Parse an HTML source string with source-location info and collect parser
|
|
20
|
+
* errors. Shared by `parseHtml` (link/anchor extraction) and `rewriteHtmlLinks`
|
|
21
|
+
* (attribute offset splicing).
|
|
22
|
+
*/
|
|
23
|
+
export function parseHtmlDocument(source) {
|
|
24
|
+
const parseErrors = [];
|
|
25
|
+
const document = parse(source, {
|
|
26
|
+
sourceCodeLocationInfo: true,
|
|
27
|
+
onParseError: (err) => {
|
|
28
|
+
// parse5's ParserError extends Location which always has startLine: number.
|
|
29
|
+
// We always include line since it is always present.
|
|
30
|
+
parseErrors.push({ message: err.code, line: err.startLine });
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
return { document, parseErrors };
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Depth-first walk yielding every element node in the tree.
|
|
37
|
+
*
|
|
38
|
+
* Deliberate gap: a `<template>` element's content lives in its separate
|
|
39
|
+
* `content` fragment (not `childNodes`), so links inside `<template>` are not
|
|
40
|
+
* walked, and foreign-content (SVG/MathML) subtrees are not special-cased.
|
|
41
|
+
* Both are rare in the content link graph we rewrite.
|
|
42
|
+
*/
|
|
43
|
+
export function* walkElements(node) {
|
|
44
|
+
if ('tagName' in node) {
|
|
45
|
+
yield node;
|
|
46
|
+
}
|
|
47
|
+
if ('childNodes' in node) {
|
|
48
|
+
for (const child of node.childNodes) {
|
|
49
|
+
yield* walkElements(child);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function getAttr(element, name) {
|
|
54
|
+
return element.attrs.find((a) => a.name === name)?.value;
|
|
55
|
+
}
|
|
56
|
+
function makeLink(href, line) {
|
|
57
|
+
return { text: '', href, type: classifyLink(href), ...(line !== undefined && { line }) };
|
|
58
|
+
}
|
|
59
|
+
function visitElement(element, links, anchors) {
|
|
60
|
+
const line = element.sourceCodeLocation?.startLine;
|
|
61
|
+
if (element.tagName === 'a') {
|
|
62
|
+
const href = getAttr(element, 'href');
|
|
63
|
+
if (href !== undefined) {
|
|
64
|
+
links.push(makeLink(href, line));
|
|
65
|
+
}
|
|
66
|
+
const name = getAttr(element, 'name');
|
|
67
|
+
if (name !== undefined && name !== '') {
|
|
68
|
+
anchors.add(name);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
else if (element.tagName === 'img') {
|
|
72
|
+
const src = getAttr(element, 'src');
|
|
73
|
+
if (src !== undefined) {
|
|
74
|
+
links.push(makeLink(src, line));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
const id = getAttr(element, 'id');
|
|
78
|
+
if (id !== undefined && id !== '') {
|
|
79
|
+
anchors.add(id);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Parse an HTML file into a `ParseResult`.
|
|
84
|
+
*
|
|
85
|
+
* @param filePath - Absolute path to the HTML file.
|
|
86
|
+
*/
|
|
87
|
+
export async function parseHtml(filePath) {
|
|
88
|
+
const [content, stats] = await Promise.all([
|
|
89
|
+
// eslint-disable-next-line security/detect-non-literal-fs-filename -- filePath is a user-provided path parameter
|
|
90
|
+
readFile(filePath, 'utf-8'),
|
|
91
|
+
// eslint-disable-next-line security/detect-non-literal-fs-filename -- filePath is a user-provided path parameter
|
|
92
|
+
stat(filePath),
|
|
93
|
+
]);
|
|
94
|
+
const { document, parseErrors } = parseHtmlDocument(content);
|
|
95
|
+
const links = [];
|
|
96
|
+
const anchors = new Set();
|
|
97
|
+
for (const element of walkElements(document)) {
|
|
98
|
+
visitElement(element, links, anchors);
|
|
99
|
+
}
|
|
100
|
+
const anchorList = [...anchors];
|
|
101
|
+
return {
|
|
102
|
+
links,
|
|
103
|
+
headings: [],
|
|
104
|
+
content,
|
|
105
|
+
sizeBytes: stats.size,
|
|
106
|
+
estimatedTokenCount: Math.ceil(content.length / 4),
|
|
107
|
+
...(anchorList.length > 0 && { anchors: anchorList }),
|
|
108
|
+
...(parseErrors.length > 0 && { parseErrors }),
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=html-link-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"html-link-parser.js","sourceRoot":"","sources":["../src/html-link-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAElD,OAAO,EAAE,KAAK,EAA8B,MAAM,QAAQ,CAAC;AAE3D,OAAO,EAAE,YAAY,EAAoB,MAAM,kBAAkB,CAAC;AAclE;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAAc;IAC9C,MAAM,WAAW,GAAqB,EAAE,CAAC;IACzC,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,EAAE;QAC7B,sBAAsB,EAAE,IAAI;QAC5B,YAAY,EAAE,CAAC,GAAG,EAAE,EAAE;YACpB,4EAA4E;YAC5E,qDAAqD;YACrD,WAAW,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,CAAC,SAAS,EAAE,CAAC,CAAC;QAC/D,CAAC;KACF,CAAC,CAAC;IACH,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,CAAC;AACnC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,SAAS,CAAC,CAAC,YAAY,CAAC,IAAY;IACxC,IAAI,SAAS,IAAI,IAAI,EAAE,CAAC;QACtB,MAAM,IAAI,CAAC;IACb,CAAC;IACD,IAAI,YAAY,IAAI,IAAI,EAAE,CAAC;QACzB,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,UAA2B,EAAE,CAAC;YACrD,KAAK,CAAC,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;QAC7B,CAAC;IACH,CAAC;AACH,CAAC;AAED,SAAS,OAAO,CAAC,OAAkB,EAAE,IAAY;IAC/C,OAAO,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,EAAE,KAAK,CAAC;AAC3D,CAAC;AAED,SAAS,QAAQ,CAAC,IAAY,EAAE,IAAwB;IACtD,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,YAAY,CAAC,IAAI,CAAC,EAAE,GAAG,CAAC,IAAI,KAAK,SAAS,IAAI,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;AAC3F,CAAC;AAED,SAAS,YAAY,CACnB,OAAkB,EAClB,KAAqB,EACrB,OAAoB;IAEpB,MAAM,IAAI,GAAG,OAAO,CAAC,kBAAkB,EAAE,SAAS,CAAC;IAEnD,IAAI,OAAO,CAAC,OAAO,KAAK,GAAG,EAAE,CAAC;QAC5B,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QACtC,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;QACnC,CAAC;QACD,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QACtC,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,EAAE,EAAE,CAAC;YACtC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACpB,CAAC;IACH,CAAC;SAAM,IAAI,OAAO,CAAC,OAAO,KAAK,KAAK,EAAE,CAAC;QACrC,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QACpC,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;YACtB,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC;QAClC,CAAC;IACH,CAAC;IAED,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAClC,IAAI,EAAE,KAAK,SAAS,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QAClC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAClB,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,QAAgB;IAC9C,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QACzC,iHAAiH;QACjH,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;QAC3B,iHAAiH;QACjH,IAAI,CAAC,QAAQ,CAAC;KACf,CAAC,CAAC;IAEH,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC;IAE7D,MAAM,KAAK,GAAmB,EAAE,CAAC;IACjC,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAElC,KAAK,MAAM,OAAO,IAAI,YAAY,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC7C,YAAY,CAAC,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;IACxC,CAAC;IAED,MAAM,UAAU,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC;IAChC,OAAO;QACL,KAAK;QACL,QAAQ,EAAE,EAAE;QACZ,OAAO;QACP,SAAS,EAAE,KAAK,CAAC,IAAI;QACrB,mBAAmB,EAAE,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC;QAClD,GAAG,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC;QACrD,GAAG,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,WAAW,EAAE,CAAC;KAC/C,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structure-preserving HTML link rewriter.
|
|
3
|
+
*
|
|
4
|
+
* Rewrites `<a href>` and `<img src>` attribute VALUES in place by splicing the
|
|
5
|
+
* original source string at parse5-reported offsets. The document is never
|
|
6
|
+
* re-serialized (parse5's serializer normalizes whitespace, quotes, void
|
|
7
|
+
* elements, and the doctype), so unchanged input round-trips byte-for-byte.
|
|
8
|
+
*
|
|
9
|
+
* Uses the same `RewriteHref` callback model as `rewriteBodyLinks` — callers
|
|
10
|
+
* supply per-href target resolution; this module owns only the splice mechanics.
|
|
11
|
+
*
|
|
12
|
+
* Non-goal: `<base href>` is not honored. Relative hrefs are resolved by the
|
|
13
|
+
* caller against the file's own directory; a `<base>` element that would
|
|
14
|
+
* override that in a browser is ignored.
|
|
15
|
+
*/
|
|
16
|
+
import type { RewriteHref } from './rewriter-helpers.js';
|
|
17
|
+
/** A wanted link rewrite that could not be spliced back into the source. */
|
|
18
|
+
export interface UnappliedRewrite {
|
|
19
|
+
tagName: string;
|
|
20
|
+
attr: string;
|
|
21
|
+
/** The original attribute value the rewrite targeted. */
|
|
22
|
+
from: string;
|
|
23
|
+
/** The value the rewrite wanted to write. */
|
|
24
|
+
to: string;
|
|
25
|
+
/**
|
|
26
|
+
* Why the splice could not be located:
|
|
27
|
+
* - `no-source-location` — parse5 omitted the attribute's offsets (synthesized
|
|
28
|
+
* during error recovery on malformed input).
|
|
29
|
+
* - `unparseable-attribute` — the attribute's source span had no locatable
|
|
30
|
+
* value (e.g. an unterminated quote).
|
|
31
|
+
*/
|
|
32
|
+
reason: 'no-source-location' | 'unparseable-attribute';
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Rewrite `<a href>` / `<img src>` values in `source` using `rewriteHref`.
|
|
36
|
+
* Returns `source` unchanged (byte-for-byte) when no value changes.
|
|
37
|
+
*
|
|
38
|
+
* A rewrite that is wanted (the href resolves to a new value) but cannot be
|
|
39
|
+
* spliced back — because parse5 omitted the source location or the attribute
|
|
40
|
+
* span is unparseable — is reported via `onUnapplied` rather than dropped
|
|
41
|
+
* silently. Malformed pages are exactly the ones that hit this path, so the
|
|
42
|
+
* caller can surface it instead of shipping a stale link.
|
|
43
|
+
*/
|
|
44
|
+
export declare function rewriteHtmlLinks(source: string, rewriteHref: RewriteHref, onUnapplied?: (info: UnappliedRewrite) => void): string;
|
|
45
|
+
//# sourceMappingURL=html-transform.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"html-transform.d.ts","sourceRoot":"","sources":["../src/html-transform.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAqEzD,4EAA4E;AAC5E,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,yDAAyD;IACzD,IAAI,EAAE,MAAM,CAAC;IACb,6CAA6C;IAC7C,EAAE,EAAE,MAAM,CAAC;IACX;;;;;;OAMG;IACH,MAAM,EAAE,oBAAoB,GAAG,uBAAuB,CAAC;CACxD;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,WAAW,EACxB,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,gBAAgB,KAAK,IAAI,GAC7C,MAAM,CAsCR"}
|