@am_shork/attest 0.9.3 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1027 -168
- package/README.md +10 -25
- package/bin/attest.js +0 -0
- package/dist/cli/action.js +8 -4
- package/dist/cli/index.js +7 -2
- package/dist/cli/json.d.ts +28 -1
- package/dist/cli/json.js +10 -1
- package/dist/core/apply.js +5 -0
- package/dist/core/archive.js +26 -3
- package/dist/core/docs.d.ts +1 -1
- package/dist/core/docs.js +2 -0
- package/dist/core/gate.d.ts +8 -0
- package/dist/core/gate.js +38 -0
- package/dist/core/locate.d.ts +27 -0
- package/dist/core/locate.js +3 -2
- package/dist/core/merge.js +1 -0
- package/dist/core/red-record.js +18 -8
- package/dist/core/render.js +8 -0
- package/dist/core/schema.d.ts +4 -0
- package/dist/core/schema.js +11 -0
- package/dist/core/skill.js +25 -0
- package/dist/core/splice.d.ts +6 -6
- package/dist/core/splice.js +51 -14
- package/dist/core/static-registry.d.ts +46 -1
- package/dist/core/static-registry.js +59 -4
- package/dist/core/validator.d.ts +2 -1
- package/dist/core/validator.js +20 -1
- package/package.json +31 -17
package/dist/core/splice.js
CHANGED
|
@@ -148,11 +148,11 @@ function paramSource(value) {
|
|
|
148
148
|
* body needs one after, and putting that decision here would mean this function
|
|
149
149
|
* had to be told which case it was in anyway.
|
|
150
150
|
*
|
|
151
|
-
* `params` and `
|
|
152
|
-
* and `[]`. The schema defaults
|
|
153
|
-
* and the shorter one is what a person writing this entry by hand
|
|
154
|
-
* produced — which is the standard for a file `--apply` is merging
|
|
155
|
-
* than generating.
|
|
151
|
+
* `params`, `outOfScope` and `open` are omitted when empty rather than written
|
|
152
|
+
* as `{}` and `[]`. The schema defaults all three, so the two spellings mean the
|
|
153
|
+
* same thing, and the shorter one is what a person writing this entry by hand
|
|
154
|
+
* would have produced — which is the standard for a file `--apply` is merging
|
|
155
|
+
* into rather than generating.
|
|
156
156
|
*/
|
|
157
157
|
export function requirementSource(id, req, indent) {
|
|
158
158
|
const inner = `${indent} `;
|
|
@@ -172,13 +172,28 @@ export function requirementSource(id, req, indent) {
|
|
|
172
172
|
lines.push(`${inner}params: { ${body} },`);
|
|
173
173
|
}
|
|
174
174
|
if (req.outOfScope.length > 0) {
|
|
175
|
-
lines.push(`${inner}outOfScope: ${
|
|
175
|
+
lines.push(`${inner}outOfScope: ${stringListSource(req.outOfScope)},`);
|
|
176
|
+
}
|
|
177
|
+
// Reachable only through a delta the gate never approves — `open-unresolved`
|
|
178
|
+
// blocks any end state that still carries one, so an ADDED entry written here
|
|
179
|
+
// has an empty list. Emitted anyway rather than left out: this function's
|
|
180
|
+
// contract is to write the requirement it is given, and a writer silently
|
|
181
|
+
// shorter than the schema is exactly the divergence the MODIFIED half below
|
|
182
|
+
// was found to have.
|
|
183
|
+
if (req.open.length > 0) {
|
|
184
|
+
lines.push(`${inner}open: ${stringListSource(req.open)},`);
|
|
176
185
|
}
|
|
177
186
|
lines.push(`${indent}}`);
|
|
178
187
|
return lines.join('\n');
|
|
179
188
|
}
|
|
180
|
-
/**
|
|
181
|
-
|
|
189
|
+
/**
|
|
190
|
+
* One list-of-strings field as source — `outOfScope` or `open`.
|
|
191
|
+
*
|
|
192
|
+
* One function for both, and named for the shape rather than for either field,
|
|
193
|
+
* so a third such field does not arrive with a third spelling of the same
|
|
194
|
+
* emitter. Shared with the modification writer below.
|
|
195
|
+
*/
|
|
196
|
+
function stringListSource(entries) {
|
|
182
197
|
return `[${entries.map((s) => tsString(s)).join(', ')}]`;
|
|
183
198
|
}
|
|
184
199
|
/** One `params` key as source, at the key order the emitter writes everywhere. */
|
|
@@ -237,9 +252,18 @@ export function spliceModifications(file, source, changes) {
|
|
|
237
252
|
const edits = [];
|
|
238
253
|
const wanted = new Map(changes.map((change) => [change.id, change]));
|
|
239
254
|
// Ids the file does not hold, first and in code-unit order: they have no
|
|
240
|
-
// position to be reported at, and the walk below is driven by positions.
|
|
255
|
+
// position to be reported at, and the walk below is driven by positions. An
|
|
256
|
+
// entry the reader refused to describe is reported as *why* rather than as
|
|
257
|
+
// absent — a file that writes one key twice does hold the entry, and
|
|
258
|
+
// `entry-not-found` would send its reader looking for something that is
|
|
259
|
+
// there. Both are decided before a single edit is pushed, because a refusal
|
|
260
|
+
// arriving mid-walk would leave `edits` half-built for an entry whose offsets
|
|
261
|
+
// are exactly the ones not to be trusted.
|
|
241
262
|
for (const id of [...wanted.keys()].sort(byCodeUnit)) {
|
|
242
|
-
|
|
263
|
+
const twice = layouts.repeated.get(id);
|
|
264
|
+
if (twice)
|
|
265
|
+
refusals.push({ reqId: id, ...twice, reason: 'duplicate-key' });
|
|
266
|
+
else if (!layouts.byId.has(id))
|
|
243
267
|
refusals.push({ reqId: id, reason: 'entry-not-found' });
|
|
244
268
|
}
|
|
245
269
|
// **The file front to back, not the delta.** Driving the walk from the layout
|
|
@@ -253,7 +277,7 @@ export function spliceModifications(file, source, changes) {
|
|
|
253
277
|
// It is also why the layout is `Map`s rather than objects at every level; the
|
|
254
278
|
// container is what carries the order, and `registryEntryLayouts` says why an
|
|
255
279
|
// object cannot.
|
|
256
|
-
for (const [id, layout] of layouts) {
|
|
280
|
+
for (const [id, layout] of layouts.byId) {
|
|
257
281
|
const change = wanted.get(id);
|
|
258
282
|
if (!change)
|
|
259
283
|
continue;
|
|
@@ -290,14 +314,24 @@ export function spliceModifications(file, source, changes) {
|
|
|
290
314
|
}
|
|
291
315
|
else if (name === 'outOfScope') {
|
|
292
316
|
if (!sameValue(before.outOfScope, after.outOfScope)) {
|
|
293
|
-
out.at(name, span,
|
|
317
|
+
out.at(name, span, stringListSource(after.outOfScope));
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
else if (name === 'open') {
|
|
321
|
+
if (!sameValue(before.open, after.open)) {
|
|
322
|
+
out.at(name, span, stringListSource(after.open));
|
|
294
323
|
}
|
|
295
324
|
}
|
|
296
325
|
else if (name === 'params') {
|
|
297
326
|
editParams(layout, before, after, out);
|
|
298
327
|
}
|
|
299
328
|
// Any other field is one the schema does not define, and not this
|
|
300
|
-
// module's to rewrite or to remove.
|
|
329
|
+
// module's to rewrite or to remove. `open` is above rather than here for
|
|
330
|
+
// exactly that reason: adding it to the schema without adding it to this
|
|
331
|
+
// loop would let the gate approve an end state where a question is closed
|
|
332
|
+
// while the file on disk still carries it — a divergence between the
|
|
333
|
+
// registry the verdict was reached on and the registry the merge commits,
|
|
334
|
+
// with nothing anywhere comparing the two.
|
|
301
335
|
}
|
|
302
336
|
if (!layout.fields.has('statement') && before.statement !== after.statement) {
|
|
303
337
|
openField('statement', tsString(after.statement));
|
|
@@ -311,7 +345,10 @@ export function spliceModifications(file, source, changes) {
|
|
|
311
345
|
openField('params', `{ ${fresh.join(', ')} }`);
|
|
312
346
|
}
|
|
313
347
|
if (!layout.fields.has('outOfScope') && !sameValue(before.outOfScope, after.outOfScope)) {
|
|
314
|
-
openField('outOfScope',
|
|
348
|
+
openField('outOfScope', stringListSource(after.outOfScope));
|
|
349
|
+
}
|
|
350
|
+
if (!layout.fields.has('open') && !sameValue(before.open, after.open)) {
|
|
351
|
+
openField('open', stringListSource(after.open));
|
|
315
352
|
}
|
|
316
353
|
if (opened.length > 0) {
|
|
317
354
|
const point = layout.fieldInsertion;
|
|
@@ -135,6 +135,31 @@ export interface RegistryEntryLayout {
|
|
|
135
135
|
/** Where a new field goes, inside the entry's own body. */
|
|
136
136
|
fieldInsertion: RegistryInsertion;
|
|
137
137
|
}
|
|
138
|
+
/**
|
|
139
|
+
* The key an entry wrote twice, as the path the author would say it —
|
|
140
|
+
* `statement`, `params.limit`.
|
|
141
|
+
*
|
|
142
|
+
* Absent when what repeats is the entry's **own id**, which no field path
|
|
143
|
+
* names: `params.limit` is a place inside a requirement, and a second entry
|
|
144
|
+
* under one id is not inside anything.
|
|
145
|
+
*/
|
|
146
|
+
export interface RepeatedKey {
|
|
147
|
+
field?: string;
|
|
148
|
+
}
|
|
149
|
+
/** Every entry's layout, and the entries this reader refuses to describe. */
|
|
150
|
+
export interface RegistryLayouts {
|
|
151
|
+
/**
|
|
152
|
+
* Value spans per requirement id, in the order the file writes them.
|
|
153
|
+
*
|
|
154
|
+
* Named `byId` rather than `entries` so nothing reads `layouts.entries` as
|
|
155
|
+
* the `Map` method of that name — the order this carries is the contract,
|
|
156
|
+
* and a reader who thinks they are calling `Map.prototype.entries` is a
|
|
157
|
+
* reader who has stopped seeing it.
|
|
158
|
+
*/
|
|
159
|
+
byId: Map<string, RegistryEntryLayout>;
|
|
160
|
+
/** Ids left out of {@link RegistryLayouts.byId} because a key repeats. */
|
|
161
|
+
repeated: Map<string, RepeatedKey>;
|
|
162
|
+
}
|
|
138
163
|
/**
|
|
139
164
|
* The layout of every entry in a registry file, by requirement id.
|
|
140
165
|
*
|
|
@@ -154,6 +179,26 @@ export interface RegistryEntryLayout {
|
|
|
154
179
|
* schema's `z.record(z.string(), …)` accepts the result. A `Map` keeps insertion
|
|
155
180
|
* order for every key type, so the property holds by construction rather than by
|
|
156
181
|
* the keys happening not to be numbers.
|
|
182
|
+
*
|
|
183
|
+
* **A key written twice defeats that pairing, which is why it is refused rather
|
|
184
|
+
* than described.** `Map.set` on a key it already holds keeps the *first*
|
|
185
|
+
* insertion's position and takes the *last* call's value — so an entry writing
|
|
186
|
+
* `params: { limit: 1, other: 'x', limit: 1 }` yields `limit` at position one
|
|
187
|
+
* carrying the span of the occurrence that sits after `other`, and the walk's
|
|
188
|
+
* offsets stop ascending — so an earlier edit moves the bytes under a later one
|
|
189
|
+
* and the replacement lands outside the value it was addressing.
|
|
190
|
+
*
|
|
191
|
+
* Nothing upstream refuses such a file, which is why the guard is here: a
|
|
192
|
+
* duplicate key is a TypeScript *semantic* error and `parseSource` asks only
|
|
193
|
+
* the syntactic question, so both readers take the file and hand back the last
|
|
194
|
+
* occurrence, and `--apply` is the one command that writes.
|
|
195
|
+
*
|
|
196
|
+
* Refused at every level for one reason, and the id level is not the mild one:
|
|
197
|
+
* two entries under a single id put a *later* entry's spans at an *earlier*
|
|
198
|
+
* entry's position, so the disorder is between entries rather than inside one.
|
|
199
|
+
* Nothing narrower is worth the reach — an entry whose keys are ambiguous is
|
|
200
|
+
* one no verdict describes, and the file is a defect to fix rather than one to
|
|
201
|
+
* edit around.
|
|
157
202
|
*/
|
|
158
|
-
export declare function registryEntryLayouts(file: string, source: string):
|
|
203
|
+
export declare function registryEntryLayouts(file: string, source: string): RegistryLayouts | undefined;
|
|
159
204
|
//# sourceMappingURL=static-registry.d.ts.map
|
|
@@ -295,11 +295,23 @@ function constInitializer(sf, name) {
|
|
|
295
295
|
* The callee is matched against the local name the import bound — an alias or a
|
|
296
296
|
* namespace import is the same call — rather than against any single-argument
|
|
297
297
|
* call, so `buildRegistry({…})` is not mistaken for a registry.
|
|
298
|
+
*
|
|
299
|
+
* **Unwrapped here, once, rather than by each caller.** `as const`, `satisfies`
|
|
300
|
+
* and a bare pair of parentheses do not change the value, so `literalValue`
|
|
301
|
+
* strips them before it reads — while the write side asks
|
|
302
|
+
* `ts.isObjectLiteralExpression` of whatever this hands back. A wrapper returned
|
|
303
|
+
* from here is therefore a registry the readers accept and the writers cannot
|
|
304
|
+
* locate, on the one command that edits a `*.reqs.ts`. Both sides come through
|
|
305
|
+
* this function precisely so they cannot disagree about which literal is the
|
|
306
|
+
* registry; leaving the unwrap to each caller is what puts the disagreement
|
|
307
|
+
* inside it.
|
|
298
308
|
*/
|
|
299
309
|
function authoringCall(expr, sf, fn) {
|
|
300
310
|
if (!ts.isCallExpression(expr) || expr.arguments.length !== 1)
|
|
301
311
|
return undefined;
|
|
302
|
-
|
|
312
|
+
if (!callsAuthoringFn(expr, localNames(sf, fn), fn))
|
|
313
|
+
return undefined;
|
|
314
|
+
return unwrap(expr.arguments[0]);
|
|
303
315
|
}
|
|
304
316
|
/**
|
|
305
317
|
* Whether a call is a call of `fn`, by the local names the imports bound to it.
|
|
@@ -520,31 +532,66 @@ function objectInsertion(sf, source, obj) {
|
|
|
520
532
|
* schema's `z.record(z.string(), …)` accepts the result. A `Map` keeps insertion
|
|
521
533
|
* order for every key type, so the property holds by construction rather than by
|
|
522
534
|
* the keys happening not to be numbers.
|
|
535
|
+
*
|
|
536
|
+
* **A key written twice defeats that pairing, which is why it is refused rather
|
|
537
|
+
* than described.** `Map.set` on a key it already holds keeps the *first*
|
|
538
|
+
* insertion's position and takes the *last* call's value — so an entry writing
|
|
539
|
+
* `params: { limit: 1, other: 'x', limit: 1 }` yields `limit` at position one
|
|
540
|
+
* carrying the span of the occurrence that sits after `other`, and the walk's
|
|
541
|
+
* offsets stop ascending — so an earlier edit moves the bytes under a later one
|
|
542
|
+
* and the replacement lands outside the value it was addressing.
|
|
543
|
+
*
|
|
544
|
+
* Nothing upstream refuses such a file, which is why the guard is here: a
|
|
545
|
+
* duplicate key is a TypeScript *semantic* error and `parseSource` asks only
|
|
546
|
+
* the syntactic question, so both readers take the file and hand back the last
|
|
547
|
+
* occurrence, and `--apply` is the one command that writes.
|
|
548
|
+
*
|
|
549
|
+
* Refused at every level for one reason, and the id level is not the mild one:
|
|
550
|
+
* two entries under a single id put a *later* entry's spans at an *earlier*
|
|
551
|
+
* entry's position, so the disorder is between entries rather than inside one.
|
|
552
|
+
* Nothing narrower is worth the reach — an entry whose keys are ambiguous is
|
|
553
|
+
* one no verdict describes, and the file is a defect to fix rather than one to
|
|
554
|
+
* edit around.
|
|
523
555
|
*/
|
|
524
556
|
export function registryEntryLayouts(file, source) {
|
|
525
557
|
const found = registryLiteral(file, source);
|
|
526
558
|
if (!found)
|
|
527
559
|
return undefined;
|
|
528
560
|
const { sf, literal } = found;
|
|
529
|
-
const
|
|
561
|
+
const byId = new Map();
|
|
562
|
+
const repeated = new Map();
|
|
530
563
|
for (const entry of literal.properties) {
|
|
531
564
|
if (!ts.isPropertyAssignment(entry))
|
|
532
565
|
continue;
|
|
533
566
|
const id = staticName(entry.name);
|
|
534
567
|
if (id === undefined || id === '__proto__')
|
|
535
568
|
continue;
|
|
569
|
+
if (byId.has(id) || repeated.has(id)) {
|
|
570
|
+
// Deleted, not left as the first occurrence: both readers take the *last*
|
|
571
|
+
// one, so describing the first would hand the writer a span whose value
|
|
572
|
+
// nothing evaluates.
|
|
573
|
+
byId.delete(id);
|
|
574
|
+
repeated.set(id, {});
|
|
575
|
+
continue;
|
|
576
|
+
}
|
|
536
577
|
const body = unwrap(entry.initializer);
|
|
537
578
|
if (!ts.isObjectLiteralExpression(body))
|
|
538
579
|
continue;
|
|
539
580
|
const fields = new Map();
|
|
540
581
|
const paramKeys = new Map();
|
|
541
582
|
let params;
|
|
583
|
+
/** The first repeated key seen, as the path the refusal names. */
|
|
584
|
+
let twice;
|
|
542
585
|
for (const field of body.properties) {
|
|
543
586
|
if (!ts.isPropertyAssignment(field))
|
|
544
587
|
continue;
|
|
545
588
|
const name = staticName(field.name);
|
|
546
589
|
if (name === undefined || name === '__proto__')
|
|
547
590
|
continue;
|
|
591
|
+
if (fields.has(name)) {
|
|
592
|
+
twice ??= name;
|
|
593
|
+
continue;
|
|
594
|
+
}
|
|
548
595
|
// The unwrapped value, so an `as const` or a parenthesis stays outside the
|
|
549
596
|
// span and survives the replacement it wraps.
|
|
550
597
|
const value = unwrap(field.initializer);
|
|
@@ -559,18 +606,26 @@ export function registryEntryLayouts(file, source) {
|
|
|
559
606
|
const key = staticName(param.name);
|
|
560
607
|
if (key === undefined || key === '__proto__')
|
|
561
608
|
continue;
|
|
609
|
+
if (paramKeys.has(key)) {
|
|
610
|
+
twice ??= `params.${key}`;
|
|
611
|
+
continue;
|
|
612
|
+
}
|
|
562
613
|
const value = unwrap(param.initializer);
|
|
563
614
|
paramKeys.set(key, { start: value.getStart(sf), end: value.getEnd() });
|
|
564
615
|
}
|
|
565
616
|
}
|
|
566
|
-
|
|
617
|
+
if (twice !== undefined) {
|
|
618
|
+
repeated.set(id, { field: twice });
|
|
619
|
+
continue;
|
|
620
|
+
}
|
|
621
|
+
byId.set(id, {
|
|
567
622
|
fields,
|
|
568
623
|
paramKeys,
|
|
569
624
|
...(params ? { paramsInsertion: objectInsertion(sf, source, params) } : {}),
|
|
570
625
|
fieldInsertion: objectInsertion(sf, source, body),
|
|
571
626
|
});
|
|
572
627
|
}
|
|
573
|
-
return
|
|
628
|
+
return { byId, repeated };
|
|
574
629
|
}
|
|
575
630
|
/**
|
|
576
631
|
* The `defineRequirements({ … })` literal of a registry file, with the source
|
package/dist/core/validator.d.ts
CHANGED
|
@@ -19,8 +19,9 @@ export declare function uncoveredIssues(registry: Registry, plan: AttestPlan): I
|
|
|
19
19
|
* - unbound-param: a statement placeholder has no matching param
|
|
20
20
|
* - non-scalar-interpolation: a statement placeholder names a structured param
|
|
21
21
|
*
|
|
22
|
-
* and
|
|
22
|
+
* and two WARNINGs:
|
|
23
23
|
* - rationale-placeholder: a `{name}` in a rationale, which is never interpolated
|
|
24
|
+
* - requirement-open: the requirement names something still undecided about it
|
|
24
25
|
*
|
|
25
26
|
* and one more WARNING, which exists only when a registry file failed to load:
|
|
26
27
|
* - orphan-from-failed-registry: the scenarios attesting ids that file declares
|
package/dist/core/validator.js
CHANGED
|
@@ -34,8 +34,9 @@ export function uncoveredIssues(registry, plan) {
|
|
|
34
34
|
* - unbound-param: a statement placeholder has no matching param
|
|
35
35
|
* - non-scalar-interpolation: a statement placeholder names a structured param
|
|
36
36
|
*
|
|
37
|
-
* and
|
|
37
|
+
* and two WARNINGs:
|
|
38
38
|
* - rationale-placeholder: a `{name}` in a rationale, which is never interpolated
|
|
39
|
+
* - requirement-open: the requirement names something still undecided about it
|
|
39
40
|
*
|
|
40
41
|
* and one more WARNING, which exists only when a registry file failed to load:
|
|
41
42
|
* - orphan-from-failed-registry: the scenarios attesting ids that file declares
|
|
@@ -208,6 +209,24 @@ export function validateStructure(registry, plan, unreadable = []) {
|
|
|
208
209
|
});
|
|
209
210
|
}
|
|
210
211
|
}
|
|
212
|
+
// requirement-open: the requirement says what is still undecided about it.
|
|
213
|
+
// WARNING and not an ERROR, because the four static commands are where an
|
|
214
|
+
// author works while a proposal legitimately still has questions in it —
|
|
215
|
+
// failing here would push them back to inventing a value, which is the
|
|
216
|
+
// behaviour the field exists to replace. What refuses to call such a registry
|
|
217
|
+
// done is the archive gate (`open-unresolved`), the same asymmetry `never-red`
|
|
218
|
+
// runs on. One issue per question rather than one per requirement: each is a
|
|
219
|
+
// separate thing to answer, and a reader resolving them wants them listed.
|
|
220
|
+
for (const [id, req] of Object.entries(registry)) {
|
|
221
|
+
for (const question of req.open) {
|
|
222
|
+
issues.push({
|
|
223
|
+
level: 'WARNING',
|
|
224
|
+
code: 'requirement-open',
|
|
225
|
+
reqId: id,
|
|
226
|
+
message: `Requirement "${id}" is still open: ${question} Answer it and remove the entry from open — the archive gate refuses a registry that still carries one.`,
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
}
|
|
211
230
|
return issues;
|
|
212
231
|
}
|
|
213
232
|
/**
|
package/package.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@am_shork/attest",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "TDD-native spec framework: tests are the source of truth for verification, ID-bound requirements the source of truth for intent.",
|
|
5
5
|
"type": "module",
|
|
6
|
+
"packageManager": "pnpm@10.28.0",
|
|
6
7
|
"engines": {
|
|
7
8
|
"node": ">=20.19"
|
|
8
9
|
},
|
|
@@ -41,6 +42,24 @@
|
|
|
41
42
|
"publishConfig": {
|
|
42
43
|
"access": "public"
|
|
43
44
|
},
|
|
45
|
+
"scripts": {
|
|
46
|
+
"clean": "node -e \"require('fs').rmSync('dist', {recursive: true, force: true})\"",
|
|
47
|
+
"build": "pnpm run clean && tsc -p tsconfig.json",
|
|
48
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
49
|
+
"typecheck:all": "tsc -p tsconfig.typecheck.json",
|
|
50
|
+
"test": "vitest run",
|
|
51
|
+
"test:watch": "vitest",
|
|
52
|
+
"test:consumer": "pnpm run build && vitest run --config vitest.consumer.config.ts",
|
|
53
|
+
"lint": "eslint .",
|
|
54
|
+
"prepack": "pnpm run build",
|
|
55
|
+
"prepublishOnly": "pnpm run test:consumer",
|
|
56
|
+
"attest": "node bin/attest.js",
|
|
57
|
+
"check:self": "node bin/attest.js check self",
|
|
58
|
+
"verify:self": "node bin/attest.js verify self",
|
|
59
|
+
"cover:self": "node bin/attest.js cover self",
|
|
60
|
+
"render:self": "node bin/attest.js render self --out self/requirements/SPEC.md",
|
|
61
|
+
"render:self:check": "node bin/attest.js render self --out self/requirements/SPEC.md --check"
|
|
62
|
+
},
|
|
44
63
|
"keywords": [
|
|
45
64
|
"tdd",
|
|
46
65
|
"spec",
|
|
@@ -67,20 +86,15 @@
|
|
|
67
86
|
"vite": "^8.1.5",
|
|
68
87
|
"vitest": "^4.1.10"
|
|
69
88
|
},
|
|
70
|
-
"
|
|
71
|
-
"
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
"
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
"check:self": "node bin/attest.js check self",
|
|
81
|
-
"verify:self": "node bin/attest.js verify self",
|
|
82
|
-
"cover:self": "node bin/attest.js cover self",
|
|
83
|
-
"render:self": "node bin/attest.js render self --out self/requirements/SPEC.md",
|
|
84
|
-
"render:self:check": "node bin/attest.js render self --out self/requirements/SPEC.md --check"
|
|
89
|
+
"pnpm": {
|
|
90
|
+
"onlyBuiltDependencies": [
|
|
91
|
+
"esbuild"
|
|
92
|
+
],
|
|
93
|
+
"overrides": {
|
|
94
|
+
"brace-expansion@<5.0.9": ">=5.0.9",
|
|
95
|
+
"js-yaml@<4.3.1": ">=4.3.1",
|
|
96
|
+
"nanoid@<3.3.17": ">=3.3.17",
|
|
97
|
+
"postcss@<8.5.23": ">=8.5.23"
|
|
98
|
+
}
|
|
85
99
|
}
|
|
86
|
-
}
|
|
100
|
+
}
|