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.
- package/README.md +102 -11
- package/dist/cli.js +1072 -77
- 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/
|
|
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
|
|
12
|
-
if (!isRecord(artifact) || !isRecord(artifact.spec_layer)) return
|
|
752
|
+
function hasContentHash(artifact) {
|
|
753
|
+
if (!isRecord(artifact) || !isRecord(artifact.spec_layer)) return false;
|
|
13
754
|
const exp = artifact.spec_layer.export;
|
|
14
|
-
|
|
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" ||
|
|
19
|
-
throw new
|
|
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
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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 !==
|
|
31
|
-
throw new
|
|
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.
|
|
34
|
-
throw new
|
|
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
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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:
|
|
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
|
|
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
|
-
|
|
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,
|
|
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 ??
|
|
874
|
+
const libraryId = flags.id ?? config?.libraryId ?? manifestLibraryId(join(cwd, outDir));
|
|
86
875
|
return {
|
|
87
876
|
libraryId,
|
|
88
877
|
outDir,
|
|
89
|
-
|
|
90
|
-
|
|
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
|
-
|
|
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
|
|
1027
|
+
aiPath
|
|
160
1028
|
});
|
|
161
1029
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
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
|
|
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,
|
|
1095
|
+
opts = resolveOptions(cwd, flags, env, (outDir) => manifestAt(outDir)?.libraryId ?? null);
|
|
224
1096
|
} catch (err) {
|
|
225
|
-
io2.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
|
|
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(
|
|
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
|
|
1168
|
+
`Pulled ${bundle.fileName ?? opts.libraryId}: ${describePull(bundle, selection, selected)} (published ${result.publishedAt}).`
|
|
269
1169
|
);
|
|
270
1170
|
} catch (err) {
|
|
271
|
-
io2.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
|
|
1178
|
+
const manifestAt = manifestReader();
|
|
1179
|
+
const opts = resolved(cwd, flags, env, io2, manifestAt);
|
|
279
1180
|
if (!opts) return 1;
|
|
280
|
-
const manifest =
|
|
1181
|
+
const manifest = manifestAt(join3(cwd, opts.outDir));
|
|
281
1182
|
if (!manifest) {
|
|
282
|
-
io2.err(
|
|
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]
|
|
309
|
-
pull [--id lib_...] [--key sl_...]
|
|
310
|
-
|
|
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 = {
|
|
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
|
+
*/
|