liaise 5.0.2 → 5.0.3

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/dist/types.d.ts CHANGED
@@ -43,7 +43,7 @@ export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
43
43
  * ceremony with no effect. Mismatch it and you get a wrong `TResponse`
44
44
  * silently, same as always — declare it as `undefined`.
45
45
  *
46
- * This describes `new Request`'s behaviour specifically. As of 4.1.0,
46
+ * This describes `new Request`'s behaviour specifically.
47
47
  * `defineRequest` DOES enforce this pairing at compile time — see
48
48
  * `EmptyBodyGuard` in `define-request.ts` — because a curried function,
49
49
  * unlike a constructor, has no permissive overload for the guard to fall
@@ -178,7 +178,7 @@ export interface RequestConfig {
178
178
  * Declaring `responseType: 'none'` alongside a schema is a contradiction —
179
179
  * there is no body to validate, and every call will fail validation. It is
180
180
  * not rejected at compile time because the runtime failure is immediate and
181
- * loud; see the spec's non-goals.
181
+ * loud.
182
182
  *
183
183
  * On this class path, `schema` and `TResponse` are also not tied together at
184
184
  * compile time: `new Request<P, User>({ ..., schema: numberSchema })`
@@ -219,12 +219,14 @@ export interface RequestConfig {
219
219
  * and an `'abort'` give-up does not (see {@link ApiConfig.onError}). A per-*request*
220
220
  * {@link RequestConfig.timeout}, by contrast, bounds the shared request
221
221
  * itself for everyone.
222
- * A call whose params are a special body type — `FormData`, `Blob`,
223
- * `ArrayBuffer`, `URLSearchParams`, or a raw string — is also never shared:
224
- * the stable serialisation used for identity can't distinguish two
225
- * different payloads of these types from each other, so sharing them could
226
- * hand one caller the response to a *different* payload than the one it
227
- * sent.
222
+ * A call whose params cannot be compared by content is also never shared.
223
+ * That is, at any depth: a BigInt, a function or symbol, an `ArrayBuffer`,
224
+ * `Blob`, `FormData` or `URLSearchParams`, a boxed primitive, a circular
225
+ * structure, or a non-plain object with no enumerable keys (an `Error`, a
226
+ * class keeping its state in private fields). These are declined because
227
+ * their content can't be keyed reliably, and a wrong match would hand one
228
+ * caller the response to another's request. A string, `Date`, `Map`, `Set` or typed
229
+ * array is compared by content and shares normally.
228
230
  *
229
231
  * `result.retry()` on a shared result re-runs the pipeline using the
230
232
  * **acquiring caller's** own per-call options (headers, signal, timeout) —
@@ -233,15 +235,6 @@ export interface RequestConfig {
233
235
  * non-aborting sharer receiving the literal same `Result` object; it is
234
236
  * unavoidable given that design, but worth knowing before relying on it.
235
237
  *
236
- * **Known limitation:** middleware (global or per-request) that replaces
237
- * `ctx.request.signal` — see {@link MiddlewareContext.request.signal} — is
238
- * re-merged with the dedupe signal under `dedupe: true`, but is **not**
239
- * currently re-merged with the share refcount controller. Combining
240
- * `share` with signal-replacing middleware means that middleware's signal,
241
- * not the refcount, ends up controlling the shared request: one sharer's
242
- * middleware-installed signal could cancel the request for every other
243
- * sharer.
244
- *
245
238
  * @default false
246
239
  */
247
240
  share?: boolean;
@@ -272,8 +265,7 @@ export interface RequestConfig {
272
265
  * **This is a whole-operation deadline, not a per-attempt budget.** It covers
273
266
  * the entire middleware chain including every retry and every backoff delay,
274
267
  * so `timeout: 5000` with `retryMiddleware(3)` still means "an answer within
275
- * 5 seconds" — not five seconds per attempt. This deliberately differs from
276
- * axios, XHR and `got`, which apply timeouts per attempt.
268
+ * 5 seconds" — not five seconds per attempt.
277
269
  *
278
270
  * For a per-attempt budget, use a signal-replacing middleware placed inside
279
271
  * the retry middleware instead:
@@ -313,8 +305,8 @@ export interface RequestConfig {
313
305
  */
314
306
  export interface SuccessResult<TResponse> {
315
307
  /**
316
- * The parsed response data, narrowed by `error === null`. Since 4.0.0 a 2xx
317
- * that carries no body under `responseType: 'json'` is a `'parse'` error
308
+ * The parsed response data, narrowed by `error === null`. A 2xx that
309
+ * carries no body under `responseType: 'json'` is a `'parse'` error
318
310
  * rather than a success, so this is not `null` for that case — declare
319
311
  * `responseType: 'none'` on an endpoint that answers with no body. See the
320
312
  * `responseType` reference.
@@ -340,7 +332,7 @@ export interface SuccessResult<TResponse> {
340
332
  * A failed API call. `error` is populated and `data` is `null`.
341
333
  *
342
334
  * `response` is present for HTTP and parse failures (the server responded)
343
- * and `null` for network failures, aborts and timeouts. Check
335
+ * and `null` for network failures, aborts, timeouts and middleware errors. Check
344
336
  * {@link ApiError.kind} to tell them apart.
345
337
  */
346
338
  export interface ErrorResult<TResponse> {
@@ -403,7 +395,10 @@ export interface CallOptions {
403
395
  * new reference that won't match the original.
404
396
  */
405
397
  skipMiddleware?: Middleware[];
406
- /** Extra headers for this call. Highest merge priority (overrides all). */
398
+ /**
399
+ * Extra headers for this call. Each one replaces the client's and the
400
+ * endpoint's value for the same header; other headers are kept.
401
+ */
407
402
  headers?: HeadersInit;
408
403
  /**
409
404
  * An `AbortSignal` to cancel this request. When the signal fires,
@@ -425,8 +420,9 @@ export interface CallOptions {
425
420
  * cannot outlast it.
426
421
  *
427
422
  * A fractional or out-of-range value is normalised rather than rejected:
428
- * rounded down to whole milliseconds, clamped to the platform timer ceiling,
429
- * and treated as "no timeout" if it is `NaN` or non-positive.
423
+ * rounded down to whole milliseconds with a 1 ms minimum, clamped to the
424
+ * platform timer ceiling, and treated as "no timeout" if it is `NaN` or
425
+ * non-positive.
430
426
  */
431
427
  timeout?: number;
432
428
  }
@@ -440,10 +436,9 @@ export interface CallOptions {
440
436
  * Middleware can read and modify `ctx.request.headers` and `ctx.request.body`
441
437
  * before calling `next()` — changes will propagate to the actual fetch call.
442
438
  *
443
- * **Typing note:** The spec defines MiddlewareContext with generic TParams and
444
- * TResponse, but middleware is intentionally loosely typed. Authors work with
445
- * `unknown` and cast internally if they need specific types. This avoids
446
- * complex generic inference issues and keeps middleware composable.
439
+ * Middleware is deliberately loosely typed: authors work with `unknown` and
440
+ * cast internally if they need specific types. This avoids complex generic
441
+ * inference issues and keeps middleware composable.
447
442
  */
448
443
  export interface MiddlewareContext {
449
444
  /** Mutable request details — middleware can modify headers and body. */
@@ -458,7 +453,7 @@ export interface MiddlewareContext {
458
453
  params: unknown;
459
454
  /** Merged headers — middleware can add/remove headers here. */
460
455
  headers: Headers;
461
- /** Serialized request body, or null for GET/DELETE requests. */
456
+ /** Serialized request body, or null when there is none. */
462
457
  body: unknown | null;
463
458
  /**
464
459
  * The AbortSignal that will be handed to `fetch`.
@@ -469,7 +464,9 @@ export interface MiddlewareContext {
469
464
  * middleware takes effect. Under `dedupe: true` the replacement is merged
470
465
  * into the dedupe signal rather than discarded: the fetch is then
471
466
  * cancelled by whichever fires first, the middleware's signal or a newer
472
- * call superseding this one.
467
+ * call superseding this one. Under `share: true` the shared request's
468
+ * refcount signal is merged back in the same way, so the request is still
469
+ * cancelled once every sharer has given up.
473
470
  *
474
471
  * While middleware runs — before `next()` reaches the core fetch — this
475
472
  * holds the caller's `CallOptions.signal` merged with the timeout signal
@@ -685,7 +682,7 @@ export interface OperationConfig {
685
682
  *
686
683
  * Unlike the REST side, a schema here does **not** supply the response type —
687
684
  * `Operation`'s `TData` stays explicit, because only the REST pipeline has a
688
- * factory that can infer it. See the spec's non-goals.
685
+ * factory that can infer it.
689
686
  */
690
687
  schema?: StandardSchemaV1<unknown>;
691
688
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "liaise",
3
- "version": "5.0.2",
3
+ "version": "5.0.3",
4
4
  "description": "Type-safe API client for REST and GraphQL on standard fetch. Never throws. Zero dependencies.",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -34,10 +34,11 @@
34
34
  "test": "vitest",
35
35
  "test:run": "vitest run",
36
36
  "test:integration": "vitest run --config vitest.integration.ts",
37
+ "docs:check": "node scripts/check-readme.mjs",
37
38
  "typecheck": "tsc --noEmit",
38
39
  "test:types": "vitest run --typecheck",
39
40
  "sessions": "test -f .claude/sessions.mjs && node .claude/sessions.mjs || echo \"npm run sessions is a local-only helper (gitignored .claude/); nothing to list in this clone\"",
40
- "prepublishOnly": "npm run typecheck && npm run test:types && npm run test:run && npm run test:integration && npm run build && npm run size"
41
+ "prepublishOnly": "npm run typecheck && npm run test:types && npm run test:run && npm run test:integration && npm run docs:check && npm run build && npm run size"
41
42
  },
42
43
  "repository": {
43
44
  "type": "git",
@@ -69,6 +70,7 @@
69
70
  "http-client"
70
71
  ],
71
72
  "devDependencies": {
73
+ "@tanstack/query-core": "5.104.1",
72
74
  "@types/node": "^22.19.18",
73
75
  "esbuild": "0.27.4",
74
76
  "typescript": "~5.8.0",