brandrail 0.3.1 → 0.3.2

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 (2) hide show
  1. package/dist/brandrail.mjs +856 -103
  2. package/package.json +3 -2
@@ -1,8 +1,491 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "node:fs";
5
- import { dirname as dirname2, join as join3, relative as relative2, resolve as resolve3 } from "node:path";
4
+ import { existsSync as existsSync3, mkdirSync, readdirSync as readdirSync2, readFileSync as readFileSync2, statSync as statSync2, writeFileSync } from "node:fs";
5
+ import { basename, dirname as dirname2, join as join3, relative as relative2, resolve as resolve3 } from "node:path";
6
+
7
+ // ../../node_modules/.pnpm/fflate@0.8.3/node_modules/fflate/esm/index.mjs
8
+ import { createRequire } from "module";
9
+ var require2 = createRequire("/");
10
+ var _a;
11
+ var Worker;
12
+ var isMarkedAsUntransferable;
13
+ try {
14
+ _a = require2("worker_threads"), Worker = _a.Worker, isMarkedAsUntransferable = _a.isMarkedAsUntransferable;
15
+ } catch (e) {
16
+ }
17
+ var u8 = Uint8Array;
18
+ var u16 = Uint16Array;
19
+ var i32 = Int32Array;
20
+ var fleb = new u8([
21
+ 0,
22
+ 0,
23
+ 0,
24
+ 0,
25
+ 0,
26
+ 0,
27
+ 0,
28
+ 0,
29
+ 1,
30
+ 1,
31
+ 1,
32
+ 1,
33
+ 2,
34
+ 2,
35
+ 2,
36
+ 2,
37
+ 3,
38
+ 3,
39
+ 3,
40
+ 3,
41
+ 4,
42
+ 4,
43
+ 4,
44
+ 4,
45
+ 5,
46
+ 5,
47
+ 5,
48
+ 5,
49
+ 0,
50
+ /* unused */
51
+ 0,
52
+ 0,
53
+ /* impossible */
54
+ 0
55
+ ]);
56
+ var fdeb = new u8([
57
+ 0,
58
+ 0,
59
+ 0,
60
+ 0,
61
+ 1,
62
+ 1,
63
+ 2,
64
+ 2,
65
+ 3,
66
+ 3,
67
+ 4,
68
+ 4,
69
+ 5,
70
+ 5,
71
+ 6,
72
+ 6,
73
+ 7,
74
+ 7,
75
+ 8,
76
+ 8,
77
+ 9,
78
+ 9,
79
+ 10,
80
+ 10,
81
+ 11,
82
+ 11,
83
+ 12,
84
+ 12,
85
+ 13,
86
+ 13,
87
+ /* unused */
88
+ 0,
89
+ 0
90
+ ]);
91
+ var clim = new u8([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]);
92
+ var freb = function(eb, start) {
93
+ var b = new u16(31);
94
+ for (var i2 = 0; i2 < 31; ++i2) {
95
+ b[i2] = start += 1 << eb[i2 - 1];
96
+ }
97
+ var r = new i32(b[30]);
98
+ for (var i2 = 1; i2 < 30; ++i2) {
99
+ for (var j = b[i2]; j < b[i2 + 1]; ++j) {
100
+ r[j] = j - b[i2] << 5 | i2;
101
+ }
102
+ }
103
+ return { b, r };
104
+ };
105
+ var _a = freb(fleb, 2);
106
+ var fl = _a.b;
107
+ var revfl = _a.r;
108
+ fl[28] = 258, revfl[258] = 28;
109
+ var _b = freb(fdeb, 0);
110
+ var fd = _b.b;
111
+ var revfd = _b.r;
112
+ var rev = new u16(32768);
113
+ for (i = 0; i < 32768; ++i) {
114
+ x = (i & 43690) >> 1 | (i & 21845) << 1;
115
+ x = (x & 52428) >> 2 | (x & 13107) << 2;
116
+ x = (x & 61680) >> 4 | (x & 3855) << 4;
117
+ rev[i] = ((x & 65280) >> 8 | (x & 255) << 8) >> 1;
118
+ }
119
+ var x;
120
+ var i;
121
+ var hMap = (function(cd, mb, r) {
122
+ var s = cd.length;
123
+ var i2 = 0;
124
+ var l = new u16(mb);
125
+ for (; i2 < s; ++i2) {
126
+ if (cd[i2])
127
+ ++l[cd[i2] - 1];
128
+ }
129
+ var le = new u16(mb);
130
+ for (i2 = 1; i2 < mb; ++i2) {
131
+ le[i2] = le[i2 - 1] + l[i2 - 1] << 1;
132
+ }
133
+ var co;
134
+ if (r) {
135
+ co = new u16(1 << mb);
136
+ var rvb = 15 - mb;
137
+ for (i2 = 0; i2 < s; ++i2) {
138
+ if (cd[i2]) {
139
+ var sv = i2 << 4 | cd[i2];
140
+ var r_1 = mb - cd[i2];
141
+ var v = le[cd[i2] - 1]++ << r_1;
142
+ for (var m = v | (1 << r_1) - 1; v <= m; ++v) {
143
+ co[rev[v] >> rvb] = sv;
144
+ }
145
+ }
146
+ }
147
+ } else {
148
+ co = new u16(s);
149
+ for (i2 = 0; i2 < s; ++i2) {
150
+ if (cd[i2]) {
151
+ co[i2] = rev[le[cd[i2] - 1]++] >> 15 - cd[i2];
152
+ }
153
+ }
154
+ }
155
+ return co;
156
+ });
157
+ var flt = new u8(288);
158
+ for (i = 0; i < 144; ++i)
159
+ flt[i] = 8;
160
+ var i;
161
+ for (i = 144; i < 256; ++i)
162
+ flt[i] = 9;
163
+ var i;
164
+ for (i = 256; i < 280; ++i)
165
+ flt[i] = 7;
166
+ var i;
167
+ for (i = 280; i < 288; ++i)
168
+ flt[i] = 8;
169
+ var i;
170
+ var fdt = new u8(32);
171
+ for (i = 0; i < 32; ++i)
172
+ fdt[i] = 5;
173
+ var i;
174
+ var flrm = /* @__PURE__ */ hMap(flt, 9, 1);
175
+ var fdrm = /* @__PURE__ */ hMap(fdt, 5, 1);
176
+ var max = function(a) {
177
+ var m = a[0];
178
+ for (var i2 = 1; i2 < a.length; ++i2) {
179
+ if (a[i2] > m)
180
+ m = a[i2];
181
+ }
182
+ return m;
183
+ };
184
+ var bits = function(d, p, m) {
185
+ var o = p / 8 | 0;
186
+ return (d[o] | d[o + 1] << 8) >> (p & 7) & m;
187
+ };
188
+ var bits16 = function(d, p) {
189
+ var o = p / 8 | 0;
190
+ return (d[o] | d[o + 1] << 8 | d[o + 2] << 16) >> (p & 7);
191
+ };
192
+ var shft = function(p) {
193
+ return (p + 7) / 8 | 0;
194
+ };
195
+ var slc = function(v, s, e) {
196
+ if (s == null || s < 0)
197
+ s = 0;
198
+ if (e == null || e > v.length)
199
+ e = v.length;
200
+ return new u8(v.subarray(s, e));
201
+ };
202
+ var ec = [
203
+ "unexpected EOF",
204
+ "invalid block type",
205
+ "invalid length/literal",
206
+ "invalid distance",
207
+ "stream finished",
208
+ "no stream handler",
209
+ ,
210
+ // determined by compression function
211
+ "no callback",
212
+ "invalid UTF-8 data",
213
+ "extra field too long",
214
+ "date not in range 1980-2099",
215
+ "filename too long",
216
+ "stream finishing",
217
+ "invalid zip data"
218
+ // determined by unknown compression method
219
+ ];
220
+ var err = function(ind, msg, nt) {
221
+ var e = new Error(msg || ec[ind]);
222
+ e.code = ind;
223
+ if (Error.captureStackTrace)
224
+ Error.captureStackTrace(e, err);
225
+ if (!nt)
226
+ throw e;
227
+ return e;
228
+ };
229
+ var inflt = function(dat, st, buf, dict) {
230
+ var sl = dat.length, dl = dict ? dict.length : 0;
231
+ if (!sl || st.f && !st.l)
232
+ return buf || new u8(0);
233
+ var noBuf = !buf;
234
+ var resize = noBuf || st.i != 2;
235
+ var noSt = st.i;
236
+ if (noBuf)
237
+ buf = new u8(sl * 3);
238
+ var cbuf = function(l2) {
239
+ var bl = buf.length;
240
+ if (l2 > bl) {
241
+ var nbuf = new u8(Math.max(bl * 2, l2));
242
+ nbuf.set(buf);
243
+ buf = nbuf;
244
+ }
245
+ };
246
+ var final = st.f || 0, pos = st.p || 0, bt = st.b || 0, lm = st.l, dm = st.d, lbt = st.m, dbt = st.n;
247
+ var tbts = sl * 8;
248
+ do {
249
+ if (!lm) {
250
+ final = bits(dat, pos, 1);
251
+ var type = bits(dat, pos + 1, 3);
252
+ pos += 3;
253
+ if (!type) {
254
+ var s = shft(pos) + 4, l = dat[s - 4] | dat[s - 3] << 8, t = s + l;
255
+ if (t > sl) {
256
+ if (noSt)
257
+ err(0);
258
+ break;
259
+ }
260
+ if (resize)
261
+ cbuf(bt + l);
262
+ buf.set(dat.subarray(s, t), bt);
263
+ st.b = bt += l, st.p = pos = t * 8, st.f = final;
264
+ continue;
265
+ } else if (type == 1)
266
+ lm = flrm, dm = fdrm, lbt = 9, dbt = 5;
267
+ else if (type == 2) {
268
+ var hLit = bits(dat, pos, 31) + 257, hcLen = bits(dat, pos + 10, 15) + 4;
269
+ var tl = hLit + bits(dat, pos + 5, 31) + 1;
270
+ pos += 14;
271
+ var ldt = new u8(tl);
272
+ var clt = new u8(19);
273
+ for (var i2 = 0; i2 < hcLen; ++i2) {
274
+ clt[clim[i2]] = bits(dat, pos + i2 * 3, 7);
275
+ }
276
+ pos += hcLen * 3;
277
+ var clb = max(clt), clbmsk = (1 << clb) - 1;
278
+ var clm = hMap(clt, clb, 1);
279
+ for (var i2 = 0; i2 < tl; ) {
280
+ var r = clm[bits(dat, pos, clbmsk)];
281
+ pos += r & 15;
282
+ var s = r >> 4;
283
+ if (s < 16) {
284
+ ldt[i2++] = s;
285
+ } else {
286
+ var c = 0, n = 0;
287
+ if (s == 16)
288
+ n = 3 + bits(dat, pos, 3), pos += 2, c = ldt[i2 - 1];
289
+ else if (s == 17)
290
+ n = 3 + bits(dat, pos, 7), pos += 3;
291
+ else if (s == 18)
292
+ n = 11 + bits(dat, pos, 127), pos += 7;
293
+ while (n--)
294
+ ldt[i2++] = c;
295
+ }
296
+ }
297
+ var lt = ldt.subarray(0, hLit), dt = ldt.subarray(hLit);
298
+ lbt = max(lt);
299
+ dbt = max(dt);
300
+ lm = hMap(lt, lbt, 1);
301
+ dm = hMap(dt, dbt, 1);
302
+ } else
303
+ err(1);
304
+ if (pos > tbts) {
305
+ if (noSt)
306
+ err(0);
307
+ break;
308
+ }
309
+ }
310
+ if (resize)
311
+ cbuf(bt + 131072);
312
+ var lms = (1 << lbt) - 1, dms = (1 << dbt) - 1;
313
+ var lpos = pos;
314
+ for (; ; lpos = pos) {
315
+ var c = lm[bits16(dat, pos) & lms], sym = c >> 4;
316
+ pos += c & 15;
317
+ if (pos > tbts) {
318
+ if (noSt)
319
+ err(0);
320
+ break;
321
+ }
322
+ if (!c)
323
+ err(2);
324
+ if (sym < 256)
325
+ buf[bt++] = sym;
326
+ else if (sym == 256) {
327
+ lpos = pos, lm = null;
328
+ break;
329
+ } else {
330
+ var add = sym - 254;
331
+ if (sym > 264) {
332
+ var i2 = sym - 257, b = fleb[i2];
333
+ add = bits(dat, pos, (1 << b) - 1) + fl[i2];
334
+ pos += b;
335
+ }
336
+ var d = dm[bits16(dat, pos) & dms], dsym = d >> 4;
337
+ if (!d)
338
+ err(3);
339
+ pos += d & 15;
340
+ var dt = fd[dsym];
341
+ if (dsym > 3) {
342
+ var b = fdeb[dsym];
343
+ dt += bits16(dat, pos) & (1 << b) - 1, pos += b;
344
+ }
345
+ if (pos > tbts) {
346
+ if (noSt)
347
+ err(0);
348
+ break;
349
+ }
350
+ if (resize)
351
+ cbuf(bt + 131072);
352
+ var end = bt + add;
353
+ if (bt < dt) {
354
+ var shift = dl - dt, dend = Math.min(dt, end);
355
+ if (shift + bt < 0)
356
+ err(3);
357
+ for (; bt < dend; ++bt)
358
+ buf[bt] = dict[shift + bt];
359
+ }
360
+ for (; bt < end; ++bt)
361
+ buf[bt] = buf[bt - dt];
362
+ }
363
+ }
364
+ st.l = lm, st.p = lpos, st.b = bt, st.f = final;
365
+ if (lm)
366
+ final = 1, st.m = lbt, st.d = dm, st.n = dbt;
367
+ } while (!final);
368
+ return bt != buf.length && noBuf ? slc(buf, 0, bt) : buf.subarray(0, bt);
369
+ };
370
+ var et = /* @__PURE__ */ new u8(0);
371
+ var b2 = function(d, b) {
372
+ return d[b] | d[b + 1] << 8;
373
+ };
374
+ var b4 = function(d, b) {
375
+ return (d[b] | d[b + 1] << 8 | d[b + 2] << 16 | d[b + 3] << 24) >>> 0;
376
+ };
377
+ var b8 = function(d, b) {
378
+ return b4(d, b) + b4(d, b + 4) * 4294967296;
379
+ };
380
+ function inflateSync(data, opts) {
381
+ return inflt(data, { i: 2 }, opts && opts.out, opts && opts.dictionary);
382
+ }
383
+ var td = typeof TextDecoder != "undefined" && /* @__PURE__ */ new TextDecoder();
384
+ var tds = 0;
385
+ try {
386
+ td.decode(et, { stream: true });
387
+ tds = 1;
388
+ } catch (e) {
389
+ }
390
+ var dutf8 = function(d) {
391
+ for (var r = "", i2 = 0; ; ) {
392
+ var c = d[i2++];
393
+ var eb = (c > 127) + (c > 223) + (c > 239);
394
+ if (i2 + eb > d.length)
395
+ return { s: r, r: slc(d, i2 - 1) };
396
+ if (!eb)
397
+ r += String.fromCharCode(c);
398
+ else if (eb == 3) {
399
+ c = ((c & 15) << 18 | (d[i2++] & 63) << 12 | (d[i2++] & 63) << 6 | d[i2++] & 63) - 65536, r += String.fromCharCode(55296 | c >> 10, 56320 | c & 1023);
400
+ } else if (eb & 1)
401
+ r += String.fromCharCode((c & 31) << 6 | d[i2++] & 63);
402
+ else
403
+ r += String.fromCharCode((c & 15) << 12 | (d[i2++] & 63) << 6 | d[i2++] & 63);
404
+ }
405
+ };
406
+ function strFromU8(dat, latin1) {
407
+ if (latin1) {
408
+ var r = "";
409
+ for (var i2 = 0; i2 < dat.length; i2 += 16384)
410
+ r += String.fromCharCode.apply(null, dat.subarray(i2, i2 + 16384));
411
+ return r;
412
+ } else if (td) {
413
+ return td.decode(dat);
414
+ } else {
415
+ var _a2 = dutf8(dat), s = _a2.s, r = _a2.r;
416
+ if (r.length)
417
+ err(8);
418
+ return s;
419
+ }
420
+ }
421
+ var slzh = function(d, b) {
422
+ return b + 30 + b2(d, b + 26) + b2(d, b + 28);
423
+ };
424
+ var zh = function(d, b, z) {
425
+ var fnl = b2(d, b + 28), efl = b2(d, b + 30), fn = strFromU8(d.subarray(b + 46, b + 46 + fnl), !(b2(d, b + 8) & 2048)), es = b + 46 + fnl;
426
+ var _a2 = z64hs(d, es, efl, z, b4(d, b + 20), b4(d, b + 24), b4(d, b + 42)), sc = _a2[0], su = _a2[1], off = _a2[2];
427
+ return [b2(d, b + 10), sc, su, fn, es + efl + b2(d, b + 32), off];
428
+ };
429
+ var z64hs = function(d, b, l, z, sc, su, off) {
430
+ var nsc = sc == 4294967295, nsu = su == 4294967295, noff = off == 4294967295, e = b + l;
431
+ var nf = nsc + nsu + noff;
432
+ if (z && nf) {
433
+ for (; b + 4 < e; b += 4 + b2(d, b + 2)) {
434
+ if (b2(d, b) == 1) {
435
+ return [
436
+ nsc ? b8(d, b + 4 + 8 * nsu) : sc,
437
+ nsu ? b8(d, b + 4) : su,
438
+ noff ? b8(d, b + 4 + 8 * (nsu + nsc)) : off,
439
+ 1
440
+ ];
441
+ }
442
+ }
443
+ if (z < 2)
444
+ err(13);
445
+ }
446
+ return [sc, su, off, 0];
447
+ };
448
+ function unzipSync(data, opts) {
449
+ var files = {};
450
+ var e = data.length - 22;
451
+ for (; b4(data, e) != 101010256; --e) {
452
+ if (!e || data.length - e > 65558)
453
+ err(13);
454
+ }
455
+ ;
456
+ var c = b2(data, e + 8);
457
+ if (!c)
458
+ return {};
459
+ var o = b4(data, e + 16);
460
+ var z = b4(data, e - 20) == 117853008;
461
+ if (z) {
462
+ var ze = b4(data, e - 12);
463
+ z = b4(data, ze) == 101075792;
464
+ if (z) {
465
+ c = b4(data, ze + 32);
466
+ o = b4(data, ze + 48);
467
+ }
468
+ }
469
+ var fltr = opts && opts.filter;
470
+ for (var i2 = 0; i2 < c; ++i2) {
471
+ var _a2 = zh(data, o, z), c_2 = _a2[0], sc = _a2[1], su = _a2[2], fn = _a2[3], no = _a2[4], off = _a2[5], b = slzh(data, off);
472
+ o = no;
473
+ if (!fltr || fltr({
474
+ name: fn,
475
+ size: sc,
476
+ originalSize: su,
477
+ compression: c_2
478
+ })) {
479
+ if (!c_2)
480
+ files[fn] = slc(data, b, b + sc);
481
+ else if (c_2 == 8)
482
+ files[fn] = inflateSync(data.subarray(b, b + sc), { out: new u8(su) });
483
+ else
484
+ err(14, "unknown compression type " + c_2);
485
+ }
486
+ }
487
+ return files;
488
+ }
6
489
 
