@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.
Files changed (75) hide show
  1. package/README.md +33 -24
  2. package/dist/frontmatter-link-validator.d.ts +9 -9
  3. package/dist/frontmatter-link-validator.d.ts.map +1 -1
  4. package/dist/frontmatter-link-validator.js +28 -27
  5. package/dist/frontmatter-link-validator.js.map +1 -1
  6. package/dist/frontmatter-validator.d.ts +3 -2
  7. package/dist/frontmatter-validator.d.ts.map +1 -1
  8. package/dist/frontmatter-validator.js +8 -14
  9. package/dist/frontmatter-validator.js.map +1 -1
  10. package/dist/html-link-parser.d.ts +47 -0
  11. package/dist/html-link-parser.d.ts.map +1 -0
  12. package/dist/html-link-parser.js +111 -0
  13. package/dist/html-link-parser.js.map +1 -0
  14. package/dist/html-transform.d.ts +45 -0
  15. package/dist/html-transform.d.ts.map +1 -0
  16. package/dist/html-transform.js +116 -0
  17. package/dist/html-transform.js.map +1 -0
  18. package/dist/index.d.ts +5 -2
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +4 -2
  21. package/dist/index.js.map +1 -1
  22. package/dist/link-parser.d.ts +26 -2
  23. package/dist/link-parser.d.ts.map +1 -1
  24. package/dist/link-parser.js +40 -6
  25. package/dist/link-parser.js.map +1 -1
  26. package/dist/link-validator.d.ts +53 -6
  27. package/dist/link-validator.d.ts.map +1 -1
  28. package/dist/link-validator.js +100 -131
  29. package/dist/link-validator.js.map +1 -1
  30. package/dist/multi-schema-validator.d.ts.map +1 -1
  31. package/dist/multi-schema-validator.js +6 -8
  32. package/dist/multi-schema-validator.js.map +1 -1
  33. package/dist/resource-registry.d.ts +56 -13
  34. package/dist/resource-registry.d.ts.map +1 -1
  35. package/dist/resource-registry.js +165 -66
  36. package/dist/resource-registry.js.map +1 -1
  37. package/dist/schemas/link-auth.d.ts +382 -0
  38. package/dist/schemas/link-auth.d.ts.map +1 -0
  39. package/dist/schemas/link-auth.js +124 -0
  40. package/dist/schemas/link-auth.js.map +1 -0
  41. package/dist/schemas/project-config.d.ts +3277 -171
  42. package/dist/schemas/project-config.d.ts.map +1 -1
  43. package/dist/schemas/project-config.js +68 -0
  44. package/dist/schemas/project-config.js.map +1 -1
  45. package/dist/schemas/resource-metadata.d.ts +46 -10
  46. package/dist/schemas/resource-metadata.d.ts.map +1 -1
  47. package/dist/schemas/resource-metadata.js +14 -1
  48. package/dist/schemas/resource-metadata.js.map +1 -1
  49. package/dist/schemas/validation-result.d.ts +36 -57
  50. package/dist/schemas/validation-result.d.ts.map +1 -1
  51. package/dist/schemas/validation-result.js +5 -27
  52. package/dist/schemas/validation-result.js.map +1 -1
  53. package/dist/types/resources.d.ts +1 -1
  54. package/dist/types/resources.d.ts.map +1 -1
  55. package/dist/types/resources.js.map +1 -1
  56. package/dist/utils.d.ts +10 -0
  57. package/dist/utils.d.ts.map +1 -1
  58. package/dist/utils.js +14 -0
  59. package/dist/utils.js.map +1 -1
  60. package/package.json +4 -3
  61. package/src/frontmatter-link-validator.ts +28 -30
  62. package/src/frontmatter-validator.ts +19 -16
  63. package/src/html-link-parser.ts +140 -0
  64. package/src/html-transform.ts +157 -0
  65. package/src/index.ts +10 -1
  66. package/src/link-parser.ts +60 -11
  67. package/src/link-validator.ts +175 -145
  68. package/src/multi-schema-validator.ts +10 -8
  69. package/src/resource-registry.ts +205 -72
  70. package/src/schemas/link-auth.ts +146 -0
  71. package/src/schemas/project-config.ts +76 -0
  72. package/src/schemas/resource-metadata.ts +17 -1
  73. package/src/schemas/validation-result.ts +5 -29
  74. package/src/types/resources.ts +2 -1
  75. package/src/utils.ts +15 -0
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Zod schema for `resources.linkAuth` config.
3
+ *
4
+ * Validates the shape of an adopter's linkAuth configuration: providers
5
+ * (each either a `use: <macro>` reference or a full inline provider) plus
6
+ * an optional cache config. Strict — typos in provider field names are
7
+ * errors, per Postel's law ("writing our output → conservative").
8
+ *
9
+ * Provider entries accept EITHER:
10
+ * - `{ use: <name>, ...overrides }` — reference a shipped macro by name;
11
+ * override fields are deep-merged on top at expansion time. Overrides
12
+ * are NOT strictly typed at this layer because they must match the
13
+ * macro's shape, which we cannot see until expansion. A typo in an
14
+ * override silently no-ops; the full provider shape is validated
15
+ * after expansion (slice 2 wires that).
16
+ * - A full inline `{ match, rewrite, auth, token, check }` — every field
17
+ * required, every nested object strict.
18
+ *
19
+ * Mirrors the runtime types in `@vibe-agent-toolkit/utils`'s `link-auth/`
20
+ * module. Keep these aligned: a config that parses here must satisfy the
21
+ * `Provider` / `LinkAuthConfig` interfaces over there.
22
+ *
23
+ * Per design issue #113 §4 (vocabulary) and §5 (macros).
24
+ */
25
+
26
+ import { z } from 'zod';
27
+
28
+ // ---------------------------------------------------------------------------
29
+ // Leaf types
30
+ // ---------------------------------------------------------------------------
31
+
32
+ export const ProviderMatchSchema = z
33
+ .object({
34
+ host: z.string().min(1).describe('Glob matched against URL hostname'),
35
+ excludeHost: z
36
+ .array(z.string().min(1))
37
+ .optional()
38
+ .describe('Globs that, if any match the hostname, exclude this provider'),
39
+ })
40
+ .strict()
41
+ .describe('Provider selection — match.host + optional excludeHost globs');
42
+
43
+ export const RewriteRuleSchema = z
44
+ .object({
45
+ when: z.string().min(1).describe('Regex source (RFC-style, JavaScript-compatible) matched against the URL'),
46
+ vars: z
47
+ .record(z.string(), z.string())
48
+ .optional()
49
+ .describe('Computed variables, each a template that references captures from `when`'),
50
+ to: z.string().min(1).describe('URL template — interpolates ${capture} and ${var}'),
51
+ })
52
+ .strict()
53
+ .describe('A single rewrite rule (one entry in a provider\'s ordered rewrite list)');
54
+
55
+ export const ProviderAuthSchema = z
56
+ .object({
57
+ headers: z
58
+ .record(z.string(), z.string())
59
+ .describe('Header name → value template. Values interpolate ${token} and any capture/var.'),
60
+ })
61
+ .strict()
62
+ .describe('Auth headers attached to the authenticated fetch');
63
+
64
+ export const TokenSourceSchema = z
65
+ .union([
66
+ z.object({ env: z.string().min(1) }).strict(),
67
+ z
68
+ .object({
69
+ command: z.union([z.string().min(1), z.array(z.string().min(1)).min(1)]),
70
+ })
71
+ .strict(),
72
+ ])
73
+ .describe('A single token source — env var, or argv command (preferred), or convenience string');
74
+
75
+ export const ProviderCheckSchema = z
76
+ .object({
77
+ method: z.enum(['GET', 'HEAD']),
78
+ aliveStatus: z
79
+ .array(z.number().int().min(100).max(599))
80
+ .min(1)
81
+ .describe('HTTP status codes meaning the URL is alive (per host semantics)'),
82
+ notFoundMeaning: z
83
+ .enum(['ambiguous', 'dead'])
84
+ .describe(
85
+ 'Per-host: does 404 mean "dead" (honest 404 host like Graph) or "ambiguous" (masking host like GitHub)?',
86
+ ),
87
+ })
88
+ .strict()
89
+ .describe('Status-to-outcome classifier (consumed by the resources layer, not the pure engine)');
90
+
91
+ // ---------------------------------------------------------------------------
92
+ // Provider entry (inline OR macro reference)
93
+ // ---------------------------------------------------------------------------
94
+
95
+ const InlineProviderSchema = z
96
+ .object({
97
+ match: ProviderMatchSchema,
98
+ rewrite: z.array(RewriteRuleSchema).min(1).describe('Ordered rewrite rules — first matching wins'),
99
+ auth: ProviderAuthSchema,
100
+ token: z.array(TokenSourceSchema).describe('Ordered token sources — first non-empty wins'),
101
+ check: ProviderCheckSchema,
102
+ })
103
+ .strict();
104
+
105
+ const MacroProviderSchema = z
106
+ .object({
107
+ use: z.string().min(1).describe('Macro name (e.g. "github", "sharepoint")'),
108
+ })
109
+ .passthrough()
110
+ .describe(
111
+ 'Reference a shipped macro by name. Override fields (match/rewrite/auth/token/check) deep-merge on top at expansion time.',
112
+ );
113
+
114
+ export const ProviderEntrySchema = z
115
+ .union([MacroProviderSchema, InlineProviderSchema])
116
+ .describe('A provider entry — either { use: <macro>, ...overrides } or a full inline provider');
117
+
118
+ // ---------------------------------------------------------------------------
119
+ // Top-level linkAuth config
120
+ // ---------------------------------------------------------------------------
121
+
122
+ export const LinkAuthCacheSchema = z
123
+ .object({
124
+ ttlMinutes: z
125
+ .number()
126
+ .int()
127
+ .positive()
128
+ .optional()
129
+ .describe('Content cache TTL in minutes (default 30 — used by slice 3 content-fetch)'),
130
+ })
131
+ .strict();
132
+
133
+ export const LinkAuthConfigSchema = z
134
+ .object({
135
+ cache: LinkAuthCacheSchema.optional(),
136
+ providers: z
137
+ .array(ProviderEntrySchema)
138
+ .describe('Ordered list of providers — first claiming-by-host wins'),
139
+ })
140
+ .strict()
141
+ .describe('`resources.linkAuth` — authenticated external link resolution config');
142
+
143
+ export type LinkAuthConfig = z.infer<typeof LinkAuthConfigSchema>;
144
+ export type ProviderEntry = z.infer<typeof ProviderEntrySchema>;
145
+ export type RewriteRule = z.infer<typeof RewriteRuleSchema>;
146
+ export type TokenSource = z.infer<typeof TokenSourceSchema>;
@@ -1,6 +1,8 @@
1
1
  import { ValidationConfigSchema } from '@vibe-agent-toolkit/agent-schema';
