@prosody-events/prosody 0.3.0 → 0.4.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/index.d.ts CHANGED
@@ -7,12 +7,52 @@
7
7
  import type {
8
8
  Configuration,
9
9
  ConsumerState,
10
- Message,
10
+ Message as NativeMessage,
11
11
  Mode,
12
12
  Timer,
13
13
  } from "./bindings";
14
14
 
15
- export { Configuration, ConsumerState, Message, Timer, Mode };
15
+ export { Configuration, ConsumerState, Timer, Mode };
16
+
17
+ /** A primitive value representable in JSON. */
18
+ export type JsonPrimitive = null | boolean | number | string;
19
+
20
+ /**
21
+ * Any value representable in JSON.
22
+ *
23
+ * This is a compile-time contract. Prosody serializes payloads at runtime but
24
+ * does not validate them against a more specific application payload type.
25
+ */
26
+ export type JsonValue =
27
+ JsonPrimitive | readonly JsonValue[] | { readonly [key: string]: JsonValue };
28
+
29
+ /**
30
+ * Maps an application type to its JSON-compatible shape. This lets APIs
31
+ * accept ordinary interfaces and type aliases without requiring an index
32
+ * signature, while rejecting non-JSON members wherever they are nested.
33
+ */
34
+ export type JsonCompatible<T> = T extends JsonPrimitive
35
+ ? T
36
+ : T extends bigint | symbol | undefined | ((...args: any[]) => unknown)
37
+ ? never
38
+ : T extends readonly (infer Item)[]
39
+ ? readonly JsonCompatible<Item>[]
40
+ : T extends object
41
+ ? { readonly [Key in keyof T]: JsonCompatible<T[Key]> }
42
+ : never;
43
+
44
+ /**
45
+ * Represents a message consumed from a Kafka topic.
46
+ *
47
+ * The optional payload type parameter is annotation-level only: the runtime
48
+ * payload is unchanged. An unparameterized `Message` uses {@link JsonValue};
49
+ * provide an application payload type for precise field-level checking.
50
+ * Message-backed keyed-state collections vend their items as `Message<P>`.
51
+ */
52
+ export interface Message<P = JsonValue> extends Omit<NativeMessage, "payload"> {
53
+ /** The message payload as a JSON-serializable value. */
54
+ payload: P;
55
+ }
16
56
 
17
57
  /**
18
58
  * Wrapper around `MessageContext` for use in Node.js bindings.
@@ -74,6 +114,421 @@ export declare class Context {
74
114
  * @throws Error if retrieval fails.
75
115
  */
76
116
  scheduled(): Promise<Array<Date>>;
