spec-layer 0.1.0 → 0.2.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.
Files changed (3) hide show
  1. package/README.md +102 -11
  2. package/dist/cli.js +1072 -77
  3. package/package.json +2 -1
package/dist/cli.js CHANGED
@@ -1,4 +1,559 @@
1
1
  #!/usr/bin/env node
2
+ import { createRequire } from 'node:module';
3
+ const require = createRequire(import.meta.url);
4
+ var __create = Object.create;
5
+ var __defProp = Object.defineProperty;
6
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
7
+ var __getOwnPropNames = Object.getOwnPropertyNames;
8
+ var __getProtoOf = Object.getPrototypeOf;
9
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
10
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
11
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
12
+ }) : x)(function(x) {
13
+ if (typeof require !== "undefined") return require.apply(this, arguments);
14
+ throw Error('Dynamic require of "' + x + '" is not supported');
15
+ });
16
+ var __commonJS = (cb, mod) => function __require2() {
17
+ try {
18
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
19
+ } catch (e) {
20
+ throw mod = 0, e;
21
+ }
22
+ };
23
+ var __copyProps = (to, from, except, desc) => {
24
+ if (from && typeof from === "object" || typeof from === "function") {
25
+ for (let key of __getOwnPropNames(from))
26
+ if (!__hasOwnProp.call(to, key) && key !== except)
27
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
28
+ }
29
+ return to;
30
+ };
31
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
32
+ // If the importer is in node compatibility mode or this is not an ESM
33
+ // file that has been converted to a CommonJS file using a Babel-
34
+ // compatible transform (i.e. "__esModule" has not been set), then set
35
+ // "default" to the CommonJS "module.exports" for node compatibility.
36
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
37
+ mod
38
+ ));
39
+
40
+ // ../../node_modules/js-sha256/src/sha256.js
41
+ var require_sha256 = __commonJS({
42
+ "../../node_modules/js-sha256/src/sha256.js"(exports, module) {
43
+ (function() {
44
+ "use strict";
45
+ var ERROR = "input is invalid type";
46
+ var WINDOW = typeof window === "object";
47
+ var root = WINDOW ? window : {};
48
+ if (root.JS_SHA256_NO_WINDOW) {
49
+ WINDOW = false;
50
+ }
51
+ var WEB_WORKER = !WINDOW && typeof self === "object";
52
+ var NODE_JS = !root.JS_SHA256_NO_NODE_JS && typeof process === "object" && process.versions && process.versions.node && process.type != "renderer";
53
+ if (NODE_JS) {
54
+ root = global;
55
+ } else if (WEB_WORKER) {
56
+ root = self;
57
+ }
58
+ var COMMON_JS = !root.JS_SHA256_NO_COMMON_JS && typeof module === "object" && module.exports;
59
+ var AMD = typeof define === "function" && define.amd;
60
+ var ARRAY_BUFFER = !root.JS_SHA256_NO_ARRAY_BUFFER && typeof ArrayBuffer !== "undefined";
61
+ var HEX_CHARS = "0123456789abcdef".split("");
62
+ var EXTRA = [-2147483648, 8388608, 32768, 128];
63
+ var SHIFT = [24, 16, 8, 0];
64
+ var K = [
65
+ 1116352408,
66
+ 1899447441,
67
+ 3049323471,
68
+ 3921009573,
69
+ 961987163,
70
+ 1508970993,
71
+ 2453635748,
72
+ 2870763221,
73
+ 3624381080,
74
+ 310598401,
75
+ 607225278,
76
+ 1426881987,
77
+ 1925078388,
78
+ 2162078206,
79
+ 2614888103,
80
+ 3248222580,
81
+ 3835390401,
82
+ 4022224774,
83
+ 264347078,
84
+ 604807628,
85
+ 770255983,
86
+ 1249150122,
87
+ 1555081692,
88
+ 1996064986,
89
+ 2554220882,
90
+ 2821834349,
91
+ 2952996808,
92
+ 3210313671,
93
+ 3336571891,
94
+ 3584528711,
95
+ 113926993,
96
+ 338241895,
97
+ 666307205,
98
+ 773529912,
99
+ 1294757372,
100
+ 1396182291,
101
+ 1695183700,
102
+ 1986661051,
103
+ 2177026350,
104
+ 2456956037,
105
+ 2730485921,
106
+ 2820302411,
107
+ 3259730800,
108
+ 3345764771,
109
+ 3516065817,
110
+ 3600352804,
111
+ 4094571909,
112
+ 275423344,
113
+ 430227734,
114
+ 506948616,
115
+ 659060556,
116
+ 883997877,
117
+ 958139571,
118
+ 1322822218,
119
+ 1537002063,
120
+ 1747873779,
121
+ 1955562222,
122
+ 2024104815,
123
+ 2227730452,
124
+ 2361852424,
125
+ 2428436474,
126
+ 2756734187,
127
+ 3204031479,
128
+ 3329325298
129
+ ];
130
+ var OUTPUT_TYPES = ["hex", "array", "digest", "arrayBuffer"];
131
+ var blocks = [];
132
+ if (root.JS_SHA256_NO_NODE_JS || !Array.isArray) {
133
+ Array.isArray = function(obj) {
134
+ return Object.prototype.toString.call(obj) === "[object Array]";
135
+ };
136
+ }
137
+ if (ARRAY_BUFFER && (root.JS_SHA256_NO_ARRAY_BUFFER_IS_VIEW || !ArrayBuffer.isView)) {
138
+ ArrayBuffer.isView = function(obj) {
139
+ return typeof obj === "object" && obj.buffer && obj.buffer.constructor === ArrayBuffer;
140
+ };
141
+ }
142
+ var createOutputMethod = function(outputType, is224) {
143
+ return function(message) {
144
+ return new Sha256(is224, true).update(message)[outputType]();
145
+ };
146
+ };
147
+ var createMethod = function(is224) {
148
+ var method = createOutputMethod("hex", is224);
149
+ if (NODE_JS) {
150
+ method = nodeWrap(method, is224);
151
+ }
152
+ method.create = function() {
153
+ return new Sha256(is224);
154
+ };
155
+ method.update = function(message) {
156
+ return method.create().update(message);
157
+ };
158
+ for (var i = 0; i < OUTPUT_TYPES.length; ++i) {
159
+ var type = OUTPUT_TYPES[i];
160
+ method[type] = createOutputMethod(type, is224);
161
+ }
162
+ return method;
163
+ };
164
+ var nodeWrap = function(method, is224) {
165
+ var crypto = __require("crypto");
166
+ var Buffer2 = __require("buffer").Buffer;
167
+ var algorithm = is224 ? "sha224" : "sha256";
168
+ var bufferFrom;
169
+ if (Buffer2.from && !root.JS_SHA256_NO_BUFFER_FROM) {
170
+ bufferFrom = Buffer2.from;
171
+ } else {
172
+ bufferFrom = function(message) {
173
+ return new Buffer2(message);
174
+ };
175
+ }
176
+ var nodeMethod = function(message) {
177
+ if (typeof message === "string") {
178
+ return crypto.createHash(algorithm).update(message, "utf8").digest("hex");
179
+ } else {
180
+ if (message === null || message === void 0) {
181
+ throw new Error(ERROR);
182
+ } else if (message.constructor === ArrayBuffer) {
183
+ message = new Uint8Array(message);
184
+ }
185
+ }
186
+ if (Array.isArray(message) || ArrayBuffer.isView(message) || message.constructor === Buffer2) {
187
+ return crypto.createHash(algorithm).update(bufferFrom(message)).digest("hex");
188
+ } else {
189
+ return method(message);
190
+ }
191
+ };
192
+ return nodeMethod;
193
+ };
194
+ var createHmacOutputMethod = function(outputType, is224) {
195
+ return function(key, message) {
196
+ return new HmacSha256(key, is224, true).update(message)[outputType]();
197
+ };
198
+ };
199
+ var createHmacMethod = function(is224) {
200
+ var method = createHmacOutputMethod("hex", is224);
201
+ method.create = function(key) {
202
+ return new HmacSha256(key, is224);
203
+ };
204
+ method.update = function(key, message) {
205
+ return method.create(key).update(message);
206
+ };
207
+ for (var i = 0; i < OUTPUT_TYPES.length; ++i) {
208
+ var type = OUTPUT_TYPES[i];
209
+ method[type] = createHmacOutputMethod(type, is224);
210
+ }
211
+ return method;
212
+ };
213
+ function Sha256(is224, sharedMemory) {
214
+ if (sharedMemory) {
215
+ blocks[0] = blocks[16] = blocks[1] = blocks[2] = blocks[3] = blocks[4] = blocks[5] = blocks[6] = blocks[7] = blocks[8] = blocks[9] = blocks[10] = blocks[11] = blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0;
216
+ this.blocks = blocks;
217
+ } else {
218
+ this.blocks = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
219
+ }
220
+ if (is224) {
221
+ this.h0 = 3238371032;
222
+ this.h1 = 914150663;
223
+ this.h2 = 812702999;
224
+ this.h3 = 4144912697;
225
+ this.h4 = 4290775857;
226
+ this.h5 = 1750603025;
227
+ this.h6 = 1694076839;
228
+ this.h7 = 3204075428;
229
+ } else {
230
+ this.h0 = 1779033703;
231
+ this.h1 = 3144134277;
232
+ this.h2 = 1013904242;
233
+ this.h3 = 2773480762;
234
+ this.h4 = 1359893119;
235
+ this.h5 = 2600822924;
236
+ this.h6 = 528734635;
237
+ this.h7 = 1541459225;
238
+ }
239
+ this.block = this.start = this.bytes = this.hBytes = 0;
240
+ this.finalized = this.hashed = false;
241
+ this.first = true;
242
+ this.is224 = is224;
243
+ }
244
+ Sha256.prototype.update = function(message) {
245
+ if (this.finalized) {
246
+ return;
247
+ }
248
+ var notString, type = typeof message;
249
+ if (type !== "string") {
250
+ if (type === "object") {
251
+ if (message === null) {
252
+ throw new Error(ERROR);
253
+ } else if (ARRAY_BUFFER && message.constructor === ArrayBuffer) {
254
+ message = new Uint8Array(message);
255
+ } else if (!Array.isArray(message)) {
256
+ if (!ARRAY_BUFFER || !ArrayBuffer.isView(message)) {
257
+ throw new Error(ERROR);
258
+ }
259
+ }
260
+ } else {
261
+ throw new Error(ERROR);
262
+ }
263
+ notString = true;
264
+ }
265
+ var code, index = 0, i, length = message.length, blocks2 = this.blocks;
266
+ while (index < length) {
267
+ if (this.hashed) {
268
+ this.hashed = false;
269
+ blocks2[0] = this.block;
270
+ this.block = blocks2[16] = blocks2[1] = blocks2[2] = blocks2[3] = blocks2[4] = blocks2[5] = blocks2[6] = blocks2[7] = blocks2[8] = blocks2[9] = blocks2[10] = blocks2[11] = blocks2[12] = blocks2[13] = blocks2[14] = blocks2[15] = 0;
271
+ }
272
+ if (notString) {
273
+ for (i = this.start; index < length && i < 64; ++index) {
274
+ blocks2[i >>> 2] |= message[index] << SHIFT[i++ & 3];
275
+ }
276
+ } else {
277
+ for (i = this.start; index < length && i < 64; ++index) {
278
+ code = message.charCodeAt(index);
279
+ if (code < 128) {
280
+ blocks2[i >>> 2] |= code << SHIFT[i++ & 3];
281
+ } else if (code < 2048) {
282
+ blocks2[i >>> 2] |= (192 | code >>> 6) << SHIFT[i++ & 3];
283
+ blocks2[i >>> 2] |= (128 | code & 63) << SHIFT[i++ & 3];
284
+ } else if (code < 55296 || code >= 57344) {
285
+ blocks2[i >>> 2] |= (224 | code >>> 12) << SHIFT[i++ & 3];
286
+ blocks2[i >>> 2] |= (128 | code >>> 6 & 63) << SHIFT[i++ & 3];
287
+ blocks2[i >>> 2] |= (128 | code & 63) << SHIFT[i++ & 3];
288
+ } else {
289
+ code = 65536 + ((code & 1023) << 10 | message.charCodeAt(++index) & 1023);
290
+ blocks2[i >>> 2] |= (240 | code >>> 18) << SHIFT[i++ & 3];
291
+ blocks2[i >>> 2] |= (128 | code >>> 12 & 63) << SHIFT[i++ & 3];
292
+ blocks2[i >>> 2] |= (128 | code >>> 6 & 63) << SHIFT[i++ & 3];
293
+ blocks2[i >>> 2] |= (128 | code & 63) << SHIFT[i++ & 3];
294
+ }
295
+ }
296
+ }
297
+ this.lastByteIndex = i;
298
+ this.bytes += i - this.start;
299
+ if (i >= 64) {
300
+ this.block = blocks2[16];
301
+ this.start = i - 64;
302
+ this.hash();
303
+ this.hashed = true;
304
+ } else {
305
+ this.start = i;
306
+ }
307
+ }
308
+ if (this.bytes > 4294967295) {
309
+ this.hBytes += this.bytes / 4294967296 << 0;
310
+ this.bytes = this.bytes % 4294967296;
311
+ }
312
+ return this;
313
+ };
314
+ Sha256.prototype.finalize = function() {
315
+ if (this.finalized) {
316
+ return;
317
+ }
318
+ this.finalized = true;
319
+ var blocks2 = this.blocks, i = this.lastByteIndex;
320
+ blocks2[16] = this.block;
321
+ blocks2[i >>> 2] |= EXTRA[i & 3];
322
+ this.block = blocks2[16];
323
+ if (i >= 56) {
324
+ if (!this.hashed) {
325
+ this.hash();
326
+ }
327
+ blocks2[0] = this.block;
328
+ blocks2[16] = blocks2[1] = blocks2[2] = blocks2[3] = blocks2[4] = blocks2[5] = blocks2[6] = blocks2[7] = blocks2[8] = blocks2[9] = blocks2[10] = blocks2[11] = blocks2[12] = blocks2[13] = blocks2[14] = blocks2[15] = 0;
329
+ }
330
+ blocks2[14] = this.hBytes << 3 | this.bytes >>> 29;
331
+ blocks2[15] = this.bytes << 3;
332
+ this.hash();
333
+ };
334
+ Sha256.prototype.hash = function() {
335
+ var a = this.h0, b = this.h1, c = this.h2, d = this.h3, e = this.h4, f = this.h5, g = this.h6, h = this.h7, blocks2 = this.blocks, j, s0, s1, maj, t1, t2, ch, ab, da, cd, bc;
336
+ for (j = 16; j < 64; ++j) {
337
+ t1 = blocks2[j - 15];
338
+ s0 = (t1 >>> 7 | t1 << 25) ^ (t1 >>> 18 | t1 << 14) ^ t1 >>> 3;
339
+ t1 = blocks2[j - 2];
340
+ s1 = (t1 >>> 17 | t1 << 15) ^ (t1 >>> 19 | t1 << 13) ^ t1 >>> 10;
341
+ blocks2[j] = blocks2[j - 16] + s0 + blocks2[j - 7] + s1 << 0;
342
+ }
343
+ bc = b & c;
344
+ for (j = 0; j < 64; j += 4) {
345
+ if (this.first) {
346
+ if (this.is224) {
347
+ ab = 300032;
348
+ t1 = blocks2[0] - 1413257819;
349
+ h = t1 - 150054599 << 0;
350
+ d = t1 + 24177077 << 0;
351
+ } else {
352
+ ab = 704751109;
353
+ t1 = blocks2[0] - 210244248;
354
+ h = t1 - 1521486534 << 0;
355
+ d = t1 + 143694565 << 0;
356
+ }
357
+ this.first = false;
358
+ } else {
359
+ s0 = (a >>> 2 | a << 30) ^ (a >>> 13 | a << 19) ^ (a >>> 22 | a << 10);
360
+ s1 = (e >>> 6 | e << 26) ^ (e >>> 11 | e << 21) ^ (e >>> 25 | e << 7);
361
+ ab = a & b;
362
+ maj = ab ^ a & c ^ bc;
363
+ ch = e & f ^ ~e & g;
364
+ t1 = h + s1 + ch + K[j] + blocks2[j];
365
+ t2 = s0 + maj;
366
+ h = d + t1 << 0;
367
+ d = t1 + t2 << 0;
368
+ }
369
+ s0 = (d >>> 2 | d << 30) ^ (d >>> 13 | d << 19) ^ (d >>> 22 | d << 10);
370
+ s1 = (h >>> 6 | h << 26) ^ (h >>> 11 | h << 21) ^ (h >>> 25 | h << 7);
371
+ da = d & a;
372
+ maj = da ^ d & b ^ ab;
373
+ ch = h & e ^ ~h & f;
374
+ t1 = g + s1 + ch + K[j + 1] + blocks2[j + 1];
375
+ t2 = s0 + maj;
376
+ g = c + t1 << 0;
377
+ c = t1 + t2 << 0;
378
+ s0 = (c >>> 2 | c << 30) ^ (c >>> 13 | c << 19) ^ (c >>> 22 | c << 10);
379
+ s1 = (g >>> 6 | g << 26) ^ (g >>> 11 | g << 21) ^ (g >>> 25 | g << 7);
380
+ cd = c & d;
381
+ maj = cd ^ c & a ^ da;
382
+ ch = g & h ^ ~g & e;
383
+ t1 = f + s1 + ch + K[j + 2] + blocks2[j + 2];
384
+ t2 = s0 + maj;
385
+ f = b + t1 << 0;
386
+ b = t1 + t2 << 0;
387
+ s0 = (b >>> 2 | b << 30) ^ (b >>> 13 | b << 19) ^ (b >>> 22 | b << 10);
388
+ s1 = (f >>> 6 | f << 26) ^ (f >>> 11 | f << 21) ^ (f >>> 25 | f << 7);
389
+ bc = b & c;
390
+ maj = bc ^ b & d ^ cd;
391
+ ch = f & g ^ ~f & h;
392
+ t1 = e + s1 + ch + K[j + 3] + blocks2[j + 3];
393
+ t2 = s0 + maj;
394
+ e = a + t1 << 0;
395
+ a = t1 + t2 << 0;
396
+ this.chromeBugWorkAround = true;
397
+ }
398
+ this.h0 = this.h0 + a << 0;
399
+ this.h1 = this.h1 + b << 0;
400
+ this.h2 = this.h2 + c << 0;
401
+ this.h3 = this.h3 + d << 0;
402
+ this.h4 = this.h4 + e << 0;
403
+ this.h5 = this.h5 + f << 0;
404
+ this.h6 = this.h6 + g << 0;
405
+ this.h7 = this.h7 + h << 0;
406
+ };
407
+ Sha256.prototype.hex = function() {
408
+ this.finalize();
409
+ var h0 = this.h0, h1 = this.h1, h2 = this.h2, h3 = this.h3, h4 = this.h4, h5 = this.h5, h6 = this.h6, h7 = this.h7;
410
+ var hex = HEX_CHARS[h0 >>> 28 & 15] + HEX_CHARS[h0 >>> 24 & 15] + HEX_CHARS[h0 >>> 20 & 15] + HEX_CHARS[h0 >>> 16 & 15] + HEX_CHARS[h0 >>> 12 & 15] + HEX_CHARS[h0 >>> 8 & 15] + HEX_CHARS[h0 >>> 4 & 15] + HEX_CHARS[h0 & 15] + HEX_CHARS[h1 >>> 28 & 15] + HEX_CHARS[h1 >>> 24 & 15] + HEX_CHARS[h1 >>> 20 & 15] + HEX_CHARS[h1 >>> 16 & 15] + HEX_CHARS[h1 >>> 12 & 15] + HEX_CHARS[h1 >>> 8 & 15] + HEX_CHARS[h1 >>> 4 & 15] + HEX_CHARS[h1 & 15] + HEX_CHARS[h2 >>> 28 & 15] + HEX_CHARS[h2 >>> 24 & 15] + HEX_CHARS[h2 >>> 20 & 15] + HEX_CHARS[h2 >>> 16 & 15] + HEX_CHARS[h2 >>> 12 & 15] + HEX_CHARS[h2 >>> 8 & 15] + HEX_CHARS[h2 >>> 4 & 15] + HEX_CHARS[h2 & 15] + HEX_CHARS[h3 >>> 28 & 15] + HEX_CHARS[h3 >>> 24 & 15] + HEX_CHARS[h3 >>> 20 & 15] + HEX_CHARS[h3 >>> 16 & 15] + HEX_CHARS[h3 >>> 12 & 15] + HEX_CHARS[h3 >>> 8 & 15] + HEX_CHARS[h3 >>> 4 & 15] + HEX_CHARS[h3 & 15] + HEX_CHARS[h4 >>> 28 & 15] + HEX_CHARS[h4 >>> 24 & 15] + HEX_CHARS[h4 >>> 20 & 15] + HEX_CHARS[h4 >>> 16 & 15] + HEX_CHARS[h4 >>> 12 & 15] + HEX_CHARS[h4 >>> 8 & 15] + HEX_CHARS[h4 >>> 4 & 15] + HEX_CHARS[h4 & 15] + HEX_CHARS[h5 >>> 28 & 15] + HEX_CHARS[h5 >>> 24 & 15] + HEX_CHARS[h5 >>> 20 & 15] + HEX_CHARS[h5 >>> 16 & 15] + HEX_CHARS[h5 >>> 12 & 15] + HEX_CHARS[h5 >>> 8 & 15] + HEX_CHARS[h5 >>> 4 & 15] + HEX_CHARS[h5 & 15] + HEX_CHARS[h6 >>> 28 & 15] + HEX_CHARS[h6 >>> 24 & 15] + HEX_CHARS[h6 >>> 20 & 15] + HEX_CHARS[h6 >>> 16 & 15] + HEX_CHARS[h6 >>> 12 & 15] + HEX_CHARS[h6 >>> 8 & 15] + HEX_CHARS[h6 >>> 4 & 15] + HEX_CHARS[h6 & 15];
411
+ if (!this.is224) {
412
+ hex += HEX_CHARS[h7 >>> 28 & 15] + HEX_CHARS[h7 >>> 24 & 15] + HEX_CHARS[h7 >>> 20 & 15] + HEX_CHARS[h7 >>> 16 & 15] + HEX_CHARS[h7 >>> 12 & 15] + HEX_CHARS[h7 >>> 8 & 15] + HEX_CHARS[h7 >>> 4 & 15] + HEX_CHARS[h7 & 15];
413
+ }
414
+ return hex;
415
+ };
416
+ Sha256.prototype.toString = Sha256.prototype.hex;
417
+ Sha256.prototype.digest = function() {
418
+ this.finalize();
419
+ var h0 = this.h0, h1 = this.h1, h2 = this.h2, h3 = this.h3, h4 = this.h4, h5 = this.h5, h6 = this.h6, h7 = this.h7;
420
+ var arr = [
421
+ h0 >>> 24 & 255,
422
+ h0 >>> 16 & 255,
423
+ h0 >>> 8 & 255,
424
+ h0 & 255,
425
+ h1 >>> 24 & 255,
426
+ h1 >>> 16 & 255,
427
+ h1 >>> 8 & 255,
428
+ h1 & 255,
429
+ h2 >>> 24 & 255,
430
+ h2 >>> 16 & 255,
431
+ h2 >>> 8 & 255,
432
+ h2 & 255,
433
+ h3 >>> 24 & 255,
434
+ h3 >>> 16 & 255,
435
+ h3 >>> 8 & 255,
436
+ h3 & 255,
437
+ h4 >>> 24 & 255,
438
+ h4 >>> 16 & 255,
439
+ h4 >>> 8 & 255,
440
+ h4 & 255,
441
+ h5 >>> 24 & 255,
442
+ h5 >>> 16 & 255,
443
+ h5 >>> 8 & 255,
444
+ h5 & 255,
445
+ h6 >>> 24 & 255,
446
+ h6 >>> 16 & 255,
447
+ h6 >>> 8 & 255,
448
+ h6 & 255
449
+ ];
450
+ if (!this.is224) {
451
+ arr.push(h7 >>> 24 & 255, h7 >>> 16 & 255, h7 >>> 8 & 255, h7 & 255);
452
+ }
453
+ return arr;
454
+ };
455
+ Sha256.prototype.array = Sha256.prototype.digest;
456
+ Sha256.prototype.arrayBuffer = function() {
457
+ this.finalize();
458
+ var buffer = new ArrayBuffer(this.is224 ? 28 : 32);
459
+ var dataView = new DataView(buffer);
460
+ dataView.setUint32(0, this.h0);
461
+ dataView.setUint32(4, this.h1);
462
+ dataView.setUint32(8, this.h2);
463
+ dataView.setUint32(12, this.h3);
464
+ dataView.setUint32(16, this.h4);
465
+ dataView.setUint32(20, this.h5);
466
+ dataView.setUint32(24, this.h6);
467
+ if (!this.is224) {
468
+ dataView.setUint32(28, this.h7);
469
+ }
470
+ return buffer;
471
+ };
472
+ function HmacSha256(key, is224, sharedMemory) {
473
+ var i, type = typeof key;
474
+ if (type === "string") {
475
+ var bytes = [], length = key.length, index = 0, code;
476
+ for (i = 0; i < length; ++i) {
477
+ code = key.charCodeAt(i);
478
+ if (code < 128) {
479
+ bytes[index++] = code;
480
+ } else if (code < 2048) {
481
+ bytes[index++] = 192 | code >>> 6;
482
+ bytes[index++] = 128 | code & 63;
483
+ } else if (code < 55296 || code >= 57344) {
484
+ bytes[index++] = 224 | code >>> 12;
485
+ bytes[index++] = 128 | code >>> 6 & 63;
486
+ bytes[index++] = 128 | code & 63;
487
+ } else {
488
+ code = 65536 + ((code & 1023) << 10 | key.charCodeAt(++i) & 1023);
489
+ bytes[index++] = 240 | code >>> 18;
490
+ bytes[index++] = 128 | code >>> 12 & 63;
491
+ bytes[index++] = 128 | code >>> 6 & 63;
492
+ bytes[index++] = 128 | code & 63;
493
+ }
494
+ }
495
+ key = bytes;
496
+ } else {
497
+ if (type === "object") {
498
+ if (key === null) {
499
+ throw new Error(ERROR);
500
+ } else if (ARRAY_BUFFER && key.constructor === ArrayBuffer) {
501
+ key = new Uint8Array(key);
502
+ } else if (!Array.isArray(key)) {
503
+ if (!ARRAY_BUFFER || !ArrayBuffer.isView(key)) {
504
+ throw new Error(ERROR);
505
+ }
506
+ }
507
+ } else {
508
+ throw new Error(ERROR);
509
+ }
510
+ }
511
+ if (key.length > 64) {
512
+ key = new Sha256(is224, true).update(key).array();
513
+ }
514
+ var oKeyPad = [], iKeyPad = [];
515
+ for (i = 0; i < 64; ++i) {
516
+ var b = key[i] || 0;
517
+ oKeyPad[i] = 92 ^ b;
518
+ iKeyPad[i] = 54 ^ b;
519
+ }
520
+ Sha256.call(this, is224, sharedMemory);
521
+ this.update(iKeyPad);
522
+ this.oKeyPad = oKeyPad;
523
+ this.inner = true;
524
+ this.sharedMemory = sharedMemory;
525
+ }
526
+ HmacSha256.prototype = new Sha256();
527
+ HmacSha256.prototype.finalize = function() {
528
+ Sha256.prototype.finalize.call(this);
529
+ if (this.inner) {
530
+ this.inner = false;
531
+ var innerHash = this.array();
532
+ Sha256.call(this, this.is224, this.sharedMemory);
533
+ this.update(this.oKeyPad);
534
+ this.update(innerHash);
535
+ Sha256.prototype.finalize.call(this);
536
+ }
537
+ };
538
+ var exports2 = createMethod();
539
+ exports2.sha256 = exports2;
540
+ exports2.sha224 = createMethod(true);
541
+ exports2.sha256.hmac = createHmacMethod();
542
+ exports2.sha224.hmac = createHmacMethod(true);
543
+ if (COMMON_JS) {
544
+ module.exports = exports2;
545
+ } else {
546
+ root.sha256 = exports2.sha256;
547
+ root.sha224 = exports2.sha224;
548
+ if (AMD) {
549
+ define(function() {
550
+ return exports2;
551
+ });
552
+ }
553
+ }
554
+ })();
555
+ }
556
+ });
2
557
 
