@cplieger/actions 2.0.0 → 2.0.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 CHANGED
@@ -1,9 +1,10 @@
1
1
  # actions
2
2
 
3
- [![CI](https://github.com/cplieger/actions/actions/workflows/ci.yaml/badge.svg)](https://github.com/cplieger/actions/actions/workflows/ci.yaml)
4
3
  [![npm](https://img.shields.io/npm/v/@cplieger/actions)](https://www.npmjs.com/package/@cplieger/actions)
5
4
  [![JSR](https://jsr.io/badges/@cplieger/actions)](https://jsr.io/@cplieger/actions)
6
- [![License: GPL-3.0](https://img.shields.io/badge/License-GPL--3.0-blue.svg)](./LICENSE)
5
+ [![Test coverage](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/cplieger/actions/badges/coverage.json)](https://github.com/cplieger/actions/actions/workflows/coverage.yml)
6
+ [![OpenSSF Best Practices](https://www.bestpractices.dev/projects/13197/badge)](https://www.bestpractices.dev/projects/13197)
7
+ [![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/cplieger/actions/badge)](https://scorecard.dev/viewer/?uri=github.com/cplieger/actions)
7
8
 
8
9
  > Declarative UI-actions framework with lifecycle management, retry, debounce, and polling.
9
10
 
package/jsr.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cplieger/actions",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
4
4
  "exports": {
5
5
  ".": "./src/index.ts",
6
6
  "./testing": "./src/testing.ts"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cplieger/actions",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "exports": {
@@ -17,14 +17,14 @@
17
17
  },
18
18
  "devDependencies": {
19
19
  "@eslint/js": "10.0.1",
20
- "@types/node": "24.13.1",
21
- "@vitest/coverage-v8": "4.1.8",
22
- "eslint": "10.4.1",
20
+ "@types/node": "24.13.2",
21
+ "@vitest/coverage-v8": "4.1.9",
22
+ "eslint": "10.5.0",
23
23
  "fast-check": "4.8.0",
24
- "happy-dom": "20.10.2",
25
- "prettier": "3.8.3",
26
- "typescript-eslint": "8.60.1",
27
- "vitest": "4.1.8"
24
+ "happy-dom": "20.10.3",
25
+ "prettier": "3.8.4",
26
+ "typescript-eslint": "8.61.1",
27
+ "vitest": "4.1.9"
28
28
  },
29
29
  "description": "Declarative async UI-action framework for TypeScript",
30
30
  "license": "GPL-3.0-or-later",
package/src/registry.ts CHANGED
@@ -33,6 +33,13 @@ let _pendingTotal = 0;
33
33
  let _liveCount = 0;
34
34
  let _head = 0;
35
35
 
36
+ // Test-only: when true, a detected _pendingTotal underflow throws instead of
37
+ // the production warn + clamp, so an add/removePending pairing bug surfaces in
38
+ // tests rather than being silently masked. Production default is false.
39
+ // Toggled via _setThrowOnInvariantViolationForTest (same _*ForTest convention
40
+ // as _resetForTest); reset to false by _resetForTest.
41
+ let _throwOnInvariantViolation = false;
42
+
36
43
  // Reactive mirrors of the pending state. pendingByName remains the source of
37
44
  // truth; these signals expose the derived counts so isPending/pendingCount can
38
45
  // be read reactively (e.g. by bindLoadingState's effect).
@@ -153,6 +160,9 @@ export function record(instance: ActionInstance): void {
153
160
  compact();
154
161
  }
155
162
  if (_pendingTotal < 0) {
163
+ if (_throwOnInvariantViolation) {
164
+ throw new Error("[actions] _pendingTotal went negative — invariant violation");
165
+ }
156
166
  console.warn("[actions] _pendingTotal went negative — invariant violation; clamping to 0");
157
167
  _pendingTotal = 0;
158
168
  pendingTotalSig.value = 0;
@@ -243,6 +253,26 @@ export function _resetForTest(): void {
243
253
  listeners.clear();
244
254
  namedListeners.clear();
245
255
  _pendingTotal = 0;
256
+ _throwOnInvariantViolation = false;
246
257
  pendingSigs.clearAll();
247
258
  pendingTotalSig.value = 0;
248
259
  }
260
+
261
+ /**
262
+ * @internal Test-only: toggle whether a detected `_pendingTotal` underflow
263
+ * throws (true) instead of the production warn + clamp (false). Lets tests
264
+ * assert that an add/removePending pairing bug surfaces loudly. Reset to false
265
+ * by `_resetForTest`.
266
+ */
267
+ export function _setThrowOnInvariantViolationForTest(enabled: boolean): void {
268
+ _throwOnInvariantViolation = enabled;
269
+ }
270
+
271
+ /**
272
+ * @internal Test-only: simulate an unpaired `removePending` (an add/remove
273
+ * mismatch) by decrementing the pending total without a matching add, driving
274
+ * the invariant negative. The next `record()` then hits the clamp branch.
275
+ */
276
+ export function _forcePendingUnderflowForTest(): void {
277
+ _pendingTotal--;
278
+ }