@pitlane/theme 0.2.0 → 0.3.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,127 @@
1
+ import { i as SchemaTag } from "./schema-CRP607Pg.mjs";
2
+ import { l as ThemeInit, r as ThemeResult, t as ThemeComponent } from "./theme-CaHfWYnM.mjs";
3
+ //#region src/dtcg.d.ts
4
+ /**
5
+ * A JSON-compatible W3C Design Tokens Community Group document.
6
+ *
7
+ * The DTCG specification permits arbitrary extension keys, so this
8
+ * deliberately remains open rather than attempting to model every
9
+ * standardized and vendor extension.
10
+ */
11
+ type DTCGDocument = Record<string, unknown>;
12
+ /**
13
+ * The runtime-shaped init {@link fromDTCG} derives from a DTCG document.
14
+ *
15
+ * Its `schema` and `tokens` use the broad {@link ThemeInit} member types: a
16
+ * JSON document cannot produce the literal information required to brand a
17
+ * token accessor. Generate a checked-in schema and token module from the
18
+ * document when a typed accessor is required.
19
+ */
20
+ interface DTCGThemeInit {
21
+ /** The derived runtime schema tree. */
22
+ schema: object;
23
+ /** The bare token values described by {@link schema}. */
24
+ tokens: object;
25
+ }
26
+ /**
27
+ * Pitlane's extension payload for a value DTCG cannot represent.
28
+ *
29
+ * The extension is stored at `$extensions["tools.pitlane"]`. The surrounding
30
+ * node deliberately has no `$value`, making it a valid DTCG group that a
31
+ * strict consumer can ignore rather than a token carrying an invalid surrogate
32
+ * value.
33
+ */
34
+ interface PitlaneDTCGExtension {
35
+ /** The source schema tag, including Pitlane-only `any` and `scale`. */
36
+ type: SchemaTag;
37
+ /** The original authoring value, with exact accessor references reversed. */
38
+ value: unknown;
39
+ /** Why the value has no standard DTCG representation. */
40
+ reason: string;
41
+ }
42
+ /**
43
+ * DTCG documents exported from a theme.
44
+ */
45
+ interface DTCGExport {
46
+ /** The theme's base document. */
47
+ document: DTCGDocument;
48
+ /** One partial document of token overrides for each theme mode. */
49
+ modes: Record<string, DTCGDocument>;
50
+ /** The number of base and mode values preserved only in Pitlane extensions. */
51
+ inexpressible: number;
52
+ }
53
+ /**
54
+ * Converts a W3C DTCG 2025.10 document into the schema tree and bare token
55
+ * tree {@link createTheme} accepts.
56
+ *
57
+ * A token resolves its type in DTCG order: its own `$type`, the resolved type
58
+ * of a whole-value `{reference}`, then the nearest ancestor group's `$type`.
59
+ * `$description`, `$deprecated`, and `$extensions` are intentionally discarded
60
+ * until schema factories gain a metadata options bag. Typography tokens are not
61
+ * supported because one typography value would need several custom properties.
62
+ *
63
+ * Runtime JSON cannot preserve literal paths, so using this init with
64
+ * {@link createTheme} produces an unbranded accessor. Generate TypeScript from
65
+ * the result for the typed path.
66
+ *
67
+ * @param document - A DTCG document using `$value`, `$type`, and dotted references
68
+ * @returns A runtime {@link ThemeInit}-shaped schema and token tree
69
+ * @throws ThemeError for malformed groups, unsupported typography, unknown types,
70
+ * unresolvable references, cycles, or malformed structured DTCG values
71
+ *
72
+ * @example
73
+ * ```ts
74
+ * import { createTheme } from "@pitlane/theme";
75
+ * import { fromDTCG } from "@pitlane/theme/dtcg";
76
+ *
77
+ * let imported = fromDTCG({
78
+ * color: {
79
+ * $type: "color",
80
+ * white: { $value: "#fff" },
81
+ * page: { $value: "{color.white}" },
82
+ * },
83
+ * });
84
+ * let theme = createTheme(imported);
85
+ * ```
86
+ */
87
+ declare function fromDTCG(document: DTCGDocument): DTCGThemeInit;
88
+ /**
89
+ * Exports a compiled theme, a published theme component, or the init behind
90
+ * either one to W3C DTCG 2025.10 documents.
91
+ *
92
+ * An export places `$type` on the lowest group whose complete descendant set
93
+ * has one expressible type; standard tokens below that group omit the redundant
94
+ * `$type`. Mixed groups put `$type` on their standard token leaves instead.
95
+ * Exact `var(--token-name)` values are converted back to `{token.path}` using
96
+ * the collector's variable-name map.
97
+ *
98
+ * DTCG cannot represent all CSS values this package accepts. Inexpressible
99
+ * values are preserved in `$extensions["tools.pitlane"]` as
100
+ * `{ type, value, reason }`, on an extension-only node with no `$value`; this
101
+ * keeps the standard document valid while preserving the original value for a
102
+ * caller that reports or handles the loss. `inexpressible` counts those nodes in
103
+ * the base document and every mode document.
104
+ *
105
+ * DTCG descriptions, deprecation metadata, and unrelated extensions are not in
106
+ * the authoring format and cannot be recovered.
107
+ *
108
+ * @param theme - A {@link ThemeResult}, its {@link ThemeComponent}, or its init
109
+ * @returns The base document, one partial document per mode, and the lossy-value count
110
+ * @throws ThemeError when the supplied init has an invalid token/schema structure
111
+ *
112
+ * @example
113
+ * ```ts
114
+ * import { createTheme } from "@pitlane/theme";
115
+ * import { toDTCG } from "@pitlane/theme/dtcg";
116
+ * import * as s from "@pitlane/theme/schema";
117
+ *
118
+ * let theme = createTheme({
119
+ * schema: { color: s.color() },
120
+ * tokens: { color: { white: "#fff" } },
121
+ * });
122
+ * let { document, inexpressible } = toDTCG(theme);
123
+ * ```
124
+ */
125
+ declare function toDTCG<T>(theme: ThemeInit | ThemeComponent<T> | ThemeResult<T>): DTCGExport;
126
+ //#endregion
127
+ export { DTCGDocument, DTCGExport, DTCGThemeInit, PitlaneDTCGExtension, fromDTCG, toDTCG };
package/dist/dtcg.mjs ADDED
@@ -0,0 +1,468 @@
1
+ import { D as referenceTarget, S as ThemeError, T as kebabSegment, b as transition, c as duration, d as gradient, f as group, i as border, l as easing, m as number, o as color, s as dimension, u as font, v as shadow, w as collectTokens, x as serializeValue, y as stroke } from "./schema-JmfNnMzr.mjs";
2
+ //#region src/brands.ts
3
+ /**
4
+ * The twelve DTCG token types, in canonical order.
5
+ *
6
+ * @internal
7
+ */
8
+ const TOKEN_TYPES = [
9
+ "color",
10
+ "dimension",
11
+ "duration",
12
+ "fontFamily",
13
+ "fontWeight",
14
+ "number",
15
+ "cubicBezier",
16
+ "shadow",
17
+ "border",
18
+ "transition",
19
+ "gradient",
20
+ "strokeStyle"
21
+ ];
22
+ //#endregion
23
+ //#region src/dtcg.ts
24
+ const DTCG_ALIAS_RE = /^\{([^{}]+)\}$/;
25
+ /** The token a DTCG `{a.b.c}` alias names, or `null`. */
26
+ function dtcgAlias(value) {
27
+ if (typeof value !== "string") return null;
28
+ let match = DTCG_ALIAS_RE.exec(value);
29
+ return match ? match[1] : null;
30
+ }
31
+ /**
32
+ * Rewrites every DTCG alias in a value to its `var()` reference, including the
33
+ * ones inside a composite's sub-values. The authoring format has one reference
34
+ * form, so braces must not survive past this module.
35
+ */
36
+ function convertAliases(value, key, varNameFor) {
37
+ let alias = dtcgAlias(value);
38
+ if (alias !== null) {
39
+ let varName = varNameFor(alias);
40
+ if (varName === void 0) throw new ThemeError(`"${key}" references unknown token "${alias}"`);
41
+ return `var(${varName})`;
42
+ }
43
+ if (Array.isArray(value)) return value.map((item) => convertAliases(item, key, varNameFor));
44
+ if (typeof value === "object" && value !== null) {
45
+ let out = {};
46
+ for (let [field, item] of Object.entries(value)) out[field] = convertAliases(item, key, varNameFor);
47
+ return out;
48
+ }
49
+ return value;
50
+ }
51
+ const ACCESSOR_REFERENCE_RE = /^var\((--[a-z0-9-]+)\)$/;
52
+ const DIMENSION_RE = /^([+-]?(?:\d+(?:\.\d+)?|\.\d+))(px|rem)$/;
53
+ const DURATION_RE = /^([+-]?(?:\d+(?:\.\d+)?|\.\d+))(ms|s)$/;
54
+ const CUBIC_BEZIER_RE = /^cubic-bezier\(\s*([+-]?(?:\d+(?:\.\d+)?|\.\d+))\s*,\s*([+-]?(?:\d+(?:\.\d+)?|\.\d+))\s*,\s*([+-]?(?:\d+(?:\.\d+)?|\.\d+))\s*,\s*([+-]?(?:\d+(?:\.\d+)?|\.\d+))\s*\)$/;
55
+ const HEX_RE = /^#([\da-f]{3,4}|[\da-f]{6}|[\da-f]{8})$/i;
56
+ /**
57
+ * Converts a W3C DTCG 2025.10 document into the schema tree and bare token
58
+ * tree {@link createTheme} accepts.
59
+ *
60
+ * A token resolves its type in DTCG order: its own `$type`, the resolved type
61
+ * of a whole-value `{reference}`, then the nearest ancestor group's `$type`.
62
+ * `$description`, `$deprecated`, and `$extensions` are intentionally discarded
63
+ * until schema factories gain a metadata options bag. Typography tokens are not
64
+ * supported because one typography value would need several custom properties.
65
+ *
66
+ * Runtime JSON cannot preserve literal paths, so using this init with
67
+ * {@link createTheme} produces an unbranded accessor. Generate TypeScript from
68
+ * the result for the typed path.
69
+ *
70
+ * @param document - A DTCG document using `$value`, `$type`, and dotted references
71
+ * @returns A runtime {@link ThemeInit}-shaped schema and token tree
72
+ * @throws ThemeError for malformed groups, unsupported typography, unknown types,
73
+ * unresolvable references, cycles, or malformed structured DTCG values
74
+ *
75
+ * @example
76
+ * ```ts
77
+ * import { createTheme } from "@pitlane/theme";
78
+ * import { fromDTCG } from "@pitlane/theme/dtcg";
79
+ *
80
+ * let imported = fromDTCG({
81
+ * color: {
82
+ * $type: "color",
83
+ * white: { $value: "#fff" },
84
+ * page: { $value: "{color.white}" },
85
+ * },
86
+ * });
87
+ * let theme = createTheme(imported);
88
+ * ```
89
+ */
90
+ function fromDTCG(document) {
91
+ let entries = /* @__PURE__ */ new Map();
92
+ let root = walkDocument(record(document, "$root"), [], void 0, entries);
93
+ let tags = resolveTags(entries);
94
+ let varNames = variableNames(entries.values());
95
+ let context = importReferenceContext(entries, tags, varNames);
96
+ return {
97
+ schema: schemaTree(root, tags),
98
+ tokens: tokenTree(root, tags, context, (key) => varNames.get(key))
99
+ };
100
+ }
101
+ /**
102
+ * Exports a compiled theme, a published theme component, or the init behind
103
+ * either one to W3C DTCG 2025.10 documents.
104
+ *
105
+ * An export places `$type` on the lowest group whose complete descendant set
106
+ * has one expressible type; standard tokens below that group omit the redundant
107
+ * `$type`. Mixed groups put `$type` on their standard token leaves instead.
108
+ * Exact `var(--token-name)` values are converted back to `{token.path}` using
109
+ * the collector's variable-name map.
110
+ *
111
+ * DTCG cannot represent all CSS values this package accepts. Inexpressible
112
+ * values are preserved in `$extensions["tools.pitlane"]` as
113
+ * `{ type, value, reason }`, on an extension-only node with no `$value`; this
114
+ * keeps the standard document valid while preserving the original value for a
115
+ * caller that reports or handles the loss. `inexpressible` counts those nodes in
116
+ * the base document and every mode document.
117
+ *
118
+ * DTCG descriptions, deprecation metadata, and unrelated extensions are not in
119
+ * the authoring format and cannot be recovered.
120
+ *
121
+ * @param theme - A {@link ThemeResult}, its {@link ThemeComponent}, or its init
122
+ * @returns The base document, one partial document per mode, and the lossy-value count
123
+ * @throws ThemeError when the supplied init has an invalid token/schema structure
124
+ *
125
+ * @example
126
+ * ```ts
127
+ * import { createTheme } from "@pitlane/theme";
128
+ * import { toDTCG } from "@pitlane/theme/dtcg";
129
+ * import * as s from "@pitlane/theme/schema";
130
+ *
131
+ * let theme = createTheme({
132
+ * schema: { color: s.color() },
133
+ * tokens: { color: { white: "#fff" } },
134
+ * });
135
+ * let { document, inexpressible } = toDTCG(theme);
136
+ * ```
137
+ */
138
+ function toDTCG(theme) {
139
+ let init = themeInit(theme);
140
+ let schema = init.schema;
141
+ let baseEntries = collectTokens(init.tokens, schema);
142
+ let base = exportDocument(baseEntries, baseEntries);
143
+ let modes = {};
144
+ let inexpressible = base.inexpressible;
145
+ for (let [name, mode] of Object.entries(init.modes ?? {})) {
146
+ let exported = exportDocument(collectTokens(mode.tokens, schema), baseEntries);
147
+ modes[name] = exported.document;
148
+ inexpressible += exported.inexpressible;
149
+ }
150
+ return {
151
+ document: base.document,
152
+ modes,
153
+ inexpressible
154
+ };
155
+ }
156
+ function walkDocument(node, path, inheritedType, entries) {
157
+ let key = path.length === 0 ? "$root" : path.join(".");
158
+ let ownType = validateType(node.$type, key);
159
+ let effectiveType = ownType ?? inheritedType;
160
+ let group = {
161
+ ownType,
162
+ children: {}
163
+ };
164
+ for (let [childName, child] of Object.entries(node)) {
165
+ if (childName.startsWith("$")) continue;
166
+ let childPath = [...path, childName];
167
+ let childKey = childPath.join(".");
168
+ validateName(childName, childKey);
169
+ let childRecord = record(child, childKey);
170
+ if ("$value" in childRecord) {
171
+ let token = {
172
+ key: childKey,
173
+ path: childPath,
174
+ ownTag: validateType(childRecord.$type, childKey),
175
+ inheritedType: effectiveType,
176
+ value: childRecord.$value
177
+ };
178
+ group.children[childName] = token;
179
+ entries.set(childKey, token);
180
+ continue;
181
+ }
182
+ group.children[childName] = walkDocument(childRecord, childPath, effectiveType, entries);
183
+ }
184
+ return group;
185
+ }
186
+ function resolveTags(entries) {
187
+ let tags = /* @__PURE__ */ new Map();
188
+ for (let key of entries.keys()) tags.set(key, resolveTag(key, entries, []));
189
+ return tags;
190
+ }
191
+ function resolveTag(key, entries, chain) {
192
+ if (chain.includes(key)) throw new ThemeError(`Alias cycle: ${[...chain, key].join(" → ")}`);
193
+ let entry = entries.get(key);
194
+ if (entry === void 0) throw new ThemeError(`"${chain[chain.length - 1] ?? key}" references unknown token "${key}"`);
195
+ if (entry.ownTag !== void 0) return entry.ownTag;
196
+ let alias = dtcgAlias(entry.value);
197
+ if (alias !== null) return resolveTag(alias, entries, [...chain, key]);
198
+ if (entry.inheritedType !== void 0) return entry.inheritedType;
199
+ throw new ThemeError(`"${key}" has no resolvable $type`);
200
+ }
201
+ function variableNames(entries) {
202
+ let byVarName = /* @__PURE__ */ new Map();
203
+ let byKey = /* @__PURE__ */ new Map();
204
+ for (let entry of entries) {
205
+ let varName = `--${entry.path.map(kebabSegment).join("-")}`;
206
+ let existing = byVarName.get(varName);
207
+ if (existing !== void 0) throw new ThemeError(`Tokens "${existing.key}" and "${entry.key}" both produce the CSS variable ${varName}`);
208
+ byVarName.set(varName, entry);
209
+ byKey.set(entry.key, varName);
210
+ }
211
+ return byKey;
212
+ }
213
+ function importReferenceContext(entries, tags, varNameByKey) {
214
+ return { varRefFor(key, from, expected) {
215
+ let target = entries.get(key);
216
+ if (target === void 0) throw new ThemeError(`"${from}" references unknown token "${key}"`);
217
+ let type = tags.get(target.key);
218
+ if (type !== expected) throw new ThemeError(`"${from}" references "${key}" of type "${type}" where "${expected}" is required`);
219
+ return `var(${varNameByKey.get(target.key)})`;
220
+ } };
221
+ }
222
+ function schemaTree(root, tags) {
223
+ let schema = {};
224
+ for (let [key, child] of Object.entries(root.children)) {
225
+ if (isRawToken(child)) {
226
+ schema[key] = schemaFor(tags.get(child.key));
227
+ continue;
228
+ }
229
+ let childSchema = groupSchema(child, root.ownType, tags);
230
+ if (childSchema === void 0) {
231
+ if (root.ownType === void 0) continue;
232
+ schema[key] = schemaFor(root.ownType);
233
+ } else if (child.ownType === void 0 && root.ownType !== void 0) schema[key] = group(schemaFor(root.ownType), childSchema);
234
+ else schema[key] = childSchema;
235
+ }
236
+ return schema;
237
+ }
238
+ function groupSchema(group$1, inherited, tags) {
239
+ let effective = group$1.ownType ?? inherited;
240
+ let children = {};
241
+ for (let [key, child] of Object.entries(group$1.children)) {
242
+ if (isRawToken(child)) {
243
+ let tag = tags.get(child.key);
244
+ if (tag !== effective) children[key] = schemaFor(tag);
245
+ continue;
246
+ }
247
+ let childSchema = groupSchema(child, effective, tags);
248
+ if (childSchema !== void 0) children[key] = childSchema;
249
+ }
250
+ if (group$1.ownType === void 0) return Object.keys(children).length === 0 ? void 0 : children;
251
+ let self = schemaFor(group$1.ownType);
252
+ return Object.keys(children).length === 0 ? self : group(self, children);
253
+ }
254
+ function tokenTree(group, tags, context, varNameFor) {
255
+ let tokens = {};
256
+ for (let [key, child] of Object.entries(group.children)) if (isRawToken(child)) tokens[key] = authorValue(child, tags.get(child.key), context, varNameFor);
257
+ else tokens[key] = tokenTree(child, tags, context, varNameFor);
258
+ return tokens;
259
+ }
260
+ function authorValue(entry, tag, context, varNameFor) {
261
+ let value = convertAliases(entry.value, entry.key, varNameFor);
262
+ if (isTokenValue(value)) return value;
263
+ return serializeValue(tag, value, context, entry.key);
264
+ }
265
+ function schemaFor(tag) {
266
+ switch (tag) {
267
+ case "color": return color();
268
+ case "dimension": return dimension();
269
+ case "duration": return duration();
270
+ case "number": return number();
271
+ case "cubicBezier": return easing();
272
+ case "shadow": return shadow();
273
+ case "border": return border();
274
+ case "transition": return transition();
275
+ case "gradient": return gradient();
276
+ case "strokeStyle": return stroke();
277
+ case "fontFamily": return font.family();
278
+ case "fontWeight": return font.weight();
279
+ }
280
+ }
281
+ function exportDocument(entries, referenceEntries) {
282
+ let byVarName = new Map(referenceEntries.map((entry) => [entry.varName, entry]));
283
+ let root = { children: /* @__PURE__ */ new Map() };
284
+ let inexpressible = 0;
285
+ for (let entry of entries) {
286
+ let node = root;
287
+ for (let [index, key] of entry.path.entries()) {
288
+ let child = node.children.get(key);
289
+ if (child === void 0) {
290
+ child = { children: /* @__PURE__ */ new Map() };
291
+ node.children.set(key, child);
292
+ }
293
+ node = child;
294
+ if (index === entry.path.length - 1) {
295
+ let encoded = encodeToken(entry, byVarName);
296
+ node.token = encoded;
297
+ if (encoded.extension !== void 0) inexpressible += 1;
298
+ }
299
+ }
300
+ }
301
+ return {
302
+ document: emitDocument(root),
303
+ inexpressible
304
+ };
305
+ }
306
+ function encodeToken(entry, byVarName) {
307
+ let value = reverseAccessorReference(entry.value, byVarName);
308
+ if (entry.kind === "untyped") return extensionToken("any", value, "Tokens declared with s.any() have no DTCG type");
309
+ let type = entry.kind === "scale" ? "dimension" : entry.type;
310
+ let encoded = encodeValue(type, value);
311
+ if (encoded === void 0) return extensionToken(entry.kind === "scale" ? "scale" : entry.type, value, entry.kind === "scale" ? "A scale token exports only its base dimension, not multiplier results" : inexpressibleReason(type));
312
+ return {
313
+ type,
314
+ value: encoded
315
+ };
316
+ }
317
+ function encodeValue(type, value) {
318
+ if (referenceTarget(value) !== null || dtcgAlias(value) !== null) return value;
319
+ switch (type) {
320
+ case "color": return typeof value === "string" ? encodeHexColor(value) : void 0;
321
+ case "dimension": return typeof value === "string" ? encodeMeasure(value, DIMENSION_RE) : void 0;
322
+ case "duration": return typeof value === "string" ? encodeMeasure(value, DURATION_RE) : void 0;
323
+ case "fontFamily": return isFontFamily(value) ? value : void 0;
324
+ case "fontWeight": return typeof value === "number" || typeof value === "string" ? value : void 0;
325
+ case "number": return typeof value === "number" && Number.isFinite(value) ? value : void 0;
326
+ case "cubicBezier": return encodeCubicBezier(value);
327
+ case "strokeStyle": return typeof value === "string" ? value : void 0;
328
+ case "shadow":
329
+ case "border":
330
+ case "transition":
331
+ case "gradient": return;
332
+ }
333
+ }
334
+ function encodeHexColor(value) {
335
+ let match = HEX_RE.exec(value);
336
+ if (match === null) return void 0;
337
+ let hex = match[1];
338
+ let pairs = hex.length <= 4 ? (hex.match(/./g) ?? []).map((part) => `${part}${part}`) : hex.match(/.{2}/g);
339
+ let components = pairs.slice(0, 3).map((part) => Number.parseInt(part, 16) / 255);
340
+ let alpha = pairs[3] === void 0 ? void 0 : Number.parseInt(pairs[3], 16) / 255;
341
+ return {
342
+ colorSpace: "srgb",
343
+ components,
344
+ ...alpha === void 0 ? {} : { alpha },
345
+ hex: value
346
+ };
347
+ }
348
+ function encodeMeasure(value, expression) {
349
+ let match = expression.exec(value);
350
+ if (match === null) return void 0;
351
+ let amount = Number(match[1]);
352
+ return Number.isFinite(amount) ? {
353
+ value: amount,
354
+ unit: match[2]
355
+ } : void 0;
356
+ }
357
+ function encodeCubicBezier(value) {
358
+ if (Array.isArray(value) && value.length === 4 && value.every((part) => typeof part === "number" && Number.isFinite(part))) return value;
359
+ if (typeof value !== "string") return void 0;
360
+ let match = CUBIC_BEZIER_RE.exec(value);
361
+ if (match === null) return void 0;
362
+ let parts = match.slice(1).map(Number);
363
+ return parts.every(Number.isFinite) ? parts : void 0;
364
+ }
365
+ function extensionToken(type, value, reason) {
366
+ return { extension: {
367
+ type,
368
+ value,
369
+ reason
370
+ } };
371
+ }
372
+ function emitDocument(node, inheritedType) {
373
+ let type = uniformType(node);
374
+ let effectiveType = type ?? inheritedType;
375
+ let document = {};
376
+ if (type !== void 0 && type !== inheritedType) document.$type = type;
377
+ for (let [key, child] of node.children) {
378
+ if (child.token !== void 0) {
379
+ if (child.token.extension !== void 0) {
380
+ document[key] = { $extensions: { "tools.pitlane": child.token.extension } };
381
+ continue;
382
+ }
383
+ let token = { $value: child.token.value };
384
+ if (child.token.type !== effectiveType) token.$type = child.token.type;
385
+ document[key] = token;
386
+ continue;
387
+ }
388
+ document[key] = emitDocument(child, effectiveType);
389
+ }
390
+ return document;
391
+ }
392
+ function uniformType(node) {
393
+ let uniform;
394
+ let isUniform = true;
395
+ let found = false;
396
+ function visit(child) {
397
+ if (!isUniform) return;
398
+ if (child.token !== void 0) {
399
+ let type = child.token.type;
400
+ if (type === void 0) {
401
+ isUniform = false;
402
+ return;
403
+ }
404
+ if (uniform !== void 0 && uniform !== type) {
405
+ isUniform = false;
406
+ return;
407
+ }
408
+ uniform = type;
409
+ found = true;
410
+ return;
411
+ }
412
+ for (let nested of child.children.values()) visit(nested);
413
+ }
414
+ for (let child of node.children.values()) visit(child);
415
+ return isUniform && found ? uniform : void 0;
416
+ }
417
+ function reverseAccessorReference(value, byVarName) {
418
+ if (typeof value !== "string") return value;
419
+ let match = ACCESSOR_REFERENCE_RE.exec(value);
420
+ if (match === null) return value;
421
+ let target = byVarName.get(match[1]);
422
+ return target === void 0 ? value : `{${target.key}}`;
423
+ }
424
+ function themeInit(theme) {
425
+ if (typeof theme === "function") return theme.$theme;
426
+ if ("Theme" in theme) return theme.Theme.$theme;
427
+ return theme;
428
+ }
429
+ function validateType(value, key) {
430
+ if (value === void 0) return void 0;
431
+ if (value === "typography") throw new ThemeError(`"${key}": typography tokens are not supported in v1`);
432
+ if (!TOKEN_TYPES.includes(value)) throw new ThemeError(`"${key}" has unknown $type ${JSON.stringify(value)}`);
433
+ return value;
434
+ }
435
+ function validateName(name, key) {
436
+ if (/[.{}]/.test(name)) throw new ThemeError(`Token or group name "${key}" contains characters reserved by references (".", "{", "}")`);
437
+ }
438
+ function record(value, key) {
439
+ if (typeof value !== "object" || value === null || Array.isArray(value)) throw new ThemeError(`"${key}" is neither a group nor a token`);
440
+ return value;
441
+ }
442
+ function isRawToken(value) {
443
+ return "key" in value;
444
+ }
445
+ function isTokenValue(value) {
446
+ return typeof value === "string" || typeof value === "number" || Array.isArray(value) && value.every((part) => typeof part === "string" || typeof part === "number");
447
+ }
448
+ function isFontFamily(value) {
449
+ return typeof value === "string" || Array.isArray(value) && value.every((part) => typeof part === "string");
450
+ }
451
+ function inexpressibleReason(type) {
452
+ switch (type) {
453
+ case "color": return "Only hexadecimal CSS colors can be represented as DTCG color values";
454
+ case "dimension": return "Only px and rem dimensions can be represented as DTCG dimension values";
455
+ case "duration": return "Only ms and s durations can be represented as DTCG duration values";
456
+ case "fontFamily":
457
+ case "fontWeight":
458
+ case "number":
459
+ case "cubicBezier":
460
+ case "strokeStyle": return "The value is outside the DTCG value grammar";
461
+ case "shadow":
462
+ case "border":
463
+ case "transition":
464
+ case "gradient": return "CSS composite text has no DTCG representation";
465
+ }
466
+ }
467
+ //#endregion
468
+ export { fromDTCG, toDTCG };