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. GitHub fallback is always
18
- * attempted when enabled — workflows must not gate it on `blob != 'true'`.
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. GitHub-fallback blob upload
24
- * must always be attempted when enabled; do not gate it on this signal. The upload subcommand's
25
- * exit code tracks the coverage pair, not the blob. A no-op outside CI (`githubOutputPath` unset).
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. GitHub fallback is always
45
- * attempted when enabled — workflows must not gate it on `blob != 'true'`.
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. GitHub-fallback blob upload
66
- * must always be attempted when enabled; do not gate it on this signal. The upload subcommand's
67
- * exit code tracks the coverage pair, not the blob. A no-op outside CI (`githubOutputPath` unset).
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",
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": {