117
+
118
+ /**
119
+ * Binds a registered message single-value collection, vending a handle whose
120
+ * item is the full `Message<P>`.
121
+ *
122
+ * The handle and any iterator it opens are valid only within this event
123
+ * attempt. Throws a {@link PermanentStateError} if the collection name is
124
+ * unregistered or its registered identity mismatches.
125
+ * @param definition - A definition from {@link messageValue}.
126
+ */
127
+ state<P>(definition: MessageValueDefinition<P>): ValueState<Message<P>>;
128
+ /**
129
+ * Binds a registered message ordered-map collection, vending a handle whose
130
+ * values are the full `Message<P>`. Valid only within this event attempt;
131
+ * throws {@link PermanentStateError} on an unregistered name or identity
132
+ * mismatch.
133
+ * @param definition - A definition from {@link messageMap}.
134
+ */
135
+ state<P>(definition: MessageMapDefinition<P>): MapState<Message<P>>;
136
+ /**
137
+ * Binds a registered message deque collection, vending a handle whose
138
+ * elements are the full `Message<P>`. Valid only within this event attempt;
139
+ * throws {@link PermanentStateError} on an unregistered name or identity
140
+ * mismatch.
141
+ * @param definition - A definition from {@link messageDeque}.
142
+ */
143
+ state<P>(definition: MessageDequeDefinition<P>): DequeState<Message<P>>;
144
+ /**
145
+ * Binds a registered single-value JSON collection. Valid only within this
146
+ * event attempt; throws {@link PermanentStateError} on an unregistered name
147
+ * or identity mismatch.
148
+ * @param definition - A definition from {@link value}.
149
+ */
150
+ state<T>(definition: ValueDefinition<T>): ValueState<T>;
151
+ /**
152
+ * Binds a registered ordered-map JSON collection (string keys). Valid only
153
+ * within this event attempt; throws {@link PermanentStateError} on an
154
+ * unregistered name or identity mismatch.
155
+ * @param definition - A definition from {@link map}.
156
+ */
157
+ state<V>(definition: MapDefinition<V>): MapState<V>;
158
+ /**
159
+ * Binds a registered deque JSON collection. Valid only within this event
160
+ * attempt; throws {@link PermanentStateError} on an unregistered name or
161
+ * identity mismatch.
162
+ * @param definition - A definition from {@link deque}.
163
+ */
164
+ state<T>(definition: DequeDefinition<T>): DequeState<T>;
165
+ }
166
+
167
+ /**
168
+ * Scan direction over a map or deque collection. `"forward"` visits a map in
169
+ * ascending key order and a deque from front to back; `"backward"` reverses
170
+ * each. Defaults to `"forward"` wherever it is optional.
171
+ */
172
+ export type ScanDirection = "forward" | "backward";
173
+
174
+ /** Options accepted by every keyed-state definition constructor. */
175
+ export interface StateDefinitionOptions {
176
+ /**
177
+ * Optional per-write TTL in whole seconds. Must be at least 1 and must
178
+ * exceed the client's recovery delay (enforced core-side).
179
+ */
180
+ ttlSeconds?: number;
181
+ /**
182
+ * Opt out of transactional staging (read-uncommitted, at-least-once).
183
+ * Defaults to transactional.
184
+ */
185
+ readUncommitted?: boolean;
186
+ }
187
+
188
+ /** Options accepted by the map definition constructors. */
189
+ export interface MapDefinitionOptions extends StateDefinitionOptions {
190
+ /**
191
+ * Keyset bound for ordered scans (`0..=4096`; default 128 core-side; `0`
192
+ * disables ordered-scan tracking). Map collections only.
193
+ */
194
+ keysetLimit?: number;
195
+ }
196
+
197
+ /** Options accepted by the deque definition constructors. */
198
+ export interface DequeDefinitionOptions extends StateDefinitionOptions {
199
+ /**
200
+ * Optional maximum element count (bounded backlog). Must be a whole number
201
+ * >= 1. Runtime-only: not persisted, not part of collection identity, and
202
+ * changeable across deploys. Enforced lazily on push — the opposite end is
203
+ * evicted toward the bound (no decode, no fetch); a shrunk deque reports its
204
+ * old length until the next push trims it. Deque collections only.
205
+ */
206
+ capacity?: number;
207
+ }
208
+
209
+ /**
210
+ * Phantom brand carrying a definition's item type. Never present at runtime —
211
+ * it exists only so the item type survives on the frozen definition object and
212
+ * flows into the vended handle.
213
+ */
214
+ declare const StateItem: unique symbol;
215
+
216
+ /** A frozen single-value JSON collection definition. */
217
+ export interface ValueDefinition<T = JsonValue> {
218
+ readonly name: string;
219
+ readonly kind: "value";
220
+ readonly payload: "json";
221
+ readonly ttlSeconds?: number;
222
+ readonly readUncommitted?: boolean;
223
+ readonly [StateItem]?: T;
224
+ }
225
+
226
+ /** A frozen ordered-map JSON collection definition (string keys). */
227
+ export interface MapDefinition<V = JsonValue> {
228
+ readonly name: string;
229
+ readonly kind: "map";
230
+ readonly payload: "json";
231
+ readonly ttlSeconds?: number;
232
+ readonly readUncommitted?: boolean;
233
+ readonly keysetLimit?: number;
234
+ readonly [StateItem]?: V;
235
+ }
236
+
237
+ /** A frozen deque JSON collection definition. */
238
+ export interface DequeDefinition<T = JsonValue> {
239
+ readonly name: string;
240
+ readonly kind: "deque";
241
+ readonly payload: "json";
242
+ readonly ttlSeconds?: number;
243
+ readonly readUncommitted?: boolean;
244
+ readonly capacity?: number;
245
+ readonly [StateItem]?: T;
246
+ }
247
+
248
+ /** A frozen single-value message collection definition (items are `Message<P>`). */
249
+ export interface MessageValueDefinition<P = JsonValue> {
250
+ readonly name: string;
251
+ readonly kind: "value";
252
+ readonly payload: "message";
253
+ readonly ttlSeconds?: number;
254
+ readonly readUncommitted?: boolean;
255
+ readonly [StateItem]?: P;
256
+ }
257
+
258
+ /** A frozen ordered-map message collection definition (values are `Message<P>`). */
259
+ export interface MessageMapDefinition<P = JsonValue> {
260
+ readonly name: string;
261
+ readonly kind: "map";
262
+ readonly payload: "message";
263
+ readonly ttlSeconds?: number;
264
+ readonly readUncommitted?: boolean;
265
+ readonly keysetLimit?: number;
266
+ readonly [StateItem]?: P;
267
+ }
268
+
269
+ /** A frozen deque message collection definition (elements are `Message<P>`). */
270
+ export interface MessageDequeDefinition<P = JsonValue> {
271
+ readonly name: string;
272
+ readonly kind: "deque";
273
+ readonly payload: "message";
274
+ readonly ttlSeconds?: number;
275
+ readonly readUncommitted?: boolean;
276
+ readonly capacity?: number;
277
+ readonly [StateItem]?: P;
278
+ }
279
+
280
+ /**
281
+ * Declares a single-value JSON collection. The returned frozen definition is
282
+ * the single source of typing: place it in `Configuration.stateCollections` to
283
+ * register the collection, and pass it to `Context.state()` to vend a typed
284
+ * handle. The type parameter annotates the stored value and is compile-time
285
+ * only — payloads cross as plain JSON with no runtime validation.
286
+ * @param name - The collection name (unique per client).
287
+ * @param options - Optional `ttlSeconds` (whole seconds) and `readUncommitted`.
288
+ */
289
+ export function value<T = JsonValue>(
290
+ name: string,
291
+ options?: StateDefinitionOptions,
292
+ ): ValueDefinition<T>;
293
+
294
+ /**
295
+ * Declares an ordered-map JSON collection. Map keys are always `string`. The
296
+ * returned frozen definition is used both in `Configuration.stateCollections`
297
+ * and with `Context.state()`. The type parameter annotates the stored value
298
+ * (compile-time only).
299
+ * @param name - The collection name (unique per client).
300
+ * @param options - Optional `ttlSeconds`, `readUncommitted`, and `keysetLimit`.
301
+ */
302
+ export function map<V = JsonValue>(
303
+ name: string,
304
+ options?: MapDefinitionOptions,
305
+ ): MapDefinition<V>;
306
+
307
+ /**
308
+ * Declares a double-ended-queue JSON collection. The returned frozen definition
309
+ * is used both in `Configuration.stateCollections` and with `Context.state()`.
310
+ * The type parameter annotates the stored element (compile-time only).
311
+ * @param name - The collection name (unique per client).
312
+ * @param options - Optional `ttlSeconds` (whole seconds), `readUncommitted`, and
313
+ * `capacity` (bounded backlog; enforced lazily on push).
314
+ */
315
+ export function deque<T = JsonValue>(
316
+ name: string,
317
+ options?: DequeDefinitionOptions,
318
+ ): DequeDefinition<T>;
319
+
320
+ /**
321
+ * Declares a single-value message collection: each stored item is the full
322
+ * Kafka `Message<P>` the handler received. The type parameter annotates the
323
+ * message payload (compile-time only).
324
+ * @param name - The collection name (unique per client).
325
+ * @param options - Optional `ttlSeconds` (whole seconds) and `readUncommitted`.
326
+ */
327
+ export function messageValue<P = JsonValue>(
328
+ name: string,
329
+ options?: StateDefinitionOptions,
330
+ ): MessageValueDefinition<P>;
331
+
332
+ /**
333
+ * Declares an ordered-map message collection (string keys; values are the full
334
+ * Kafka `Message<P>`). The type parameter annotates the message payload
335
+ * (compile-time only).
336
+ * @param name - The collection name (unique per client).
337
+ * @param options - Optional `ttlSeconds`, `readUncommitted`, and `keysetLimit`.
338
+ */
339
+ export function messageMap<P = JsonValue>(
340
+ name: string,
341
+ options?: MapDefinitionOptions,
342
+ ): MessageMapDefinition<P>;
343
+
344
+ /**
345
+ * Declares a double-ended-queue message collection: each stored element is the
346
+ * full Kafka `Message<P>`. The type parameter annotates the message payload
347
+ * (compile-time only).
348
+ * @param name - The collection name (unique per client).
349
+ * @param options - Optional `ttlSeconds` (whole seconds), `readUncommitted`, and
350
+ * `capacity` (bounded backlog; enforced lazily on push).
351
+ */
352
+ export function messageDeque<P = JsonValue>(
353
+ name: string,
354
+ options?: DequeDefinitionOptions,
355
+ ): MessageDequeDefinition<P>;
356
+
357
+ /**
358
+ * Typed handle over a single-value keyed-state collection, vended by
359
+ * `Context.state()`. Valid only within the handler invocation (attempt) that
360
+ * vended it. Every method opens its own per-operation trace span.
361
+ */
362
+ export declare class ValueState<T = JsonValue> {
363
+ /** Vended only by {@link Context#state}; not constructible directly. */
364
+ private constructor(native: unknown);
365
+ /** Reads the current value, or null when absent/cleared. */
366
+ get(): Promise<T | null>;
367
+ /**
368
+ * Buffers a write of the value. The parameter type excludes `null`/`undefined`
369
+ * (via {@link !NonNullable}) because a top-level `null` is not a storable
370
+ * value — writing one (or an unrepresentable value) is a caller mistake,
371
+ * rejected at runtime with a {@link TransientStateError} naming `clear()` —
372
+ * use {@link ValueState#clear}. Transient so it retries and stays visible
373
+ * rather than discarding the message. (Nested `null`, e.g. inside an object or
374
+ * array, is permitted and round-trips.)
375
+ */
376
+ set(value: NonNullable<T>): Promise<void>;
377
+ /** Deletes the stored value. */
378
+ clear(): Promise<void>;
379
+ /**
380
+ * Durably commits the buffered operations mid-handler (at-least-once).
381
+ * Resolves with no value — the erased seam drops the store outcome.
382
+ */
383
+ commit(): Promise<void>;
384
+ /** Discards buffered uncommitted operations back to the committed floor. */
385
+ rollback(): Promise<void>;
386
+ }
387
+
388
+ /**
389
+ * Typed handle over an ordered-map keyed-state collection, vended by
390
+ * `Context.state()`. Map keys are always `string`. Valid only within the
391
+ * handler invocation (attempt) that vended it. Every method opens its own
392
+ * per-operation trace span.
393
+ */
394
+ export declare class MapState<V = JsonValue> {
395
+ /** Vended only by {@link Context#state}; not constructible directly. */
396
+ private constructor(native: unknown);
397
+ /** Reads the value for `key`, or null when the key is absent. */
398
+ get(key: string): Promise<V | null>;
399
+ /**
400
+ * Reads several keys in a single call. Returns an array with one entry per
401
+ * key, in the same order you asked, so `result[i]` is the value for
402
+ * `keys[i]`. A key that isn't there comes back as `null`, and a key you list
403
+ * more than once is answered at each spot. The whole read happens as one
404
+ * step, so no other change to this event's state can slip in partway through.
405
+ */
406
+ getMany(keys: readonly string[]): Promise<(V | null)[]>;
407
+ /**
408
+ * Reports whether `key` currently has a stored value. A presence check that
409
+ * skips the value decode and the resolver: for a message-backed map it
410
+ * answers with zero Kafka fetches and can report `true` for a message that
411
+ * can no longer be fetched. Not zero-I/O — a cache miss can still reach the
412
+ * store — but cheaper than {@link MapState#get} when you only need presence.
413
+ */
414
+ has(key: string): Promise<boolean>;
415
+ /**
416
+ * Inserts or overwrites `key`. The value type excludes `null`/`undefined`
417
+ * (via {@link !NonNullable}) because a top-level `null` is not a storable
418
+ * value — writing one (or an unrepresentable value) is a caller mistake,
419
+ * rejected at runtime with a {@link TransientStateError} — use
420
+ * {@link MapState#delete} to remove. Transient so it retries and stays visible
421
+ * rather than discarding the message. (Nested `null` is permitted.)
422
+ */
423
+ set(key: string, value: NonNullable<V>): Promise<void>;
424
+ /**
425
+ * Removes `key`.
426
+ *
427
+ * Deliberate divergence from `Map#delete`: returns void, NOT a boolean
428
+ * "was present" flag — surfacing that boolean would force a hidden read on
429
+ * every delete. (The underlying native operation is named `remove`.)
430
+ */
431
+ delete(key: string): Promise<void>;
432
+ /** Removes every entry. */
433
+ clear(): Promise<void>;
434
+ /**
435
+ * Async iterator over the live `[key, value]` entries in key order. Valid
436
+ * only within the handler invocation (attempt) that opened it; early exit
437
+ * from a `for await` loop closes the underlying cursor.
438
+ */
439
+ entries(direction?: ScanDirection): AsyncIterableIterator<[string, V]>;
440
+ /**
441
+ * Async iterator over the live keys in key order. Skips the value decode and
442
+ * the resolver, so a message-backed map enumerates keys with zero Kafka
443
+ * fetches; it still reads presence, so it is not zero-I/O. Valid only within
444
+ * the handler invocation (attempt) that opened it; early exit from a
445
+ * `for await` loop closes the underlying cursor.
446
+ */
447
+ keys(direction?: ScanDirection): AsyncIterableIterator<string>;
448
+ /**
449
+ * Async iterator over the live values in key order. Valid only within the
450
+ * handler invocation (attempt) that opened it; early exit from a `for await`
451
+ * loop closes the underlying cursor.
452
+ */
453
+ values(direction?: ScanDirection): AsyncIterableIterator<V>;
454
+ /**
455
+ * Forward iteration over `[key, value]` entries. Valid only within the
456
+ * handler invocation (attempt) that opened it.
457
+ */
458
+ [Symbol.asyncIterator](): AsyncIterableIterator<[string, V]>;
459
+ /**
460
+ * Durably commits the buffered operations mid-handler (at-least-once).
461
+ * Resolves with no value — the erased seam drops the store outcome.
462
+ */
463
+ commit(): Promise<void>;
464
+ /** Discards buffered uncommitted operations back to the committed floor. */
465
+ rollback(): Promise<void>;
466
+ }
467
+
468
+ /**
469
+ * Typed handle over a double-ended-queue keyed-state collection, vended by
470
+ * `Context.state()`. Valid only within the handler invocation (attempt) that
471
+ * vended it. Every method opens its own per-operation trace span.
472
+ */
473
+ export declare class DequeState<T = JsonValue> {
474
+ /** Vended only by {@link Context#state}; not constructible directly. */
475
+ private constructor(native: unknown);
476
+ /**
477
+ * Appends an element at the back. The item type excludes `null`/`undefined`
478
+ * (via {@link !NonNullable}) because a top-level `null` is not a storable
479
+ * element — writing one (or an unrepresentable value) is a caller mistake,
480
+ * rejected at runtime with a {@link TransientStateError} so it retries and
481
+ * stays visible rather than discarding the message. (Nested `null` is
482
+ * permitted.)
483
+ */
484
+ push(item: NonNullable<T>): Promise<void>;
485
+ /**
486
+ * Prepends an element at the front. The item type excludes `null`/`undefined`
487
+ * (via {@link !NonNullable}); see {@link DequeState#push}.
488
+ */
489
+ unshift(item: NonNullable<T>): Promise<void>;
490
+ /** Removes and returns the back element, or null when empty. */
491
+ pop(): Promise<T | null>;
492
+ /** Removes and returns the front element, or null when empty. */
493
+ shift(): Promise<T | null>;
494
+ /** Returns the number of live elements. */
495
+ length(): Promise<number>;
496
+ /** Reports whether the deque holds no live elements. */
497
+ isEmpty(): Promise<boolean>;
498
+ /** Removes every element. */
499
+ clear(): Promise<void>;
500
+ /**
501
+ * Reads the element at `index`, like `Array.prototype.at`: a non-negative
502
+ * `index` counts from the front (`0` is the front), a negative `index` counts
503
+ * back from the end (`-1` is the back). Any out-of-range position — including
504
+ * every index on an empty deque — resolves to null. `index` must be a safe
505
+ * integer; a fractional, `NaN`, or infinite value is a caller mistake,
506
+ * rejected with a {@link TransientStateError} (it retries and stays visible).
507
+ *
508
+ * The endpoints `at(0)` and `at(-1)` ride the front/back peeks — a single
509
+ * read, and `at(-1)` makes no length read. Any other negative `index` is
510
+ * resolved against the current {@link DequeState#length}, so it makes an
511
+ * extra boundary crossing that a non-negative one does not.
512
+ */
513
+ at(index: number): Promise<T | null>;
514
+ /**
515
+ * Async iterator over the live elements in index order. Valid only within
516
+ * the handler invocation (attempt) that opened it; early exit from a
517
+ * `for await` loop closes the underlying cursor.
518
+ */
519
+ values(direction?: ScanDirection): AsyncIterableIterator<T>;
520
+ /**
521
+ * Forward iteration over the elements. Valid only within the handler
522
+ * invocation (attempt) that opened it.
523
+ */
524
+ [Symbol.asyncIterator](): AsyncIterableIterator<T>;
525
+ /**
526
+ * Durably commits the buffered operations mid-handler (at-least-once).
527
+ * Resolves with no value — the erased seam drops the store outcome.
528
+ */
529
+ commit(): Promise<void>;
530
+ /** Discards buffered uncommitted operations back to the committed floor. */
531
+ rollback(): Promise<void>;
77
532
  }
