@ttoss/fsl-theme 1.1.13 → 1.1.15

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 (91) hide show
  1. package/dist/Types-BiBa17RL.d.cts +1427 -0
  2. package/dist/Types-BiBa17RL.d.mts +1427 -0
  3. package/dist/baseBundle-DxvXyhGa.mjs +17 -0
  4. package/dist/baseBundle-iEFf5nqT.cjs +22 -0
  5. package/dist/{esm/chunk-SE5Z52RE.js → createTheme-BLNYztZU.mjs} +76 -172
  6. package/dist/createTheme-Cv6RP9D6.cjs +1825 -0
  7. package/dist/css.cjs +48 -0
  8. package/dist/{css.d.ts → css.d.cts} +67 -63
  9. package/dist/css.d.mts +168 -0
  10. package/dist/css.mjs +42 -0
  11. package/dist/dataviz/index.cjs +45 -0
  12. package/dist/dataviz/{index.d.ts → index.d.cts} +9 -5
  13. package/dist/dataviz/index.d.mts +66 -0
  14. package/dist/dataviz/index.mjs +39 -0
  15. package/dist/dtcg.cjs +115 -0
  16. package/dist/{dtcg.d.ts → dtcg.d.cts} +9 -7
  17. package/dist/dtcg.d.mts +51 -0
  18. package/dist/dtcg.mjs +112 -0
  19. package/dist/helpers-4p4-QVt_.cjs +258 -0
  20. package/dist/helpers-CaswNJMy.mjs +211 -0
  21. package/dist/{index.d.ts → index-CsIjfw86.d.cts} +42 -34
  22. package/dist/index-nJrjI0BA.d.mts +94 -0
  23. package/dist/index.cjs +16 -0
  24. package/dist/index.d.cts +6 -0
  25. package/dist/index.d.mts +6 -0
  26. package/dist/index.mjs +7 -0
  27. package/dist/{react.d.ts → react-CGa6FlNL.d.cts} +130 -106
  28. package/dist/react-DnKxR2gK.d.mts +370 -0
  29. package/dist/react-EUwpdvY7.cjs +481 -0
  30. package/dist/react.cjs +12 -0
  31. package/dist/react.d.cts +4 -0
  32. package/dist/react.d.mts +4 -0
  33. package/dist/react.mjs +412 -0
  34. package/dist/runtime-entry.cjs +9 -0
  35. package/dist/runtime-entry.d.cts +3 -0
  36. package/dist/runtime-entry.d.mts +3 -0
  37. package/dist/runtime-entry.mjs +3 -0
  38. package/dist/{runtime-entry.d.ts → ssrScript-BVysxDws.d.cts} +26 -23
  39. package/dist/ssrScript-BVysxDws.d.mts +98 -0
  40. package/dist/ssrScript-CRfrN8Pm.cjs +202 -0
  41. package/dist/ssrScript-D3kGPQpi.mjs +179 -0
  42. package/dist/themes/bruttal.cjs +75 -0
  43. package/dist/themes/bruttal.d.cts +3 -0
  44. package/dist/themes/bruttal.d.mts +3 -0
  45. package/dist/themes/bruttal.mjs +72 -0
  46. package/dist/themes/corporate.cjs +34 -0
  47. package/dist/themes/corporate.d.cts +3 -0
  48. package/dist/themes/corporate.d.mts +3 -0
  49. package/dist/{esm/chunk-TPMN75JM.js → themes/corporate.mjs} +7 -5
  50. package/dist/themes/oca.cjs +34 -0
  51. package/dist/themes/oca.d.cts +3 -0
  52. package/dist/themes/oca.d.mts +3 -0
  53. package/dist/{esm/chunk-DU4QDQUC.js → themes/oca.mjs} +7 -5
  54. package/dist/themes/ventures.cjs +34 -0
  55. package/dist/themes/ventures.d.cts +3 -0
  56. package/dist/themes/ventures.d.mts +3 -0
  57. package/dist/{esm/chunk-BXKVVQEP.js → themes/ventures.mjs} +7 -5
  58. package/dist/toCssVars-CYZCe-on.mjs +286 -0
  59. package/dist/toCssVars-DudHKvt2.cjs +297 -0
  60. package/dist/{esm/chunk-4Q4P3JBB.js → tokenRegistry-DjgSN3oU.mjs} +23 -20
  61. package/dist/tokenRegistry-OhaJ9sPJ.cjs +199 -0
  62. package/dist/vars.cjs +127 -0
  63. package/dist/{vars.d.ts → vars.d.cts} +8 -7
  64. package/dist/vars.d.mts +128 -0
  65. package/dist/vars.mjs +123 -0
  66. package/dist/withDataviz-B4pVsOwV.cjs +192 -0
  67. package/dist/{esm/chunk-FBVUI2PK.js → withDataviz-DY5s7R51.mjs} +40 -12
  68. package/package.json +20 -20
  69. package/dist/Types-6tR0_2Ss.d.ts +0 -1452
  70. package/dist/esm/chunk-5PWPAQMC.js +0 -9
  71. package/dist/esm/chunk-HRNXVRS3.js +0 -54
  72. package/dist/esm/chunk-IJGA42O6.js +0 -141
  73. package/dist/esm/chunk-PQPQNZ73.js +0 -262
  74. package/dist/esm/chunk-UMRQ4OTX.js +0 -11
  75. package/dist/esm/chunk-VL6EGE6Z.js +0 -222
  76. package/dist/esm/chunk-WVQSTQD5.js +0 -192
  77. package/dist/esm/css.js +0 -6
  78. package/dist/esm/dataviz/index.js +0 -19
  79. package/dist/esm/dtcg.js +0 -65
  80. package/dist/esm/index.js +0 -10
  81. package/dist/esm/react.js +0 -8
  82. package/dist/esm/runtime-entry.js +0 -4
  83. package/dist/esm/themes/bruttal.js +0 -6
  84. package/dist/esm/themes/corporate.js +0 -6
  85. package/dist/esm/themes/oca.js +0 -6
  86. package/dist/esm/themes/ventures.js +0 -6
  87. package/dist/esm/vars.js +0 -28
  88. package/dist/themes/bruttal.d.ts +0 -5
  89. package/dist/themes/corporate.d.ts +0 -5
  90. package/dist/themes/oca.d.ts +0 -5
  91. package/dist/themes/ventures.d.ts +0 -5