3
558
  // src/cli.ts
4
559
  import { parseArgs } from "node:util";
@@ -6,57 +561,287 @@ import { parseArgs } from "node:util";
6
561
  // src/commands.ts
7
562
  import { join as join3 } from "node:path";
8
563
 
9
- // src/bundle.ts
564
+ // ../extractor/src/statesMatrix.ts
565
+ var STATE_ORDER = [
566
+ "default",
567
+ "enabled",
568
+ "rest",
569
+ "hover",
570
+ "hovered",
571
+ "focus",
572
+ "focused",
573
+ "active",
574
+ "pressed",
575
+ "selected",
576
+ "filled",
577
+ "disabled",
578
+ "error",
579
+ "danger",
580
+ "warning",
581
+ "success",
582
+ "loading",
583
+ "empty",
584
+ "checked",
585
+ "invalid",
586
+ "readonly",
587
+ "visited"
588
+ ];
589
+ var STATE_VOCAB = new Set(STATE_ORDER);
590
+
591
+ // ../extractor/src/hash.ts
592
+ var import_js_sha256 = __toESM(require_sha256(), 1);
593
+
594
+ // ../extractor/src/prose/prompt.ts
595
+ var PROSE_SYSTEM_PROMPT = [
596
+ "You write component guideline prose for a design-system specification tool.",
597
+ "Your output fills three spec sections: Definition, Accessibility, and Do's & Don'ts,",
598
+ "in the voice of best-in-class design systems (Atlassian, Material, Polaris, Carbon).",
599
+ "",
600
+ "Core rule: every guideline states the rule AND the reason it matters. A rule without its",
601
+ "consequence reads like a lint message; the reason is what makes it useful guidance.",
602
+ "",
603
+ "Voice:",
604
+ '- Imperative and verb-first ("Use...", "Keep...", "Avoid...", "Never...").',
605
+ '- Write for people, not "the user". Say "people", "someone", or the concrete role.',
606
+ '- Be concrete: anchor rules in real situations (forms, dialogs, toolbars), not "certain contexts".',
607
+ "- Reference only the component's actual variants, props, and states. Never invent options it lacks.",
608
+ `- Pair a Don't with its alternative (for example, "use a Toggle instead").`,
609
+ "",
610
+ "Punctuation and formatting (this matters for readability):",
611
+ "- Never use em dashes (the long dash) or en dashes as punctuation. Use a period, comma, colon,",
612
+ " or parentheses instead. A hyphen is fine in ranges like 3-5 and in compound words.",
613
+ "- Keep sentences short. One idea per sentence. Split a long sentence into two.",
614
+ "",
615
+ "Sections (this is Markdown and renders as-is, so structure each one to scan at a glance):",
616
+ "- Overview: open with ONE sentence defining what the component is (this becomes the header).",
617
+ " Then a short, benefit-led overview: where and how it is used, the value it gives people, its",
618
+ " role in the product, and a brief guiding principle. Do NOT name specific variants or styles",
619
+ ' and do NOT give a "when to use which" guide; those belong to the Variants guide below.',
620
+ "- Accessibility: a bulleted list. Give each bullet a short bold lead-in naming the topic, then",
621
+ ' the guidance, for example "- **Keyboard:** ...". Always include a bullet flagging what the',
622
+ " design file cannot encode (focus order, live-region behaviour, immediate vs deferred effect).",
623
+ ' If the list runs long (about six or more points), group the bullets under level-3 ("###")',
624
+ " subheadings.",
625
+ "- Variants guide (optional): 1-2 sentences orienting the reader to what varies across the",
626
+ " component's options (the axes and their values). Then, when it has several meaningful types,",
627
+ ' a bulleted "when to use which type" guide, one per line, type name in bold:',
628
+ ' "- **Filled**: the single most important action.". Do not restate the plain definition.',
629
+ "- Anatomy summary (optional, 1-2 sentences): orient the reader to the component's structure,",
630
+ " naming its key parts and what each contributes. Describe what the parts are, not how to",
631
+ " configure them.",
632
+ "- Anatomy parts (optional): for each part listed in the prompt, one concise sentence naming its",
633
+ " role and why it exists. Match each part name exactly. Describe purpose, not styling. Skip a",
634
+ " part when you cannot describe it without guessing.",
635
+ "- Do's & Don'ts: one rule per bullet. Start each with a short bold lead-in stating the rule, then",
636
+ ' a sentence giving the reason: "**Use one primary action per view.** Its weight tells people',
637
+ ' where to go next." Do not add check or cross marks yourself; they are added on render.',
638
+ '- Use Markdown structure where it helps: bold lead-ins, bullet lists, and at most level-3 ("###")',
639
+ ' subheadings. Never use level-1 ("#") or level-2 ("##") headings.',
640
+ "",
641
+ "Return only the JSON object requested in the user message. No preamble and no prose outside the",
642
+ "JSON."
643
+ ].join("\n");
644
+ var FEW_SHOT_PROMPT = [
645
+ "Component: Button",
646
+ "",
647
+ "Anatomy: Container, Label, Leading icon (component)",
648
+ "",
649
+ "Variants:",
650
+ " Style: Filled \xB7 Outlined \xB7 Text",
651
+ "",
652
+ "States: Enabled, Hovered, Focused, Pressed, Disabled",
653
+ "",
654
+ `Return ONLY a JSON object with keys: definition (one sentence defining what it is, then a short benefit-led overview: where it is used, the value it gives people, its role, and a guiding principle; no style names and no when-to-use guide), variantsSummary (1-2 sentences on what varies across the options, then a bulleted "when to use which type" guide with bold type names when it has several types), anatomySummary (1-2 sentences orienting the reader to the component structure and the role of its key parts), anatomyParts (array of { name, description } where each name EXACTLY matches one of the Anatomy part names above and description is one concise sentence naming that part's role), accessibility (a bulleted list; give each bullet a short bold lead-in then the guidance; include one bullet flagging what cannot be known from the design file), interactions (Markdown under "### Mouse", "### Keyboard", "### Other" subheadings, 2-3 bullets each, anchored to the States above), designConsiderations (3-4 designer-facing bullets on contrast, state distinguishability, and missing-state flags), contentConsiderations (3-4 bullets on label writing, truncation, and internationalization), dos (string[], 3 to 5 items, each starting with a bold rule summary then the reason), donts (string[], 3 to 5 items, same shape). Use Markdown (bold lead-ins, lists, at most "###" subheadings); never "#" or "##" headings. Do not use em dashes. Do not include any prose outside the JSON.`
655
+ ].join("\n");
656
+ var FEW_SHOT_RESPONSE = {
657
+ definition: "A Button triggers an action when activated. Used across products to perform common actions, it gives people a familiar, accessible way to engage with the interface and keeps frequent tasks fast and predictable. It is essential for guiding people through workflows and performing the key actions on a screen. Create buttons that are clear, easy to identify, and accessible.",
658
+ variantsSummary: [
659
+ "Style sets the visual weight and states cover the interactive feedback; all styles share the same anatomy.",
660
+ "",
661
+ "**When to use each type:**",
662
+ "- **Filled**: the single most important action in a view.",
663
+ "- **Outlined**: secondary actions that still need a visible boundary.",
664
+ "- **Text**: low-emphasis actions in dense layouts."
665
+ ].join("\n"),
666
+ anatomySummary: "A Button pairs a text label with an optional leading icon inside a single container. The container sets the tap target and carries the visual weight.",
667
+ anatomyParts: [
668
+ { name: "Container", description: "Holds the label and icon and defines the clickable area and visual weight." },
669
+ { name: "Label", description: "Names the action in one to three words so people know what the button does." },
670
+ { name: "Leading icon", description: "Optional glyph that reinforces the label; never the only signal of meaning." }
671
+ ],
672
+ accessibility: [
673
+ '- **Semantics:** render as a native `<button>` so keyboard and screen-reader behaviour work without extra code. Use role="button" only when a non-button element must act as one.',
674
+ "- **Accessible name:** the label names the button. For an icon-only button, supply `aria-label`, since an icon alone announces nothing.",
675
+ '- **Disabled vs aria-disabled:** `disabled` removes the button from the tab order, while `aria-disabled="true"` keeps it focusable to explain why it is unavailable. The design file cannot tell you which to use.',
676
+ "- **Not in the design file:** focus order and live-region behaviour are not encoded in the design. Confirm the focus ring meets WCAG 2.1 contrast (at least 3:1) in implementation."
677
+ ].join("\n"),
678
+ interactions: [
679
+ "### Mouse",
680
+ "- Clicking anywhere on the container activates the action; the whole button is the target, not just the label.",
681
+ "- On hover the surface changes to signal it is interactive, and the cursor becomes a pointer.",
682
+ "### Keyboard",
683
+ "- Tab moves focus to the button in reading order, and a visible focus ring shows where focus landed.",
684
+ "- Enter or Space activates the focused button.",
685
+ "### Other",
686
+ "- Screen readers announce the label and the button role; an icon-only button needs an explicit name.",
687
+ "- Keep the touch target at least 44 by 44 px so it is comfortable to tap."
688
+ ].join("\n"),
689
+ designConsiderations: [
690
+ "- Keep label-to-background contrast at 4.5:1 or better in every style so the action stays legible.",
691
+ "- Make the interactive states visually distinct from each other, so hover, focus, and pressed never look identical.",
692
+ "- Confirm a visible focus state exists in build; focus styling is not always encoded in the design file."
693
+ ].join("\n"),
694
+ contentConsiderations: [
695
+ '- Write labels as a verb-first action in one to three words ("Save", "Add item"), not a vague "OK".',
696
+ "- Plan for labels that wrap or truncate; do not rely on a fixed width holding every translation.",
697
+ "- Allow for text expansion of roughly 30-40% and mirrored layout in right-to-left languages."
698
+ ].join("\n"),
699
+ dos: [
700
+ "**Use the Filled variant for the single most important action in a view.** Its weight tells people where to go next.",
701
+ '**Keep labels to one to three words, verb first** ("Save", "Add item"). People can then scan the action without reading a sentence.',
702
+ "**Use the Text variant in dense toolbars or dialogs.** A filled button there would add visual noise."
703
+ ],
704
+ donts: [
705
+ "**Don't place more than one Filled button in the same view.** Competing primary actions make it unclear which one matters most.",
706
+ "**Don't use a button for plain navigation.** Screen readers announce links and buttons differently, so use a link (`<a>`) when it just goes somewhere.",
707
+ "**Don't disable a button without explaining why.** A disabled control gives no reason and drops out of the tab order, so use inline validation instead."
708
+ ]
709
+ };
710
+
711
+ // ../extractor/src/prose/foundationPrompt.ts
712
+ var FOUNDATION_SYSTEM_PROMPT = [
713
+ "You write short descriptions of design-token groups for a design-system reference.",
714
+ "Each description sits under a group heading in a generated documentation frame.",
715
+ "",
716
+ "You are given the token names in the group and their resolved values. That is ALL you know.",
717
+ "Describe what the group is for, as its names and values actually show.",
718
+ "",
719
+ "Never invent: no component names the tokens do not mention, no counts, no accessibility",
720
+ "claims, no history, no rules the names do not support. If the names are too generic to",
721
+ "support a purpose, describe the shape of the set plainly instead and stop. A vague but true",
722
+ "sentence is correct; a specific but invented one is a defect.",
723
+ "",
724
+ "Voice:",
725
+ "- One or two sentences. Under 220 characters. No heading, no list, no markdown.",
726
+ "- Plain and factual, the tone of a peer explaining their own file.",
727
+ '- Say what the group is for and when to reach for it. Lead with the purpose, not "This group".',
728
+ '- Write for people, not "the user".',
729
+ "- Never use em dashes or en dashes. Use a period, comma, colon, or parentheses.",
730
+ '- Do not restate the heading as a sentence ("Surface colours are colours for surfaces").',
731
+ "",
732
+ "Return ONLY a JSON object mapping each group key to its description string.",
733
+ "No prose outside the JSON, no code fence."
734
+ ].join("\n");
735
+
736
+ // ../extractor/src/v5/canonical.ts
737
+ var import_js_sha2562 = __toESM(require_sha256(), 1);
738
+
739
+ // ../extractor/src/v5/componentContext.ts
740
+ var import_js_sha2563 = __toESM(require_sha256(), 1);
741
+
742
+ // ../extractor/src/libraryBundle.ts
743
+ var LIBRARY_BUNDLE_SCHEMA = "spec-layer-library-bundle";
744
+ var LibraryBundleError = class extends Error {
745
+ constructor(code, message) {
746
+ super(message);
747
+ this.name = "LibraryBundleError";
748
+ this.code = code;
749
+ }
750
+ };
10
751
  var isRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