7
490
  // ../engine/src/color.ts
8
491
  var clamp = (n, lo = 0, hi = 255) => Math.min(hi, Math.max(lo, n));
@@ -11,7 +494,7 @@ function parseHex(hex) {
11
494
  if (!/^[0-9a-f]+$/.test(h)) return null;
12
495
  if (h.length === 3 || h.length === 4) h = h.split("").map((c) => c + c).join("");
13
496
  if (h.length !== 6 && h.length !== 8) return null;
14
- const n = (i) => parseInt(h.slice(i, i + 2), 16);
497
+ const n = (i2) => parseInt(h.slice(i2, i2 + 2), 16);
15
498
  return { r: n(0), g: n(2), b: n(4), a: h.length === 8 ? n(6) / 255 : 1 };
16
499
  }
17
500
  function hslToRgb(h, s, l) {
@@ -30,12 +513,12 @@ function parseColor(input) {
30
513
  if (!m) return null;
31
514
  const parts = m[2].split(/[\s,/]+/).filter(Boolean);
32
515
  if (parts.length < 3) return null;
33
- const num = (p, max) => p.endsWith("%") ? parseFloat(p) / 100 * max : parseFloat(p);
516
+ const num = (p, max2) => p.endsWith("%") ? parseFloat(p) / 100 * max2 : parseFloat(p);
34
517
  const alpha = parts[3] !== void 0 ? num(parts[3], 1) : 1;
35
518
  if (m[1].startsWith("rgb")) {
36
- const [r2, g2, b2] = parts.slice(0, 3).map((p) => num(p, 255));
37
- if ([r2, g2, b2].some((v) => Number.isNaN(v))) return null;
38
- return { r: clamp(r2), g: clamp(g2), b: clamp(b2), a: alpha };
519
+ const [r2, g2, b3] = parts.slice(0, 3).map((p) => num(p, 255));
520
+ if ([r2, g2, b3].some((v) => Number.isNaN(v))) return null;
521
+ return { r: clamp(r2), g: clamp(g2), b: clamp(b3), a: alpha };
39
522
  }
40
523
  const [h, sat, l] = [parseFloat(parts[0]), parseFloat(parts[1]), parseFloat(parts[2])];
41
524
  if ([h, sat, l].some((v) => Number.isNaN(v))) return null;
@@ -51,11 +534,11 @@ function rgbToLab({ r, g, b }) {
51
534
  return v <= 0.04045 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4;
52
535
  };
53
536
  const [R, G, B] = [lin(r), lin(g), lin(b)];
54
- const x = (R * 0.4124 + G * 0.3576 + B * 0.1805) / 0.95047;
537
+ const x2 = (R * 0.4124 + G * 0.3576 + B * 0.1805) / 0.95047;
55
538
  const y = R * 0.2126 + G * 0.7152 + B * 0.0722;
56
539
  const z = (R * 0.0193 + G * 0.1192 + B * 0.9505) / 1.08883;
57
540
  const f = (t) => t > 8856e-6 ? Math.cbrt(t) : 7.787 * t + 16 / 116;
58
- return { l: 116 * f(y) - 16, a: 500 * (f(x) - f(y)), b: 200 * (f(y) - f(z)) };
541
+ return { l: 116 * f(y) - 16, a: 500 * (f(x2) - f(y)), b: 200 * (f(y) - f(z)) };
59
542
  }
60
543
  function deltaE(a, b) {
61
544
  return Math.sqrt((a.l - b.l) ** 2 + (a.a - b.a) ** 2 + (a.b - b.b) ** 2);
@@ -68,7 +551,7 @@ function contrastRatio(a, b) {
68
551
  });
69
552
  return 0.2126 * c[0] + 0.7152 * c[1] + 0.0722 * c[2];
70
553
  };