package/dist/dtcg.cjs ADDED
@@ -0,0 +1,115 @@
1
+ /** Powered by @ttoss/config. https://ttoss.dev/docs/modules/packages/config/ */
2
+ Object.defineProperty(exports, Symbol.toStringTag, {
3
+ value: 'Module'
4
+ });
5
+ const require_helpers = require('./helpers-4p4-QVt_.cjs');
6
+ const require_tokenRegistry = require('./tokenRegistry-OhaJ9sPJ.cjs');
7
+
8
+ //#region src/roots/toDTCG.ts
9
+ /**
10
+ * Suffix-level overrides applied after prefix lookup.
11
+ *
12
+ * The token registry uses a single prefix per semantic subtree (e.g.
13
+ * `semantic.motion.`, `semantic.text.`) which cannot express per-property
14
+ * DTCG types for composite token shapes (TextStyle, SemanticMotionEntry).
15
+ * These overrides catch every case where the leaf property name alone
16
+ * carries the required type — regardless of the token family it belongs to.
17
+ *
18
+ * Rule: a suffix override takes precedence over the prefix-based $type.
19
+ *
20
+ * Typography leaves (TextStyle) that need non-string $types:
21
+ * - `.fontFamily` → 'fontFamily' (DTCG named type for font stacks)
22
+ * - `.fontSize` → 'dimension' (clamp() / px value)
23
+ * - `.fontWeight` → 'fontWeight' (numeric weight: 400, 500, 700 …)
24
+ * - `.lineHeight` → 'number' (unitless multiplier: 1.15, 1.5 …)
25
+ * - `.letterSpacing` → 'dimension' (em value: '-0.02em', '0.04em' …)
26
+ *
27
+ * Motion leaves (SemanticMotionEntry) overridden separately:
28
+ * - `.duration` → 'duration'
29
+ */
30
+ const SUFFIX_TYPE_OVERRIDES = [[".duration", "duration"], [".fontFamily", "fontFamily"], [".fontSize", "dimension"], [".fontWeight", "fontWeight"], [".lineHeight", "number"], [".letterSpacing", "dimension"], [".ring.color", "color"]];
31
+ const inferType = path => {
32
+ for (const [suffix, type] of SUFFIX_TYPE_OVERRIDES) if (path.endsWith(suffix)) return type;
33
+ for (const [prefix, type] of require_tokenRegistry.DTCG_TYPE_PREFIXES) if (path.startsWith(prefix)) return type;
34
+ return "string";
35
+ };
36
+ /**
37
+ * Set a value at a dot-path in a nested object, creating intermediate
38
+ * objects as needed.
39
+ */
40
+ const setNestedValue = (root, path, value) => {
41
+ const segments = path.split(".");
42
+ let current = root;
43
+ for (let i = 0; i < segments.length - 1; i++) {
44
+ const seg = segments[i];
45
+ if (!(seg in current) || typeof current[seg] !== "object") current[seg] = {};
46
+ current = current[seg];
47
+ }
48
+ current[segments[segments.length - 1]] = value;
49
+ };
50
+ /** Semantic hit token prefix that carries coarse-pointer overrides. */
51
+ const SEMANTIC_HIT_PREFIX = "semantic.sizing.hit.";
52
+ /**
53
+ * Build the `$extensions` metadata for a semantic hit token.
54
+ *
55
+ * Each `semantic.sizing.hit.{step}` token defaults to the fine-pointer value.
56
+ * The extension declares the corresponding coarse-pointer raw value so that
57
+ * non-CSS consumers (React Native, design tool pipelines) can locate and
58
+ * apply touch-target overrides without reading `toCssVars` source code.
59
+ */
60
+ const buildHitExtension = (step, theme) => {
61
+ const coarseValue = theme.core.sizing.hit.coarse[step];
62
+ if (coarseValue === void 0) return void 0;
63
+ return {
64
+ "com.ttoss.pointer-override": {
65
+ condition: "any-pointer: coarse",
66
+ value: coarseValue
67
+ }
68
+ };
69
+ };
70
+ /**
71
+ * Root 3 — W3C Design Tokens (DTCG JSON).
72
+ *
73
+ * Convert a `ThemeTokens` into a structured token tree following the
74
+ * W3C Design Tokens Community Group format. Every leaf node has `$value`
75
+ * (fully resolved) and `$type` (inferred from the token path).
76
+ *
77
+ * Semantic hit tokens (`semantic.sizing.hit.*`) include a `$extensions`
78
+ * field declaring their coarse-pointer override value, so non-CSS consumers
79
+ * can apply touch-target ergonomics without reading the CSS emitter source.
80
+ *
81
+ * This is the interchange format for design tools (Tokens Studio, Figma,
82
+ * Style Dictionary, Specify, Supernova) and CI/CD token pipelines.
83
+ *
84
+ * @example
85
+ * ```ts
86
+ * import { toDTCG } from '@ttoss/fsl-theme/dtcg';
87
+ * import { createTheme } from '@ttoss/fsl-theme';
88
+ *
89
+ * const myBundle = createTheme();
90
+ * const tokens = toDTCG(myBundle.base);
91
+ * // tokens.core.colors.brand['500'] === { $value: '#0469E3', $type: 'color' }
92
+ *
93
+ * // Write to file (build script / token pipeline)
94
+ * await fs.writeFile('tokens.json', JSON.stringify(tokens, null, 2));
95
+ * ```
96
+ */
97
+ const toDTCG = theme => {
98
+ const flat = require_helpers.toFlatTokens(theme);
99
+ const tree = {};
100
+ for (const [path, value] of Object.entries(flat)) {
101
+ const token = {
102
+ $value: value,
103
+ $type: inferType(path)
104
+ };
105
+ if (path.startsWith(SEMANTIC_HIT_PREFIX)) {
106
+ const ext = buildHitExtension(path.slice(20), theme);
107
+ if (ext) token.$extensions = ext;
108
+ }
109
+ setNestedValue(tree, path, token);
110
+ }
111
+ return tree;
112
+ };
113
+
114
+ //#endregion
115
+ exports.toDTCG = toDTCG;
@@ -1,5 +1,7 @@
1
- import { T as ThemeTokens } from './Types-6tR0_2Ss.js';
2
1
 