11
- function contentHash(artifact) {
12
- if (!isRecord(artifact) || !isRecord(artifact.spec_layer)) return null;
752
+ function hasContentHash(artifact) {
753
+ if (!isRecord(artifact) || !isRecord(artifact.spec_layer)) return false;
13
754
  const exp = artifact.spec_layer.export;
14
- if (!isRecord(exp) || typeof exp.content_hash !== "string") return null;
15
- return exp.content_hash;
755
+ return isRecord(exp) && typeof exp.content_hash === "string";
16
756
  }
17
757
  function entry(v, where) {
18
- if (!isRecord(v) || typeof v.name !== "string" || typeof v.ai !== "string" || contentHash(v.artifact) === null) {
19
- throw new Error(`The ${where} entry in this bundle is malformed.`);
758
+ if (!isRecord(v) || typeof v.name !== "string" || typeof v.ai !== "string" || !hasContentHash(v.artifact)) {
759
+ throw new LibraryBundleError("malformed", `The ${where} entry in this bundle is malformed.`);
20
760
  }
21
- return v;
761
+ return { name: v.name, ai: v.ai, artifact: v.artifact };
22
762
  }
23
- function parseBundle(raw) {
24
- let parsed;
25
- try {
26
- parsed = JSON.parse(raw);
27
- } catch {
28
- throw new Error("The server response is not valid JSON.");
763
+ function supportedVersion(version) {
764
+ return typeof version === "string" && /^1\.\d+\.\d+$/.test(version);
765
+ }
766
+ function parseLibraryBundle(input) {
767
+ let parsed = input;
768
+ if (typeof input === "string") {
769
+ try {
770
+ parsed = JSON.parse(input);
771
+ } catch {
772
+ throw new LibraryBundleError("not_json", "This is not valid JSON.");
773
+ }
29
774
  }
30
- if (!isRecord(parsed) || parsed.schema !== "spec-layer-library-bundle") {
31
- throw new Error("The server response is not a Spec Layer library bundle.");
775
+ if (!isRecord(parsed) || parsed.schema !== LIBRARY_BUNDLE_SCHEMA) {
776
+ throw new LibraryBundleError("not_bundle", "This is not a Spec Layer library bundle.");
777
+ }
778
+ if (!supportedVersion(parsed.version)) {
779
+ const version = typeof parsed.version === "string" ? parsed.version : String(parsed.version);
780
+ throw new LibraryBundleError(
781
+ "unsupported_version",
782
+ `This bundle is version ${version}, which this reader does not support.`
783
+ );
32
784
  }
33
- if (typeof parsed.version !== "string" || typeof parsed.extractorVersion !== "string" || !Array.isArray(parsed.components)) {
34
- throw new Error("This bundle is missing required fields.");
785
+ if (typeof parsed.extractorVersion !== "string" || !Array.isArray(parsed.components)) {
786
+ throw new LibraryBundleError("malformed", "This bundle is missing required fields.");
35
787
  }
36
- const foundation = parsed.foundation ?? null;
37
- if (foundation !== null) {
38
- if (!isRecord(foundation) || typeof foundation.ai !== "string" || contentHash(foundation.artifact) === null) {
39
- throw new Error("The foundation entry in this bundle is malformed.");
788
+ const foundationRaw = parsed.foundation ?? null;
789
+ let foundation = null;
790
+ if (foundationRaw !== null) {
791
+ if (!isRecord(foundationRaw) || typeof foundationRaw.ai !== "string" || !hasContentHash(foundationRaw.artifact)) {
792
+ throw new LibraryBundleError("malformed", "The foundation entry in this bundle is malformed.");
40
793
  }
794
+ foundation = { ai: foundationRaw.ai, artifact: foundationRaw.artifact };
41
795
  }
42
- const components = parsed.components.map((c, i) => entry(c, `component ${i}`));
43
796
  return {
44
- schema: "spec-layer-library-bundle",
797
+ schema: LIBRARY_BUNDLE_SCHEMA,
45
798
  version: parsed.version,
46
799
  fileName: typeof parsed.fileName === "string" ? parsed.fileName : null,
47
800
  pluginVersion: typeof parsed.pluginVersion === "string" ? parsed.pluginVersion : null,
48
801
  extractorVersion: parsed.extractorVersion,
49
802
  foundation,
50
- components
803
+ components: parsed.components.map((c, i) => entry(c, `component ${i}`))
51
804
  };
52
805
  }
53
806
 
807
+ // src/bundle.ts
808
+ function parseBundle(raw) {
809
+ try {
810
+ return parseLibraryBundle(raw);
811
+ } catch (err) {
812
+ if (!(err instanceof LibraryBundleError)) throw err;
813
+ switch (err.code) {
814
+ case "not_json":
815
+ throw new Error("The server response is not valid JSON.");
816
+ case "not_bundle":
817
+ throw new Error("The server response is not a Spec Layer library bundle.");
818
+ case "unsupported_version":
819
+ throw new Error(`${err.message} Update spec-layer and try again.`);
820
+ case "malformed":
821
+ throw new Error(err.message);
822
+ }
823
+ }
824
+ }
825
+
54
826
  // src/config.ts
55
827
  import { readFileSync, writeFileSync, existsSync } from "node:fs";
56
828
  import { join } from "node:path";
57
829
  var DEFAULT_API = "https://api.spec-layer.com";
58
830
  var DEFAULT_OUT_DIR = ".speclayer";
59
831
  var CONFIG_NAME = "speclayer.json";
832
+ var invalidConfig = () => new Error(`${CONFIG_NAME} is not valid JSON. Fix or delete it, then retry.`);
833
+ function parseInclude(value) {
834
+ if (typeof value !== "object" || value === null || Array.isArray(value)) throw invalidConfig();
835
+ const record = value;
836
+ if (record.foundation !== void 0 && typeof record.foundation !== "boolean") throw invalidConfig();
837
+ if (record.components !== void 0 && !(Array.isArray(record.components) && record.components.every((c) => typeof c === "string"))) {
838
+ throw invalidConfig();
839
+ }
840
+ return {
841
+ foundation: record.foundation === void 0 ? true : record.foundation,
842
+ components: record.components === void 0 ? null : record.components
843
+ };
844
+ }
60
845
  function readConfig(cwd) {
61
846
  const path = join(cwd, CONFIG_NAME);
62
847
  if (!existsSync(path)) return null;
@@ -64,30 +849,36 @@ function readConfig(cwd) {
64
849
  try {
65
850
  parsed = JSON.parse(readFileSync(path, "utf8"));
66
851
  } catch {
67
- throw new Error(`${CONFIG_NAME} is not valid JSON. Fix or delete it, then retry.`);
68
- }
69
- if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
70
- throw new Error(`${CONFIG_NAME} is not valid JSON. Fix or delete it, then retry.`);
852
+ throw invalidConfig();
71
853
  }
854
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) throw invalidConfig();
72
855
  const record = parsed;
73
856
  return {
74
857
  ...typeof record.libraryId === "string" ? { libraryId: record.libraryId } : {},
75
- ...typeof record.outDir === "string" ? { outDir: record.outDir } : {}
858
+ ...typeof record.outDir === "string" ? { outDir: record.outDir } : {},
859
+ ...record.include !== void 0 ? { include: parseInclude(record.include) } : {}
76
860
  };
77
861
  }
78
862
  function writeConfig(cwd, config) {
79
- writeFileSync(join(cwd, CONFIG_NAME), `${JSON.stringify(config, null, 2)}
863
+ const body = {
864
+ libraryId: config.libraryId,
865
+ outDir: config.outDir,
866
+ ...config.include ? { include: config.include } : {}
867
+ };
868
+ writeFileSync(join(cwd, CONFIG_NAME), `${JSON.stringify(body, null, 2)}
80
869
  `);
81
870
  }
82
- function resolveOptions(cwd, flags, env, manifestLibraryId2) {
871
+ function resolveOptions(cwd, flags, env, manifestLibraryId) {
83
872
  const config = readConfig(cwd);
84
873
  const outDir = flags.out ?? config?.outDir ?? DEFAULT_OUT_DIR;
85
- const libraryId = flags.id ?? config?.libraryId ?? manifestLibraryId2(join(cwd, outDir));
874
+ const libraryId = flags.id ?? config?.libraryId ?? manifestLibraryId(join(cwd, outDir));
86
875
  return {
87
876
  libraryId,
88
877
  outDir,
89
- api: flags.api ?? env.SPEC_LAYER_API ?? DEFAULT_API,
90
- key: flags.key ?? env.SPEC_LAYER_KEY ?? null
878
+ // A trailing slash would build "//v1/..." paths the proxy router 404s on.
879
+ api: (flags.api ?? env.SPEC_LAYER_API ?? DEFAULT_API).replace(/\/+$/, ""),
880
+ key: flags.key ?? env.SPEC_LAYER_KEY ?? null,
881
+ ...config?.include ? { include: config.include } : {}
91
882
  };
92
883
  }
93
884
 
@@ -122,8 +913,41 @@ async function fetchBundle(opts) {
122
913
  }
123
914
 
124
915
  // src/files.ts
125
- import { mkdirSync, writeFileSync as writeFileSync2, readFileSync as readFileSync2, rmSync, renameSync, existsSync as existsSync2 } from "node:fs";
126
- import { join as join2, dirname } from "node:path";
916
+ import { mkdirSync, writeFileSync as writeFileSync2, readFileSync as readFileSync2, readdirSync, rmSync, renameSync, existsSync as existsSync2 } from "node:fs";
917
+ import { join as join2, dirname, relative, resolve, isAbsolute } from "node:path";
918
+
919
+ // src/selection.ts
920
+ var DEFAULT_SELECTION = { foundation: true, components: null };
921
+ function selectionFromFlags(flags) {
922
+ const named = flags.component !== void 0 && flags.component.length > 0 ? flags.component : null;
923
+ if (flags.only === void 0) return named ? { foundation: true, components: named } : null;
924
+ if (flags.only === "foundation") {
925
+ if (named) throw new Error("--only foundation cannot be combined with --component. Drop one of them.");
926
+ return { foundation: true, components: [] };
927
+ }
928
+ if (flags.only === "components") return { foundation: false, components: named };
929
+ throw new Error(`--only takes "foundation" or "components", not "${flags.only}".`);
930
+ }
931
+ function resolveSelection(flags, config) {
932
+ return selectionFromFlags(flags) ?? config?.include ?? DEFAULT_SELECTION;
933
+ }
934
+ var matchesName = (input, name) => slugify(input) === slugify(name);
935
+ function selectComponents(bundle, selection) {
936
+ const wanted = selection.components;
937
+ if (wanted === null) return bundle.components.map(() => true);
938
+ const hasMatch = (name) => bundle.components.some((c) => matchesName(name, c.name));
939
+ const missing = wanted.filter((name) => !hasMatch(name));
940
+ if (missing.length > 0) {
941
+ const available = bundle.components.map((c) => c.name).join(", ");
942
+ throw new Error(
943
+ `No component named ${missing.map((m) => `"${m}"`).join(", ")} in this library.
944
+ Available: ${available || "none"}.`
945
+ );
946
+ }
947
+ return bundle.components.map((c) => wanted.some((name) => matchesName(name, c.name)));
948
+ }
949
+
950
+ // src/files.ts
127
951
  function slugify(name) {
128
952
  const slug = name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
129
953
  return slug || "component";
@@ -137,7 +961,50 @@ function readManifest(outDir) {
137
961
  return null;
138
962
  }
139
963
  }
964
+ function readLocalBundle(outDir) {
965
+ const path = join2(outDir, "bundle.json");
966
+ if (!existsSync2(path)) return null;
967
+ try {
968
+ return parseBundle(readFileSync2(path, "utf8"));
969
+ } catch {
970
+ throw new Error(`${path} could not be read as a library bundle. Run spec-layer pull again.`);
971
+ }
972
+ }
973
+ function componentSlugs(bundle) {
974
+ const usedSlugs = /* @__PURE__ */ new Set();
975
+ const nextSuffix = /* @__PURE__ */ new Map();
976
+ return bundle.components.map((component) => {
977
+ const base = slugify(component.name);
978
+ let slug = base;
979
+ if (usedSlugs.has(slug)) {
980
+ let n = (nextSuffix.get(base) ?? 1) + 1;
981
+ slug = `${base}-${n}`;
982
+ while (usedSlugs.has(slug)) {
983
+ n += 1;
984
+ slug = `${base}-${n}`;
985
+ }
986
+ nextSuffix.set(base, n);
987
+ } else {
988
+ nextSuffix.set(base, 1);
989
+ }
990
+ usedSlugs.add(slug);
991
+ return slug;
992
+ });
993
+ }
994
+ function assertReplaceable(outDir, cwd) {
995
+ const rel = relative(resolve(cwd), resolve(outDir));
996
+ if (rel === "" || rel.startsWith("..") || isAbsolute(rel)) {
997
+ throw new Error('The output directory must sit inside the current directory, not be "." or a parent of it.');
998
+ }
999
+ if (existsSync2(outDir) && !existsSync2(join2(outDir, "manifest.json")) && readdirSync(outDir).length > 0) {
1000
+ throw new Error(`${outDir} exists and was not written by spec-layer pull. Choose an empty or new directory.`);
1001
+ }
1002
+ }
140
1003
  function writeBundleFiles(opts) {
1004
+ assertReplaceable(opts.outDir, opts.cwd);
1005
+ const selection = opts.selection ?? DEFAULT_SELECTION;
1006
+ const selected = selectComponents(opts.bundle, selection);
1007
+ const slugs = componentSlugs(opts.bundle);
141
1008
  const staging = `${opts.outDir}.partial`;
142
1009
  rmSync(staging, { recursive: true, force: true });
143
1010
  const written = [];
@@ -151,46 +1018,32 @@ function writeBundleFiles(opts) {
151
1018
  put("bundle.json", opts.raw);
152
1019
  const artifacts = [];
153
1020
  if (opts.bundle.foundation) {
154
- put("ai/foundation.yaml", opts.bundle.foundation.ai);
1021
+ const aiPath = selection.foundation ? "ai/foundation.yaml" : null;
1022
+ if (aiPath) put(aiPath, opts.bundle.foundation.ai);
155
1023
  artifacts.push({
156
1024
  kind: "foundation",
157
1025
  name: "foundation",
158
1026
  contentHash: opts.bundle.foundation.artifact.spec_layer.export.content_hash,
159
- aiPath: "ai/foundation.yaml"
1027
+ aiPath
160
1028
  });
161
1029
  }
162
- const usedSlugs = /* @__PURE__ */ new Set();
163
- const nextSuffix = /* @__PURE__ */ new Map();
164
- for (const component of opts.bundle.components) {
165
- const base = slugify(component.name);
166
- let slug = base;
167
- if (usedSlugs.has(slug)) {
168
- let n = (nextSuffix.get(base) ?? 1) + 1;
169
- slug = `${base}-${n}`;
170
- while (usedSlugs.has(slug)) {
171
- n += 1;
172
- slug = `${base}-${n}`;
173
- }
174
- nextSuffix.set(base, n);
175
- } else {
176
- nextSuffix.set(base, 1);
177
- }
178
- usedSlugs.add(slug);
179
- const aiPath = `ai/components/${slug}.yaml`;
180
- put(aiPath, component.ai);
1030
+ opts.bundle.components.forEach((component, i) => {
1031
+ const aiPath = selected[i] ? `ai/components/${slugs[i]}.yaml` : null;
1032
+ if (aiPath) put(aiPath, component.ai);
181
1033
  artifacts.push({
182
1034
  kind: "component",
183
1035
  name: component.name,
184
1036
  contentHash: component.artifact.spec_layer.export.content_hash,
185
1037
  aiPath
186
1038
  });
187
- }
1039
+ });
188
1040
  const manifest = {
189
1041
  libraryId: opts.libraryId,
190
1042
  publishedAt: opts.publishedAt,
191
1043
  bundleHash: opts.bundleHash,
192
1044
  pluginVersion: opts.bundle.pluginVersion,
193
1045
  extractorVersion: opts.bundle.extractorVersion,
1046
+ selection,
194
1047
  artifacts
195
1048
  };
196
1049
  put("manifest.json", `${JSON.stringify(manifest, null, 2)}
@@ -205,24 +1058,43 @@ function writeBundleFiles(opts) {
205
1058
  }
206
1059
 
207
1060
  // src/commands.ts
208
- var manifestLibraryId = (outDir) => readManifest(outDir)?.libraryId ?? null;
1061
+ var NO_LOCAL_PULL = "No local pull found. Run spec-layer pull.";
1062
+ function manifestReader() {
1063
+ const cache = /* @__PURE__ */ new Map();
1064
+ return (outDir) => {
1065
+ if (!cache.has(outDir)) cache.set(outDir, readManifest(outDir));
1066
+ return cache.get(outDir) ?? null;
1067
+ };
1068
+ }
1069
+ function sameSelection(a, b) {
1070
+ const key = (s) => JSON.stringify([s.foundation, s.components === null ? null : [...new Set(s.components.map(slugify))].sort()]);
1071
+ return key(a) === key(b);
1072
+ }
1073
+ var errorText = (err) => err instanceof Error ? err.message : String(err);
209
1074
  function runInit(cwd, flags, io2) {
210
1075
  if (!flags.id) {
211
1076
  io2.err("spec-layer init needs --id lib_... (shown in the plugin after publishing).");
212
1077
  return 1;
213
1078
  }
1079
+ let include;
1080
+ try {
1081
+ include = selectionFromFlags(flags);
1082
+ } catch (err) {
1083
+ io2.err(errorText(err));
1084
+ return 1;
1085
+ }
214
1086
  const outDir = flags.out ?? DEFAULT_OUT_DIR;
215
- writeConfig(cwd, { libraryId: flags.id, outDir });
1087
+ writeConfig(cwd, { libraryId: flags.id, outDir, ...include ? { include } : {} });
216
1088
  io2.out(`Wrote speclayer.json (library ${flags.id}, output ${outDir}).`);
217
1089
  io2.out("The pull key is never stored here. Set SPEC_LAYER_KEY in your environment or pass --key.");
218
1090
  return 0;
219
1091
  }
220
- function resolved(cwd, flags, env, io2) {
1092
+ function resolved(cwd, flags, env, io2, manifestAt) {
221
1093
  let opts;
222
1094
  try {
223
- opts = resolveOptions(cwd, flags, env, manifestLibraryId);
1095
+ opts = resolveOptions(cwd, flags, env, (outDir) => manifestAt(outDir)?.libraryId ?? null);
224
1096
  } catch (err) {
225
- io2.err(err instanceof Error ? err.message : String(err));
1097
+ io2.err(errorText(err));
226
1098
  return null;
227
1099
  }
228
1100
  if (!opts.libraryId) {
@@ -235,13 +1107,39 @@ function resolved(cwd, flags, env, io2) {
235
1107
  }
236
1108
  return opts;
237
1109
  }
1110
+ function resolvedOutDir(cwd, flags, io2) {
1111
+ try {
1112
+ return join3(cwd, flags.out ?? readConfig(cwd)?.outDir ?? DEFAULT_OUT_DIR);
1113
+ } catch (err) {
1114
+ io2.err(errorText(err));
1115
+ return null;
1116
+ }
1117
+ }
1118
+ function describePull(bundle, selection, selected) {
1119
+ const total = bundle.components.length;
1120
+ const count = selected.filter(Boolean).length;
1121
+ const components = count === total ? `${total} component${total === 1 ? "" : "s"}` : `${count} of ${total} components`;
1122
+ if (!bundle.foundation) return components;
1123
+ return selection.foundation ? `foundation + ${components}` : `${components}, no foundation`;
1124
+ }
238
1125
  async function runPull(cwd, flags, env, io2, fetcher) {
239
- const opts = resolved(cwd, flags, env, io2);
1126
+ const manifestAt = manifestReader();
1127
+ const opts = resolved(cwd, flags, env, io2, manifestAt);
240
1128
  if (!opts) return 1;
1129
+ let selection;
1130
+ try {
1131
+ selection = resolveSelection(flags, opts);
1132
+ } catch (err) {
1133
+ io2.err(errorText(err));
1134
+ return 1;
1135
+ }
1136
+ const manifest = manifestAt(join3(cwd, opts.outDir));
1137
+ const etag = manifest && sameSelection(manifest.selection ?? DEFAULT_SELECTION, selection) ? manifest.bundleHash : void 0;
241
1138
  const result = await fetchBundle({
242
1139
  api: opts.api,
243
1140
  libraryId: opts.libraryId,
244
1141
  key: opts.key,
1142
+ ...etag ? { etag } : {},
245
1143
  ...fetcher ? { fetcher } : {}
246
1144
  });
247
1145
  if (result.kind === "error") {
@@ -249,37 +1147,40 @@ async function runPull(cwd, flags, env, io2, fetcher) {
249
1147
  return 1;
250
1148
  }
251
1149
  if (result.kind === "not_modified") {
252
- io2.out("Already up to date.");
1150
+ io2.out(`Already up to date (published ${manifest?.publishedAt ?? "unknown"}).`);
253
1151
  return 0;
254
1152
  }
255
1153
  let written;
256
1154
  try {
257
1155
  const bundle = parseBundle(result.raw);
1156
+ const selected = selectComponents(bundle, selection);
258
1157
  written = writeBundleFiles({
259
1158
  outDir: join3(cwd, opts.outDir),
1159
+ cwd,
260
1160
  raw: result.raw,
261
1161
  bundle,
1162
+ selection,
262
1163
  libraryId: opts.libraryId,
263
1164
  publishedAt: result.publishedAt,
264
1165
  bundleHash: result.bundleHash
265
1166
  });
266
- const components = bundle.components.length;
267
1167
  io2.out(
268
- `Pulled ${bundle.fileName ?? opts.libraryId}: ${bundle.foundation ? "foundation + " : ""}${components} component${components === 1 ? "" : "s"} (published ${result.publishedAt}).`
1168
+ `Pulled ${bundle.fileName ?? opts.libraryId}: ${describePull(bundle, selection, selected)} (published ${result.publishedAt}).`
269
1169
  );
270
1170
  } catch (err) {
271
- io2.err(err instanceof Error ? err.message : String(err));
1171
+ io2.err(errorText(err));
272
1172
  return 1;
273
1173
  }
274
1174
  io2.out(`Wrote ${written.length} files under ${opts.outDir}/.`);
275
1175
  return 0;
276
1176
  }
277
1177
  async function runStatus(cwd, flags, env, io2, fetcher) {
278
- const opts = resolved(cwd, flags, env, io2);
1178
+ const manifestAt = manifestReader();
1179
+ const opts = resolved(cwd, flags, env, io2, manifestAt);
279
1180
  if (!opts) return 1;
280
- const manifest = readManifest(join3(cwd, opts.outDir));
1181
+ const manifest = manifestAt(join3(cwd, opts.outDir));
281
1182
  if (!manifest) {
282
- io2.err("No local pull found. Run spec-layer pull.");
1183
+ io2.err(NO_LOCAL_PULL);
283
1184
  return 2;
284
1185
  }
285
1186
  const result = await fetchBundle({
@@ -300,19 +1201,96 @@ async function runStatus(cwd, flags, env, io2, fetcher) {
300
1201
  io2.out(`Behind: remote published ${result.publishedAt}. Run spec-layer pull.`);
301
1202
  return 2;
302
1203
  }
1204
+ function runList(cwd, flags, io2) {
1205
+ const outDir = resolvedOutDir(cwd, flags, io2);
1206
+ if (!outDir) return 1;
1207
+ const manifest = readManifest(outDir);
1208
+ if (!manifest) {
1209
+ io2.err(NO_LOCAL_PULL);
1210
+ return 1;
1211
+ }
1212
+ io2.out(`Library ${manifest.libraryId}, published ${manifest.publishedAt}.`);
1213
+ const rows = manifest.artifacts.map((a) => [a.kind, a.name, a.aiPath ?? "not written", a.contentHash]);
1214
+ const widths = [0, 1, 2].map((i) => Math.max(...rows.map((r) => r[i].length)));
1215
+ for (const row of rows) {
1216
+ io2.out(row.map((cell, i) => i < 3 ? cell.padEnd(widths[i]) : cell).join(" "));
1217
+ }
1218
+ return 0;
1219
+ }
1220
+ var SHOW_USAGE = 'spec-layer show takes "foundation" or "component NAME".';
1221
+ function runShow(cwd, flags, args, io2) {
1222
+ const [target, name] = args;
1223
+ const wantsFoundation = target === "foundation" && name === void 0;
1224
+ const wantsComponent = target === "component" && typeof name === "string";
1225
+ if (!wantsFoundation && !wantsComponent) {
1226
+ io2.err(SHOW_USAGE);
1227
+ return 1;
1228
+ }
1229
+ const outDir = resolvedOutDir(cwd, flags, io2);
1230
+ if (!outDir) return 1;
1231
+ let bundle;
1232
+ try {
1233
+ bundle = readLocalBundle(outDir);
1234
+ } catch (err) {
1235
+ io2.err(errorText(err));
1236
+ return 1;
1237
+ }
1238
+ if (!bundle) {
1239
+ io2.err(NO_LOCAL_PULL);
1240
+ return 1;
1241
+ }
1242
+ let entry2;
1243
+ if (wantsFoundation) {
1244
+ if (!bundle.foundation) {
1245
+ io2.err("This library has no Foundation. Run spec-layer list to see what it holds.");
1246
+ return 1;
1247
+ }
1248
+ entry2 = bundle.foundation;
1249
+ } else {
1250
+ const matches = bundle.components.filter((c) => matchesName(name, c.name));
1251
+ if (matches.length === 0) {
1252
+ const available = bundle.components.map((c) => c.name).join(", ");
1253
+ io2.err(`No component named "${name}" in this library.
1254
+ Available: ${available || "none"}.`);
1255
+ return 1;
1256
+ }
1257
+ if (matches.length > 1) {
1258
+ io2.err(`"${name}" matches ${matches.length} components. Run spec-layer list to see them, then rename one in Figma to tell them apart.`);
1259
+ return 1;
1260
+ }
1261
+ entry2 = matches[0];
1262
+ }
1263
+ io2.write(flags.canonical ? `${JSON.stringify(entry2.artifact, null, 2)}
1264
+ ` : entry2.ai);
1265
+ return 0;
1266
+ }
303
1267
 
304
1268
  // src/cli.ts
305
1269
  var USAGE = `spec-layer <command>
306
1270
 
307
1271
  Commands:
308
- init --id lib_... [--out DIR] write speclayer.json
309
- pull [--id lib_...] [--key sl_...] fetch the library into DIR (default .speclayer)
310
- status [--id lib_...] [--key sl_...] check freshness; exits 2 when behind
1272
+ init --id lib_... [--out DIR] [selection] write speclayer.json
1273
+ pull [--id lib_...] [--key sl_...] [selection]
1274
+ fetch the library into DIR (default .speclayer)
1275
+ status [--id lib_...] [--key sl_...] check freshness; exits 2 when behind
1276
+ list list every artifact in the last pull
1277
+ show foundation | component NAME [--canonical]
1278
+ print one artifact's AI YAML (or canonical JSON)
1279
+
1280
+ Selection (pull and init; flags replace the include block in speclayer.json):
1281
+ --only foundation | components write just the foundation, or just components
1282
+ --component NAME write only this component (repeatable, matched by slug)
311
1283
 
312
1284
  Options:
313
1285
  --api URL override the API origin (default https://api.spec-layer.com)
314
1286
  The pull key comes from --key or the SPEC_LAYER_KEY environment variable.`;
315
- var io = { out: (l) => console.log(l), err: (l) => console.error(l) };
1287
+ var io = {
1288
+ out: (l) => console.log(l),
1289
+ err: (l) => console.error(l),
1290
+ write: (t) => {
1291
+ process.stdout.write(t);
1292
+ }
1293
+ };
316
1294
  async function main() {
317
1295
  let values;
318
1296
  let positionals;
@@ -323,7 +1301,10 @@ async function main() {
323
1301
  id: { type: "string" },
324
1302
  out: { type: "string" },
325
1303
  key: { type: "string" },
326
- api: { type: "string" }
1304
+ api: { type: "string" },
1305
+ only: { type: "string" },
1306
+ component: { type: "string", multiple: true },
1307
+ canonical: { type: "boolean" }
327
1308
  }
328
1309
  }));
329
1310
  } catch {
@@ -336,6 +1317,8 @@ async function main() {
336
1317
  if (command === "init") return runInit(cwd, values, io);
337
1318
  if (command === "pull") return await runPull(cwd, values, process.env, io);
338
1319
  if (command === "status") return await runStatus(cwd, values, process.env, io);
1320
+ if (command === "list") return runList(cwd, values, io);
1321
+ if (command === "show") return runShow(cwd, values, positionals.slice(1), io);
339
1322
  io.err(USAGE);
340
1323
  return 1;
341
1324
  } catch (err) {
@@ -344,3 +1327,15 @@ async function main() {
344
1327
  }
345
1328
  }
346
1329
  process.exitCode = await main();
1330
+ /*! Bundled license information:
1331
+
1332
+ js-sha256/src/sha256.js:
1333
+ (**
1334
+ * [js-sha256]{@link https://github.com/emn178/js-sha256}
1335
+ *
1336
+ * @version 0.11.1
1337
+ * @author Chen, Yi-Cyuan [emn178@gmail.com]
1338
+ * @copyright Chen, Yi-Cyuan 2014-2025
1339
+ * @license MIT
1340
+ *)
1341
+ */