@cplieger/actions 2.0.12 → 3.0.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/README.md CHANGED
@@ -3,6 +3,7 @@
3
3
  [![npm](https://img.shields.io/npm/v/@cplieger/actions)](https://www.npmjs.com/package/@cplieger/actions)
4
4
  [![JSR](https://jsr.io/badges/@cplieger/actions)](https://jsr.io/@cplieger/actions)
5
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
+ [![Mutation (TS)](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/cplieger/actions/badges/mutation-ts.json)](https://github.com/cplieger/actions/issues?q=label%3Astryker-tracker)
6
7
  [![OpenSSF Best Practices](https://www.bestpractices.dev/projects/13197/badge)](https://www.bestpractices.dev/projects/13197)
7
8
  [![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/cplieger/actions/badge)](https://scorecard.dev/viewer/?uri=github.com/cplieger/actions)
8
9
 
@@ -115,10 +116,10 @@ const action = apiAction({
115
116
  - `retryNetwork` — preset retry classifier for transient failures
116
117
  - `classifyFetchError(err)` — classify fetch errors (network vs timeout vs HTTP)
117
118
  - `hasErrorString(err)` — type guard for objects with a `.message` string
118
- - `withTimeout(signal, ms)` — compose an AbortSignal with a timeout
119
- - `API_TIMEOUT_MS` — default API request timeout (30 000 ms)
120
119
  - `RETRY_STANDARD` — standard retry config (2 retries, 300ms)
121
120
 
121
+ > `withTimeout(signal, ms)` and `API_TIMEOUT_MS` moved to [`@cplieger/fetch`](https://github.com/cplieger/fetch) (the layer that owns timeout composition); import them from there.
122
+
122
123
  ### Test utilities (`@cplieger/actions/testing`)
123
124
 
124
125
  The `./testing` subpath exports test-only helpers. Import only from test code:
package/jsr.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cplieger/actions",
3
- "version": "2.0.12",
3
+ "version": "3.0.0",
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.12",
3
+ "version": "3.0.0",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "exports": {
@@ -13,15 +13,18 @@
13
13
  "test": "vitest --run"
14
14
  },
15
15
  "dependencies": {
16
- "@cplieger/fetch": "1.1.2",
16
+ "@cplieger/fetch": "2.0.0",
17
17
  "@cplieger/reactive": "1.2.4"
18
18
  },
19
19
  "devDependencies": {
20
20
  "@eslint/js": "10.0.1",
21
+ "@stryker-mutator/core": "9.6.1",
22
+ "@stryker-mutator/typescript-checker": "9.6.1",
23
+ "@stryker-mutator/vitest-runner": "9.6.1",
21
24
  "@types/node": "24.13.3",
22
25
  "@typescript/native": "npm:typescript@7.0.2",
23
26
  "@vitest/coverage-v8": "4.1.10",
24
- "eslint": "10.6.0",
27
+ "eslint": "10.7.0",
25
28
  "fast-check": "4.9.0",
26
29
  "happy-dom": "20.10.6",
27
30
  "prettier": "3.9.5",
package/src/api.ts CHANGED
@@ -1,36 +1,18 @@
1
1
  // apiAction: factory for HTTP-backed actions. The run() implementation is just
2
2
  // a request descriptor (RequestSpec); the request/response envelope is owned by
3
- // @cplieger/fetch. actions holds its OWN isolated fetch instance (createFetch)
4
- // so configureApi() never collides with a consuming app's global configureFetch,
5
- // then maps fetch's ApiResult envelope onto ActionError so callers see the
6
- // identical typed errors they always have.
3
+ // @cplieger/fetch. actions holds its OWN fetch instance (createFetch) — fully
4
+ // isolated from any instance a consuming app builds (fetch v2 is
5
+ // instances-only) — and maps fetch's ApiResult envelope onto ActionError so
6
+ // callers see the identical typed errors they always have.
7
7
  // ---------------------------------------------------------------------------
8
8
 
9
- import { createFetch } from "@cplieger/fetch";
9
+ import { API_TIMEOUT_MS, createFetch } from "@cplieger/fetch";
10
10
  import type { ApiErr, FetchConfig, FetchInstance, RequestOptions } from "@cplieger/fetch";
11
11
 
12
12
  import { defineAction, IDEMPOTENCY_HEADER } from "./define.js";
13
13
  import { ActionError } from "./error.js";
14
14
  import type { Action, ActionContext, ActionDefinition, RequestSpec } from "./types.js";
15
15
 
16
- /** Default request timeout in milliseconds. */
17
- export const API_TIMEOUT_MS = 30_000;
18
-
19
- /**
20
- * Compose an optional caller signal with a fresh timeout signal.
21
- * If the caller provides an existing signal, the result aborts when
22
- * either the caller signal or the timeout fires — whichever comes first.
23
- *
24
- * @param signal - Existing signal to compose with (may be undefined).
25
- * @param ms - Timeout in milliseconds.
26
- * @returns A composed AbortSignal.
27
- */
28
- export function withTimeout(signal: AbortSignal | undefined, ms: number): AbortSignal {
29
- return signal !== undefined
30
- ? AbortSignal.any([signal, AbortSignal.timeout(ms)])
31
- : AbortSignal.timeout(ms);
32
- }
33
-
34
16
  const JSON_CT = "application/json";
35
17
 
36
18
  // ---------------------------------------------------------------------------
@@ -57,14 +39,15 @@ export interface ApiConfig {
57
39
  readonly fetchFn?: typeof fetch;
58
40
  }
59
41
 
60
- // actions owns a PRIVATE, isolated @cplieger/fetch instance rather than the
61
- // library's module-global configureFetch: a consuming app (subflux/vibekit) may
62
- // call configureFetch() for its own direct fetch usage, and actions must never
63
- // read or clobber that shared default. baseUrl / credentials / fetchFn are
64
- // projected onto this instance. prepareHeaders stays here (not on the instance)
65
- // because actions' hook is spec-aware — `(headers, { spec })` — a shape fetch's
66
- // `(headers)`-only hook can't express, so executeRequest runs it itself and
67
- // passes the result as the per-request headers.
42
+ // actions owns a PRIVATE @cplieger/fetch instance. fetch v2 is instances-only
43
+ // with config frozen at construction, so isolation from the consuming app's
44
+ // own fetch instances is inherent and configureApi's replace semantics are
45
+ // implemented the only way v2 allows: by rebuilding the instance. baseUrl /
46
+ // credentials / fetchFn are projected onto this instance. prepareHeaders stays
47
+ // here (not on the instance) because actions' hook is spec-aware —
48
+ // `(headers, { spec })` — a shape fetch's `(headers)`-only hook can't express,
49
+ // so executeRequest runs it itself and passes the result as the per-request
50
+ // headers.
68
51
  let apiFetch: FetchInstance = createFetch();
69
52
  let apiPrepareHeaders: ApiConfig["prepareHeaders"];
70
53
 
@@ -94,8 +77,8 @@ export function configureApi(config: ApiConfig): void {
94
77
  if (config.fetchFn !== undefined) {
95
78
  fetchConfig.fetchFn = config.fetchFn;
96
79
  }
97
- // Rebuild the instance so this call REPLACES the previous config (a bare
98
- // instance.configure() would shallow-merge/accumulate instead).
80
+ // Rebuild the instance: fetch v2 instances are immutable, and configureApi's
81
+ // contract is replace-not-merge, which a fresh instance implements exactly.
99
82
  apiFetch = createFetch(fetchConfig);
100
83
  apiPrepareHeaders = config.prepareHeaders;
101
84
  }
package/src/index.ts CHANGED
@@ -11,7 +11,7 @@ export type { TransportSendResult, TransportCommand, TransportSendFn } from "./t
11
11
 
12
12
  // Action factories
13
13
  export { defineAction } from "./define.js";
14
- export { apiAction, configureApi, API_TIMEOUT_MS, withTimeout } from "./api.js";
14
+ export { apiAction, configureApi } from "./api.js";
15
15
  export type { ApiConfig, ApiActionDefinition } from "./api.js";
16
16
 
17
17
  // Error class + utilities