2
+ import { i as ThemeTokens } from "./Types-BiBa17RL.cjs";
3
+
4
+ //#region src/roots/toDTCG.d.ts
3
5
  /**
4
6
  * A single DTCG token node with `$value` and `$type`.
5
7
  *
@@ -7,15 +9,15 @@ import { T as ThemeTokens } from './Types-6tR0_2Ss.js';
7
9
  * https://design-tokens.github.io/community-group/format/
8
10
  */
9
11
  interface DTCGToken {
10
- $value: string | number;
11
- $type: string;
12
- $extensions?: Record<string, unknown>;
12
+ $value: string | number;
13
+ $type: string;
14
+ $extensions?: Record<string, unknown>;
13
15
  }
14
16
  /**
15
17
  * A recursive tree where every leaf is a `DTCGToken`.
16
18
  */
17
19
  type DTCGTokenTree = DTCGToken | {
18
- [key: string]: DTCGTokenTree;
20
+ [key: string]: DTCGTokenTree;
19
21
  };
20
22
  /**
21
23
  * Root 3 — W3C Design Tokens (DTCG JSON).
@@ -45,5 +47,5 @@ type DTCGTokenTree = DTCGToken | {
45
47
  * ```
46
48
  */
47
49
  declare const toDTCG: (theme: ThemeTokens) => DTCGTokenTree;
