@uzum-tech/design-tokens-resolver 0.0.0-stage → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.cjs ADDED
@@ -0,0 +1,755 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ ArgsUtils: () => ArgsUtils,
24
+ ConfigFileResolver: () => ConfigFileResolver,
25
+ ConfigResolver: () => ConfigResolver,
26
+ ConfigTypes: () => ConfigTypes,
27
+ DesignClient: () => DesignClient,
28
+ DesignTokensResolver: () => DesignTokensResolver,
29
+ DocumentMapper: () => DocumentMapper,
30
+ ErrorTypes: () => ErrorTypes,
31
+ FileUtils: () => FileUtils,
32
+ HashUtils: () => HashUtils,
33
+ HttpClient: () => HttpClient,
34
+ HttpTypes: () => HttpTypes,
35
+ LockMapper: () => LockMapper,
36
+ ResolveMapper: () => ResolveMapper,
37
+ ResolveUtils: () => ResolveUtils,
38
+ ResolverError: () => ResolverError,
39
+ UrlUtils: () => UrlUtils,
40
+ default: () => DesignTokensResolver
41
+ });
42
+ module.exports = __toCommonJS(index_exports);
43
+
44
+ // src/constants/design.constants.ts
45
+ var designPaths = {
46
+ release: "/dynamic/design/admin/token-releases/details",
47
+ releasePayload: "/dynamic/design/admin/token-release-payloads/details",
48
+ channel: "/dynamic/design/admin/token-channels/details"
49
+ };
50
+ var engineBaseUrls = {
51
+ local: "http://localhost:3003/api/v1",
52
+ development: "https://esb-data-engine-int-dev.kapitalbank.uz/api/v1",
53
+ production: "https://data-engine-int.kapitalbank.uz/api/v1"
54
+ };
55
+ var designConfigFiles = [
56
+ "design-tokens.config.mjs",
57
+ "design-tokens.config.js",
58
+ "design-tokens.config.cjs",
59
+ "design-tokens.config.json"
60
+ ];
61
+ var JSON_CONFIG_SUFFIX = ".json";
62
+ var designDefaults = {
63
+ env: "production",
64
+ requestTimeoutInMs: 3e4,
65
+ retryCount: 2,
66
+ retryDelayInMs: 500,
67
+ fileName: "tokens.json",
68
+ lockFileName: "tokens.lock.json",
69
+ authEnvHint: "DESIGN_TOKENS_API_KEY \u043B\u0438\u0431\u043E DESIGN_TOKENS_USERNAME \u0438 DESIGN_TOKENS_PASSWORD"
70
+ };
71
+ var JSON_INDENT = 2;
72
+ var LINE_BREAK = "\n";
73
+ var HASH_ALGORITHM = "sha256";
74
+ var HASH_ENCODING = "hex";
75
+
76
+ // src/types/error.types.ts
77
+ var ErrorTypes;
78
+ ((ErrorTypes2) => {
79
+ let Code;
80
+ ((Code2) => {
81
+ Code2["CONFIG_INVALID"] = "CONFIG_INVALID";
82
+ Code2["REQUEST_FAILED"] = "REQUEST_FAILED";
83
+ Code2["RELEASE_NOT_FOUND"] = "RELEASE_NOT_FOUND";
84
+ Code2["CHANNEL_EMPTY"] = "CHANNEL_EMPTY";
85
+ Code2["INTEGRITY_MISMATCH"] = "INTEGRITY_MISMATCH";
86
+ Code2["WRITE_FAILED"] = "WRITE_FAILED";
87
+ })(Code = ErrorTypes2.Code || (ErrorTypes2.Code = {}));
88
+ })(ErrorTypes || (ErrorTypes = {}));
89
+
90
+ // src/constants/regular-expressions.constants.ts
91
+ var regularExpressions = {
92
+ trailingSlashes: /\/+$/,
93
+ leadingSlashes: /^\/+/,
94
+ kebabSegment: /-([a-z])/g,
95
+ dtcgAlias: /^\{(.+)\}$/
96
+ };
97
+
98
+ // src/utils/url.utils.ts
99
+ var UrlUtils = class {
100
+ static join(...parts) {
101
+ const [head, ...rest] = parts.filter(Boolean);
102
+ const tail = rest.map((part) => part.replace(regularExpressions.leadingSlashes, "").replace(regularExpressions.trailingSlashes, "")).filter(Boolean);
103
+ const base = (head ?? "").replace(regularExpressions.trailingSlashes, "");
104
+ return [base, ...tail].join("/");
105
+ }
106
+ static toQuery(params) {
107
+ const search = new URLSearchParams();
108
+ for (const key of Object.keys(params)) {
109
+ const value = params[key];
110
+ if (!value) continue;
111
+ search.set(key, String(value));
112
+ }
113
+ const query = search.toString();
114
+ return query ? `?${query}` : "";
115
+ }
116
+ };
117
+
118
+ // src/constants/http.constants.ts
119
+ var httpHeaders = {
120
+ authorization: "authorization",
121
+ apiKey: "x-api-key"
122
+ };
123
+
124
+ // src/errors/resolver.error.ts
125
+ var ResolverError = class _ResolverError extends Error {
126
+ code;
127
+ statusCode;
128
+ body;
129
+ constructor(message, options) {
130
+ super(message, {
131
+ cause: options.cause
132
+ });
133
+ this.name = "ResolverError";
134
+ this.code = options.code;
135
+ this.statusCode = options.statusCode;
136
+ this.body = options.body;
137
+ Error.captureStackTrace?.(this, _ResolverError);
138
+ }
139
+ static is(error, code) {
140
+ const isResolverError = error instanceof _ResolverError;
141
+ return code ? isResolverError && error.code === code : isResolverError;
142
+ }
143
+ };
144
+
145
+ // src/types/http.types.ts
146
+ var HttpTypes;
147
+ ((HttpTypes2) => {
148
+ let Status;
149
+ ((Status2) => {
150
+ Status2[Status2["UNAUTHORIZED"] = 401] = "UNAUTHORIZED";
151
+ Status2[Status2["NOT_FOUND"] = 404] = "NOT_FOUND";
152
+ Status2[Status2["TOO_MANY_REQUESTS"] = 429] = "TOO_MANY_REQUESTS";
153
+ Status2[Status2["INTERNAL_SERVER_ERROR"] = 500] = "INTERNAL_SERVER_ERROR";
154
+ })(Status = HttpTypes2.Status || (HttpTypes2.Status = {}));
155
+ })(HttpTypes || (HttpTypes = {}));
156
+
157
+ // src/client/http.client.ts
158
+ var HttpClient = class _HttpClient {
159
+ constructor(config) {
160
+ this.config = config;
161
+ }
162
+ static isRetryable(status) {
163
+ return status === HttpTypes.Status.TOO_MANY_REQUESTS || status >= HttpTypes.Status.INTERNAL_SERVER_ERROR;
164
+ }
165
+ static wait(delayInMs) {
166
+ return new Promise((resolve2) => {
167
+ setTimeout(resolve2, delayInMs);
168
+ });
169
+ }
170
+ async getJson(path, notFoundCode) {
171
+ const response = await this.request(path, notFoundCode);
172
+ const body = await response.json();
173
+ return body.data ?? body;
174
+ }
175
+ async request(path, notFoundCode = ErrorTypes.Code.REQUEST_FAILED) {
176
+ const url = UrlUtils.join(this.config.baseUrl, path);
177
+ const response = await this.sendWithRetries(url);
178
+ if (response.ok) return response;
179
+ const status = response.status;
180
+ throw new ResolverError(
181
+ `Design tokens request failed: ${status} ${url}`,
182
+ {
183
+ code: status === HttpTypes.Status.NOT_FOUND ? notFoundCode : ErrorTypes.Code.REQUEST_FAILED,
184
+ statusCode: status,
185
+ body: await response.text()
186
+ }
187
+ );
188
+ }
189
+ async sendWithRetries(url) {
190
+ let attempt = 0;
191
+ while (true) {
192
+ const isLast = attempt >= this.config.retryCount;
193
+ try {
194
+ const response = await this.send(url);
195
+ if (isLast || !_HttpClient.isRetryable(response.status)) return response;
196
+ } catch (error) {
197
+ if (isLast) throw error;
198
+ }
199
+ attempt += 1;
200
+ await _HttpClient.wait(this.config.retryDelayInMs * attempt);
201
+ }
202
+ }
203
+ async send(url) {
204
+ try {
205
+ return await fetch(url, {
206
+ headers: this.toHeaders(),
207
+ signal: AbortSignal.timeout(this.config.requestTimeoutInMs)
208
+ });
209
+ } catch (error) {
210
+ throw new ResolverError(
211
+ `Design tokens request failed: ${url}`,
212
+ {
213
+ code: ErrorTypes.Code.REQUEST_FAILED,
214
+ cause: error
215
+ }
216
+ );
217
+ }
218
+ }
219
+ toHeaders() {
220
+ const headers = {};
221
+ if (this.config.apiKey) {
222
+ headers[httpHeaders.apiKey] = this.config.apiKey;
223
+ }
224
+ if (this.config.auth) {
225
+ const credentials = Buffer.from(`${this.config.auth.username}:${this.config.auth.password}`).toString("base64");
226
+ headers[httpHeaders.authorization] = `Basic ${credentials}`;
227
+ }
228
+ return headers;
229
+ }
230
+ };
231
+
232
+ // src/client/design.client.ts
233
+ var DesignClient = class extends HttpClient {
234
+ async getRelease(version) {
235
+ return this.getJson(
236
+ designPaths.release + UrlUtils.toQuery({
237
+ version
238
+ }),
239
+ ErrorTypes.Code.RELEASE_NOT_FOUND
240
+ );
241
+ }
242
+ async getChannel(slug) {
243
+ return this.getJson(
244
+ designPaths.channel + UrlUtils.toQuery({
245
+ slug
246
+ }),
247
+ ErrorTypes.Code.RELEASE_NOT_FOUND
248
+ );
249
+ }
250
+ async getDocument(version) {
251
+ const payload = await this.getJson(
252
+ designPaths.releasePayload + UrlUtils.toQuery({
253
+ version
254
+ }),
255
+ ErrorTypes.Code.RELEASE_NOT_FOUND
256
+ );
257
+ return payload.document;
258
+ }
259
+ };
260
+
261
+ // src/core/config.resolver.ts
262
+ var ConfigResolver = class _ConfigResolver {
263
+ static withDefaults(config) {
264
+ const baseUrl = _ConfigResolver.toBaseUrl(config);
265
+ _ConfigResolver.validateAuth(config);
266
+ return {
267
+ baseUrl,
268
+ auth: config.auth ?? null,
269
+ apiKey: config.apiKey ?? null,
270
+ requestTimeoutInMs: config.requestTimeoutInMs ?? designDefaults.requestTimeoutInMs,
271
+ retryCount: config.retryCount ?? designDefaults.retryCount,
272
+ retryDelayInMs: config.retryDelayInMs ?? designDefaults.retryDelayInMs
273
+ };
274
+ }
275
+ static toBaseUrl(config) {
276
+ if (config.baseUrl) return config.baseUrl;
277
+ const env = config.env ?? designDefaults.env;
278
+ const baseUrl = engineBaseUrls[env];
279
+ if (!baseUrl) {
280
+ throw new ResolverError(
281
+ `Unknown environment "${env}". Known: ${Object.keys(engineBaseUrls).join(", ")}`,
282
+ {
283
+ code: ErrorTypes.Code.CONFIG_INVALID
284
+ }
285
+ );
286
+ }
287
+ return baseUrl;
288
+ }
289
+ static validateAuth(config) {
290
+ if (!config.auth) return;
291
+ if (config.auth.username && config.auth.password) return;
292
+ throw new ResolverError(
293
+ 'Design tokens config is invalid: both "username" and "password" are required for basic auth',
294
+ {
295
+ code: ErrorTypes.Code.CONFIG_INVALID
296
+ }
297
+ );
298
+ }
299
+ };
300
+
301
+ // src/core/config-file.resolver.ts
302
+ var import_node_fs = require("node:fs");
303
+ var import_node_path = require("node:path");
304
+ var import_node_url = require("node:url");
305
+ var ConfigFileResolver = class _ConfigFileResolver {
306
+ static async load(startPath) {
307
+ const path = _ConfigFileResolver.find(startPath);
308
+ if (!path) return null;
309
+ return {
310
+ path,
311
+ config: await _ConfigFileResolver.read(path)
312
+ };
313
+ }
314
+ static find(startPath) {
315
+ let current = (0, import_node_path.resolve)(startPath);
316
+ const stopAt = (0, import_node_path.parse)(current).root;
317
+ let found = _ConfigFileResolver.findIn(current);
318
+ while (!found && current !== stopAt) {
319
+ current = (0, import_node_path.dirname)(current);
320
+ found = _ConfigFileResolver.findIn(current);
321
+ }
322
+ return found;
323
+ }
324
+ static findIn(directory) {
325
+ for (const name of designConfigFiles) {
326
+ const path = (0, import_node_path.join)(directory, name);
327
+ if ((0, import_node_fs.existsSync)(path)) return path;
328
+ }
329
+ return null;
330
+ }
331
+ static async read(path) {
332
+ try {
333
+ return path.endsWith(JSON_CONFIG_SUFFIX) ? _ConfigFileResolver.readJson(path) : await _ConfigFileResolver.readModule(path);
334
+ } catch (error) {
335
+ if (ResolverError.is(error)) throw error;
336
+ throw new ResolverError(
337
+ `Failed to read design tokens config at ${path}`,
338
+ {
339
+ code: ErrorTypes.Code.CONFIG_INVALID,
340
+ cause: error
341
+ }
342
+ );
343
+ }
344
+ }
345
+ static readJson(path) {
346
+ return JSON.parse((0, import_node_fs.readFileSync)(path, "utf8"));
347
+ }
348
+ static async readModule(path) {
349
+ const imported = await import((0, import_node_url.pathToFileURL)(path).href);
350
+ const exported = imported.default;
351
+ if (!exported) {
352
+ throw new ResolverError(
353
+ `Design tokens config at ${path} has no default export`,
354
+ {
355
+ code: ErrorTypes.Code.CONFIG_INVALID
356
+ }
357
+ );
358
+ }
359
+ return typeof exported === "function" ? await exported() : exported;
360
+ }
361
+ };
362
+
363
+ // src/constants/document.constants.ts
364
+ var DOCUMENT_PATH_SEPARATOR = ".";
365
+ var DOCUMENT_VALUE_KEY_SEPARATOR = ":";
366
+ var DOCUMENT_MAX_ALIAS_DEPTH = 20;
367
+ var DOCUMENT_ALIAS_VALUE_GROUP = 1;
368
+
369
+ // src/types/config.types.ts
370
+ var ConfigTypes;
371
+ ((ConfigTypes2) => {
372
+ let Format;
373
+ ((Format2) => {
374
+ Format2["FLAT"] = "FLAT";
375
+ Format2["DTCG"] = "DTCG";
376
+ })(Format = ConfigTypes2.Format || (ConfigTypes2.Format = {}));
377
+ let Env;
378
+ ((Env2) => {
379
+ Env2["LOCAL"] = "local";
380
+ Env2["DEVELOPMENT"] = "development";
381
+ Env2["PRODUCTION"] = "production";
382
+ })(Env = ConfigTypes2.Env || (ConfigTypes2.Env = {}));
383
+ })(ConfigTypes || (ConfigTypes = {}));
384
+
385
+ // src/mappers/resolve.mapper.ts
386
+ var ResolveMapper = class _ResolveMapper {
387
+ static toScope(document) {
388
+ const modes = {};
389
+ for (const collection of document.collections) {
390
+ const defaultMode = collection.modes.find((mode) => mode.isDefault) ?? collection.modes[0];
391
+ if (!defaultMode) continue;
392
+ modes[collection.slug] = {
393
+ slugs: new Set(collection.modes.map((mode) => mode.slug)),
394
+ defaultSlug: defaultMode.slug
395
+ };
396
+ }
397
+ const literals = {};
398
+ for (const token of document.tokens) {
399
+ literals[_ResolveMapper.toLiteralKey(token.collection, token.mode, token.path)] = token.$value;
400
+ }
401
+ return {
402
+ literals,
403
+ modes
404
+ };
405
+ }
406
+ static toLiteralKey(collection, mode, path) {
407
+ return [collection, mode, path].join(DOCUMENT_VALUE_KEY_SEPARATOR);
408
+ }
409
+ static toTarget(alias, state) {
410
+ const separatorIndex = alias.indexOf(DOCUMENT_PATH_SEPARATOR);
411
+ if (separatorIndex <= 0) return null;
412
+ const collection = alias.slice(0, separatorIndex);
413
+ const collectionModes = state.scope.modes[collection];
414
+ if (!collectionModes) return null;
415
+ return {
416
+ path: alias.slice(separatorIndex + 1),
417
+ point: {
418
+ collection,
419
+ mode: collectionModes.slugs.has(state.point.mode) ? state.point.mode : collectionModes.defaultSlug
420
+ }
421
+ };
422
+ }
423
+ };
424
+
425
+ // src/utils/resolve.utils.ts
426
+ var ResolveUtils = class _ResolveUtils {
427
+ static resolve(value, state) {
428
+ if (state.depth >= DOCUMENT_MAX_ALIAS_DEPTH) return value;
429
+ if (typeof value === "string") return _ResolveUtils.resolveAlias(value, state);
430
+ const deeper = {
431
+ ...state,
432
+ depth: state.depth + 1
433
+ };
434
+ if (Array.isArray(value)) {
435
+ return value.map((item) => _ResolveUtils.resolve(item, deeper));
436
+ }
437
+ if (value === null || typeof value !== "object") return value;
438
+ const result = {};
439
+ for (const key of Object.keys(value)) {
440
+ result[key] = _ResolveUtils.resolve(value[key], deeper);
441
+ }
442
+ return result;
443
+ }
444
+ static parseAlias(value) {
445
+ const match = value.trim().match(regularExpressions.dtcgAlias);
446
+ return match ? match[DOCUMENT_ALIAS_VALUE_GROUP].trim() : null;
447
+ }
448
+ static resolveAlias(value, state) {
449
+ const alias = _ResolveUtils.parseAlias(value);
450
+ if (!alias) return value;
451
+ const target = ResolveMapper.toTarget(alias, state);
452
+ if (!target) return value;
453
+ const literal = state.scope.literals[ResolveMapper.toLiteralKey(
454
+ target.point.collection,
455
+ target.point.mode,
456
+ target.path
457
+ )];
458
+ if (literal === void 0) return value;
459
+ return _ResolveUtils.resolve(literal, {
460
+ scope: state.scope,
461
+ point: target.point,
462
+ depth: state.depth + 1
463
+ });
464
+ }
465
+ };
466
+
467
+ // src/mappers/document.mapper.ts
468
+ var DocumentMapper = class _DocumentMapper {
469
+ static toJson(canonical, query) {
470
+ if (_DocumentMapper.isCanonical(query)) return canonical;
471
+ const document = JSON.parse(canonical);
472
+ const output = _DocumentMapper.toOutput(document, query);
473
+ return `${JSON.stringify(output, null, JSON_INDENT)}${LINE_BREAK}`;
474
+ }
475
+ static toOutput(document, query) {
476
+ if (query.format !== ConfigTypes.Format.DTCG) {
477
+ return _DocumentMapper.toFlatDocument(document, query);
478
+ }
479
+ if (query.collection) return _DocumentMapper.toDtcg(document, query);
480
+ const trees = {};
481
+ for (const collection of document.collections) {
482
+ trees[collection.slug] = _DocumentMapper.toDtcg(document, {
483
+ ...query,
484
+ collection: collection.slug
485
+ });
486
+ }
487
+ return trees;
488
+ }
489
+ static isCanonical(query) {
490
+ return !query.collection && !query.resolve && (!query.format || query.format === ConfigTypes.Format.FLAT);
491
+ }
492
+ static toFlatDocument(document, query) {
493
+ return {
494
+ schemaVersion: document.schemaVersion,
495
+ collections: query.collection ? document.collections.filter((item) => item.slug === query.collection) : document.collections,
496
+ tokens: _DocumentMapper.toTokens(document, query)
497
+ };
498
+ }
499
+ static toTokens(document, query) {
500
+ const tokens = query.collection ? document.tokens.filter((token) => token.collection === query.collection) : document.tokens;
501
+ if (!query.resolve) return tokens;
502
+ const scope = ResolveMapper.toScope(document);
503
+ return tokens.map((token) => ({
504
+ ...token,
505
+ $value: ResolveUtils.resolve(token.$value, {
506
+ scope,
507
+ point: {
508
+ collection: token.collection,
509
+ mode: token.mode
510
+ },
511
+ depth: 0
512
+ })
513
+ }));
514
+ }
515
+ static toDtcg(document, query) {
516
+ const collection = document.collections.find(
517
+ (item) => item.slug === query.collection
518
+ );
519
+ if (!collection) return {};
520
+ const byMode = _DocumentMapper.groupByMode(
521
+ _DocumentMapper.toTokens(document, query)
522
+ );
523
+ if (collection.modes.length <= 1) {
524
+ const [mode] = collection.modes;
525
+ return _DocumentMapper.toTree(byMode[mode?.slug] ?? []);
526
+ }
527
+ const root = {};
528
+ for (const mode of collection.modes) {
529
+ root[mode.name] = _DocumentMapper.toTree(byMode[mode.slug] ?? []);
530
+ }
531
+ return root;
532
+ }
533
+ static groupByMode(tokens) {
534
+ const grouped = {};
535
+ for (const token of tokens) {
536
+ grouped[token.mode] ??= [];
537
+ grouped[token.mode].push(token);
538
+ }
539
+ return grouped;
540
+ }
541
+ static toTree(tokens) {
542
+ const root = {};
543
+ for (const token of tokens) {
544
+ const segments = token.path.split(DOCUMENT_PATH_SEPARATOR);
545
+ const leaf = segments.pop();
546
+ if (!leaf) continue;
547
+ let cursor = root;
548
+ for (const segment of segments) {
549
+ const next = cursor[segment];
550
+ if (!next || typeof next !== "object") {
551
+ cursor[segment] = {};
552
+ }
553
+ cursor = cursor[segment];
554
+ }
555
+ cursor[leaf] = {
556
+ $type: token.$type,
557
+ $value: token.$value
558
+ };
559
+ }
560
+ return root;
561
+ }
562
+ };
563
+
564
+ // src/mappers/lock.mapper.ts
565
+ var LockMapper = class {
566
+ static toLock(release, source) {
567
+ return {
568
+ version: release.version,
569
+ contentHash: release.contentHash,
570
+ baseUrl: source.baseUrl,
571
+ byteSize: release.byteSize,
572
+ publishedAt: release.createdAt,
573
+ publishedBy: release.publishedBy,
574
+ sourceLabel: release.sourceLabel
575
+ };
576
+ }
577
+ static toJson(lock) {
578
+ return `${JSON.stringify(lock, null, JSON_INDENT)}${LINE_BREAK}`;
579
+ }
580
+ };
581
+
582
+ // src/utils/file.utils.ts
583
+ var import_node_fs2 = require("node:fs");
584
+ var import_node_path2 = require("node:path");
585
+ var FileUtils = class {
586
+ static write(outputPath, fileName, body) {
587
+ const target = (0, import_node_path2.join)(outputPath, fileName);
588
+ try {
589
+ (0, import_node_fs2.mkdirSync)((0, import_node_path2.dirname)(target), {
590
+ recursive: true
591
+ });
592
+ (0, import_node_fs2.writeFileSync)(target, body, "utf8");
593
+ return target;
594
+ } catch (error) {
595
+ throw new ResolverError(
596
+ `Failed to write design tokens to ${target}`,
597
+ {
598
+ code: ErrorTypes.Code.WRITE_FAILED,
599
+ cause: error
600
+ }
601
+ );
602
+ }
603
+ }
604
+ };
605
+
606
+ // src/utils/hash.utils.ts
607
+ var import_node_crypto = require("node:crypto");
608
+ var HashUtils = class {
609
+ static toContentHash(body) {
610
+ return (0, import_node_crypto.createHash)(HASH_ALGORITHM).update(body, "utf8").digest(HASH_ENCODING);
611
+ }
612
+ };
613
+
614
+ // src/core/design-tokens-resolver.ts
615
+ var DesignTokensResolver = class _DesignTokensResolver {
616
+ client;
617
+ config;
618
+ constructor(config) {
619
+ this.config = ConfigResolver.withDefaults(config);
620
+ this.client = new DesignClient(this.config);
621
+ }
622
+ static checkIntegrity(release, canonical) {
623
+ const actual = HashUtils.toContentHash(canonical);
624
+ if (actual === release.contentHash) return;
625
+ throw new ResolverError(
626
+ `Design tokens integrity check failed for ${release.version}: expected ${release.contentHash}, got ${actual}`,
627
+ {
628
+ code: ErrorTypes.Code.INTEGRITY_MISMATCH
629
+ }
630
+ );
631
+ }
632
+ getSource() {
633
+ return {
634
+ baseUrl: this.config.baseUrl
635
+ };
636
+ }
637
+ async resolveVersion(target) {
638
+ if (target.version) return target.version;
639
+ if (!target.channel) {
640
+ throw new ResolverError(
641
+ 'Either "version" or "channel" must be provided',
642
+ {
643
+ code: ErrorTypes.Code.CONFIG_INVALID
644
+ }
645
+ );
646
+ }
647
+ const channel = await this.client.getChannel(target.channel);
648
+ if (channel.version) return channel.version;
649
+ throw new ResolverError(
650
+ `Channel "${target.channel}" does not point at any version yet`,
651
+ {
652
+ code: ErrorTypes.Code.CHANNEL_EMPTY
653
+ }
654
+ );
655
+ }
656
+ async fetchVerified(target) {
657
+ const version = await this.resolveVersion(target);
658
+ const release = await this.client.getRelease(version);
659
+ const canonical = await this.client.getDocument(version);
660
+ _DesignTokensResolver.checkIntegrity(release, canonical);
661
+ return {
662
+ release,
663
+ body: DocumentMapper.toJson(canonical, target)
664
+ };
665
+ }
666
+ async resolve(request) {
667
+ const verified = await this.fetchVerified(request);
668
+ const tokensPath = FileUtils.write(
669
+ request.outputPath,
670
+ request.fileName ?? designDefaults.fileName,
671
+ verified.body
672
+ );
673
+ const lockPath = request.lock === false ? null : FileUtils.write(
674
+ request.outputPath,
675
+ designDefaults.lockFileName,
676
+ LockMapper.toJson(LockMapper.toLock(verified.release, this.getSource()))
677
+ );
678
+ return {
679
+ release: verified.release,
680
+ source: this.getSource(),
681
+ tokensPath,
682
+ lockPath,
683
+ bytesWritten: Buffer.byteLength(verified.body, "utf8")
684
+ };
685
+ }
686
+ };
687
+
688
+ // src/constants/cli.constants.ts
689
+ var FLAG_PREFIX = "--";
690
+ var NEGATIVE_FLAG_PREFIX = "--no-";
691
+
692
+ // src/utils/args.utils.ts
693
+ var ArgsUtils = class _ArgsUtils {
694
+ static parse(args) {
695
+ const parsed = {};
696
+ for (let index = 0; index < args.length; index += 1) {
697
+ const token = args[index];
698
+ if (!token?.startsWith(FLAG_PREFIX)) continue;
699
+ if (token.startsWith(NEGATIVE_FLAG_PREFIX)) {
700
+ parsed[_ArgsUtils.toKey(token.slice(NEGATIVE_FLAG_PREFIX.length))] = false;
701
+ continue;
702
+ }
703
+ const name = _ArgsUtils.toKey(token.slice(FLAG_PREFIX.length));
704
+ const next = args[index + 1];
705
+ if (!next || next.startsWith(FLAG_PREFIX)) {
706
+ parsed[name] = true;
707
+ continue;
708
+ }
709
+ parsed[name] = next;
710
+ index += 1;
711
+ }
712
+ return parsed;
713
+ }
714
+ static toFormat(value) {
715
+ if (typeof value !== "string") return;
716
+ const normalized = value.trim().toUpperCase();
717
+ return normalized === ConfigTypes.Format.DTCG ? ConfigTypes.Format.DTCG : ConfigTypes.Format.FLAT;
718
+ }
719
+ static toEnv(value) {
720
+ return _ArgsUtils.toNormalized(value);
721
+ }
722
+ static toText(value) {
723
+ return typeof value === "string" ? value : void 0;
724
+ }
725
+ static toNormalized(value) {
726
+ if (typeof value !== "string") return;
727
+ return value.trim().toLowerCase();
728
+ }
729
+ static toKey(name) {
730
+ return name.replace(
731
+ regularExpressions.kebabSegment,
732
+ (_, letter) => letter.toUpperCase()
733
+ );
734
+ }
735
+ };
736
+ // Annotate the CommonJS export names for ESM import in node:
737
+ 0 && (module.exports = {
738
+ ArgsUtils,
739
+ ConfigFileResolver,
740
+ ConfigResolver,
741
+ ConfigTypes,
742
+ DesignClient,
743
+ DesignTokensResolver,
744
+ DocumentMapper,
745
+ ErrorTypes,
746
+ FileUtils,
747
+ HashUtils,
748
+ HttpClient,
749
+ HttpTypes,
750
+ LockMapper,
751
+ ResolveMapper,
752
+ ResolveUtils,
753
+ ResolverError,
754
+ UrlUtils
755
+ });