71
- const [l1, l2] = [lum(a), lum(b)].sort((x, y) => y - x);
554
+ const [l1, l2] = [lum(a), lum(b)].sort((x2, y) => y - x2);
72
555
  return (l1 + 0.05) / (l2 + 0.05);
73
556
  }
74
557
 
@@ -165,7 +648,7 @@ function normalizeFontFamily(value) {
165
648
  function isGenericFont(family) {
166
649
  return GENERIC_FONTS.has(family) || family.startsWith("var(");
167
650
  }
168
- var uniqSorted = (xs) => [...new Set(xs.map((x) => Math.round(x * 100) / 100))].sort((a, b) => a - b);
651
+ var uniqSorted = (xs) => [...new Set(xs.map((x2) => Math.round(x2 * 100) / 100))].sort((a, b) => a - b);
169
652
  function resolveConfig(config) {
170
653
  const all = leaves(config.tokens);
171
654
  const colors = [];
@@ -233,8 +716,8 @@ function* matches(re, text) {
233
716
  if (m[0].length === 0) g.lastIndex++;
234
717
  }
235
718
  }
236
- var lineBefore = (code, offset, max = 80) => {
237
- const start = Math.max(code.lastIndexOf("\n", offset - 1) + 1, offset - max);
719
+ var lineBefore = (code, offset, max2 = 80) => {
720
+ const start = Math.max(code.lastIndexOf("\n", offset - 1) + 1, offset - max2);
238
721
  return code.slice(start, offset);
239
722
  };
240
723
  var lineOf = (code, offset) => {
@@ -244,9 +727,9 @@ var lineOf = (code, offset) => {
244
727
  };
245
728
  var fmt = (n) => `${Math.round(n * 100) / 100}px`;
246
729
  function neighbours(scale, v) {
247
- const lower = [...scale].reverse().find((x) => x <= v);
248
- const upper = scale.find((x) => x >= v);
249
- const opts = [...new Set([lower, upper].filter((x) => x !== void 0))];
730
+ const lower = [...scale].reverse().find((x2) => x2 <= v);
731
+ const upper = scale.find((x2) => x2 >= v);
732
+ const opts = [...new Set([lower, upper].filter((x2) => x2 !== void 0))];
250
733
  return opts.map(fmt).join(" or ");
251
734
  }
252
735
  function pxValues(value) {
@@ -501,15 +984,15 @@ function importedComponents(code, components) {
501
984
  }
502
985
  return out;
503
986
  }
504
- function skipBalanced(code, i) {
505
- const open = code[i];
987
+ function skipBalanced(code, i2) {
988
+ const open = code[i2];
506
989
  if (open === '"' || open === "'") {
507
- const end = code.indexOf(open, i + 1);
990
+ const end = code.indexOf(open, i2 + 1);
508
991
  return end === -1 ? code.length : end + 1;
509
992
  }
510
993
  let depth = 0;
511
994
  let quote = null;
512
- for (let j = i; j < code.length; j++) {
995
+ for (let j = i2; j < code.length; j++) {
513
996
  const c = code[j];
514
997
  if (quote) {
515
998
  if (c === "\\") j++;
@@ -525,27 +1008,27 @@ function skipBalanced(code, i) {
525
1008
  function parseAttributes(code, start) {
526
1009
  const attrs = [];
527
1010
  let spread = false;
528
- let i = start;
529
- while (i < code.length) {
530
- while (/\s/.test(code[i] ?? "")) i++;
531
- const c = code[i];
1011
+ let i2 = start;
1012
+ while (i2 < code.length) {
1013
+ while (/\s/.test(code[i2] ?? "")) i2++;
1014
+ const c = code[i2];
532
1015
  if (c === void 0) return null;
533
1016
  if (c === ">") return { attrs, spread, selfClosing: false };
534
- if (c === "/" && code[i + 1] === ">") return { attrs, spread, selfClosing: true };
1017
+ if (c === "/" && code[i2 + 1] === ">") return { attrs, spread, selfClosing: true };
535
1018
  if (c === "{") {
536
1019
  spread = true;
537
- i = skipBalanced(code, i);
1020
+ i2 = skipBalanced(code, i2);
538
1021
  continue;
539
1022
  }
540
- const name = /^[A-Za-z_$][\w$:.-]*/.exec(code.slice(i, i + 80))?.[0];
1023
+ const name = /^[A-Za-z_$][\w$:.-]*/.exec(code.slice(i2, i2 + 80))?.[0];
541
1024
  if (!name) return null;
542
- attrs.push({ name, offset: i });
543
- i += name.length;
544
- while (/\s/.test(code[i] ?? "")) i++;
545
- if (code[i] === "=") {
546
- i++;
547
- while (/\s/.test(code[i] ?? "")) i++;
548
- i = skipBalanced(code, i);
1025
+ attrs.push({ name, offset: i2 });
1026
+ i2 += name.length;
1027
+ while (/\s/.test(code[i2] ?? "")) i2++;
1028
+ if (code[i2] === "=") {
1029
+ i2++;
1030
+ while (/\s/.test(code[i2] ?? "")) i2++;
1031
+ i2 = skipBalanced(code, i2);
549
1032
  }
550
1033
  }
551
1034
  return null;
@@ -626,36 +1109,36 @@ function stripComments(code, kind) {
626
1109
  return out.replace(/\/\*[\s\S]*?\*\//g, blank).replace(/(^|[^:])\/\/[^\n]*/g, (m, p1) => p1 + blank(m.slice(p1.length)));
627
1110
  }
628
1111
  const chars = out.split("");
629
- let i = 0;
1112
+ let i2 = 0;
630
1113
  let quote = null;
631
- while (i < chars.length) {
632
- const c = chars[i];
633
- const n = chars[i + 1];
1114
+ while (i2 < chars.length) {
1115
+ const c = chars[i2];
1116
+ const n = chars[i2 + 1];
634
1117
  if (quote) {
635
- if (c === "\\") i += 2;
1118
+ if (c === "\\") i2 += 2;
636
1119
  else {
637
1120
  if (c === quote) quote = null;
638
- i++;
1121
+ i2++;
639
1122
  }
640
1123
  continue;
641
1124
  }
642
1125
  if (c === '"' || c === "'" || c === "`") {
643
1126
  quote = c;
644
- i++;
1127
+ i2++;
645
1128
  continue;
646
1129
  }
647
1130
  if (c === "/" && n === "*") {
648
- const end = out.indexOf("*/", i + 2);
1131
+ const end = out.indexOf("*/", i2 + 2);
649
1132
  const stop = end === -1 ? chars.length : end + 2;
650
- for (let j = i; j < stop; j++) if (chars[j] !== "\n") chars[j] = " ";
651
- i = stop;
1133
+ for (let j = i2; j < stop; j++) if (chars[j] !== "\n") chars[j] = " ";
1134
+ i2 = stop;
652
1135
  continue;
653
1136
  }
654
- if (c === "/" && n === "/" && chars[i - 1] !== ":" && kind === "jsx") {
655
- while (i < chars.length && chars[i] !== "\n") chars[i++] = " ";
1137
+ if (c === "/" && n === "/" && chars[i2 - 1] !== ":" && kind === "jsx") {
1138
+ while (i2 < chars.length && chars[i2] !== "\n") chars[i2++] = " ";
656
1139
  continue;
657
1140
  }
658
- i++;
1141
+ i2++;
659
1142
  }
660
1143
  return chars.join("");
661
1144
  }
@@ -664,7 +1147,7 @@ var LineIndex = class {
664
1147
  text;
665
1148
  constructor(text) {
666
1149
  this.text = text;
667
- for (let i = 0; i < text.length; i++) if (text.charCodeAt(i) === 10) this.starts.push(i + 1);
1150
+ for (let i2 = 0; i2 < text.length; i2++) if (text.charCodeAt(i2) === 10) this.starts.push(i2 + 1);
668
1151
  }
669
1152
  position(offset) {
670
1153
  let lo = 0;
@@ -687,19 +1170,19 @@ var LineIndex = class {
687
1170
  };
688
1171
  function globToRegExp(glob) {
689
1172
  let re = "";
690
- for (let i = 0; i < glob.length; i++) {
691
- const c = glob[i];
1173
+ for (let i2 = 0; i2 < glob.length; i2++) {
1174
+ const c = glob[i2];
692
1175
  if (c === "*") {
693
- if (glob[i + 1] === "*") {
694
- const slash = glob[i + 2] === "/";
1176
+ if (glob[i2 + 1] === "*") {
1177
+ const slash = glob[i2 + 2] === "/";
695
1178
  re += slash ? "(?:.*/)?" : ".*";
696
- i += slash ? 2 : 1;
1179
+ i2 += slash ? 2 : 1;
697
1180
  } else re += "[^/]*";
698
1181
  } else if (c === "?") re += "[^/]";
699
1182
  else if (c === "{") {
700
- const end = glob.indexOf("}", i);
701
- re += `(?:${glob.slice(i + 1, end).split(",").map((s) => s.replace(/[.+^$()|[\]\\]/g, "\\$&")).join("|")})`;
702
- i = end;
1183
+ const end = glob.indexOf("}", i2);
1184
+ re += `(?:${glob.slice(i2 + 1, end).split(",").map((s) => s.replace(/[.+^$()|[\]\\]/g, "\\$&")).join("|")})`;
1185
+ i2 = end;
703
1186
  } else re += c.replace(/[.+^$()|[\]\\]/g, "\\$&");
704
1187
  }
705
1188
  return new RegExp(`^${re}$`);
@@ -713,7 +1196,7 @@ function makeIgnore(globs) {
713
1196
  }
714
1197
 
715
1198
  // ../engine/src/check.ts
716
- var ENGINE_VERSION = "0.3.1";
1199
+ var ENGINE_VERSION = "0.3.2";
717
1200
  var WEIGHT = { error: 4, warning: 1.5, info: 0.25 };
718
1201
  function computeScore(counts, lines) {
719
1202
  const penalty = counts.error * WEIGHT.error + counts.warning * WEIGHT.warning + counts.info * WEIGHT.info;
@@ -789,12 +1272,12 @@ var ansi = (on) => {
789
1272
  return { red: w("31"), yellow: w("33"), cyan: w("36"), green: w("32"), dim: w("2"), bold: w("1") };
790
1273
  };
791
1274
  var rendered = (r) => r.violations.length > 0 && r.violations.every((v) => v.line === 0);
792
- function formatPretty(report, { color = true, max = 200 } = {}) {
1275
+ function formatPretty(report, { color = true, max: max2 = 200 } = {}) {
793
1276
  const c = ansi(color);
794
1277
  const sev = { error: c.red, warning: c.yellow, info: c.cyan };
795
1278
  const lines = [];
796
1279
  let lastFile = "";
797
- for (const v of report.violations.slice(0, max)) {
1280
+ for (const v of report.violations.slice(0, max2)) {
798
1281
  if (v.file !== lastFile) {
799
1282
  lines.push("", c.bold(v.file));
800
1283
  lastFile = v.file;
@@ -804,7 +1287,7 @@ function formatPretty(report, { color = true, max = 200 } = {}) {
804
1287
  if (v.suggestion) lines.push(` ${" ".repeat(16)} ${c.green("\u2192")} ${v.suggestion}`);
805
1288
  if (rendered2 && v.snippet) lines.push(` ${" ".repeat(16)} ${c.dim(`on ${v.snippet}`)}`);
806
1289
  }
807
- if (report.violations.length > max) lines.push("", c.dim(`\u2026 and ${report.violations.length - max} more`));
1290
+ if (report.violations.length > max2) lines.push("", c.dim(`\u2026 and ${report.violations.length - max2} more`));
808
1291
  const s = report.score;
809
1292
  const scoreStr = s >= 80 ? c.green(String(s)) : s >= 65 ? c.yellow(String(s)) : c.red(String(s));
810
1293
  lines.push(
@@ -816,9 +1299,9 @@ function formatPretty(report, { color = true, max = 200 } = {}) {
816
1299
  }
817
1300
  var esc = (s) => s.replace(/%/g, "%25").replace(/\r/g, "%0D").replace(/\n/g, "%0A");
818
1301
  var escProp = (s) => esc(s).replace(/:/g, "%3A").replace(/,/g, "%2C");
819
- function formatGithubAnnotations(report, max = 50) {
1302
+ function formatGithubAnnotations(report, max2 = 50) {
820
1303
  const level = { error: "error", warning: "warning", info: "notice" };
821
- return report.violations.slice(0, max).map(
1304
+ return report.violations.slice(0, max2).map(
822
1305
  (v) => `::${level[v.severity]} file=${escProp(v.file)},line=${v.line},col=${v.column},title=${escProp(`Brandrail: ${title(v.ruleId)}`)}::${esc(
823
1306
  v.suggestion ? `${v.message}. ${v.suggestion}` : v.message
824
1307
  )}`
@@ -931,6 +1414,17 @@ function fontWeight(v) {
931
1414
  return WEIGHTS[slug(v)] ?? (/^\d{3}$/.test(v.trim()) ? v.trim() : v);
932
1415
  }
933
1416
  function colorValue(v) {
1417
+ if (v && typeof v === "object" && ("components" in v || "hex" in v)) {
1418
+ const c = v;
1419
+ const alpha = typeof c.alpha === "number" ? c.alpha : 1;
1420
+ const withAlpha = (hex2) => alpha < 1 ? `${hex2.slice(0, 7)}${Math.round(alpha * 255).toString(16).padStart(2, "0")}` : hex2.slice(0, 7);
1421
+ if (typeof c.hex === "string" && /^#[0-9a-f]{6}/i.test(c.hex)) return withAlpha(c.hex.toLowerCase());
1422
+ const comps = c.components?.map((x2) => typeof x2 === "number" ? x2 : 0);
1423
+ if (comps?.length === 3 && (!c.colorSpace || /srgb|display-p3/i.test(c.colorSpace))) {
1424
+ return withAlpha(toHex({ r: comps[0] * 255, g: comps[1] * 255, b: comps[2] * 255, a: 1 }));
1425
+ }
1426
+ return null;
1427
+ }
934
1428
  if (v && typeof v === "object" && "r" in v) {
935
1429
  const c = v;
936
1430
  const hex2 = toHex({ r: c.r * 255, g: c.g * 255, b: c.b * 255, a: 1 });
@@ -948,7 +1442,14 @@ function colorValue(v) {
948
1442
  const hex = toHex(rgb);
949
1443
  return rgb.a < 1 ? `${hex}${Math.round(rgb.a * 255).toString(16).padStart(2, "0")}` : hex;
950
1444
  }
951
- var dim = (v) => typeof v === "number" ? `${Math.round(v * 100) / 100}px` : String(v).trim();
1445
+ var dim = (v) => {
1446
+ if (v && typeof v === "object" && "value" in v) {
1447
+ const d = v;
1448
+ const n = typeof d.value === "number" ? d.value : parseFloat(d.value);
1449
+ return d.unit === "rem" ? `${Math.round(n * 16 * 100) / 100}px` : `${Math.round(n * 100) / 100}px`;
1450
+ }
1451
+ return typeof v === "number" ? `${Math.round(v * 100) / 100}px` : String(v).trim();
1452
+ };
952
1453
  function classify(name, kind, value, opts = {}) {
953
1454
  const parts = segs(name);
954
1455
  const hay = slug(`${opts.context ?? ""} ${name}`);
@@ -980,7 +1481,7 @@ function classify(name, kind, value, opts = {}) {
980
1481
  return null;
981
1482
  }
982
1483
  const v = dim(value);
983
- const has = (...scope) => scope.some((x) => scopes.includes(x));
1484
+ const has = (...scope) => scope.some((x2) => scopes.includes(x2));
984
1485
  const nm = (...prefixes) => stripPrefix(last, ...prefixes) || last;
985
1486
  if (has("CORNER_RADIUS") || /radius|(^|-)rad(-|$)|corner|rounded/.test(hay)) return { path: `radius.${nm("radius", "rad", "corner", "rounded")}`, type: "dimension", value: v };
986
1487
  if (has("FONT_SIZE") || /font-size|text-size/.test(hay)) return { path: `font.size.${nm("font-size", "text-size", "size")}`, type: "dimension", value: v };
@@ -990,7 +1491,7 @@ function classify(name, kind, value, opts = {}) {
990
1491
  if (/icon/.test(last)) return { path: `size.icon.${nm("icon")}`, type: "dimension", value: v };
991
1492
  if (has("GAP") || /spac|gap|padding|margin|inset|(^|-)space(-|$)/.test(hay)) return { path: `space.${nm("spacing", "space", "gap")}`, type: "dimension", value: v };
992
1493
  if (has("STROKE_FLOAT") || /stroke|border-width/.test(hay)) return { path: `border.width.${nm("border-width", "stroke")}`, type: "dimension", value: v };
993
- if (has("WIDTH_HEIGHT") || /size|width|height/.test(hay)) return { path: ["size", ...rest.filter((x) => x !== "size"), last].join("."), type: "dimension", value: v };
1494
+ if (has("WIDTH_HEIGHT") || /size|width|height/.test(hay)) return { path: ["size", ...rest.filter((x2) => x2 !== "size"), last].join("."), type: "dimension", value: v };
994
1495
  return { path: ["number", ...parts].join("."), type: "number", value: String(value) };
995
1496
  }
996
1497
  function rawLeaves(node, prefix, out, studio, inherited) {
@@ -1051,8 +1552,8 @@ function flattenTokens(doc) {
1051
1552
  if (!v) continue;
1052
1553
  const d = r.dark !== void 0 ? colorValue(r.dark) : null;
1053
1554
  out.push({ path: r.path, type: "color", value: v, ...d ? { dark: d } : {}, ...r.description ? { description: r.description } : {} });
1054
- } else if (typeof r.value === "string" || typeof r.value === "number") {
1055
- const type = t === "fontfamily" || t === "fontfamilies" ? "fontFamily" : t === "fontweight" || t === "fontweights" ? "fontWeight" : TYPE_KIND[t] === "number" ? "dimension" : t === "number" ? "number" : /^font\.family\./.test(r.path) ? "fontFamily" : typeof r.value === "number" ? "number" : /px$|rem$/.test(String(r.value)) ? "dimension" : "string";
1555
+ } else if (typeof r.value === "string" || typeof r.value === "number" || r.value && typeof r.value === "object" && "unit" in r.value) {
1556
+ const type = typeof r.value === "object" ? "dimension" : t === "fontfamily" || t === "fontfamilies" ? "fontFamily" : t === "fontweight" || t === "fontweights" ? "fontWeight" : TYPE_KIND[t] === "number" ? "dimension" : t === "number" ? "number" : /^font\.family\./.test(r.path) ? "fontFamily" : typeof r.value === "number" ? "number" : /px$|rem$/.test(String(r.value)) ? "dimension" : "string";
1056
1557
  out.push({ path: r.path, type, value: type === "dimension" ? dim(r.value) : String(r.value), ...r.description ? { description: r.description } : {} });
1057
1558
  }
1058
1559
  }
@@ -1111,8 +1612,8 @@ function buildDtcg(tokens) {
1111
1612
  for (const t of tokens) {
1112
1613
  const parts = t.path.split(".");
1113
1614
  let node = tree;
1114
- parts.forEach((p, i) => {
1115
- if (i === parts.length - 1) {
1615
+ parts.forEach((p, i2) => {
1616
+ if (i2 === parts.length - 1) {
1116
1617
  node[p] = {
1117
1618
  $type: t.type,
1118
1619
  $value: t.value,
@@ -1209,7 +1710,7 @@ function fromFigmaVariables(meta, opts = {}) {
1209
1710
  if (!c) continue;
1210
1711
  let darkValue;
1211
1712
  if (c.type === "color") {
1212
- const hasDarkSomewhere = Object.values(cols).some((x) => x.modes.some((m) => DARK.test(m.name)));
1713
+ const hasDarkSomewhere = Object.values(cols).some((x2) => x2.modes.some((m) => DARK.test(m.name)));
1213
1714
  if (hasDarkSomewhere) {
1214
1715
  const d = colorValue(resolve4(v, "dark"));
1215
1716
  if (d && d !== c.value) darkValue = d;
@@ -1243,7 +1744,7 @@ function fromFigmaStyles(styles, opts = {}) {
1243
1744
  }
1244
1745
  }
1245
1746
  const fams = [...families.entries()].sort((a, b) => b[1] - a[1]);
1246
- fams.forEach(([f], i) => out.push({ path: `font.family.${i === 0 ? "sans" : /mono|code/i.test(f) ? "mono" : slug(f)}`, type: "fontFamily", value: f }));
1747
+ fams.forEach(([f], i2) => out.push({ path: `font.family.${i2 === 0 ? "sans" : /mono|code/i.test(f) ? "mono" : slug(f)}`, type: "fontFamily", value: f }));
1247
1748
  const notes = skipped ? [`${skipped} fill style${skipped === 1 ? "" : "s"} (gradients or images) were skipped.`] : [];
1248
1749
  return finish("figma-styles", out, notes, opts);
1249
1750
  }
@@ -1261,7 +1762,7 @@ function fromModeObject(obj, opts = {}) {
1261
1762
  const leafSets = modes.map(([name, v]) => {
1262
1763
  const l = [];
1263
1764
  plainLeaves(v, [], l);
1264
- return { name, leaves: l, keys: new Set(l.map((x) => x.key.join("/"))) };
1765
+ return { name, leaves: l, keys: new Set(l.map((x2) => x2.key.join("/"))) };
1265
1766
  });
1266
1767
  const groups = [];
1267
1768
  for (const m of leafSets) {
@@ -1310,7 +1811,7 @@ function fromModeObject(obj, opts = {}) {
1310
1811
  if (!c) continue;
1311
1812
  let d;
1312
1813
  if (c.type === "color") {
1313
- const darkLeaf = dark && dark !== def ? dark.leaves.find((x) => x.key.join("/") === l.key.join("/")) : void 0;
1814
+ const darkLeaf = dark && dark !== def ? dark.leaves.find((x2) => x2.key.join("/") === l.key.join("/")) : void 0;
1314
1815
  const dv = colorValue(deref(darkLeaf ? darkLeaf.value : l.value, "dark"));
1315
1816
  if (dv && dv !== c.value) d = dv;
1316
1817
  }
@@ -1320,12 +1821,125 @@ function fromModeObject(obj, opts = {}) {
1320
1821
  for (const p of pending) out.push({ ...p.t, referenced: referenced.has(p.key) });
1321
1822
  return finish("mode-object", out, notes, opts);
1322
1823
  }
1824
+ var figmaExt = (node, key) => {
1825
+ const ext = node.$extensions;
1826
+ if (!ext) return void 0;
1827
+ if (`com.figma.${key}` in ext) return ext[`com.figma.${key}`];
1828
+ const nested = ext["com.figma"];
1829
+ return nested?.[key];
1830
+ };
1831
+ function fileLeaves(node, prefix, out, inherited) {
1832
+ if (!node || typeof node !== "object" || Array.isArray(node)) return;
1833
+ const o = node;
1834
+ const groupType = o.$type ?? inherited;
1835
+ if ("$value" in o) {
1836
+ const alias = figmaExt(o, "aliasData");
1837
+ const scopes = figmaExt(o, "scopes");
1838
+ out.push({
1839
+ path: prefix,
1840
+ type: o.$type ?? groupType,
1841
+ value: o.$value,
1842
+ description: o.$description,
1843
+ scopes: Array.isArray(scopes) ? scopes : void 0,
1844
+ aliasTarget: alias?.targetVariableName?.replace(/\//g, "."),
1845
+ hidden: figmaExt(o, "hiddenFromPublishing") === true || void 0
1846
+ });
1847
+ return;
1848
+ }
1849
+ for (const [k, v] of Object.entries(o)) {
1850
+ if (k.startsWith("$")) continue;
1851
+ fileLeaves(v, prefix ? `${prefix}.${k}` : k, out, groupType);
1852
+ }
1853
+ }
1854
+ var hasFigmaExt = (node, depth = 0) => {
1855
+ if (!node || typeof node !== "object" || depth > 12) return false;
1856
+ const ext = node.$extensions;
1857
+ if (ext && Object.keys(ext).some((k) => k.startsWith("com.figma"))) return true;
1858
+ return Object.values(node).some((v) => hasFigmaExt(v, depth + 1));
1859
+ };
1860
+ function modeOf(file) {
1861
+ const root = file.data ?? {};
1862
+ const parts = file.name.replace(/\\/g, "/").split("/").filter(Boolean);
1863
+ const base = (parts.pop() ?? "tokens").replace(/(\.tokens)?\.json$/i, "");
1864
+ const named = figmaExt(root, "modeName");
1865
+ const split = base.match(/^(.+?)[\s._-]+(light|dark|night|day|default|mode[\s_-]*\d+)$/i);
1866
+ return {
1867
+ collection: parts.join("/") || (split ? split[1] : ""),
1868
+ mode: typeof named === "string" ? named : split ? split[2] : base
1869
+ };
1870
+ }
1871
+ function importTokenFiles(files, opts = {}) {
1872
+ const parsed = files.filter((f) => f.data && typeof f.data === "object");
1873
+ if (!parsed.length) throw new Error("No JSON token files found.");
1874
+ const figma = parsed.some((f) => hasFigmaExt(f.data));
1875
+ if (parsed.length === 1 && !figma) return importTokens(parsed[0].data, opts);
1876
+ const notes = [];
1877
+ const withMode = parsed.map((f) => ({ ...f, ...modeOf(f) }));
1878
+ const byCollection = /* @__PURE__ */ new Map();
1879
+ for (const f of withMode) byCollection.set(f.collection, [...byCollection.get(f.collection) ?? [], f]);
1880
+ const light = /* @__PURE__ */ new Map();
1881
+ const darkOverrides = /* @__PURE__ */ new Map();
1882
+ for (const [collection, list] of byCollection) {
1883
+ const dark = list.find((f) => DARK.test(f.mode));
1884
+ const def = list.find((f) => LIGHT.test(f.mode) && !DARK.test(f.mode)) ?? list.find((f) => !DARK.test(f.mode)) ?? list[0];
1885
+ if (list.length > 1) {
1886
+ const label = collection ? `\u201C${collection}\u201D` : "The files";
1887
+ notes.push(`${label}: modes ${list.map((f) => f.mode).join(", ")}; used \u201C${def.mode}\u201D${dark && dark !== def ? ` (and \u201C${dark.mode}\u201D as dark)` : ""}.`);
1888
+ }
1889
+ const read = (f, into) => {
1890
+ const leaves2 = [];
1891
+ fileLeaves(f.data, "", leaves2);
1892
+ for (const l of leaves2) into.set(l.path, { path: l.path, type: l.type, value: l.value, description: l.description, leaf: l });
1893
+ };
1894
+ read(def, light);
1895
+ if (dark && dark !== def) read(dark, darkOverrides);
1896
+ }
1897
+ const lightRaws = resolveRefs([...light.values()]);
1898
+ const darkRaws = resolveRefs([...light.values()].map((r) => darkOverrides.get(r.path) ?? r));
1899
+ const darkByPath = new Map(darkRaws.map((r) => [r.path, r.value]));
1900
+ const referenced = /* @__PURE__ */ new Set();
1901
+ for (const r of [...light.values(), ...darkOverrides.values()]) {
1902
+ if (r.leaf.aliasTarget) referenced.add(r.leaf.aliasTarget);
1903
+ const m = typeof r.value === "string" ? r.value.trim().match(REF) : null;
1904
+ if (m) referenced.add(m[1]);
1905
+ }
1906
+ const out = [];
1907
+ let hidden = 0;
1908
+ for (const r of lightRaws) {
1909
+ const leaf = light.get(r.path).leaf;
1910
+ if (leaf.hidden) {
1911
+ hidden++;
1912
+ continue;
1913
+ }
1914
+ const t = (r.type ?? "").toLowerCase();
1915
+ const kind = t === "color" ? "color" : t === "boolean" ? "boolean" : t === "string" || t === "fontfamily" || t === "fontweight" ? "string" : typeof r.value === "string" && !/^-?[\d.]+(px|rem)?$/.test(r.value) && !colorValue(r.value) ? "string" : colorValue(r.value) && t !== "number" && t !== "dimension" ? "color" : "number";
1916
+ let tok;
1917
+ if (figma) {
1918
+ const value = kind === "number" && r.value && typeof r.value === "object" ? parseFloat(dim(r.value)) : r.value;
1919
+ tok = classify(r.path.replace(/\./g, "/"), kind, value, { scopes: leaf.scopes?.filter((x2) => x2 !== "ALL_SCOPES") });
1920
+ } else {
1921
+ const flat = flattenTokens({ x: { $type: r.type, $value: r.value } })[0];
1922
+ tok = flat ? { path: flat.type === "color" && !r.path.startsWith("color.") ? `color.${r.path}` : r.path, type: flat.type, value: flat.value } : null;
1923
+ }
1924
+ if (!tok) continue;
1925
+ let darkValue;
1926
+ if (tok.type === "color" && darkOverrides.size) {
1927
+ const d = colorValue(darkByPath.get(r.path));
1928
+ if (d && d !== tok.value) darkValue = d;
1929
+ }
1930
+ out.push({ ...tok, ...darkValue ? { dark: darkValue } : {}, ...r.description ? { description: r.description } : {}, referenced: referenced.has(r.path) });
1931
+ }
1932
+ if (hidden) notes.push(`${hidden} variable${hidden === 1 ? "" : "s"} hidden from publishing were skipped.`);
1933
+ return finish(figma ? "figma-export" : "dtcg", out, notes, opts);
1934
+ }
1323
1935
  function importTokens(input, opts = {}) {
1324
1936
  if (!input || typeof input !== "object") throw new Error("Expected a JSON object.");
1325
1937
  const o = input;
1938
+ if (Array.isArray(o.$files)) return importTokenFiles(o.$files, opts);
1326
1939
  const meta = o.meta ?? o;
1327
1940
  if (meta.variables && meta.variableCollections) return fromFigmaVariables(meta, opts);
1328
1941
  if (Array.isArray(o.styles) && o.styles.every((s) => s && "styleType" in s)) return fromFigmaStyles(o.styles, opts);
1942
+ if (hasDollarValue(o) && hasFigmaExt(o)) return importTokenFiles([{ name: "tokens.json", data: o }], opts);
1329
1943
  if (hasDollarValue(o) || hasStudioValue(o)) {
1330
1944
  const studio = !hasDollarValue(o);
1331
1945
  const flat = flattenTokens(o);
@@ -1416,7 +2030,7 @@ function generateInstructions(config, opts = {}) {
1416
2030
  const radiusVars = named(/^radius\./);
1417
2031
  const sizeVars = named(/^font\.size\./);
1418
2032
  lines.push(`- Spacing (padding, margin, gap) must be one of: ${r.spacing.filter((s) => s > 2).map((s) => `${s}px`).join(", ")}.${spaceVars.length ? ` Prefer the variables: ${spaceVars.join(", ")}.` : ""}`);
1419
- lines.push(`- Corner radius must be one of: ${r.radii.filter((x) => x > 0 && x < 999).map((x) => `${x}px`).join(", ")} (or fully rounded).${radiusVars.length ? ` Variables: ${radiusVars.join(", ")}.` : ""}`);
2033
+ lines.push(`- Corner radius must be one of: ${r.radii.filter((x2) => x2 > 0 && x2 < 999).map((x2) => `${x2}px`).join(", ")} (or fully rounded).${radiusVars.length ? ` Variables: ${radiusVars.join(", ")}.` : ""}`);
1420
2034
  if (sizeVars.length) lines.push(`- Font sizes come from the type scale: ${sizeVars.join(", ")}. Matching line heights are \`--font-line-height-*\`.`);
1421
2035
  if (r.fonts.length) lines.push(`- Fonts: use ${r.fontVars.map((v) => `\`var(${v})\``).join(" or ")}; never set another font-family.`);
1422
2036
  lines.push("");
@@ -1589,9 +2203,9 @@ function collectRenderedNodes(maxNodes = 3e3) {
1589
2203
  }
1590
2204
  var near = (scale, v, tol = 0.5) => scale.some((s) => Math.abs(s - v) <= tol);
1591
2205
  var neighbours2 = (scale, v) => {
1592
- const lo = [...scale].reverse().find((x) => x <= v);
1593
- const hi = scale.find((x) => x >= v);
1594
- return [...new Set([lo, hi].filter((x) => x !== void 0))].map((x) => `${x}px`).join(" or ");
2206
+ const lo = [...scale].reverse().find((x2) => x2 <= v);
2207
+ const hi = scale.find((x2) => x2 >= v);
2208
+ return [...new Set([lo, hi].filter((x2) => x2 !== void 0))].map((x2) => `${x2}px`).join(" or ");
1595
2209
  };
1596
2210
  function closestToken(config, rgb) {
1597
2211
  const lab = rgbToLab(rgb);
@@ -1657,17 +2271,17 @@ function checkRendered(pages, configInput, opts = {}) {
1657
2271
  const bg = n.effectiveBackground ? parseColor(n.effectiveBackground) : null;
1658
2272
  if (fg && bg && fg.a > 0.05) {
1659
2273
  const blended = fg.a < 1 ? { r: fg.r * fg.a + bg.r * (1 - fg.a), g: fg.g * fg.a + bg.g * (1 - fg.a), b: fg.b * fg.a + bg.b * (1 - fg.a), a: 1 } : fg;
1660
- const ratio = contrastRatio(blended, bg);
2274
+ const ratio2 = contrastRatio(blended, bg);
1661
2275
  const large = n.fontSize >= 24 || n.fontSize >= 18.66 && n.fontWeight >= 700;
1662
2276
  const min = large ? 3 : 4.5;
1663
- if (ratio < min) {
2277
+ if (ratio2 < min) {
1664
2278
  const pair = `${colorLabel(fg)} on ${toHex(bg)}`;
1665
2279
  add(
1666
2280
  `contrast:${pair}:${large}`,
1667
2281
  {
1668
2282
  ruleId: "text-contrast",
1669
2283
  severity: "error",
1670
- message: (c) => `${pair} is ${ratio.toFixed(2)}:1, below ${min}:1${large ? " for large text" : ""} (${c} element${c === 1 ? "" : "s"})`,
2284
+ message: (c) => `${pair} is ${ratio2.toFixed(2)}:1, below ${min}:1${large ? " for large text" : ""} (${c} element${c === 1 ? "" : "s"})`,
1671
2285
  suggestion: "Use the Brand Kit's text colours on its background colours, e.g. fg.default on canvas.default",
1672
2286
  sample: n.text
1673
2287
  },
@@ -1847,15 +2461,15 @@ var px2 = (v) => {
1847
2461
  const m = v.trim().match(/^(-?\d*\.?\d+)(px|rem)?$/);
1848
2462
  return m ? Number.parseFloat(m[1]) * (m[2] === "rem" ? 16 : 1) : null;
1849
2463
  };
1850
- var nearest2 = (values, v) => values.reduce((best, x) => Math.abs(x - v) < Math.abs(best - v) ? x : best, values[0]);
2464
+ var nearest2 = (values, v) => values.reduce((best, x2) => Math.abs(x2 - v) < Math.abs(best - v) ? x2 : best, values[0]);
1851
2465
  function luminance(hex) {
1852
2466
  const n = hex.replace("#", "").slice(0, 6);
1853
- const [r, g, b] = [0, 2, 4].map((i) => parseInt(n.slice(i, i + 2), 16) / 255).map((c) => c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4);
2467
+ const [r, g, b] = [0, 2, 4].map((i2) => parseInt(n.slice(i2, i2 + 2), 16) / 255).map((c) => c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4);
1854
2468
  return 0.2126 * r + 0.7152 * g + 0.0722 * b;
1855
2469
  }
1856
2470
  var contrast = (a, b) => {
1857
- const [x, y] = [luminance(a), luminance(b)].sort((p, q) => q - p);
1858
- return (x + 0.05) / (y + 0.05);
2471
+ const [x2, y] = [luminance(a), luminance(b)].sort((p, q) => q - p);
2472
+ return (x2 + 0.05) / (y + 0.05);
1859
2473
  };
1860
2474
  function onColor(bg, candidates) {
1861
2475
  return candidates.filter((c) => HEX.test(c)).sort((a, b) => contrast(b, bg) - contrast(a, bg))[0] ?? "#ffffff";
@@ -1895,7 +2509,7 @@ function scaleVars(s) {
1895
2509
  function colorVars(colors, dark, font) {
1896
2510
  const r = pickColorRoles(colors, dark);
1897
2511
  const find = (re) => {
1898
- const c = colors.find((x) => re.test(x.path));
2512
+ const c = colors.find((x2) => re.test(x2.path));
1899
2513
  return c ? dark ? c.dark ?? c.value : c.value : void 0;
1900
2514
  };
1901
2515
  const success = find(/success/);
@@ -1945,8 +2559,126 @@ function kitBridgeCss(tokens, opts = {}) {
1945
2559
  return [block(sel.scale, scales), block(sel.light, colorVars(colors, false, sc.font)), block(sel.dark, colorVars(colors, true, sc.font))].filter(Boolean).join("\n");
1946
2560
  }
1947
2561
 
2562
+ // ../engine/src/brand-kit-audit.ts
2563
+ var ratio = (a, b) => {
2564
+ const x2 = parseColor(a);
2565
+ const y = parseColor(b);
2566
+ return x2 && y ? Math.round(contrastRatio(x2, y) * 100) / 100 : 21;
2567
+ };
2568
+ var r1 = (n) => `${Math.round(n * 10) / 10}:1`;
2569
+ var STATUS = [
2570
+ { key: "success", re: /success|positive/, label: "success", usedBy: "success notifications, checks and toggles" },
2571
+ { key: "warning", re: /warning|attention|caution/, label: "warning", usedBy: "warning notifications and states" },
2572
+ { key: "danger", re: /danger|error|critical|negative/, label: "danger / error", usedBy: "errors, destructive buttons and invalid fields" }
2573
+ ];
2574
+ function auditBrandKit(tokens) {
2575
+ const colors = flattenTokens(tokens).filter((t) => t.type === "color").map((t) => ({ path: t.path, value: t.value, dark: t.dark }));
2576
+ const issues = [];
2577
+ if (!colors.length) return issues;
2578
+ const modes = colors.some((c) => c.dark) ? ["light", "dark"] : ["light"];
2579
+ const light = pickColorRoles(colors, false);
2580
+ for (const mode of modes) {
2581
+ const dark = mode === "dark";
2582
+ const r = pickColorRoles(colors, dark);
2583
+ const where = modes.length > 1 ? ` (${mode} mode)` : "";
2584
+ const paths = (...xs) => xs.filter((x2) => !!x2);
2585
+ const text = ratio(r.fg.value, r.canvas.value);
2586
+ if (text < 4.5) {
2587
+ issues.push({
2588
+ severity: "error",
2589
+ code: "text-contrast",
2590
+ mode,
2591
+ paths: paths(r.fg.path, r.canvas.path),
2592
+ message: `Body text on the page background is ${r1(text)}${where}. It needs at least 4.5:1 to be readable.`
2593
+ });
2594
+ }
2595
+ const muted = ratio(r.muted.value, r.canvas.value);
2596
+ if (muted < 4.5) {
2597
+ issues.push({
2598
+ severity: muted < 3 ? "error" : "warning",
2599
+ code: "muted-contrast",
2600
+ mode,
2601
+ paths: paths(r.muted.path, r.canvas.path),
2602
+ message: `Secondary text on the page background is ${r1(muted)}${where}. Captions and help text need 4.5:1; darken ${r.muted.path ?? "the muted colour"}.`
2603
+ });
2604
+ }
2605
+ for (const [role, what] of [
2606
+ ["primary", "primary buttons"],
2607
+ ["danger", "destructive buttons"]
2608
+ ]) {
2609
+ if (!r[role].path) continue;
2610
+ const fill = r[role].value;
2611
+ const best = Math.max(ratio(light.canvas.value, fill), ratio(light.fg.value, fill));
2612
+ if (best < 4.5) {
2613
+ issues.push({
2614
+ severity: best < 3 ? "error" : "warning",
2615
+ code: "on-fill-contrast",
2616
+ mode,
2617
+ paths: [r[role].path],
2618
+ message: `Labels on ${what} are at most ${r1(best)}${where}: neither your background nor your text colour reads well on ${fill}. Use a darker or lighter ${r[role].path} (4.5:1 for text).`
2619
+ });
2620
+ }
2621
+ }
2622
+ for (const [role, what] of [
2623
+ ["primary", "ghost buttons, selected items and focus rings"],
2624
+ ["accent", "links and highlights"]
2625
+ ]) {
2626
+ if (!r[role].path) continue;
2627
+ const vs = ratio(r[role].value, r.canvas.value);
2628
+ if (vs < 4.5) {
2629
+ issues.push({
2630
+ severity: vs < 3 ? "error" : "warning",
2631
+ code: "status-contrast",
2632
+ mode,
2633
+ paths: [r[role].path],
2634
+ message: `${r[role].path} (${r[role].value}) is ${r1(vs)} on the page background${where}. Components use it as text and outlines for ${what}, which need 4.5:1 for text and 3:1 for icons and borders. Darken it, or add a darker variant for text.`
2635
+ });
2636
+ }
2637
+ }
2638
+ for (const s of STATUS) {
2639
+ const c = colors.find((x2) => s.re.test(x2.path));
2640
+ if (!c) continue;
2641
+ const value = dark ? c.dark ?? c.value : c.value;
2642
+ const vs = ratio(value, r.canvas.value);
2643
+ if (vs < 3) {
2644
+ issues.push({
2645
+ severity: "warning",
2646
+ code: "status-contrast",
2647
+ mode,
2648
+ paths: [c.path],
2649
+ message: `${c.path} (${value}) is ${r1(vs)} on the page background${where}: too light for text or icons (3:1 for icons, 4.5:1 for text). It's fine as a fill behind dark text; add a darker variant for text.`
2650
+ });
2651
+ }
2652
+ }
2653
+ }
2654
+ if (!light.primary.path) {
2655
+ issues.push({
2656
+ severity: "warning",
2657
+ code: "missing-color",
2658
+ paths: [],
2659
+ message: "No primary colour (e.g. color.action.primary). Buttons and links fall back to the kit's default green."
2660
+ });
2661
+ }
2662
+ for (const s of STATUS) {
2663
+ if (colors.some((x2) => s.re.test(x2.path))) continue;
2664
+ issues.push({
2665
+ severity: "warning",
2666
+ code: "missing-color",
2667
+ paths: [],
2668
+ message: `No ${s.label} colour (e.g. color.status.${s.key}). It's used for ${s.usedBy}, which fall back to the kit's own colour instead of your brand.`
2669
+ });
2670
+ }
2671
+ const seen = /* @__PURE__ */ new Set();
2672
+ return issues.filter((i2) => {
2673
+ const key = `${i2.code}:${i2.paths.join(",")}:${i2.message.replace(/ \((light|dark) mode\)/, "").replace(/[\d.]+:1/g, "")}`;
2674
+ if (seen.has(key)) return false;
2675
+ seen.add(key);
2676
+ return true;
2677
+ }).sort((a, b) => a.severity === b.severity ? 0 : a.severity === "error" ? -1 : 1);
2678
+ }
2679
+
1948
2680
  // src/api.ts
1949
- var DEFAULT_API = "https://web-phi-nine-77.vercel.app";
2681
+ var DEFAULT_API = "https://www.brandrail.com.au";
1950
2682
  function apiBase() {
1951
2683
  return (process.env.BRANDRAIL_API_URL ?? DEFAULT_API).replace(/\/$/, "");
1952
2684
  }
@@ -2057,7 +2789,7 @@ function parseChangedLines(diff) {
2057
2789
  const start = Number(h[1]);
2058
2790
  const count = h[2] === void 0 ? 1 : Number(h[2]);
2059
2791
  const set = out.get(file);
2060
- for (let i = 0; i < count; i++) set.add(start + i);
2792
+ for (let i2 = 0; i2 < count; i2++) set.add(start + i2);
2061
2793
  }
2062
2794
  }
2063
2795
  return out;
@@ -2070,13 +2802,13 @@ function changedLines(cwd, base) {
2070
2802
 
2071
2803
  // src/scan.ts
2072
2804
  import { existsSync as existsSync2 } from "node:fs";
2073
- import { createRequire } from "node:module";
2805
+ import { createRequire as createRequire2 } from "node:module";
2074
2806
  import { join as join2, resolve as resolve2 } from "node:path";
2075
2807
  function loadPlaywright(cwd) {
2076
2808
  for (const base of [join2(cwd, "package.json"), import.meta.url]) {
2077
2809
  for (const name of ["playwright", "playwright-core"]) {
2078
2810
  try {
2079
- return createRequire(base)(name);
2811
+ return createRequire2(base)(name);
2080
2812
  } catch {
2081
2813
  }
2082
2814
  }
@@ -2132,7 +2864,7 @@ Usage
2132
2864
  brandrail check [paths...] Scan files and print violations
2133
2865
  brandrail init Create brandrail.config.json and AI rule files
2134
2866
  brandrail scan <url> Check a running page (localhost, staging, behind a login) in a real browser
2135
- brandrail import <file> Load tokens (DTCG, Tokens Studio, Figma variables JSON) into the config
2867
+ brandrail import <files\u2026> Load tokens into the config: DTCG, Tokens Studio, Figma variables JSON, or Figma's Export modes ZIP
2136
2868
  brandrail rules List the rules
2137
2869
 
2138
2870
  Options for check
@@ -2171,16 +2903,16 @@ function parseArgs(argv) {
2171
2903
  const [cmd = "help", ...rest] = argv;
2172
2904
  const positional = [];
2173
2905
  const flags = {};
2174
- for (let i = 0; i < rest.length; i++) {
2175
- const a = rest[i];
2906
+ for (let i2 = 0; i2 < rest.length; i2++) {
2907
+ const a = rest[i2];
2176
2908
  if (a.startsWith("--no-")) flags[a.slice(5)] = false;
2177
2909
  else if (a.startsWith("--")) {
2178
2910
  const [k, v] = a.slice(2).split("=", 2);
2179
- const next = rest[i + 1];
2911
+ const next = rest[i2 + 1];
2180
2912
  if (v !== void 0) flags[k] = v;
2181
2913
  else if (next !== void 0 && !next.startsWith("-") && ["config", "format", "fail-below", "changed", "pages", "storage-state", "wait", "width", "tokens-css"].includes(k)) {
2182
2914
  flags[k] = next;
2183
- i++;
2915
+ i2++;
2184
2916
  } else flags[k] = true;
2185
2917
  } else positional.push(a);
2186
2918
  }
@@ -2315,7 +3047,7 @@ function writeMcpConfigs(root) {
2315
3047
  for (const [k, v] of Object.entries(ours)) {
2316
3048
  if (Array.isArray(v)) {
2317
3049
  const prev = Array.isArray(existing[k]) ? existing[k] : [];
2318
- merged[k] = [...prev.filter((x) => !v.some((y) => y.id === x.id)), ...v];
3050
+ merged[k] = [...prev.filter((x2) => !v.some((y) => y.id === x2.id)), ...v];
2319
3051
  } else merged[k] = { ...existing[k] ?? {}, ...v };
2320
3052
  }
2321
3053
  } catch {
@@ -2327,7 +3059,7 @@ function writeMcpConfigs(root) {
2327
3059
  `);
2328
3060
  log(`\u2714 added brandrail to ${c.file} (${c.name})`);
2329
3061
  }
2330
- for (const c of mcpClientConfigs(url).filter((x) => !x.projectFile)) {
3062
+ for (const c of mcpClientConfigs(url).filter((x2) => !x2.projectFile)) {
2331
3063
  log(`
2332
3064
  ${c.name}: ${c.command ? `run
2333
3065
  ${c.command}
@@ -2374,7 +3106,7 @@ async function cmdScan(cwd, positional, flags) {
2374
3106
  const url = positional[0];
2375
3107
  if (!url) throw new Error("Usage: brandrail scan <url> [--pages 3] [--storage-state auth.json] [--upload]");
2376
3108
  const { config, source } = await loadConfig(cwd, flags);
2377
- const num = (k, d, max) => typeof flags[k] === "string" ? Math.min(Math.max(Number(flags[k]) || d, 0), max) : d;
3109
+ const num = (k, d, max2) => typeof flags[k] === "string" ? Math.min(Math.max(Number(flags[k]) || d, 0), max2) : d;
2378
3110
  log(`Scanning ${url} \u2026`);
2379
3111
  const { report, pages } = await scanPages(
2380
3112
  cwd,
@@ -2423,11 +3155,31 @@ Brand score ${report.score} is below ${threshold}.`);
2423
3155
  }
2424
3156
  return 0;
2425
3157
  }
3158
+ function readTokenInputs(cwd, inputs) {
3159
+ const files = [];
3160
+ const add = (name, text) => files.push({ name, data: JSON.parse(text) });
3161
+ const walk = (abs, rel) => {
3162
+ if (statSync2(abs).isDirectory()) {
3163
+ for (const e of readdirSync2(abs)) if (!e.startsWith(".")) walk(join3(abs, e), rel ? `${rel}/${e}` : e);
3164
+ } else if (/\.zip$/i.test(abs)) {
3165
+ const entries = unzipSync(new Uint8Array(readFileSync2(abs)), { filter: (e) => /\.json$/i.test(e.name) && !/(^|\/)(__MACOSX|\.)/.test(e.name) });
3166
+ for (const [name, bytes] of Object.entries(entries)) add(name, strFromU8(bytes));
3167
+ } else if (/\.json$/i.test(abs)) {
3168
+ add(rel || basename(abs), readFileSync2(abs, "utf8"));
3169
+ }
3170
+ };
3171
+ for (const input of inputs) {
3172
+ const abs = resolve3(cwd, input);
3173
+ walk(abs, statSync2(abs).isDirectory() ? "" : basename(abs));
3174
+ }
3175
+ return files;
3176
+ }
2426
3177
  function cmdImport(cwd, positional, flags) {
2427
- const file = positional[0];
2428
- if (!file) throw new Error("Usage: brandrail import <tokens.json> [--replace] [--primitives]");
2429
- const raw = JSON.parse(readFileSync2(resolve3(cwd, file), "utf8"));
2430
- const res = importTokens(raw, { includePrimitives: !!flags.primitives });
3178
+ if (!positional.length) throw new Error("Usage: brandrail import <tokens.json | folder | export.zip> [more files\u2026] [--replace] [--primitives]");
3179
+ const files = readTokenInputs(cwd, positional);
3180
+ if (!files.length) throw new Error("No .json token files found.");
3181
+ const res = importTokenFiles(files, { includePrimitives: !!flags.primitives });
3182
+ const file = files.length === 1 ? positional[0] : `${files.length} files`;
2431
3183
  const path = (typeof flags.config === "string" ? resolve3(cwd, flags.config) : findConfig(cwd)) ?? join3(cwd, CONFIG_FILE);
2432
3184
  const config = existsSync3(path) ? readConfig(path) : { ...starterConfig, tokens: {} };
2433
3185
  const next = flags.replace ? res.tokens : mergeTokens(config.tokens, res.tokens);
@@ -2436,6 +3188,7 @@ function cmdImport(cwd, positional, flags) {
2436
3188
  `);
2437
3189
  log(`\u2714 ${res.count} tokens from ${file} (${res.format})${res.primitives ? `, ${res.primitives} palette primitives left out (--primitives to keep)` : ""}`);
2438
3190
  for (const n of res.notes) log(` \xB7 ${n}`);
3191
+ for (const i2 of auditBrandKit(next)) log(` ${i2.severity === "error" ? "\u2716" : "!"} ${i2.message}`);
2439
3192
  log(`\u2714 ${relative2(cwd, path) || CONFIG_FILE}: ${d.changed.length} changed, ${d.added.length} added, ${d.removed.length} removed`);
2440
3193
  log("\nNext: npx brandrail init (to refresh AI rule files), then npx brandrail check");
2441
3194
  return 0;
@@ -2471,8 +3224,8 @@ async function main(argv = process.argv.slice(2)) {
2471
3224
  }
2472
3225
  main().then(
2473
3226
  (code) => process.exit(code),
2474
- (err) => {
2475
- log(`brandrail: ${err.message}`);
3227
+ (err2) => {
3228
+ log(`brandrail: ${err2.message}`);
2476
3229
  process.exit(2);
2477
3230
  }
2478
3231
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brandrail",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "description": "Keep AI-generated UI on-brand: checks code and live pages against your design system, and wires your Brand Kit into Claude Code, Cursor, Copilot and Codex.",
5
5
  "keywords": [
6
6
  "design-system",
@@ -14,7 +14,7 @@
14
14
  "tailwind",
15
15
  "brand"
16
16
  ],
17
- "homepage": "https://web-phi-nine-77.vercel.app",
17
+ "homepage": "https://www.brandrail.com.au",
18
18
  "repository": {
19
19
  "type": "git",
20
20
  "url": "git+https://github.com/ThiBaptistella/brandrail.git",
@@ -38,6 +38,7 @@
38
38
  "devDependencies": {
39
39
  "@types/node": "^22",
40
40
  "esbuild": "^0.28.2",
41
+ "fflate": "0.8.3",
41
42
  "playwright-core": "1.63.0",
42
43
  "typescript": "5.9.3",
43
44
  "@brandrail/engine": "0.1.0"