2
2
  import { z } from 'zod';
3
3
 
4
+ import { LinkAuthConfigSchema } from './link-auth.js';
5
+
4
6
  /**
5
7
  * Official semver regex from https://semver.org/ (anchored).
6
8
  *
@@ -96,6 +98,10 @@ export const ResourcesConfigSchema = z.object({
96
98
  .describe('Global default exclude patterns (not used by collections in Phase 2)'),
97
99
  collections: z.record(z.string(), CollectionConfigSchema).optional()
98
100
  .describe('Named collections of resources'),
101
+ validation: ValidationConfigSchema.optional()
102
+ .describe('Validation framework config: severity overrides and per-code allow entries (applied inside ResourceRegistry.validate)'),
103
+ linkAuth: LinkAuthConfigSchema.optional()
104
+ .describe('Authenticated external link resolution config (issue #113 / link-auth engine)'),
99
105
  }).describe('Resources section of project configuration');
100
106
 
101
107
  export type ResourcesConfig = z.infer<typeof ResourcesConfigSchema>;
@@ -134,6 +140,72 @@ export const SkillFileEntrySchema = z.object({
134
140
 
135
141
  export type SkillFileEntry = z.infer<typeof SkillFileEntrySchema>;
136
142
 
143
+ /**
144
+ * A typed "skill source" descriptor as it appears in vibe-agent-toolkit.config.yaml.
145
+ *
146
+ * This is the CONFIG representation. Task 13's staging maps it onto Plan 1's
147
+ * runtime `SkillSource` union before calling `resolveSkillSource`. Kept here so
148
+ * `configure`/`run` parse a single strict source of truth.
149
+ */
150
+ export const SkillSourceDescriptorSchema = z.union([
151
+ z.object({ workspace: z.string().min(1) }).strict(),
152
+ z.object({ npm: z.string().min(1) }).strict(),
153
+ z.object({ url: z.string().min(1), sha256: z.string().min(1).optional() }).strict(),
154
+ z.object({ path: z.string().min(1) }).strict(),
155
+ z.object({ vendored: z.literal(true) }).strict(),
156
+ ]).describe('Typed skill source: workspace | npm | url(+sha256) | path | vendored');
157
+
158
+ export type SkillSourceDescriptor = z.infer<typeof SkillSourceDescriptorSchema>;
159
+
160
+ /**
161
+ * Per-skill `test:` block for `vat skill test`. Strict — unknown keys are a
162
+ * config error (Postel: this is vat-produced/validated data, §schema strategy).
163
+ * Every field is optional; omitted knobs fall back to built-in defaults.
164
+ */
165
+ export const TestConfigSchema = z.object({
166
+ model: z.string().min(1).optional()
167
+ .describe('Pinned model for reproducibility (default: a fixed model, not "whatever the caller has")'),
168
+ maxTurns: z.number().int().positive().optional()
169
+ .describe('Cap on experimenter turns'),
170
+ maxBudgetUsd: z.number().positive().optional()
171
+ .describe('Hard USD budget cap passed to the CLI'),
172
+ timeout: z.number().int().positive().optional()
173
+ .describe('Wall-clock timeout in seconds'),
174
+ stall: z.number().int().positive().optional()
175
+ .describe('Stall-watchdog seconds (kill on no stream output)'),
176
+ evals: z.string().min(1).optional()
177
+ .describe('Path to evals.json (relative to skill source)'),
178
+ experimenterPrompt: z.string().min(1).optional()
179
+ .describe('Path to an override experimenter prompt (must preserve §6c invariants)'),
180
+ auth: z.enum(['inherit', 'subscription', 'api-key', 'auto']).optional()
181
+ .describe('Auth-mechanism selection (default: inherit)'),
182
+ requireAuth: z.enum(['subscription', 'api-key']).optional()
183
+ .describe('Fail-fast guard: preflight exits 2 if effective mechanism is not this'),
184
+ baseline: z.boolean().optional()
185
+ .describe('Run the opt-in with/without A/B baseline (default: false)'),
186
+ skillCreator: SkillSourceDescriptorSchema.optional()
187
+ .describe('Source for skill-creator (default: { vendored: true })'),
188
+ with: z.array(SkillSourceDescriptorSchema).optional()
189
+ .describe('Declared-dependency skills/plugins to stage'),
190
+ optional: z.array(SkillSourceDescriptorSchema).optional()
191
+ .describe('Optional dependencies, absent by default'),
192
+ /**
193
+ * Shell command to run once, before staging, to generate build artifacts.
194
+ * Runs with cwd = config root (directory containing vibe-agent-toolkit.config.yaml).
195
+ * Useful when a skill references a generated file (e.g. `node scripts/report.mjs`)
196
+ * that is produced by a bundler step and not committed to source.
197
+ * A non-zero exit code aborts the run (preflight failure, exit 2).
198
+ */
199
+ env: z.record(z.string(), z.string()).optional()
200
+ .describe('Feature B: explicit env var injections for the experimenter spawn. Values support ${fixturesDir}, ${stagedSkillDir}, ${harnessRoot}, ${resultsDir} interpolation (resolved at stage time). Protected names (PATH, auth, model, admin) cannot be overridden.'),
201
+ passEnv: z.array(z.string().min(1)).optional()
202
+ .describe('Feature A: names of host env vars to forward to the experimenter spawn if present. Protected names are ignored with a warning.'),
203
+ build: z.string().min(1).optional()
204
+ .describe('Shell command run once, before staging, to generate build artifacts (cwd = config root)'),
205
+ }).strict().describe('Per-skill vat skill test configuration');
206
+
207
+ export type TestConfig = z.infer<typeof TestConfigSchema>;
208
+
137
209
  /**
138
210
  * Skill packaging configuration.
139
211
  *
@@ -153,10 +225,14 @@ export const SkillPackagingConfigSchema = z.object({
153
225
  targets: z.array(z.enum(['claude-chat', 'claude-cowork', 'claude-code'])).optional()
154
226
  .describe('Declared runtime targets for this skill. Suppresses non-applicable compat verdicts.'),
155
227
  files: z.array(SkillFileEntrySchema).optional().describe('Explicit source→dest file mappings for build artifacts, unlinked files, or routing overrides'),
228
+ test: TestConfigSchema.optional()
229
+ .describe('vat skill test configuration for this skill'),
156
230
  }).strict().describe('Skill packaging configuration');
157
231
 
158
232
  export type SkillPackagingConfig = z.infer<typeof SkillPackagingConfigSchema>;
159
233
 
234
+ // NOTE: project-config has no generated JSON Schema generator (unlike packages with generate:schemas script); Zod schema is the tracked source of truth.
235
+
160
236
  /**
161
237
  * Skills discovery and packaging configuration.
162
238
  *
@@ -6,6 +6,7 @@ import { SHA256Schema } from './checksum.js';
6
6
  * Type of link found in markdown resources.
7
7
  *
8
8
  * - `local_file`: Link to a local file (relative or absolute path)
9
+ * - `local_directory`: Link to a local directory, e.g. a ref ending in `/`
9
10
  * - `anchor`: Link to a heading anchor (e.g., #heading-slug)
10
11
  * - `external`: HTTP/HTTPS URL to external resource
11
12
  * - `email`: Mailto link
@@ -13,6 +14,7 @@ import { SHA256Schema } from './checksum.js';
13
14
  */