78
533
 
79
534
  /**
@@ -94,7 +549,7 @@ export interface Logger {
94
549
  trace: (message: string | undefined | null, metadata?: any) => void;
95
550
  }
96
551
 
97
- export interface EventHandler {
552
+ export interface EventHandler<P = JsonValue> {
98
553
  /**
99
554
  * Callback function to handle incoming messages.
100
555
  *
@@ -105,7 +560,7 @@ export interface EventHandler {
105
560
  */
106
561
  onMessage?: (
107
562
  context: Context,
108
- message: Message,
563
+ message: Message<P>,
109
564
  signal: AbortSignal,
110
565
  ) => Promise<void>;
111
566
 
@@ -173,10 +628,10 @@ export declare class ProsodyClient {
173
628
  * @returns A promise that resolves when the message has been successfully sent.
174
629
  * @throws Error if the send operation fails or is aborted.
175
630
  */
176
- send(
631
+ send<P>(
177
632
  topic: string,
178
633
  key: string,
179
- payload: unknown,
634
+ payload: P & JsonCompatible<P>,
180
635
  signal?: AbortSignal,
181
636
  ): Promise<void>;
182
637
 
@@ -187,7 +642,7 @@ export declare class ProsodyClient {
187
642
  * @returns A promise that resolves when the subscription is successfully established and the consumer is ready to receive messages.
188
643
  * @throws Error if the subscription fails to establish.
189
644
  */
190
- subscribe(eventHandler: EventHandler): Promise<void>;
645
+ subscribe<P = JsonValue>(eventHandler: EventHandler<P>): Promise<void>;
191
646
 
192
647
  /**
193
648
  * Unsubscribes from receiving messages and shuts down the consumer.
@@ -277,6 +732,38 @@ export class PermanentError extends EventHandlerError {
277
732
  get isPermanent(): true;
278
733
  }
279
734
 
735
+ /**
736
+ * Represents a transient keyed-state failure that may succeed on a later
737
+ * attempt — a store read/write timeout, AND every caller mistake (a
738
+ * null/unrepresentable write, an item-shape mismatch, an out-of-range index, an
739
+ * invalid scan direction). Caller mistakes are transient on purpose so they
740
+ * retry and stay visible rather than discarding the message. Subclasses
741
+ * {@link TransientError}, so rethrowing it from a handler classifies the event
742
+ * transient through the existing error bridge unchanged.
743
+ */
744
+ export class TransientStateError extends TransientError {}
745
+
746
+ /**
747
+ * Represents a permanent keyed-state failure a retry cannot resolve in-process
748
+ * — an unregistered collection name, a registered-identity mismatch, or a
749
+ * duplicate registration (or one a handler throws explicitly). Caller mistakes
750
+ * are NOT permanent; they are {@link TransientStateError}. Subclasses
751
+ * {@link PermanentError}, so rethrowing it from a handler classifies the event
752
+ * permanent through the existing error bridge unchanged.
753
+ */
754
+ export class PermanentStateError extends PermanentError {}
755
+
756
+ /**
757
+ * Name-branded predicate that narrows to either keyed-state error class,
758
+ * regardless of category.
759
+ *
760
+ * @param error - The value to test.
761
+ * @returns True when the value is a keyed-state error.
762
+ */
763
+ export function isStateError(
764
+ error: unknown,
765
+ ): error is PermanentStateError | TransientStateError;
766
+
280
767
  /** Type alias for a constructor of an Error subclass. */
281
768
  type ErrorClass<T extends Error> = new (...args: any[]) => T;
282
769