@agent-scope/tokens 1.0.1

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.
@@ -0,0 +1,585 @@
1
+ /**
2
+ * @agent-scope/tokens — Color utilities
3
+ *
4
+ * Converts hex colors to CIE Lab color space and computes perceptual distance.
5
+ * Uses a Euclidean distance in Lab space as an approximation of CIEDE2000.
6
+ *
7
+ * @packageDocumentation
8
+ */
9
+ /**
10
+ * Converts a hex color string to CIE Lab [L, a, b].
11
+ */
12
+ declare function hexToLab(hex: string): [number, number, number];
13
+ /**
14
+ * Euclidean distance in CIE Lab space.
15
+ * Approximates perceptual color difference (simplified CIEDE2000).
16
+ */
17
+ declare function labDistance(a: [number, number, number], b: [number, number, number]): number;
18
+ /**
19
+ * Parses a color string to CIE Lab.
20
+ * Supports #RGB, #RRGGBB, #RRGGBBAA formats.
21
+ * Returns null if the string is not a recognisable hex color.
22
+ */
23
+ declare function parseColorToLab(color: string): [number, number, number] | null;
24
+
25
+ /**
26
+ * @agent-scope/tokens — Type definitions
27
+ *
28
+ * Core types for the design token file parser and resolution engine.
29
+ *
30
+ * @packageDocumentation
31
+ */
32
+ type TokenType = "color" | "dimension" | "fontFamily" | "fontWeight" | "number" | "shadow" | "duration" | "cubicBezier";
33
+ declare const TOKEN_TYPES: ReadonlyArray<TokenType>;
34
+ interface TokenValue {
35
+ value: string | number;
36
+ type: TokenType;
37
+ description?: string;
38
+ }
39
+ interface Token {
40
+ /** Dot-notation path, e.g. "color.primary.500" */
41
+ path: string;
42
+ /** Raw value from the token file (may be a reference like "{color.primary.500}") */
43
+ value: string | number;
44
+ /** Fully resolved value, always a string */
45
+ resolvedValue: string;
46
+ type: TokenType;
47
+ description?: string;
48
+ }
49
+ interface TokenFileMeta {
50
+ name?: string;
51
+ lastUpdated?: string;
52
+ updatedBy?: string;
53
+ }
54
+ interface TokenFile {
55
+ $schema?: string;
56
+ version: string;
57
+ meta?: TokenFileMeta;
58
+ tokens: Record<string, unknown>;
59
+ themes?: Record<string, Record<string, string>>;
60
+ }
61
+ interface TokenMatch {
62
+ token: Token;
63
+ /** True when the resolved value exactly matches the queried value */
64
+ exact: boolean;
65
+ /** 0 for exact matches; otherwise the computed distance (Lab distance / numeric diff) */
66
+ distance: number;
67
+ }
68
+ interface ParsedTokens {
69
+ tokens: Token[];
70
+ rawFile: TokenFile;
71
+ }
72
+ type TokenParseErrorCode = "CIRCULAR_REFERENCE" | "INVALID_REFERENCE" | "INVALID_SCHEMA" | "PARSE_ERROR";
73
+ declare class TokenParseError extends Error {
74
+ readonly code: TokenParseErrorCode;
75
+ readonly path: string | undefined;
76
+ constructor(message: string, code: TokenParseErrorCode, path?: string);
77
+ }
78
+ interface ValidationError {
79
+ path: string;
80
+ message: string;
81
+ code: string;
82
+ }
83
+ declare class TokenValidationError extends Error {
84
+ readonly errors: ValidationError[];
85
+ constructor(message: string, errors: ValidationError[]);
86
+ }
87
+
88
+ /**
89
+ * @agent-scope/tokens — Token resolution engine
90
+ *
91
+ * Provides path-based lookup, exact and nearest-match search over a resolved
92
+ * token set.
93
+ *
94
+ * @packageDocumentation
95
+ */
96
+
97
+ declare class TokenResolver {
98
+ private readonly tokens;
99
+ private readonly tokenMap;
100
+ constructor(tokens: Token[]);
101
+ /**
102
+ * Resolves a token path to its computed string value.
103
+ * @throws TokenParseError with code INVALID_REFERENCE if path is not found
104
+ */
105
+ resolve(path: string): string;
106
+ /**
107
+ * Returns an exact-match TokenMatch if any token of the given type has a
108
+ * resolvedValue that equals the queried value (case-insensitive for colors).
109
+ * Returns null if no exact match exists.
110
+ */
111
+ match(value: string, type: TokenType): TokenMatch | null;
112
+ /**
113
+ * Returns the TokenMatch with the smallest computed distance to `value`
114
+ * among all tokens of the given type.
115
+ *
116
+ * Distance computation:
117
+ * - color: Euclidean distance in CIE Lab space
118
+ * - dimension / duration: |parsed numeric value difference|
119
+ * - fontWeight / number: |numeric value difference|
120
+ * - shadow / fontFamily / cubicBezier: string equality (distance 0 or 1)
121
+ *
122
+ * @throws Error if there are no tokens of the specified type
123
+ */
124
+ nearest(value: string, type: TokenType): TokenMatch;
125
+ /**
126
+ * Lists all tokens, optionally filtered by type and/or category
127
+ * (the first segment of the dot-notation path, e.g. "color" in "color.primary.500").
128
+ */
129
+ list(type?: TokenType, category?: string): Token[];
130
+ private computeDistance;
131
+ }
132
+
133
+ /**
134
+ * @agent-scope/tokens — Compliance Engine
135
+ *
136
+ * Audits rendered component CSS styles against a resolved token set.
137
+ * Reports per-property compliance status, nearest-match suggestions, and
138
+ * an aggregate compliance percentage.
139
+ *
140
+ * @packageDocumentation
141
+ */
142
+
143
+ /**
144
+ * Represents the structured CSS output from a rendered component, grouped by
145
+ * property category.
146
+ */
147
+ type ComputedStyles = {
148
+ /** Colour properties — values must be hex (#RGB / #RRGGBB / #RRGGBBAA) for
149
+ * Lab-distance matching; other formats fall back to exact string match. */
150
+ colors: Record<string, string>;
151
+ /** Spacing / layout properties — e.g. paddingTop, marginBottom, gap */
152
+ spacing: Record<string, string>;
153
+ /** Typography properties — fontFamily, fontSize, fontWeight, lineHeight */
154
+ typography: Record<string, string>;
155
+ /** Border properties — borderRadius, borderWidth */
156
+ borders: Record<string, string>;
157
+ /** Shadow properties — boxShadow */
158
+ shadows: Record<string, string>;
159
+ };
160
+ /** Status for a single audited property. */
161
+ type ComplianceStatus = "on_system" | "OFF_SYSTEM";
162
+ /** Per-property audit result. */
163
+ interface PropertyResult {
164
+ /** CSS property name, e.g. "background" */
165
+ property: string;
166
+ /** The value that was audited */
167
+ value: string;
168
+ /** Whether the value maps to a design token (within tolerance) */
169
+ status: ComplianceStatus;
170
+ /** Token path of the matched token (only when status is "on_system") */
171
+ token?: string;
172
+ /** Nearest token found in the system — always present when a candidate
173
+ * type had at least one token. */
174
+ nearest?: {
175
+ token: string;
176
+ value: string;
177
+ distance: number;
178
+ };
179
+ }
180
+ /** Full audit result for a single component / style set. */
181
+ interface ComplianceReport {
182
+ /** Map of property name → result */
183
+ properties: Record<string, PropertyResult>;
184
+ /** Number of properties audited (excludes skipped/unsupported) */
185
+ total: number;
186
+ /** Number of properties that are on-system */
187
+ onSystem: number;
188
+ /** Number of properties that are off-system */
189
+ offSystem: number;
190
+ /** on-system count / total; 1 when total === 0 */
191
+ compliance: number;
192
+ /** ISO timestamp of when the audit was run */
193
+ auditedAt: string;
194
+ }
195
+ /** Batch audit report — summary across multiple components. */
196
+ interface BatchReport {
197
+ /** Per-component compliance report, keyed by component name */
198
+ components: Record<string, ComplianceReport>;
199
+ /** Total properties audited across all components */
200
+ totalProperties: number;
201
+ /** Total on-system properties across all components */
202
+ totalOnSystem: number;
203
+ /** Total off-system properties across all components */
204
+ totalOffSystem: number;
205
+ /** Aggregate compliance percentage across all components */
206
+ aggregateCompliance: number;
207
+ /** ISO timestamp of when the batch audit was run */
208
+ auditedAt: string;
209
+ }
210
+ /** Configures fuzzy-matching thresholds per property category. */
211
+ interface ComplianceTolerances {
212
+ /**
213
+ * Maximum CIE Lab Euclidean distance for a colour to be considered
214
+ * on-system. Default: 3.
215
+ */
216
+ colorTolerance: number;
217
+ /**
218
+ * Maximum pixel difference for a dimension (spacing / border / font-size)
219
+ * to be considered on-system. Default: 2.
220
+ */
221
+ dimensionTolerance: number;
222
+ /**
223
+ * Maximum font-weight numeric difference to be considered on-system.
224
+ * Default: 0 (exact only).
225
+ */
226
+ fontWeightTolerance: number;
227
+ }
228
+ /**
229
+ * Audits rendered component styles against a resolved token set.
230
+ *
231
+ * @example
232
+ * ```ts
233
+ * const { tokens } = parseTokenFileSync(fs.readFileSync("tokens.json", "utf8"));
234
+ * const resolver = new TokenResolver(tokens);
235
+ * const engine = new ComplianceEngine(resolver);
236
+ *
237
+ * const report = engine.audit({
238
+ * colors: { background: "#3B82F6", color: "#ffffff" },
239
+ * spacing: { paddingTop: "16px", gap: "8px" },
240
+ * typography: { fontFamily: "Inter", fontSize: "14px" },
241
+ * borders: { borderRadius: "4px" },
242
+ * shadows: { boxShadow: "0 1px 3px rgba(0,0,0,0.1)" },
243
+ * });
244
+ *
245
+ * console.log(report.compliance); // e.g. 0.83
246
+ * ```
247
+ */
248
+ declare class ComplianceEngine {
249
+ private readonly resolver;
250
+ private readonly tolerances;
251
+ constructor(resolver: TokenResolver, tolerances?: Partial<ComplianceTolerances>);
252
+ /**
253
+ * Audits a single component's computed styles against the token set.
254
+ */
255
+ audit(styles: ComputedStyles): ComplianceReport;
256
+ /**
257
+ * Audits multiple components at once and returns a BatchReport with
258
+ * per-component scores and an aggregate compliance figure.
259
+ */
260
+ auditBatch(components: Map<string, ComputedStyles>): BatchReport;
261
+ /**
262
+ * Serialises a ComplianceReport to a JSON string.
263
+ */
264
+ static toJSON(report: ComplianceReport | BatchReport): string;
265
+ /**
266
+ * Determines the TokenType for a typography property.
267
+ * lineHeight is dimension if it has a unit, otherwise "number".
268
+ */
269
+ private resolveTypographyType;
270
+ /**
271
+ * Audits a single CSS property value against a specific token type.
272
+ * Returns null if the resolver has no tokens of that type (nothing to
273
+ * audit against — property excluded from totals).
274
+ */
275
+ private auditProperty;
276
+ private getToleranceForType;
277
+ private buildReport;
278
+ }
279
+
280
+ /**
281
+ * @agent-scope/tokens — Export Formats
282
+ *
283
+ * Converts a resolved token set into various output formats for downstream
284
+ * tooling consumption.
285
+ *
286
+ * Supported formats:
287
+ * - `css` — CSS custom properties (:root { --color-primary-500: … })
288
+ * - `ts` — TypeScript const exports (export const colorPrimary500 = …)
289
+ * - `scss` — SCSS variable declarations ($color-primary-500: …)
290
+ * - `tailwind` — Tailwind CSS theme.extend object
291
+ * - `figma` — Figma Tokens JSON format
292
+ *
293
+ * All formats support theme-aware exports via the `themes` option.
294
+ *
295
+ * @packageDocumentation
296
+ */
297
+
298
+ /** Supported output format identifiers. */
299
+ type ExportFormat = "css" | "ts" | "scss" | "tailwind" | "figma";
300
+ /** Options shared across all export formats. */
301
+ interface ExportOptions {
302
+ /**
303
+ * Theme-aware export: map of theme name → Map<tokenPath, resolvedValue>.
304
+ *
305
+ * When present:
306
+ * - CSS: emits `[data-theme="<name>"] { … }` selector blocks
307
+ * - TS: emits a `themes` record alongside the default constants
308
+ * - SCSS: emits `[data-theme="<name>"] { … }` blocks using custom props
309
+ * - tailwind/figma: includes per-theme variants in the output
310
+ */
311
+ themes?: Map<string, Map<string, string>>;
312
+ /**
313
+ * CSS / SCSS: prefix for custom property / variable names.
314
+ * Default: no prefix (plain dot-path conversion, e.g. `color-primary-500`)
315
+ */
316
+ prefix?: string;
317
+ /**
318
+ * CSS: selector for the root block.
319
+ * Default: `:root`
320
+ */
321
+ rootSelector?: string;
322
+ }
323
+ /**
324
+ * Exports a resolved token set to the specified format.
325
+ *
326
+ * @param tokens - Flat, resolved token array (from TokenResolver or parseTokenFileSync)
327
+ * @param format - Target output format
328
+ * @param options - Optional configuration (prefix, themes, rootSelector)
329
+ * @returns The formatted output string
330
+ *
331
+ * @example
332
+ * ```ts
333
+ * const { tokens } = parseTokenFileSync(source);
334
+ * const css = exportTokens(tokens, "css");
335
+ * const ts = exportTokens(tokens, "ts");
336
+ * ```
337
+ */
338
+ declare function exportTokens(tokens: Token[], format: ExportFormat, options?: ExportOptions): string;
339
+
340
+ /**
341
+ * @agent-scope/tokens — Impact Analysis Engine
342
+ *
343
+ * Analyses the downstream effects of a design token change on a set of
344
+ * audited components. Given a token path and a proposed new value, it
345
+ * returns which components and properties would be affected, together with
346
+ * a perceptual severity estimate for colour tokens.
347
+ *
348
+ * @packageDocumentation
349
+ */
350
+
351
+ /** Severity of the visual change introduced by the token edit. */
352
+ type VisualSeverity = "none" | "subtle" | "moderate" | "significant";
353
+ /** Details of which properties within one component reference the token. */
354
+ interface AffectedComponent {
355
+ /** Component name (key used in the compliance batch report) */
356
+ name: string;
357
+ /** List of CSS property names that reference the changed token */
358
+ affectedProperties: string[];
359
+ /**
360
+ * Perceptual severity of the change for this component.
361
+ * Derived from the colour Lab distance or property-type heuristics.
362
+ */
363
+ severity: VisualSeverity;
364
+ }
365
+ /** Full impact report for a single token change. */
366
+ interface ImpactReport {
367
+ /** The token path that was changed */
368
+ tokenPath: string;
369
+ /** The previous resolved value */
370
+ oldValue: string;
371
+ /** The proposed new value */
372
+ newValue: string;
373
+ /** Type of the token */
374
+ tokenType: TokenType;
375
+ /** Number of components that reference this token */
376
+ affectedComponentCount: number;
377
+ /** Per-component breakdown */
378
+ components: AffectedComponent[];
379
+ /**
380
+ * Overall visual severity — the maximum severity across all components.
381
+ * "none" when no components are affected.
382
+ */
383
+ overallSeverity: VisualSeverity;
384
+ /** For colour tokens: CIE Lab Euclidean distance between old and new values */
385
+ colorDelta?: number;
386
+ }
387
+ /**
388
+ * Analyses design-token changes and reports which components / properties
389
+ * would be visually affected.
390
+ *
391
+ * @example
392
+ * ```ts
393
+ * const analyzer = new ImpactAnalyzer(resolver, complianceReports);
394
+ * const report = analyzer.impactOf("color.primary.500", "#2563EB");
395
+ * console.log(report.affectedComponentCount); // e.g. 4
396
+ * ```
397
+ */
398
+ declare class ImpactAnalyzer {
399
+ private readonly resolver;
400
+ private readonly complianceReports;
401
+ /**
402
+ * @param resolver - The token resolver for the current token set
403
+ * @param complianceReports - Map of component name → ComplianceReport
404
+ * (typically from ComplianceEngine.auditBatch)
405
+ */
406
+ constructor(resolver: TokenResolver, complianceReports: Map<string, ComplianceReport>);
407
+ /**
408
+ * Returns an ImpactReport describing the consequences of changing a token.
409
+ *
410
+ * @param tokenPath - Dot-notation path of the token to change
411
+ * @param newValue - The proposed new resolved value for the token
412
+ * @throws {Error} if the token does not exist in the resolver
413
+ */
414
+ impactOf(tokenPath: string, newValue: string): ImpactReport;
415
+ }
416
+
417
+ /**
418
+ * @agent-scope/tokens — Token file parser
419
+ *
420
+ * Parses reactscope.tokens.json or .yaml files into a flat, resolved Token[].
421
+ *
422
+ * Features:
423
+ * - JSON and YAML support (auto-detected)
424
+ * - Schema validation via validateTokenFile()
425
+ * - Reference resolution: {path.to.token} → resolved value
426
+ * - Circular reference detection via DFS with visited + inStack sets
427
+ *
428
+ * @packageDocumentation
429
+ */
430
+
431
+ /**
432
+ * Parses a token file (JSON or YAML) into a flat, resolved token array.
433
+ *
434
+ * @param input - The raw file contents as a string
435
+ * @param format - Force "json" or "yaml". If omitted, JSON is tried first,
436
+ * then YAML as a fallback.
437
+ * @returns ParsedTokens — flat Token[] and the validated raw file
438
+ * @throws TokenParseError on reference errors or circular references
439
+ * @throws TokenValidationError on schema violations
440
+ */
441
+ declare function parseTokenFile(input: string, format?: "json" | "yaml"): Promise<ParsedTokens>;
442
+ /**
443
+ * Synchronous version of parseTokenFile. Only supports JSON format
444
+ * (YAML requires an async import). Throws if a YAML file is passed.
445
+ */
446
+ declare function parseTokenFileSync(input: string): ParsedTokens;
447
+
448
+ /**
449
+ * @agent-scope/tokens — Theme Support
450
+ *
451
+ * Extends the base token resolution with named theme overlays.
452
+ *
453
+ * Token files can declare themes in one of two formats:
454
+ *
455
+ * (a) Flat override map (original format, as validated by validator.ts):
456
+ * ```json
457
+ * { "themes": { "dark": { "color.primary.500": "#60A5FA" } } }
458
+ * ```
459
+ *
460
+ * (b) Nested W3C DTCG-style tree (new structured format per spec):
461
+ * ```json
462
+ * {
463
+ * "base": { "color": { "primary": { "500": { "$value": "#3B82F6" } } } },
464
+ * "themes": {
465
+ * "dark": { "color": { "primary": { "500": { "$value": "#60A5FA" } } } }
466
+ * }
467
+ * }
468
+ * ```
469
+ *
470
+ * Theme resolution inherits all base tokens and only overrides specified paths.
471
+ *
472
+ * @packageDocumentation
473
+ */
474
+
475
+ /**
476
+ * Extended token file format that supports named theme overlays.
477
+ * Compatible with both flat-override and nested-tree theme declarations.
478
+ */
479
+ interface ThemedTokenFile {
480
+ $schema?: string;
481
+ version: string;
482
+ /** Base token tree — the default set every theme inherits from */
483
+ base?: Record<string, unknown>;
484
+ /** Standard `tokens` field (used when `base` is absent) */
485
+ tokens?: Record<string, unknown>;
486
+ /** Named theme overlays — each key is a theme name */
487
+ themes?: Record<string, unknown>;
488
+ [key: string]: unknown;
489
+ }
490
+ /**
491
+ * Extends TokenResolver with theme-aware resolution.
492
+ *
493
+ * Themes are layered on top of the base token set: a themed token resolver
494
+ * uses the same resolved values as the base resolver, but substitutes
495
+ * override values for any paths declared in the theme.
496
+ *
497
+ * @example
498
+ * ```ts
499
+ * const { tokens } = parseTokenFileSync(JSON.stringify(tokenFile));
500
+ * const resolver = new TokenResolver(tokens);
501
+ * const themeResolver = ThemeResolver.fromTokenFile(resolver, tokenFile);
502
+ *
503
+ * themeResolver.resolveThemed("color.primary.500", "dark"); // "#60A5FA"
504
+ * themeResolver.listThemes(); // ["dark", "brand-b"]
505
+ * themeResolver.resolveAllThemes("color.primary.500");
506
+ * // => { base: "#3B82F6", dark: "#60A5FA", "brand-b": "#8B5CF6" }
507
+ * ```
508
+ */
509
+ declare class ThemeResolver {
510
+ private readonly baseResolver;
511
+ /** Parsed theme overrides: Map<themeName, Map<tokenPath, resolvedValue>> */
512
+ private readonly themes;
513
+ constructor(baseResolver: TokenResolver, themes: Map<string, Map<string, string>>);
514
+ /**
515
+ * Constructs a ThemeResolver from a TokenResolver and a raw token file object.
516
+ *
517
+ * Supports both the flat-override theme format and the nested DTCG-style
518
+ * format described in the module docstring.
519
+ */
520
+ static fromTokenFile(baseResolver: TokenResolver, rawFile: ThemedTokenFile): ThemeResolver;
521
+ /**
522
+ * Constructs a ThemeResolver from a TokenResolver and a pre-built
523
+ * themes map (for use when building programmatically).
524
+ */
525
+ static fromThemeMap(baseResolver: TokenResolver, themes: Map<string, Map<string, string>>): ThemeResolver;
526
+ /**
527
+ * Resolves a token path to its value in the given theme.
528
+ * Falls back to the base resolved value if the theme does not override
529
+ * the requested path.
530
+ *
531
+ * @throws {Error} if the token path is not found in the base resolver
532
+ * @throws {Error} if the theme name is not registered
533
+ */
534
+ resolveThemed(path: string, themeName: string): string;
535
+ /**
536
+ * Returns the names of all registered themes.
537
+ */
538
+ listThemes(): string[];
539
+ /**
540
+ * Returns the resolved value for the given token path in every theme,
541
+ * including the implicit "base" theme.
542
+ *
543
+ * Useful for parallel rendering comparisons (e.g. side-by-side theme preview).
544
+ *
545
+ * @throws {Error} if the token path is not found in the base resolver
546
+ */
547
+ resolveAllThemes(path: string): Record<string, string>;
548
+ /**
549
+ * Resolves a token path using the base (no theme) values.
550
+ * Delegates to the underlying TokenResolver.
551
+ */
552
+ resolve(path: string): string;
553
+ /**
554
+ * Lists all base tokens, optionally filtered by type and/or category.
555
+ * Delegates to the underlying TokenResolver.
556
+ */
557
+ list(type?: TokenType, category?: string): Token[];
558
+ /**
559
+ * Builds a Token[] for the given theme by merging overrides on top of the
560
+ * base token set.
561
+ *
562
+ * The returned tokens have their `resolvedValue` set to the theme value
563
+ * (or the base value when the theme does not override that path).
564
+ *
565
+ * @throws {Error} if the theme name is not registered
566
+ */
567
+ buildThemedTokens(themeName: string): Token[];
568
+ }
569
+
570
+ /**
571
+ * @agent-scope/tokens — Token file validator
572
+ *
573
+ * Validates a raw parsed JSON/YAML object against the token file schema.
574
+ * Collects all errors before throwing so callers get a complete picture.
575
+ *
576
+ * @packageDocumentation
577
+ */
578
+
579
+ /**
580
+ * Validates a raw parsed object and asserts it matches the `TokenFile` shape.
581
+ * Throws `TokenValidationError` with all collected issues if validation fails.
582
+ */
583
+ declare function validateTokenFile(raw: unknown): asserts raw is TokenFile;
584
+
585
+ export { type AffectedComponent, type BatchReport, ComplianceEngine, type ComplianceReport, type ComplianceStatus, type ComplianceTolerances, type ComputedStyles, type ExportFormat, type ExportOptions, ImpactAnalyzer, type ImpactReport, type ParsedTokens, type PropertyResult, TOKEN_TYPES, ThemeResolver, type ThemedTokenFile, type Token, type TokenFile, type TokenFileMeta, type TokenMatch, TokenParseError, type TokenParseErrorCode, TokenResolver, type TokenType, TokenValidationError, type TokenValue, type ValidationError, type VisualSeverity, exportTokens, hexToLab, labDistance, parseColorToLab, parseTokenFile, parseTokenFileSync, validateTokenFile };