14
15
  export const LinkTypeSchema = z.enum([
15
16
  'local_file',
17
+ 'local_directory',
16
18
  'anchor',
17
19
  'external',
18
20
  'email',
@@ -36,6 +38,16 @@ export const LinkNodeTypeSchema = z.enum([
36
38
 
37
39
  export type LinkNodeType = z.infer<typeof LinkNodeTypeSchema>;
38
40
 
41
+ /**
42
+ * Zod schema for an HTML well-formedness diagnostic.
43
+ */
44
+ export const HtmlParseErrorSchema = z.object({
45
+ message: z.string().describe('parse5 error code (e.g. "missing-end-tag")'),
46
+ line: z.number().int().positive().optional().describe('1-based source line, when known'),
47
+ }).describe('HTML well-formedness diagnostic');
48
+
49
+ export type HtmlParseError = z.infer<typeof HtmlParseErrorSchema>;
50
+
39
51
  /**
40
52
  * Represents a heading node in the document's table of contents.
41
53
  *
@@ -96,6 +108,10 @@ export const ResourceMetadataSchema = z.object({
96
108
  filePath: z.string().describe('Absolute path to the resource file'),
97
109
  links: z.array(ResourceLinkSchema).describe('All links found in the resource'),
98
110
  headings: z.array(HeadingNodeSchema).describe('Document table of contents (top-level headings only; children are nested)'),
111
+ anchors: z.array(z.string()).optional()
112
+ .describe('Fragment targets for anchor validation (HTML id/name attributes)'),
113
+ parseErrors: z.array(HtmlParseErrorSchema).optional()
114
+ .describe('HTML well-formedness diagnostics (populated for HTML resources only)'),
99
115
  frontmatter: z.record(z.string(), z.unknown()).optional()
100
116
  .describe('Parsed YAML frontmatter (if present in markdown file)'),
101
117
  frontmatterError: z.string().optional()
@@ -106,6 +122,6 @@ export const ResourceMetadataSchema = z.object({
106
122
  checksum: SHA256Schema.describe('SHA-256 checksum of file content'),
107
123
  collections: z.array(z.string()).optional()
108
124
  .describe('Collection names this resource belongs to (populated when using config-based discovery)'),
109
- }).describe('Complete metadata for a markdown resource');
125
+ }).strict().describe('Complete metadata for a markdown resource');
110
126
 
111
127
  export type ResourceMetadata = z.infer<typeof ResourceMetadataSchema>;
@@ -1,32 +1,7 @@
1
+ import { ValidationIssueSchema } from '@vibe-agent-toolkit/agent-schema';
1
2
  import { z } from 'zod';
2
3
 
3
- /**
4
- * A single validation issue found during link validation.
5
- *
6
- * Issue types:
7
- * - broken_file: Local file link points to non-existent file
8
- * - broken_anchor: Anchor link points to non-existent heading
9
- * - frontmatter_missing: Schema requires frontmatter, file has none
10
- * - frontmatter_invalid_yaml: YAML syntax error in frontmatter
11
- * - frontmatter_schema_error: Frontmatter fails JSON Schema validation
12
- * - external_url_dead: External URL returned error status (4xx, 5xx)
13
- * - external_url_timeout: External URL request timed out
14
- * - external_url_error: External URL validation failed (DNS, network, etc.)
15
- * - unknown_link: Unknown link type
16
- *
17
- * Includes details about what went wrong, where it occurred, and optionally
18
- * how to fix it.
19
- */
20
- export const ValidationIssueSchema = z.object({
21
- resourcePath: z.string().describe('Absolute path to the resource containing the issue'),
22
- line: z.number().int().positive().optional().describe('Line number where the issue occurs'),
23
- type: z.string().describe('Issue type identifier (e.g., "broken_file", "broken_anchor", "frontmatter_schema_error", "unknown_link")'),
24
- link: z.string().describe('The problematic link'),
25
- message: z.string().describe('Human-readable description of the issue'),
26
- suggestion: z.string().optional().describe('Optional suggestion for fixing the issue'),
27
- }).describe('A single validation issue found during link validation');
28
-
29
- export type ValidationIssue = z.infer<typeof ValidationIssueSchema>;
4
+ export { type ValidationIssue, ValidationIssueSchema } from '@vibe-agent-toolkit/agent-schema';
30
5
 
31
6
  /**
32
7
  * Complete results from validating a collection of resources.
@@ -38,9 +13,10 @@ export const ValidationResultSchema = z.object({
38
13
  totalResources: z.number().int().nonnegative().describe('Total number of resources validated'),
39
14
  totalLinks: z.number().int().nonnegative().describe('Total number of links found across all resources'),
40
15
  linksByType: z.record(z.string(), z.number().int().nonnegative()).describe('Count of links by type (e.g., {"local_file": 10, "external": 5})'),
41
- issues: z.array(ValidationIssueSchema).describe('All validation issues found'),
42
- errorCount: z.number().int().nonnegative().describe('Number of issues found'),
16
+ issues: z.array(ValidationIssueSchema).describe('Emitted validation issues (allow-filtered + severity-resolved; ignored issues dropped)'),
17
+ errorCount: z.number().int().nonnegative().describe('Number of emitted issues'),
43
18
  passed: z.boolean().describe('True if validation succeeded (errorCount === 0)'),
19
+ hasErrors: z.boolean().describe('True when any emitted issue has resolved severity "error"'),
44
20
  durationMs: z.number().nonnegative().describe('Validation duration in milliseconds'),
45
21
  timestamp: z.date().describe('When validation was performed'),
46
22
  }).describe('Complete results from validating a collection of resources');
@@ -6,8 +6,9 @@
6
6
  * with optional absolutePath for runtime operations.
7
7
  */
8
8
 
9
+ import type { ValidationIssue } from '@vibe-agent-toolkit/agent-schema';
10
+
9
11
  import type { SHA256 } from '../schemas/checksum.js';
10
- import type { ValidationIssue } from '../schemas/validation-result.js';
11
12
 
12
13
  /**
13
14
  * Resource type discriminator for type-safe handling
package/src/utils.ts CHANGED
@@ -9,6 +9,21 @@ import path from 'node:path';
9
9
  import { toForwardSlash, safePath } from '@vibe-agent-toolkit/utils';
10
10
  import picomatch from 'picomatch';
11
11
 
12
+ /**
13
+ * Compute a `ValidationIssue.location`: the (absolute) source file path made
14
+ * relative to the project root. When no project root is known, fall back to the
15
+ * source path forward-slashed so the location is still useful and stable.
16
+ *
17
+ * @param sourceFilePath - Absolute path to the file the issue was found in.
18
+ * @param projectRoot - Project root, or undefined when none could be determined.
19
+ * @returns Forward-slashed relative location (or the forward-slashed absolute path).
20
+ */
21
+ export function issueLocation(sourceFilePath: string, projectRoot: string | undefined): string {
22
+ return projectRoot === undefined
23
+ ? toForwardSlash(sourceFilePath)
24
+ : safePath.relative(projectRoot, sourceFilePath);
25
+ }
26
+
12
27
  /**
13
28
  * Check if a file path matches a glob pattern.
14
29
  *