vouchington-tooling 0.13.3 → 0.13.4
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.
|
@@ -12,6 +12,10 @@ ignores:
|
|
|
12
12
|
- 'web/test-helpers/**'
|
|
13
13
|
rule:
|
|
14
14
|
all:
|
|
15
|
+
- not:
|
|
16
|
+
any:
|
|
17
|
+
- matches: async-disposable-acquisition
|
|
18
|
+
- matches: async-disposable-commit
|
|
15
19
|
- not:
|
|
16
20
|
follows:
|
|
17
21
|
stopBy: neighbor
|
|
@@ -72,6 +76,10 @@ rule:
|
|
|
72
76
|
pattern: $W = await Promise.all($$$)
|
|
73
77
|
- not:
|
|
74
78
|
pattern: $W = await Promise.allSettled($$$)
|
|
79
|
+
not:
|
|
80
|
+
any:
|
|
81
|
+
- matches: async-disposable-acquisition
|
|
82
|
+
- matches: async-disposable-commit
|
|
75
83
|
- any:
|
|
76
84
|
- all:
|
|
77
85
|
- pattern:
|
|
@@ -189,8 +197,16 @@ rule:
|
|
|
189
197
|
pattern: $Y = await Promise.all($$$)
|
|
190
198
|
- not:
|
|
191
199
|
pattern: $Y = await Promise.allSettled($$$)
|
|
200
|
+
- not:
|
|
201
|
+
any:
|
|
202
|
+
- matches: async-disposable-acquisition
|
|
203
|
+
- matches: async-disposable-commit
|
|
192
204
|
- precedes:
|
|
193
205
|
stopBy: neighbor
|
|
206
|
+
not:
|
|
207
|
+
any:
|
|
208
|
+
- matches: async-disposable-acquisition
|
|
209
|
+
- matches: async-disposable-commit
|
|
194
210
|
any:
|
|
195
211
|
- all:
|
|
196
212
|
- pattern:
|
|
@@ -261,6 +277,69 @@ constraints:
|
|
|
261
277
|
P:
|
|
262
278
|
not:
|
|
263
279
|
regex: '^Promise\.all(Settled)?\b'
|
|
280
|
+
utils:
|
|
281
|
+
async-disposable-acquisition:
|
|
282
|
+
all:
|
|
283
|
+
- pattern:
|
|
284
|
+
context: |
|
|
285
|
+
async function run() {
|
|
286
|
+
await using $RESOURCE = await $ACQUIRE
|
|
287
|
+
}
|
|
288
|
+
selector: expression_statement
|
|
289
|
+
- precedes:
|
|
290
|
+
# Pair only with the nearest lifecycle endpoint to the right.
|
|
291
|
+
# Overlapping-resource policy belongs to a separate lifecycle rule.
|
|
292
|
+
stopBy:
|
|
293
|
+
any:
|
|
294
|
+
- pattern:
|
|
295
|
+
context: |
|
|
296
|
+
async function run() {
|
|
297
|
+
await using $_ = await $_
|
|
298
|
+
}
|
|
299
|
+
selector: expression_statement
|
|
300
|
+
- pattern:
|
|
301
|
+
context: |
|
|
302
|
+
async function run() {
|
|
303
|
+
await $_.commit($$$)
|
|
304
|
+
}
|
|
305
|
+
selector: expression_statement
|
|
306
|
+
pattern:
|
|
307
|
+
context: |
|
|
308
|
+
async function run() {
|
|
309
|
+
await $RESOURCE.commit($$$)
|
|
310
|
+
}
|
|
311
|
+
selector: expression_statement
|
|
312
|
+
async-disposable-commit:
|
|
313
|
+
all:
|
|
314
|
+
- pattern:
|
|
315
|
+
context: |
|
|
316
|
+
async function run() {
|
|
317
|
+
await $RESOURCE.commit($$$)
|
|
318
|
+
}
|
|
319
|
+
selector: expression_statement
|
|
320
|
+
- follows:
|
|
321
|
+
# Pair only with the nearest lifecycle endpoint to the left.
|
|
322
|
+
# Overlapping-resource policy belongs to a separate lifecycle rule.
|
|
323
|
+
stopBy:
|
|
324
|
+
any:
|
|
325
|
+
- pattern:
|
|
326
|
+
context: |
|
|
327
|
+
async function run() {
|
|
328
|
+
await using $_ = await $_
|
|
329
|
+
}
|
|
330
|
+
selector: expression_statement
|
|
331
|
+
- pattern:
|
|
332
|
+
context: |
|
|
333
|
+
async function run() {
|
|
334
|
+
await $_.commit($$$)
|
|
335
|
+
}
|
|
336
|
+
selector: expression_statement
|
|
337
|
+
pattern:
|
|
338
|
+
context: |
|
|
339
|
+
async function run() {
|
|
340
|
+
await using $RESOURCE = await $ACQUIRE
|
|
341
|
+
}
|
|
342
|
+
selector: expression_statement
|
|
264
343
|
examples:
|
|
265
344
|
- code: |
|
|
266
345
|
async function run() {
|
|
@@ -286,3 +365,102 @@ examples:
|
|
|
286
365
|
}
|
|
287
366
|
isValid: true
|
|
288
367
|
file: 'web/app/example.tsx'
|
|
368
|
+
- code: |
|
|
369
|
+
async function run() {
|
|
370
|
+
await using transaction = await beginTransaction()
|
|
371
|
+
await applyChange(transaction)
|
|
372
|
+
await transaction.commit()
|
|
373
|
+
return <div />
|
|
374
|
+
}
|
|
375
|
+
isValid: true
|
|
376
|
+
file: 'web/app/example.tsx'
|
|
377
|
+
- code: |
|
|
378
|
+
async function run() {
|
|
379
|
+
await using transaction = await beginBoundedTransaction({ timeoutMs: 1000 })
|
|
380
|
+
await applyFirstChange(transaction)
|
|
381
|
+
await applySecondChange(transaction)
|
|
382
|
+
await transaction.commit()
|
|
383
|
+
return <div />
|
|
384
|
+
}
|
|
385
|
+
isValid: true
|
|
386
|
+
file: 'web/app/example.tsx'
|
|
387
|
+
- code: |
|
|
388
|
+
async function run() {
|
|
389
|
+
await using transaction = await beginTransaction()
|
|
390
|
+
await applyFirstChange(transaction)
|
|
391
|
+
await applySecondChange(transaction)
|
|
392
|
+
await applyThirdChange(transaction)
|
|
393
|
+
await transaction.commit()
|
|
394
|
+
return <div />
|
|
395
|
+
}
|
|
396
|
+
isValid: false
|
|
397
|
+
file: 'web/app/example.tsx'
|
|
398
|
+
- code: |
|
|
399
|
+
async function run() {
|
|
400
|
+
await unrelatedFirst()
|
|
401
|
+
await unrelatedSecond()
|
|
402
|
+
await unrelatedThird()
|
|
403
|
+
await using transaction = await beginTransaction()
|
|
404
|
+
await applyChange(transaction)
|
|
405
|
+
await transaction.commit()
|
|
406
|
+
return <div />
|
|
407
|
+
}
|
|
408
|
+
isValid: false
|
|
409
|
+
file: 'web/app/example.tsx'
|
|
410
|
+
- code: |
|
|
411
|
+
async function run() {
|
|
412
|
+
await using transaction = await beginTransaction()
|
|
413
|
+
await applyFirstChange(transaction)
|
|
414
|
+
await applySecondChange(transaction)
|
|
415
|
+
await other.commit()
|
|
416
|
+
return <div />
|
|
417
|
+
}
|
|
418
|
+
isValid: false
|
|
419
|
+
file: 'web/app/example.tsx'
|
|
420
|
+
- code: |
|
|
421
|
+
async function run() {
|
|
422
|
+
await using transaction = await beginTransaction()
|
|
423
|
+
await applyChange(transaction)
|
|
424
|
+
await transaction.commit()
|
|
425
|
+
await afterFirst()
|
|
426
|
+
await afterSecond()
|
|
427
|
+
await afterThird()
|
|
428
|
+
return <div />
|
|
429
|
+
}
|
|
430
|
+
isValid: false
|
|
431
|
+
file: 'web/app/example.tsx'
|
|
432
|
+
- code: |
|
|
433
|
+
async function run() {
|
|
434
|
+
await using outer = await acquire()
|
|
435
|
+
{
|
|
436
|
+
await using inner = await acquire()
|
|
437
|
+
await innerFirst(inner)
|
|
438
|
+
await innerSecond(inner)
|
|
439
|
+
await innerThird(inner)
|
|
440
|
+
await inner.commit()
|
|
441
|
+
}
|
|
442
|
+
await outer.commit()
|
|
443
|
+
return <div />
|
|
444
|
+
}
|
|
445
|
+
isValid: false
|
|
446
|
+
file: 'web/app/example.tsx'
|
|
447
|
+
- code: |
|
|
448
|
+
async function run() {
|
|
449
|
+
await using transaction = await beginTransaction()
|
|
450
|
+
await applyFirstChange(transaction)
|
|
451
|
+
await applySecondChange(transaction)
|
|
452
|
+
await transaction.rollback()
|
|
453
|
+
return <div />
|
|
454
|
+
}
|
|
455
|
+
isValid: false
|
|
456
|
+
file: 'web/app/example.tsx'
|
|
457
|
+
- code: |
|
|
458
|
+
async function run() {
|
|
459
|
+
await using resource = await acquire()
|
|
460
|
+
await useFirst(resource)
|
|
461
|
+
await useSecond(resource)
|
|
462
|
+
await resource.dispose()
|
|
463
|
+
return <div />
|
|
464
|
+
}
|
|
465
|
+
isValid: false
|
|
466
|
+
file: 'web/app/example.tsx'
|
|
@@ -33,6 +33,10 @@ ignores:
|
|
|
33
33
|
- 'backend/scripts/explain-analyze/**'
|
|
34
34
|
rule:
|
|
35
35
|
all:
|
|
36
|
+
- not:
|
|
37
|
+
any:
|
|
38
|
+
- matches: async-disposable-acquisition
|
|
39
|
+
- matches: async-disposable-commit
|
|
36
40
|
- not:
|
|
37
41
|
follows:
|
|
38
42
|
stopBy: neighbor
|
|
@@ -73,6 +77,10 @@ rule:
|
|
|
73
77
|
pattern: $W = await Promise.all($$$)
|
|
74
78
|
- not:
|
|
75
79
|
pattern: $W = await Promise.allSettled($$$)
|
|
80
|
+
not:
|
|
81
|
+
any:
|
|
82
|
+
- matches: async-disposable-acquisition
|
|
83
|
+
- matches: async-disposable-commit
|
|
76
84
|
- any:
|
|
77
85
|
- all:
|
|
78
86
|
- pattern:
|
|
@@ -150,8 +158,16 @@ rule:
|
|
|
150
158
|
pattern: $Y = await Promise.all($$$)
|
|
151
159
|
- not:
|
|
152
160
|
pattern: $Y = await Promise.allSettled($$$)
|
|
161
|
+
- not:
|
|
162
|
+
any:
|
|
163
|
+
- matches: async-disposable-acquisition
|
|
164
|
+
- matches: async-disposable-commit
|
|
153
165
|
- precedes:
|
|
154
166
|
stopBy: neighbor
|
|
167
|
+
not:
|
|
168
|
+
any:
|
|
169
|
+
- matches: async-disposable-acquisition
|
|
170
|
+
- matches: async-disposable-commit
|
|
155
171
|
any:
|
|
156
172
|
- all:
|
|
157
173
|
- pattern:
|
|
@@ -202,6 +218,69 @@ constraints:
|
|
|
202
218
|
P:
|
|
203
219
|
not:
|
|
204
220
|
regex: '^Promise\.all(Settled)?\b'
|
|
221
|
+
utils:
|
|
222
|
+
async-disposable-acquisition:
|
|
223
|
+
all:
|
|
224
|
+
- pattern:
|
|
225
|
+
context: |
|
|
226
|
+
async function run() {
|
|
227
|
+
await using $RESOURCE = await $ACQUIRE
|
|
228
|
+
}
|
|
229
|
+
selector: expression_statement
|
|
230
|
+
- precedes:
|
|
231
|
+
# Pair only with the nearest lifecycle endpoint to the right.
|
|
232
|
+
# Overlapping-resource policy belongs to a separate lifecycle rule.
|
|
233
|
+
stopBy:
|
|
234
|
+
any:
|
|
235
|
+
- pattern:
|
|
236
|
+
context: |
|
|
237
|
+
async function run() {
|
|
238
|
+
await using $_ = await $_
|
|
239
|
+
}
|
|
240
|
+
selector: expression_statement
|
|
241
|
+
- pattern:
|
|
242
|
+
context: |
|
|
243
|
+
async function run() {
|
|
244
|
+
await $_.commit($$$)
|
|
245
|
+
}
|
|
246
|
+
selector: expression_statement
|
|
247
|
+
pattern:
|
|
248
|
+
context: |
|
|
249
|
+
async function run() {
|
|
250
|
+
await $RESOURCE.commit($$$)
|
|
251
|
+
}
|
|
252
|
+
selector: expression_statement
|
|
253
|
+
async-disposable-commit:
|
|
254
|
+
all:
|
|
255
|
+
- pattern:
|
|
256
|
+
context: |
|
|
257
|
+
async function run() {
|
|
258
|
+
await $RESOURCE.commit($$$)
|
|
259
|
+
}
|
|
260
|
+
selector: expression_statement
|
|
261
|
+
- follows:
|
|
262
|
+
# Pair only with the nearest lifecycle endpoint to the left.
|
|
263
|
+
# Overlapping-resource policy belongs to a separate lifecycle rule.
|
|
264
|
+
stopBy:
|
|
265
|
+
any:
|
|
266
|
+
- pattern:
|
|
267
|
+
context: |
|
|
268
|
+
async function run() {
|
|
269
|
+
await using $_ = await $_
|
|
270
|
+
}
|
|
271
|
+
selector: expression_statement
|
|
272
|
+
- pattern:
|
|
273
|
+
context: |
|
|
274
|
+
async function run() {
|
|
275
|
+
await $_.commit($$$)
|
|
276
|
+
}
|
|
277
|
+
selector: expression_statement
|
|
278
|
+
pattern:
|
|
279
|
+
context: |
|
|
280
|
+
async function run() {
|
|
281
|
+
await using $RESOURCE = await $ACQUIRE
|
|
282
|
+
}
|
|
283
|
+
selector: expression_statement
|
|
205
284
|
examples:
|
|
206
285
|
- code: |
|
|
207
286
|
async function run() {
|
|
@@ -240,3 +319,93 @@ examples:
|
|
|
240
319
|
}
|
|
241
320
|
isValid: true
|
|
242
321
|
file: 'backend/services/example.mts'
|
|
322
|
+
- code: |
|
|
323
|
+
async function run() {
|
|
324
|
+
await using transaction = await beginTransaction()
|
|
325
|
+
await applyChange(transaction)
|
|
326
|
+
await transaction.commit()
|
|
327
|
+
}
|
|
328
|
+
isValid: true
|
|
329
|
+
file: 'backend/services/example.mts'
|
|
330
|
+
- code: |
|
|
331
|
+
async function run() {
|
|
332
|
+
await using transaction = await beginBoundedTransaction({ timeoutMs: 1000 })
|
|
333
|
+
await applyFirstChange(transaction)
|
|
334
|
+
await applySecondChange(transaction)
|
|
335
|
+
await transaction.commit()
|
|
336
|
+
}
|
|
337
|
+
isValid: true
|
|
338
|
+
file: 'backend/services/example.mts'
|
|
339
|
+
- code: |
|
|
340
|
+
async function run() {
|
|
341
|
+
await using transaction = await beginTransaction()
|
|
342
|
+
await applyFirstChange(transaction)
|
|
343
|
+
await applySecondChange(transaction)
|
|
344
|
+
await applyThirdChange(transaction)
|
|
345
|
+
await transaction.commit()
|
|
346
|
+
}
|
|
347
|
+
isValid: false
|
|
348
|
+
file: 'backend/services/example.mts'
|
|
349
|
+
- code: |
|
|
350
|
+
async function run() {
|
|
351
|
+
await unrelatedFirst()
|
|
352
|
+
await unrelatedSecond()
|
|
353
|
+
await unrelatedThird()
|
|
354
|
+
await using transaction = await beginTransaction()
|
|
355
|
+
await applyChange(transaction)
|
|
356
|
+
await transaction.commit()
|
|
357
|
+
}
|
|
358
|
+
isValid: false
|
|
359
|
+
file: 'backend/services/example.mts'
|
|
360
|
+
- code: |
|
|
361
|
+
async function run() {
|
|
362
|
+
await using transaction = await beginTransaction()
|
|
363
|
+
await applyFirstChange(transaction)
|
|
364
|
+
await applySecondChange(transaction)
|
|
365
|
+
await other.commit()
|
|
366
|
+
}
|
|
367
|
+
isValid: false
|
|
368
|
+
file: 'backend/services/example.mts'
|
|
369
|
+
- code: |
|
|
370
|
+
async function run() {
|
|
371
|
+
await using transaction = await beginTransaction()
|
|
372
|
+
await applyChange(transaction)
|
|
373
|
+
await transaction.commit()
|
|
374
|
+
await afterFirst()
|
|
375
|
+
await afterSecond()
|
|
376
|
+
await afterThird()
|
|
377
|
+
}
|
|
378
|
+
isValid: false
|
|
379
|
+
file: 'backend/services/example.mts'
|
|
380
|
+
- code: |
|
|
381
|
+
async function run() {
|
|
382
|
+
await using outer = await acquire()
|
|
383
|
+
{
|
|
384
|
+
await using inner = await acquire()
|
|
385
|
+
await innerFirst(inner)
|
|
386
|
+
await innerSecond(inner)
|
|
387
|
+
await innerThird(inner)
|
|
388
|
+
await inner.commit()
|
|
389
|
+
}
|
|
390
|
+
await outer.commit()
|
|
391
|
+
}
|
|
392
|
+
isValid: false
|
|
393
|
+
file: 'backend/services/example.mts'
|
|
394
|
+
- code: |
|
|
395
|
+
async function run() {
|
|
396
|
+
await using transaction = await beginTransaction()
|
|
397
|
+
await applyFirstChange(transaction)
|
|
398
|
+
await applySecondChange(transaction)
|
|
399
|
+
await transaction.rollback()
|
|
400
|
+
}
|
|
401
|
+
isValid: false
|
|
402
|
+
file: 'backend/services/example.mts'
|
|
403
|
+
- code: |
|
|
404
|
+
async function run() {
|
|
405
|
+
await using resource = await acquire()
|
|
406
|
+
await useFirst(resource)
|
|
407
|
+
await useSecond(resource)
|
|
408
|
+
await resource.dispose()
|
|
409
|
+
}
|
|
410
|
+
isValid: false
|
|
411
|
+
file: 'backend/services/example.mts'
|
|
@@ -14,15 +14,16 @@ export type BlobPrimaryState = 'true' | 'false' | 'skipped';
|
|
|
14
14
|
export declare function isBlobPrimaryState(value: string | undefined): value is BlobPrimaryState;
|
|
15
15
|
/**
|
|
16
16
|
* Sibling to `assertCoverageTransportOutcome` for the Vitest blob. `true`/`false` are the S3
|
|
17
|
-
* upload step's `blob` output; `skipped` means that step never ran.
|
|
18
|
-
*
|
|
17
|
+
* upload step's `blob` output; `skipped` means that step never ran. Workflows gate the GitHub
|
|
18
|
+
* fallback on `blob != 'true'`, so a persisted S3 primary with no GitHub artifact attempt is the
|
|
19
|
+
* expected steady state, not a degraded one.
|
|
19
20
|
*/
|
|
20
21
|
export declare function assertCoverageTransportBlobOutcome(suite: string, primaryPersisted: BlobPrimaryState, artifactAttempt1: StepOutcome, artifactAttempt2: StepOutcome, emit?: (line: string) => void): boolean;
|
|
21
22
|
export type AppendOutput = (path: string, data: string) => void;
|
|
22
23
|
/**
|
|
23
|
-
* Writes `blob=true|false` to `$GITHUB_OUTPUT` for outcome reporting.
|
|
24
|
-
*
|
|
25
|
-
*
|
|
24
|
+
* Writes `blob=true|false` to `$GITHUB_OUTPUT` for outcome reporting. Workflows gate the
|
|
25
|
+
* GitHub-fallback blob upload on this signal (`blob != 'true'`). The upload subcommand's exit
|
|
26
|
+
* code tracks the coverage pair, not the blob. A no-op outside CI (`githubOutputPath` unset).
|
|
26
27
|
*/
|
|
27
28
|
export declare function writeUploadOutcomeOutput(outcome: {
|
|
28
29
|
readonly blob: boolean;
|
|
@@ -41,15 +41,13 @@ export function isBlobPrimaryState(value) {
|
|
|
41
41
|
}
|
|
42
42
|
/**
|
|
43
43
|
* Sibling to `assertCoverageTransportOutcome` for the Vitest blob. `true`/`false` are the S3
|
|
44
|
-
* upload step's `blob` output; `skipped` means that step never ran.
|
|
45
|
-
*
|
|
44
|
+
* upload step's `blob` output; `skipped` means that step never ran. Workflows gate the GitHub
|
|
45
|
+
* fallback on `blob != 'true'`, so a persisted S3 primary with no GitHub artifact attempt is the
|
|
46
|
+
* expected steady state, not a degraded one.
|
|
46
47
|
*/
|
|
47
48
|
export function assertCoverageTransportBlobOutcome(suite, primaryPersisted, artifactAttempt1, artifactAttempt2, emit = (line) => process.stderr.write(`${line}\n`)) {
|
|
48
49
|
const artifactSucceeded = artifactAttempt1 === 'success' || artifactAttempt2 === 'success';
|
|
49
50
|
if (primaryPersisted === 'true') {
|
|
50
|
-
if (!artifactSucceeded) {
|
|
51
|
-
emit(`::warning::Vitest blob persisted only to S3 for suite=${suite}; GitHub artifact fallback is degraded.`);
|
|
52
|
-
}
|
|
53
51
|
return true;
|
|
54
52
|
}
|
|
55
53
|
if (artifactSucceeded) {
|
|
@@ -62,9 +60,9 @@ export function assertCoverageTransportBlobOutcome(suite, primaryPersisted, arti
|
|
|
62
60
|
return false;
|
|
63
61
|
}
|
|
64
62
|
/**
|
|
65
|
-
* Writes `blob=true|false` to `$GITHUB_OUTPUT` for outcome reporting.
|
|
66
|
-
*
|
|
67
|
-
*
|
|
63
|
+
* Writes `blob=true|false` to `$GITHUB_OUTPUT` for outcome reporting. Workflows gate the
|
|
64
|
+
* GitHub-fallback blob upload on this signal (`blob != 'true'`). The upload subcommand's exit
|
|
65
|
+
* code tracks the coverage pair, not the blob. A no-op outside CI (`githubOutputPath` unset).
|
|
68
66
|
*/
|
|
69
67
|
export function writeUploadOutcomeOutput(outcome, githubOutputPath, appendOutput = appendFileSync) {
|
|
70
68
|
if (githubOutputPath)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vouchington-tooling",
|
|
3
|
-
"version": "0.13.
|
|
3
|
+
"version": "0.13.4",
|
|
4
4
|
"description": "Vouchington CLI and extractable tooling libraries.",
|
|
5
5
|
"homepage": "https://github.com/vouchington/vouchington-tooling/tree/main/packages/vouchington-tooling#readme",
|
|
6
6
|
"bugs": {
|