48
-
49
- export { type DTCGToken, type DTCGTokenTree, toDTCG };
50
+ //#endregion
51
+ export { type DTCGToken, type DTCGTokenTree, toDTCG };
@@ -0,0 +1,51 @@
1
+
2
+ import { i as ThemeTokens } from "./Types-BiBa17RL.mjs";
3
+
4
+ //#region src/roots/toDTCG.d.ts
5
+ /**
6
+ * A single DTCG token node with `$value` and `$type`.
7
+ *
8
+ * Follows the W3C Design Tokens Community Group format:
9
+ * https://design-tokens.github.io/community-group/format/
10
+ */
11
+ interface DTCGToken {
12
+ $value: string | number;
13
+ $type: string;
14
+ $extensions?: Record<string, unknown>;
15
+ }
16
+ /**
17
+ * A recursive tree where every leaf is a `DTCGToken`.
18
+ */
19
+ type DTCGTokenTree = DTCGToken | {
20
+ [key: string]: DTCGTokenTree;
21
+ };
22
+ /**
23
+ * Root 3 — W3C Design Tokens (DTCG JSON).
24
+ *
25
+ * Convert a `ThemeTokens` into a structured token tree following the
26
+ * W3C Design Tokens Community Group format. Every leaf node has `$value`
27
+ * (fully resolved) and `$type` (inferred from the token path).
28
+ *
29
+ * Semantic hit tokens (`semantic.sizing.hit.*`) include a `$extensions`
30
+ * field declaring their coarse-pointer override value, so non-CSS consumers
31
+ * can apply touch-target ergonomics without reading the CSS emitter source.
32
+ *
33
+ * This is the interchange format for design tools (Tokens Studio, Figma,
34
+ * Style Dictionary, Specify, Supernova) and CI/CD token pipelines.
35
+ *
36
+ * @example
37
+ * ```ts
38
+ * import { toDTCG } from '@ttoss/fsl-theme/dtcg';
39
+ * import { createTheme } from '@ttoss/fsl-theme';
40
+ *
41
+ * const myBundle = createTheme();
42
+ * const tokens = toDTCG(myBundle.base);
43
+ * // tokens.core.colors.brand['500'] === { $value: '#0469E3', $type: 'color' }
44
+ *
45
+ * // Write to file (build script / token pipeline)
46
+ * await fs.writeFile('tokens.json', JSON.stringify(tokens, null, 2));
47
+ * ```
48
+ */
49
+ declare const toDTCG: (theme: ThemeTokens) => DTCGTokenTree;
50
+ //#endregion
51
+ export { type DTCGToken, type DTCGTokenTree, toDTCG };
package/dist/dtcg.mjs ADDED
@@ -0,0 +1,112 @@
1
+ /** Powered by @ttoss/config. https://ttoss.dev/docs/modules/packages/config/ */
2
+ import { s as toFlatTokens } from "./helpers-CaswNJMy.mjs";
3
+ import { n as DTCG_TYPE_PREFIXES } from "./tokenRegistry-DjgSN3oU.mjs";
4
+
5
+ //#region src/roots/toDTCG.ts
6
+ /**
7
+ * Suffix-level overrides applied after prefix lookup.
8
+ *
9
+ * The token registry uses a single prefix per semantic subtree (e.g.
10
+ * `semantic.motion.`, `semantic.text.`) which cannot express per-property
11
+ * DTCG types for composite token shapes (TextStyle, SemanticMotionEntry).
12
+ * These overrides catch every case where the leaf property name alone
13
+ * carries the required type — regardless of the token family it belongs to.
14
+ *
15
+ * Rule: a suffix override takes precedence over the prefix-based $type.
16
+ *
17
+ * Typography leaves (TextStyle) that need non-string $types:
18
+ * - `.fontFamily` → 'fontFamily' (DTCG named type for font stacks)
19
+ * - `.fontSize` → 'dimension' (clamp() / px value)
20
+ * - `.fontWeight` → 'fontWeight' (numeric weight: 400, 500, 700 …)
21
+ * - `.lineHeight` → 'number' (unitless multiplier: 1.15, 1.5 …)
22
+ * - `.letterSpacing` → 'dimension' (em value: '-0.02em', '0.04em' …)
23
+ *
24
+ * Motion leaves (SemanticMotionEntry) overridden separately:
25
+ * - `.duration` → 'duration'
26
+ */
27
+ const SUFFIX_TYPE_OVERRIDES = [[".duration", "duration"], [".fontFamily", "fontFamily"], [".fontSize", "dimension"], [".fontWeight", "fontWeight"], [".lineHeight", "number"], [".letterSpacing", "dimension"], [".ring.color", "color"]];
28
+ const inferType = path => {
29
+ for (const [suffix, type] of SUFFIX_TYPE_OVERRIDES) if (path.endsWith(suffix)) return type;
30
+ for (const [prefix, type] of DTCG_TYPE_PREFIXES) if (path.startsWith(prefix)) return type;
31
+ return "string";
32
+ };
33
+ /**
34
+ * Set a value at a dot-path in a nested object, creating intermediate
35
+ * objects as needed.
36
+ */
37
+ const setNestedValue = (root, path, value) => {
38
+ const segments = path.split(".");
39
+ let current = root;
40
+ for (let i = 0; i < segments.length - 1; i++) {
41
+ const seg = segments[i];
42
+ if (!(seg in current) || typeof current[seg] !== "object") current[seg] = {};
43
+ current = current[seg];
44
+ }
45
+ current[segments[segments.length - 1]] = value;
46
+ };
47
+ /** Semantic hit token prefix that carries coarse-pointer overrides. */
48
+ const SEMANTIC_HIT_PREFIX = "semantic.sizing.hit.";
49
+ /**
50
+ * Build the `$extensions` metadata for a semantic hit token.
51
+ *
52
+ * Each `semantic.sizing.hit.{step}` token defaults to the fine-pointer value.
53
+ * The extension declares the corresponding coarse-pointer raw value so that
54
+ * non-CSS consumers (React Native, design tool pipelines) can locate and
55
+ * apply touch-target overrides without reading `toCssVars` source code.
56
+ */
57
+ const buildHitExtension = (step, theme) => {
58
+ const coarseValue = theme.core.sizing.hit.coarse[step];
59
+ if (coarseValue === void 0) return void 0;
60
+ return {
61
+ "com.ttoss.pointer-override": {
62
+ condition: "any-pointer: coarse",
63
+ value: coarseValue
64
+ }
65
+ };
66
+ };
67
+ /**
68
+ * Root 3 — W3C Design Tokens (DTCG JSON).
69
+ *
70
+ * Convert a `ThemeTokens` into a structured token tree following the
71
+ * W3C Design Tokens Community Group format. Every leaf node has `$value`
72
+ * (fully resolved) and `$type` (inferred from the token path).
73
+ *
74
+ * Semantic hit tokens (`semantic.sizing.hit.*`) include a `$extensions`
75
+ * field declaring their coarse-pointer override value, so non-CSS consumers
76
+ * can apply touch-target ergonomics without reading the CSS emitter source.
77
+ *
78
+ * This is the interchange format for design tools (Tokens Studio, Figma,
79
+ * Style Dictionary, Specify, Supernova) and CI/CD token pipelines.
80
+ *
81
+ * @example
82
+ * ```ts
83
+ * import { toDTCG } from '@ttoss/fsl-theme/dtcg';
84
+ * import { createTheme } from '@ttoss/fsl-theme';
85
+ *
86
+ * const myBundle = createTheme();
87
+ * const tokens = toDTCG(myBundle.base);
88
+ * // tokens.core.colors.brand['500'] === { $value: '#0469E3', $type: 'color' }
89
+ *
90
+ * // Write to file (build script / token pipeline)
91
+ * await fs.writeFile('tokens.json', JSON.stringify(tokens, null, 2));
92
+ * ```
93
+ */
94
+ const toDTCG = theme => {
95
+ const flat = toFlatTokens(theme);
96
+ const tree = {};
97
+ for (const [path, value] of Object.entries(flat)) {
98
+ const token = {
99
+ $value: value,
100
+ $type: inferType(path)
101
+ };
102
+ if (path.startsWith(SEMANTIC_HIT_PREFIX)) {
103
+ const ext = buildHitExtension(path.slice(20), theme);
104
+ if (ext) token.$extensions = ext;
105
+ }
106
+ setNestedValue(tree, path, token);
107
+ }
108
+ return tree;
109
+ };
110
+
111
+ //#endregion
112
+ export { toDTCG };
@@ -0,0 +1,258 @@
1
+ /** Powered by @ttoss/config. https://ttoss.dev/docs/modules/packages/config/ */
2
+ //#region src/roots/helpers.ts
3
+ /** Check if a value is a token reference like `{core.colors.brand.500}` */
4
+ const isTokenRef = value => {
5
+ return typeof value === "string" && value.length > 2 && value.startsWith("{") && value.endsWith("}");
6
+ };
7
+ /** Extract the inner path from a token reference: `{core.colors.brand.500}` → `core.colors.brand.500` */
8
+ const extractRefPath = ref => {
9
+ return ref.slice(1, -1);
10
+ };
11
+ /**
12
+ * Allowed characters for a theme identifier: alphanumeric, hyphens, underscores.
13
+ * Shared between toCssVars (CSS selector injection guard) and ssrScript (SSR inline script).
14
+ * A single definition prevents the two validation paths from silently diverging.
15
+ */
16
+ const SAFE_ID_RE = /^[a-zA-Z0-9_-]+$/;
17
+ /**
18
+ * Matches every `{token.path}` reference embedded in a string value.
19
+ * Shared between helpers.ts (toFlatTokens) and toCssVars.ts (inlineRefsToVars)
20
+ * so a single definition governs the `{…}` syntax in both resolution paths.
21
+ */
22
+ const COMPOUND_REF_RE = /\{([^}]+)\}/g;
23
+ const isPlainObject = value => {
24
+ return typeof value === "object" && value !== null && !Array.isArray(value);
25
+ };
26
+ /**
27
+ * Recursively merges `overrides` into `base`.
28
+ * - Plain objects are merged recursively.
29
+ * - All other values (primitives, arrays) are replaced.
30
+ */
31
+ const deepMerge = (base, overrides) => {
32
+ if (!isPlainObject(base) || !isPlainObject(overrides)) return overrides ?? base;
33
+ const result = {
34
+ ...base
35
+ };
36
+ for (const key of Object.keys(overrides)) {
37
+ const baseVal = result[key];
38
+ const overVal = overrides[key];
39
+ if (overVal === void 0) continue;
40
+ if (isPlainObject(baseVal) && isPlainObject(overVal)) result[key] = deepMerge(baseVal, overVal);else result[key] = overVal;
41
+ }
42
+ return result;
43
+ };
44
+ /**
45
+ * Flatten a nested object into a flat record with dot-separated keys.
46
+ *
47
+ * `{ brand: { 500: '#0469E3' } }` → `{ 'brand.500': '#0469E3' }`
48
+ */
49
+ const flattenObject = (obj, prefix = "") => {
50
+ const result = {};
51
+ for (const [key, value] of Object.entries(obj)) {
52
+ const fullKey = prefix ? `${prefix}.${key}` : key;
53
+ if (isPlainObject(value)) Object.assign(result, flattenObject(value, fullKey));else if (typeof value === "string" || typeof value === "number") result[fullKey] = value;
54
+ }
55
+ return result;
56
+ };
57
+ /**
58
+ * Flatten a `ThemeTokens` into separate `{ core, semantic }` flat records
59
+ * with dot-separated keys. Centralizes the unsafe casts needed to traverse
60
+ * the opaque token trees.
61
+ *
62
+ * Used by both `toFlatTokens` (resolution) and `buildCssVars` (CSS emission)
63
+ * so the casts live in exactly one place.
64
+ */
65
+ const flattenTheme = theme => {
66
+ return {
67
+ core: flattenObject(theme.core, "core"),
68
+ semantic: flattenObject(theme.semantic, "semantic")
69
+ };
70
+ };
71
+ /**
72
+ * Compute the Levenshtein edit distance between two strings.
73
+ * Used exclusively by `validateRefs` to power "did you mean?" suggestions.
74
+ */
75
+ const levenshtein = (a, b) => {
76
+ const m = a.length;
77
+ const n = b.length;
78
+ const dp = Array.from({
79
+ length: m + 1
80
+ }, () => {
81
+ return Array.from({
82
+ length: n + 1
83
+ }, () => {
84
+ return 0;
85
+ });
86
+ });
87
+ for (let i = 0; i <= m; i++) dp[i][0] = i;
88
+ for (let j = 0; j <= n; j++) dp[0][j] = j;
89
+ for (let i = 1; i <= m; i++) for (let j = 1; j <= n; j++) dp[i][j] = a[i - 1] === b[j - 1] ? dp[i - 1][j - 1] : 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]);
90
+ return dp[m][n];
91
+ };
92
+ /**
93
+ * Validate that every `{ref}` in the merged theme points to an existing path.
94
+ * Emits `console.warn` for each broken reference with a "did you mean?" suggestion.
95
+ *
96
+ * **DEV-only** — callers gate this behind `process.env.NODE_ENV !== 'production'`
97
+ * so bundlers tree-shake the entire call in production builds.
98
+ */
99
+ const validateRefs = theme => {
100
+ const {
101
+ core,
102
+ semantic
103
+ } = flattenTheme(theme);
104
+ const all = {
105
+ ...core,
106
+ ...semantic
107
+ };
108
+ const allKeys = Object.keys(all);
109
+ const findSuggestion = (refPath, candidates) => {
110
+ if (candidates.length === 0) return "";
111
+ let bestDist = Infinity;
112
+ let bestKey = "";
113
+ for (const candidate of candidates) {
114
+ const dist = levenshtein(refPath, candidate);
115
+ if (dist < bestDist) {
116
+ bestDist = dist;
117
+ bestKey = candidate;
118
+ }
119
+ }
120
+ if (bestDist <= Math.ceil(refPath.length * .4)) return `\n Did you mean '{${bestKey}}'?`;
121
+ return "";
122
+ };
123
+ for (const [ownerKey, value] of Object.entries(all)) {
124
+ if (typeof value !== "string" || !value.includes("{")) continue;
125
+ let match;
126
+ const re = new RegExp(COMPOUND_REF_RE.source, COMPOUND_REF_RE.flags);
127
+ while ((match = re.exec(value)) !== null) {
128
+ const refPath = match[1];
129
+ if (all[refPath] !== void 0) continue;
130
+ const prefix = refPath.split(".")[0];
131
+ const suggestion = findSuggestion(refPath, allKeys.filter(k => {
132
+ return k.startsWith(prefix + ".");
133
+ }));
134
+ console.warn(`[fsl-theme] Invalid token reference '{${refPath}}' at path '${ownerKey}'.${suggestion}`);
135
+ }
136
+ }
137
+ };
138
+ /**
139
+ * Flatten a `ThemeTokens` into a `Record<string, string | number>` with
140
+ * every `{ref}` recursively resolved to its final raw value where possible.
141
+ *
142
+ * By default, unresolvable references (missing target or circular dependency)
143
+ * are preserved as-is in the output. Pass `{ strict: true }` to instead throw
144
+ * on any unresolved reference — useful in tests and build steps that must
145
+ * fail loudly on palette drift.
146
+ *
147
+ * This is the universal primitive — every root is derived from this.
148
+ */
149
+ const toFlatTokens = (theme, options = {}) => {
150
+ const {
151
+ strict = false
152
+ } = options;
153
+ const {
154
+ core: coreFlat,
155
+ semantic: semanticFlat
156
+ } = flattenTheme(theme);
157
+ const all = {
158
+ ...coreFlat,
159
+ ...semanticFlat
160
+ };
161
+ const unresolved = [];
162
+ const reportUnresolved = (key, path, reason) => {
163
+ if (strict) unresolved.push(`${key} → {${path}} (${reason})`);
164
+ };
165
+ /** Resolve a single pure `{path}` reference to its raw value. */
166
+ const resolveRef = (value, seen, ownerKey) => {
167
+ if (typeof value !== "string" || !isTokenRef(value)) return value;
168
+ const path = extractRefPath(value);
169
+ if (seen.has(path)) {
170
+ reportUnresolved(ownerKey, path, "circular reference");
171
+ return value;
172
+ }
173
+ const target = all[path];
174
+ if (target === void 0) {
175
+ reportUnresolved(ownerKey, path, "missing target");
176
+ return value;
177
+ }
178
+ seen.add(path);
179
+ return resolveRef(target, seen, ownerKey);
180
+ };
181
+ /**
182
+ * Resolve all embedded `{path}` refs in a raw string expression.
183
+ *
184
+ * Handles both pure refs (`{core.space.4}`) and compound expressions
185
+ * (`clamp({core.space.4}, {core.space.6}, {core.space.12})`).
186
+ */
187
+ const resolveInline = (value, seen, ownerKey) => {
188
+ return value.replace(COMPOUND_REF_RE, (_match, path) => {
189
+ const target = all[path];
190
+ if (target === void 0) {
191
+ reportUnresolved(ownerKey, path, "missing target");
192
+ return `{${path}}`;
193
+ }
194
+ if (seen.has(path)) {
195
+ reportUnresolved(ownerKey, path, "circular reference");
196
+ return `{${path}}`;
197
+ }
198
+ const resolved = resolveRef(target, new Set(seen).add(path), ownerKey);
199
+ return String(resolved);
200
+ });
201
+ };
202
+ const resolved = {};
203
+ for (const [key, value] of Object.entries(all)) if (typeof value === "string") {
204
+ if (isTokenRef(value)) resolved[key] = resolveRef(value, /* @__PURE__ */new Set(), key);else if (value.includes("{")) resolved[key] = resolveInline(value, /* @__PURE__ */new Set(), key);else resolved[key] = value;
205
+ } else resolved[key] = value;
206
+ if (strict && unresolved.length > 0) throw new Error(`toFlatTokens: ${unresolved.length} unresolved reference(s):\n ${unresolved.join("\n ")}`);
207
+ return resolved;
208
+ };
209
+
210
+ //#endregion
211
+ Object.defineProperty(exports, 'COMPOUND_REF_RE', {
212
+ enumerable: true,
213
+ get: function () {
214
+ return COMPOUND_REF_RE;
215
+ }
216
+ });
217
+ Object.defineProperty(exports, 'SAFE_ID_RE', {
218
+ enumerable: true,
219
+ get: function () {
220
+ return SAFE_ID_RE;
221
+ }
222
+ });
223
+ Object.defineProperty(exports, 'deepMerge', {
224
+ enumerable: true,
225
+ get: function () {
226
+ return deepMerge;
227
+ }
228
+ });
229
+ Object.defineProperty(exports, 'flattenObject', {
230
+ enumerable: true,
231
+ get: function () {
232
+ return flattenObject;
233
+ }
234
+ });
235
+ Object.defineProperty(exports, 'flattenTheme', {
236
+ enumerable: true,
237
+ get: function () {
238
+ return flattenTheme;
239
+ }
240
+ });
241
+ Object.defineProperty(exports, 'isPlainObject', {
242
+ enumerable: true,
243
+ get: function () {
244
+ return isPlainObject;
245
+ }
246
+ });
247
+ Object.defineProperty(exports, 'toFlatTokens', {
248
+ enumerable: true,
249
+ get: function () {
250
+ return toFlatTokens;
251
+ }
252
+ });
253
+ Object.defineProperty(exports, 'validateRefs', {
254
+ enumerable: true,
255
+ get: function () {
256
+ return validateRefs;
257
+ }
258
+ });