@prosody-events/prosody 0.5.0 → 0.6.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 +157 -55
- package/bindings.d.ts +302 -404
- package/bindings.js +168 -85
- package/index.d.ts +30 -962
- package/index.js +42 -1898
- package/lib/client.d.ts +232 -0
- package/lib/client.js +347 -0
- package/lib/context.d.ts +161 -0
- package/lib/context.js +86 -0
- package/lib/errors.d.ts +103 -0
- package/lib/errors.js +102 -0
- package/lib/logging.d.ts +75 -0
- package/lib/logging.js +99 -0
- package/lib/payload.d.ts +59 -0
- package/lib/state/bridge.js +103 -0
- package/lib/state/codec.js +206 -0
- package/lib/state/definitions.d.ts +233 -0
- package/lib/state/definitions.js +151 -0
- package/lib/state/deque.d.ts +75 -0
- package/lib/state/deque.js +77 -0
- package/lib/state/handle.d.ts +25 -0
- package/lib/state/handle.js +26 -0
- package/lib/state/iterator.js +66 -0
- package/lib/state/map.d.ts +94 -0
- package/lib/state/map.js +79 -0
- package/lib/state/published.d.ts +113 -0
- package/lib/state/published.js +147 -0
- package/lib/state/query.d.ts +85 -0
- package/lib/state/query.js +206 -0
- package/lib/state/set.d.ts +52 -0
- package/lib/state/set.js +54 -0
- package/lib/state/value.d.ts +33 -0
- package/lib/state/value.js +31 -0
- package/package.json +21 -20
package/README.md
CHANGED
|
@@ -62,7 +62,7 @@ async function main() {
|
|
|
62
62
|
|
|
63
63
|
onMessage: async (context, message, signal) => {
|
|
64
64
|
// Process the received message
|
|
65
|
-
console.log(`Received message: ${JSON.stringify(message)}`);
|
|
65
|
+
console.log(`Received message: ${JSON.stringify(message.payload)}`);
|
|
66
66
|
|
|
67
67
|
// Schedule a timer for delayed processing
|
|
68
68
|
if (message.payload.scheduleFollowup) {
|
|
@@ -221,7 +221,7 @@ Or via environment variables:
|
|
|
221
221
|
|
|
222
222
|
```bash
|
|
223
223
|
PROSODY_PROBE_PORT=8000 # Set to 'none' to disable
|
|
224
|
-
PROSODY_STALL_THRESHOLD=15s #
|
|
224
|
+
PROSODY_STALL_THRESHOLD=15s # The default is 5m
|
|
225
225
|
```
|
|
226
226
|
|
|
227
227
|
### Important Notes
|
|
@@ -234,14 +234,14 @@ PROSODY_STALL_THRESHOLD=15s # Default stall detection threshold
|
|
|
234
234
|
issues.
|
|
235
235
|
5. The probe server is only active when consuming messages (not for producer-only usage).
|
|
236
236
|
|
|
237
|
-
You can monitor the stall state programmatically
|
|
237
|
+
You can monitor the stall state programmatically through the client's async methods:
|
|
238
238
|
|
|
239
239
|
```javascript
|
|
240
240
|
// Get the number of partitions currently assigned to this consumer
|
|
241
|
-
const partitionCount = client.assignedPartitionCount;
|
|
241
|
+
const partitionCount = await client.assignedPartitionCount();
|
|
242
242
|
|
|
243
243
|
// You can use these in your own health checks or monitoring
|
|
244
|
-
if (client.isStalled) {
|
|
244
|
+
if (await client.isStalled()) {
|
|
245
245
|
console.warn("Consumer has stalled partitions");
|
|
246
246
|
}
|
|
247
247
|
```
|
|
@@ -309,19 +309,19 @@ All messages must be processed. Retries indefinitely. Uses defer and monopolizat
|
|
|
309
309
|
**Middleware stack:**
|
|
310
310
|
|
|
311
311
|
```
|
|
312
|
-
Kafka →
|
|
312
|
+
Kafka → Retry → Defer → Monopolization → Deduplication → Cancellation → Scheduler → Timeout → Telemetry → Handler
|
|
313
313
|
```
|
|
314
314
|
|
|
315
|
-
| Layer | Purpose
|
|
316
|
-
| -------------- |
|
|
317
|
-
|
|
|
318
|
-
|
|
|
319
|
-
|
|
|
320
|
-
|
|
|
321
|
-
|
|
|
322
|
-
| Scheduler | Enforces concurrency limits and VT-based priority
|
|
323
|
-
| Timeout | Cancels handlers exceeding deadline
|
|
324
|
-
| Telemetry | Emits handler lifecycle events
|
|
315
|
+
| Layer | Purpose |
|
|
316
|
+
| -------------- | ------------------------------------------------------ |
|
|
317
|
+
| Retry | Retries transient errors indefinitely |
|
|
318
|
+
| Defer | Stores failing messages for timer-based retry |
|
|
319
|
+
| Monopolization | Rejects keys exceeding execution time threshold |
|
|
320
|
+
| Deduplication | Filters duplicate messages via local cache + Cassandra |
|
|
321
|
+
| Cancellation | Skips work once shutdown or cancellation is signaled |
|
|
322
|
+
| Scheduler | Enforces concurrency limits and VT-based priority |
|
|
323
|
+
| Timeout | Cancels handlers exceeding deadline |
|
|
324
|
+
| Telemetry | Emits handler lifecycle events |
|
|
325
325
|
|
|
326
326
|
```javascript
|
|
327
327
|
const client = await ProsodyClient.create({
|
|
@@ -439,7 +439,10 @@ Deduplication uses a two-tier approach:
|
|
|
439
439
|
- **Global in-memory cache**: A single cache shared across all partitions within the same consumer instance. Survives
|
|
440
440
|
partition reassignments within the same process. Controlled by `idempotenceCacheSize` (default 8192).
|
|
441
441
|
- **Cassandra-backed persistent store**: Survives restarts and rebalances across instances. TTL controlled by
|
|
442
|
-
`
|
|
442
|
+
`idempotenceTtlSeconds` (default 7 days, i.e. 604800s).
|
|
443
|
+
|
|
444
|
+
The producer also keeps a cache of the event IDs it sent. It skips a send that repeats a recent ID for the same topic
|
|
445
|
+
and key. `idempotenceCacheSize` sets the capacity of this cache too.
|
|
443
446
|
|
|
444
447
|
Deduplication is always active. `idempotenceCacheSize` must be greater than `0`; a value of `0` (via either the option
|
|
445
448
|
or `PROSODY_IDEMPOTENCE_CACHE_SIZE=0`) is rejected when the client is constructed.
|
|
@@ -571,6 +574,17 @@ default, all errors are considered transient.
|
|
|
571
574
|
|
|
572
575
|
The error classes and decorators apply to `onMessage`, `onExcise`, and `onTimer`.
|
|
573
576
|
|
|
577
|
+
A handler can read `context.demand` to learn whether it runs for a retry. The retry count is 1 on the first retry. It is an estimate, so keep an exact attempt count in keyed state if you need one:
|
|
578
|
+
|
|
579
|
+
```typescript
|
|
580
|
+
async onMessage(context, message) {
|
|
581
|
+
if (context.demand.kind === "failure") {
|
|
582
|
+
console.warn(`retry ${context.demand.retry} for ${message.key}`);
|
|
583
|
+
}
|
|
584
|
+
return null;
|
|
585
|
+
}
|
|
586
|
+
```
|
|
587
|
+
|
|
574
588
|
#### Using Decorators
|
|
575
589
|
|
|
576
590
|
If you're using TypeScript or a JavaScript environment that supports decorators, you can use the `@permanent` decorator
|
|
@@ -579,11 +593,13 @@ to classify exceptions that should not be retried:
|
|
|
579
593
|
```javascript
|
|
580
594
|
import { permanent, ProsodyClient } from "@prosody-events/prosody";
|
|
581
595
|
|
|
596
|
+
class ValidationError extends Error {}
|
|
597
|
+
|
|
582
598
|
class MyHandler {
|
|
583
|
-
@permanent(TypeError,
|
|
599
|
+
@permanent(TypeError, ValidationError)
|
|
584
600
|
async onMessage(context, message, signal) {
|
|
585
601
|
// Your message handling logic here
|
|
586
|
-
// TypeError and
|
|
602
|
+
// TypeError and ValidationError will be treated as permanent
|
|
587
603
|
// All other exceptions will be treated as transient (default behavior)
|
|
588
604
|
return null;
|
|
589
605
|
}
|
|
@@ -606,13 +622,15 @@ If you're not using decorators, you can still classify errors as permanent by th
|
|
|
606
622
|
```javascript
|
|
607
623
|
import { PermanentError, ProsodyClient } from "@prosody-events/prosody";
|
|
608
624
|
|
|
625
|
+
class ValidationError extends Error {}
|
|
626
|
+
|
|
609
627
|
const messageHandler = {
|
|
610
628
|
onMessage: async (context, message, signal) => {
|
|
611
629
|
try {
|
|
612
630
|
// Your message handling logic here
|
|
613
631
|
} catch (error) {
|
|
614
|
-
if (error instanceof TypeError || error instanceof
|
|
615
|
-
throw new PermanentError(error.message);
|
|
632
|
+
if (error instanceof TypeError || error instanceof ValidationError) {
|
|
633
|
+
throw new PermanentError(error.message, { cause: error });
|
|
616
634
|
}
|
|
617
635
|
// All other exceptions will be treated as transient (default behavior)
|
|
618
636
|
throw error;
|
|
@@ -736,15 +754,60 @@ A definition sets a collection's durable name, kind, and options. Register it on
|
|
|
736
754
|
|
|
737
755
|
Do not reuse a durable name for a different collection kind or payload type. Create handles inside the handler. Do not retain handles or iterators.
|
|
738
756
|
|
|
739
|
-
| Collection | JSON payload | Kafka message | Main operations
|
|
740
|
-
| ------------------ | ------------ | ----------------- |
|
|
741
|
-
| Value | `value<T>` | `messageValue<P>` | `get`, `set`, `clear`
|
|
742
|
-
| Ordered string map | `map<V>` | `messageMap<P>` | `get`, `getMany`, `has`, `set`, `delete`, `entries`, `keys`, `clear` |
|
|
743
|
-
| Deque | `deque<T>` | `messageDeque<P>` | `push`, `unshift`, `pop`, `shift`, `at`, `length`, `values`, `clear`
|
|
757
|
+
| Collection | JSON payload | Kafka message | Main operations |
|
|
758
|
+
| ------------------ | ------------ | ----------------- | ------------------------------------------------------------------------------------------ |
|
|
759
|
+
| Value | `value<T>` | `messageValue<P>` | `get`, `set`, `clear` |
|
|
760
|
+
| Ordered string map | `map<V>` | `messageMap<P>` | `get`, `getMany`, `has`, `hasMany`, `isEmpty`, `set`, `delete`, `entries`, `keys`, `clear` |
|
|
761
|
+
| Deque | `deque<T>` | `messageDeque<P>` | `push`, `unshift`, `pop`, `shift`, `at`, `length`, `values`, `clear` |
|
|
762
|
+
| Ordered string set | `set` | (none) | `add`, `has`, `hasMany`, `delete`, `isEmpty`, `keys`, `values`, `clear` |
|
|
763
|
+
|
|
764
|
+
All operations are asynchronous. Map, set, and deque scans are asynchronous iterables. A `for await` loop can stop early safely.
|
|
765
|
+
|
|
766
|
+
A set stores string members and no values. Its methods follow the JavaScript `Set`, but each returns a promise. Use a set to record which IDs a key has seen:
|
|
767
|
+
|
|
768
|
+
```typescript
|
|
769
|
+
const SEEN = set("seen-orders", { ttlSeconds: 7 * 24 * 60 * 60 });
|
|
770
|
+
|
|
771
|
+
// In a handler:
|
|
772
|
+
const seen = context.state(SEEN);
|
|
773
|
+
if (await seen.has(message.payload.orderId)) return null;
|
|
774
|
+
await seen.add(message.payload.orderId);
|
|
775
|
+
```
|
|
776
|
+
|
|
777
|
+
### Query a collection
|
|
778
|
+
|
|
779
|
+
Map `entries`, `keys`, and `values` and set `keys` and `values` accept a direction or a `KeyQueryOptions`. Deque `values` accepts a direction or a `PositionQueryOptions`. Prosody applies each option in storage, so a query reads only the entries, members, or values it selects.
|
|
780
|
+
|
|
781
|
+
| Option | Effect |
|
|
782
|
+
| ---------------- | ---------------------------------------------------------------------------------------------------------------- |
|
|
783
|
+
| `direction` | `"forward"` (the default) or `"backward"` |
|
|
784
|
+
| `from` / `after` | Start at a bound, or start after it. Set at most one. |
|
|
785
|
+
| `to` / `before` | Stop at a bound, or stop before it. Set at most one. |
|
|
786
|
+
| `range` | Keep keys or positions from `start` up to, but not including, `end`: `[start, end]`. Use `null` for an open end. |
|
|
787
|
+
| `prefix` | Keep keys that start with a prefix. Keys only. |
|
|
788
|
+
| `limit` | Return at most this many items. Use a positive integer. |
|
|
789
|
+
|
|
790
|
+
The edges `from`, `after`, `to`, and `before` are in query order, so a backward query starts at the high end. A `range` is ascending and applies in both directions, so `{ range: ["a", "m"], direction: "backward" }` yields the forward keys in the opposite order. A `null` bound leaves its end open, so `[2, null]` keeps position 2 and every later one. A range whose start is not below its end selects nothing. Bounds and `prefix` narrow the selection, so a query that sets a range and edges keeps their overlap. Deque positions count from the front and cannot be negative. To read the last N elements, use `values({ direction: "backward", limit: N })`.
|
|
744
791
|
|
|
745
|
-
|
|
792
|
+
To page through a map or a set, pass the last key of the previous page as `after`:
|
|
746
793
|
|
|
747
|
-
|
|
794
|
+
```typescript
|
|
795
|
+
const ORDERS = map<Order>("orders");
|
|
796
|
+
|
|
797
|
+
async function orderPage(context: Context, after?: string) {
|
|
798
|
+
const page: [string, Order][] = [];
|
|
799
|
+
for await (const entry of context
|
|
800
|
+
.state(ORDERS)
|
|
801
|
+
.entries({ after, limit: 50 })) {
|
|
802
|
+
page.push(entry);
|
|
803
|
+
}
|
|
804
|
+
return page; // Pass page.at(-1)?.[0] as `after` for the next page.
|
|
805
|
+
}
|
|
806
|
+
```
|
|
807
|
+
|
|
808
|
+
Set `direction: "backward"` to page from the highest key down.
|
|
809
|
+
|
|
810
|
+
Map keys are strings. `null` and `undefined` mean absence. Do not store these values. Prosody rejects a JSON `null` write with a `PermanentStateError`. Use `clear()` (value, deque) or `delete()` (map) to delete.
|
|
748
811
|
|
|
749
812
|
### When keyed-state changes become visible
|
|
750
813
|
|
|
@@ -756,6 +819,8 @@ This transaction applies only to keyed state. Some workflows need state changes
|
|
|
756
819
|
- `commit()` commits the collection's pending changes before the handler ends. A later handler failure does not remove them.
|
|
757
820
|
- `rollback()` discards pending changes since the last `commit()`. It cannot undo committed changes.
|
|
758
821
|
|
|
822
|
+
Both resolve to a `StoreOutcome`: `"applied"` when the call wrote or discarded pending changes, or `"noOp"` when the collection had no pending changes.
|
|
823
|
+
|
|
759
824
|
### Published state
|
|
760
825
|
|
|
761
826
|
Some callers need only the current value for a key. They can accept a stale value or a race with a concurrent update.
|
|
@@ -777,7 +842,7 @@ const currentOrder = context.state(CURRENT_ORDER);
|
|
|
777
842
|
await currentOrder.set({ sku: "book" });
|
|
778
843
|
```
|
|
779
844
|
|
|
780
|
-
Read published state from a handler or other application code. The Prosody client does not need an active subscription.
|
|
845
|
+
Read published state from a handler or other application code. The Prosody client does not need an active subscription. A client that only reads published state does not need `subscribedTopics`.
|
|
781
846
|
|
|
782
847
|
Use the subsystem and the same definition to open a reader:
|
|
783
848
|
|
|
@@ -786,11 +851,11 @@ const orderReader = await client.state("checkout", CURRENT_ORDER);
|
|
|
786
851
|
const currentOrder = await orderReader.get("customer-123");
|
|
787
852
|
```
|
|
788
853
|
|
|
789
|
-
The reader cannot see pending changes that exist only in a handler. It cannot change the collection. Each read takes an explicit key because no handler supplies one.
|
|
854
|
+
The reader cannot see pending changes that exist only in a handler. It cannot change the collection. Each read takes an explicit key because no handler supplies one. A failed read rejects with a `TransientStateError` or a `PermanentStateError`, like an owned read. A reader error at open, such as a zero `readCache` TTL, rejects `state()` the same way.
|
|
790
855
|
|
|
791
|
-
Map and deque readers fetch data in chunks. They do not load the complete collection before iteration starts.
|
|
856
|
+
Map, set, and deque readers fetch data in chunks. They do not load the complete collection before iteration starts.
|
|
792
857
|
|
|
793
|
-
The default cache window is five seconds. Set `readCache: { ttlMs }` to select a different window. Set `readCache: false` to bypass the cache.
|
|
858
|
+
The default cache window is five seconds. Set `readCache: { ttlMs }` to select a different window. Set `readCache: false` to bypass the cache. The client option `stateReadCache` sets the default for every reader and takes the same two forms.
|
|
794
859
|
|
|
795
860
|
To stop publication, deploy the definition with `published: false`. Keep the definition registered during that deployment. Keep the subsystem configured during that deployment.
|
|
796
861
|
|
|
@@ -838,7 +903,7 @@ const tracer = opentelemetry.trace.getTracer("my-service-name");
|
|
|
838
903
|
Set the following standard OpenTelemetry environment variables:
|
|
839
904
|
|
|
840
905
|
```
|
|
841
|
-
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:
|
|
906
|
+
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
|
|
842
907
|
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
|
|
843
908
|
OTEL_SERVICE_NAME=my-service-name
|
|
844
909
|
```
|
|
@@ -870,7 +935,7 @@ const messageHandler = {
|
|
|
870
935
|
try {
|
|
871
936
|
// Process the received message
|
|
872
937
|
span.addEvent("message.received", {
|
|
873
|
-
"message.payload": JSON.stringify(message),
|
|
938
|
+
"message.payload": JSON.stringify(message.payload),
|
|
874
939
|
});
|
|
875
940
|
} finally {
|
|
876
941
|
span.end();
|
|
@@ -943,6 +1008,17 @@ Call `shutdown()` when the application terminates. It stops all client services
|
|
|
943
1008
|
await client.shutdown();
|
|
944
1009
|
```
|
|
945
1010
|
|
|
1011
|
+
A client is `AsyncDisposable`. Declare it with `await using` to shut it down when the block ends, also when the block throws:
|
|
1012
|
+
|
|
1013
|
+
```javascript
|
|
1014
|
+
{
|
|
1015
|
+
await using client = await ProsodyClient.create(config);
|
|
1016
|
+
await client.send("orders", "order-1", { type: "order.created" });
|
|
1017
|
+
} // The client shuts down here.
|
|
1018
|
+
```
|
|
1019
|
+
|
|
1020
|
+
Concurrent and repeated shutdowns await the same operation, so an explicit `shutdown()` inside the block is safe.
|
|
1021
|
+
|
|
946
1022
|
Handle application shutdown:
|
|
947
1023
|
|
|
948
1024
|
```javascript
|
|
@@ -1090,7 +1166,7 @@ your changes before merging to `main`.
|
|
|
1090
1166
|
- `send<P>(topic: string, key: string, payload: P & JsonCompatible<P>, signal?: AbortSignal): Promise<void>`: Send a statically checked JSON-compatible message to a specified
|
|
1091
1167
|
topic.
|
|
1092
1168
|
- `excise(topic: string, key: string, signal?: AbortSignal): Promise<void>`: Send an excise record for a key.
|
|
1093
|
-
- `request<R>(topic, key, payload:
|
|
1169
|
+
- `request<R, P>(topic, key, payload: P, options): Promise<ReadonlyMap<string, Outcome<R>>>`: Return one outcome for each subsystem. `P` is inferred from the payload when you give no type arguments.
|
|
1094
1170
|
- `requestExcise<R>(topic, key, options): Promise<ReadonlyMap<string, Outcome<R>>>`: Return one excise outcome for each subsystem.
|
|
1095
1171
|
- `consumerState(): Promise<ConsumerState>`: Get the current state of the consumer.
|
|
1096
1172
|
- `assignedPartitionCount(): Promise<number>`: Get the assigned partition count.
|
|
@@ -1098,15 +1174,17 @@ your changes before merging to `main`.
|
|
|
1098
1174
|
- `sourceSystem: string`: Get the source system identifier configured for the client.
|
|
1099
1175
|
- `state<T>(subsystem: string, definition: ValueDefinition<T>): Promise<PublishedValue<T>>`: Open a read-only published value.
|
|
1100
1176
|
- `state<V>(subsystem: string, definition: MapDefinition<V>): Promise<PublishedMap<V>>`: Open a read-only published map.
|
|
1177
|
+
- `state(subsystem: string, definition: SetDefinition): Promise<PublishedSet>`: Open a read-only published set.
|
|
1101
1178
|
- `state<T>(subsystem: string, definition: DequeDefinition<T>): Promise<PublishedDeque<T>>`: Open a read-only published deque.
|
|
1102
1179
|
- `subscribe<P = JsonValue, R = JsonValue>(eventHandler: EventHandler<P, R>): Promise<void>`: Subscribe with typed payload and response values.
|
|
1103
1180
|
- `unsubscribe(): Promise<void>`: Stop the consumer. You can subscribe again later.
|
|
1104
1181
|
- `shutdown(): Promise<void>`: Stop all client services. Concurrent and repeated calls await the same operation.
|
|
1182
|
+
- `[Symbol.asyncDispose](): Promise<void>`: Call `shutdown()` when an `await using` block ends.
|
|
1105
1183
|
|
|
1106
1184
|
### AdminClient
|
|
1107
1185
|
|
|
1108
1186
|
- `new AdminClient(bootstrapServers)`: Create an admin client for the specified Kafka servers.
|
|
1109
|
-
- `createTopic(name, partitions, replicationFactor)`: Create a Kafka topic.
|
|
1187
|
+
- `createTopic(name, partitions, replicationFactor, options?)`: Create a Kafka topic. `options` takes `cleanupPolicy`, such as `"compact"`, and `retentionMs`. The cluster default applies to each setting you omit.
|
|
1110
1188
|
- `deleteTopic(name)`: Delete a Kafka topic.
|
|
1111
1189
|
|
|
1112
1190
|
### EventHandler
|
|
@@ -1128,6 +1206,8 @@ Represents a Kafka message with the following properties:
|
|
|
1128
1206
|
- `timestamp: Date`: The timestamp when the message was created or sent.
|
|
1129
1207
|
- `key: string`: The message key.
|
|
1130
1208
|
- `payload: P`: The statically typed message payload.
|
|
1209
|
+
- `sourceSystem: string | null`: The source system of the producer, or `null` when the message has no source system header.
|
|
1210
|
+
- `responseRequested: boolean`: Whether the sender waits for a response. The handler result becomes the response only when this is `true`.
|
|
1131
1211
|
|
|
1132
1212
|
`Message` takes an optional payload type parameter, `Message<P>`, used by handlers and message-backed state collections to type `payload`. Unparameterized `Message` is `Message<JsonValue>`, preserving useful JSON safety without requiring an application-specific payload type.
|
|
1133
1213
|
|
|
@@ -1137,7 +1217,7 @@ Ordinary interfaces work with `send()`. TypeScript rejects functions, `undefined
|
|
|
1137
1217
|
|
|
1138
1218
|
### ExciseMessage
|
|
1139
1219
|
|
|
1140
|
-
An `ExciseMessage` has `topic`, `partition`, `offset`, `timestamp`, and `
|
|
1220
|
+
An `ExciseMessage` has `topic`, `partition`, `offset`, `timestamp`, `key`, `sourceSystem`, and `responseRequested` properties. It has no `payload` property.
|
|
1141
1221
|
|
|
1142
1222
|
### Context
|
|
1143
1223
|
|
|
@@ -1145,6 +1225,7 @@ Represents the current event context:
|
|
|
1145
1225
|
|
|
1146
1226
|
- `onCancel(): Promise<void>`: A method that resolves when the context is cancelled.
|
|
1147
1227
|
- `shouldCancel: boolean`: A property indicating whether the context has been cancelled.
|
|
1228
|
+
- `demand: Demand`: Why the handler runs. `kind` is `"normal"` or `"failure"`. `retry` is the retry count: 0 for normal demand and 1 on the first retry. The count is an estimate. Keep an exact attempt count in keyed state if you need one.
|
|
1148
1229
|
|
|
1149
1230
|
Timer scheduling methods:
|
|
1150
1231
|
|
|
@@ -1156,7 +1237,7 @@ Timer scheduling methods:
|
|
|
1156
1237
|
|
|
1157
1238
|
Keyed-state binding:
|
|
1158
1239
|
|
|
1159
|
-
- `state(definition): ValueState<T> | MapState<V> | DequeState<T>`: Bind a registered collection for the current attempt. Message definitions return handles that contain `Message<P>`. An unregistered or mismatched definition throws `PermanentStateError`. See [Keyed State](#keyed-state
|
|
1240
|
+
- `state(definition): ValueState<T> | MapState<V> | SetState | DequeState<T>`: Bind a registered collection for the current attempt. Message definitions return handles that contain `Message<P>`. An unregistered or mismatched definition throws `PermanentStateError`. See [Keyed State](#keyed-state).
|
|
1160
1241
|
|
|
1161
1242
|
### Timer
|
|
1162
1243
|
|
|
@@ -1194,35 +1275,52 @@ Definition constructors (each returns a frozen definition object used both in `C
|
|
|
1194
1275
|
|
|
1195
1276
|
- `value<T = JsonValue>(name: string, options?: PublishedStateDefinitionOptions): ValueDefinition<T>`
|
|
1196
1277
|
- `map<V = JsonValue>(name: string, options?: MapDefinitionOptions): MapDefinition<V>`
|
|
1278
|
+
- `set(name: string, options?: SetDefinitionOptions): SetDefinition`
|
|
1197
1279
|
- `deque<T = JsonValue>(name: string, options?: DequeDefinitionOptions): DequeDefinition<T>`
|
|
1198
1280
|
- `messageValue<P = JsonValue>(name: string, options?: StateDefinitionOptions): MessageValueDefinition<P>`
|
|
1199
1281
|
- `messageMap<P = JsonValue>(name: string, options?: MessageMapDefinitionOptions): MessageMapDefinition<P>`
|
|
1200
1282
|
- `messageDeque<P = JsonValue>(name: string, options?: MessageDequeDefinitionOptions): MessageDequeDefinition<P>`
|
|
1201
1283
|
|
|
1202
|
-
`StateDefinitionOptions`: `{ ttlSeconds?: number; readUncommitted?: boolean }`. `PublishedStateDefinitionOptions` adds `{ published?: boolean; readCache?: { ttlMs: number } | false }` for JSON definitions. Map and deque option types add `keysetLimit` and `capacity`, respectively; their message equivalents omit publication options.
|
|
1284
|
+
`StateDefinitionOptions`: `{ ttlSeconds?: number; readUncommitted?: boolean }`. `PublishedStateDefinitionOptions` adds `{ published?: boolean; readCache?: { ttlMs: number } | false }` for JSON definitions. Map and deque option types add `keysetLimit` and `capacity`, respectively; their message equivalents omit publication options. `SetDefinitionOptions` is the map option type: publication options and `keysetLimit`.
|
|
1203
1285
|
|
|
1204
1286
|
`ValueState<T>`:
|
|
1205
1287
|
|
|
1206
1288
|
- `get(): Promise<T | null>`
|
|
1207
1289
|
- `set(value: T): Promise<void>`
|
|
1208
1290
|
- `clear(): Promise<void>`
|
|
1209
|
-
- `commit(): Promise<
|
|
1210
|
-
- `rollback(): Promise<
|
|
1291
|
+
- `commit(): Promise<StoreOutcome>`
|
|
1292
|
+
- `rollback(): Promise<StoreOutcome>`
|
|
1211
1293
|
|
|
1212
1294
|
`MapState<V>` (keys are `string`):
|
|
1213
1295
|
|
|
1214
1296
|
- `get(key: string): Promise<V | null>`
|
|
1215
1297
|
- `getMany(keys: readonly string[]): Promise<(V | null)[]>`
|
|
1216
1298
|
- `has(key: string): Promise<boolean>`
|
|
1299
|
+
- `hasMany(keys: readonly string[]): Promise<boolean[]>`
|
|
1300
|
+
- `isEmpty(): Promise<boolean>`
|
|
1217
1301
|
- `set(key: string, value: V): Promise<void>`
|
|
1218
1302
|
- `delete(key: string): Promise<void>`
|
|
1219
1303
|
- `clear(): Promise<void>`
|
|
1220
|
-
- `entries(
|
|
1221
|
-
- `keys(
|
|
1222
|
-
- `values(
|
|
1304
|
+
- `entries(options?: ScanDirection | KeyQueryOptions): AsyncIterableIterator<[string, V]>`
|
|
1305
|
+
- `keys(options?: ScanDirection | KeyQueryOptions): AsyncIterableIterator<string>`
|
|
1306
|
+
- `values(options?: ScanDirection | KeyQueryOptions): AsyncIterableIterator<V>`
|
|
1223
1307
|
- `[Symbol.asyncIterator](): AsyncIterableIterator<[string, V]>`
|
|
1224
|
-
- `commit(): Promise<
|
|
1225
|
-
- `rollback(): Promise<
|
|
1308
|
+
- `commit(): Promise<StoreOutcome>`
|
|
1309
|
+
- `rollback(): Promise<StoreOutcome>`
|
|
1310
|
+
|
|
1311
|
+
`SetState` (members are `string`):
|
|
1312
|
+
|
|
1313
|
+
- `add(member: string): Promise<void>`
|
|
1314
|
+
- `has(member: string): Promise<boolean>`
|
|
1315
|
+
- `hasMany(members: readonly string[]): Promise<boolean[]>`
|
|
1316
|
+
- `delete(member: string): Promise<void>`
|
|
1317
|
+
- `clear(): Promise<void>`
|
|
1318
|
+
- `isEmpty(): Promise<boolean>`
|
|
1319
|
+
- `keys(options?: ScanDirection | KeyQueryOptions): AsyncIterableIterator<string>`
|
|
1320
|
+
- `values(options?: ScanDirection | KeyQueryOptions): AsyncIterableIterator<string>`
|
|
1321
|
+
- `[Symbol.asyncIterator](): AsyncIterableIterator<string>`
|
|
1322
|
+
- `commit(): Promise<StoreOutcome>`
|
|
1323
|
+
- `rollback(): Promise<StoreOutcome>`
|
|
1226
1324
|
|
|
1227
1325
|
`DequeState<T>`:
|
|
1228
1326
|
|
|
@@ -1234,22 +1332,26 @@ Definition constructors (each returns a frozen definition object used both in `C
|
|
|
1234
1332
|
- `isEmpty(): Promise<boolean>`
|
|
1235
1333
|
- `clear(): Promise<void>`
|
|
1236
1334
|
- `at(index: number): Promise<T | null>`
|
|
1237
|
-
- `values(
|
|
1335
|
+
- `values(options?: ScanDirection | PositionQueryOptions): AsyncIterableIterator<T>`
|
|
1238
1336
|
- `[Symbol.asyncIterator](): AsyncIterableIterator<T>`
|
|
1239
|
-
- `commit(): Promise<
|
|
1240
|
-
- `rollback(): Promise<
|
|
1337
|
+
- `commit(): Promise<StoreOutcome>`
|
|
1338
|
+
- `rollback(): Promise<StoreOutcome>`
|
|
1241
1339
|
|
|
1242
1340
|
`ScanDirection`: `"forward" | "backward"`.
|
|
1243
1341
|
|
|
1244
|
-
|
|
1342
|
+
`StoreOutcome`: `"applied" | "noOp"`. `commit()` and `rollback()` resolve to it.
|
|
1343
|
+
|
|
1344
|
+
`KeyQueryOptions`: `{ direction?, prefix?, from? | after?, to? | before?, range?: [start, end], limit? }` with string bounds. A `null` range bound is open. `PositionQueryOptions`: the same without `prefix`, with non-negative integer positions. A `TypeError` reports a wrong option type, an unknown option or direction, both edges of a pair, or a range that is not an array of two bounds. A `RangeError` reports an invalid `limit` or position. See [Query a collection](#query-a-collection).
|
|
1345
|
+
|
|
1346
|
+
Published readers take the user key as their first argument. `PublishedValue<T>` provides `get`. `PublishedMap<V>` provides `get`, `getMany`, `has`, `hasMany`, `isEmpty`, `entries`, `keys`, and `values`. `PublishedSet` provides `has`, `hasMany`, `isEmpty`, `keys`, and `values`. `PublishedDeque<T>` provides `at`, `length`, `isEmpty`, and `values`. The scan methods return `AsyncIterableIterator` directly. They take the same query options as the handler handles.
|
|
1245
1347
|
|
|
1246
|
-
`StateCollectionConfig` defines one `stateCollections` entry. It contains `name`, `kind`, `payload
|
|
1348
|
+
`StateCollectionConfig` defines one `stateCollections` entry. It contains `name`, `kind`, `payload` (except for a set), and the applicable collection options. Use a definition constructor to create this value.
|
|
1247
1349
|
|
|
1248
1350
|
JSON definitions also accept `readCache`. This option applies when the definition opens published state. It is not part of `StateCollectionConfig`.
|
|
1249
1351
|
|
|
1250
|
-
The public definition types are `ValueDefinition<T>`, `MapDefinition<V>`, `DequeDefinition<T>`, `MessageValueDefinition<P>`, `MessageMapDefinition<P>`, and `MessageDequeDefinition<P>`.
|
|
1352
|
+
The public definition types are `ValueDefinition<T>`, `MapDefinition<V>`, `SetDefinition`, `DequeDefinition<T>`, `MessageValueDefinition<P>`, `MessageMapDefinition<P>`, and `MessageDequeDefinition<P>`.
|
|
1251
1353
|
|
|
1252
|
-
All definitions expose `name`, `kind`, `
|
|
1354
|
+
All definitions expose `name`, `kind`, `ttlSeconds`, and `readUncommitted`. Every definition except a set exposes `payload`. JSON and set definitions also expose `published` and `readCache`. Map and set definitions expose `keysetLimit`. Deque definitions expose `capacity`.
|
|
1253
1355
|
|
|
1254
1356
|
Errors:
|
|
1255
1357
|
|
|
@@ -1269,9 +1371,9 @@ Handler error types and decorators:
|
|
|
1269
1371
|
|
|
1270
1372
|
- `Logger`: Provides `error`, `warn`, `info`, `debug`, and `trace` methods.
|
|
1271
1373
|
- `initialize()`: Prepare the logging and tracing system during application startup.
|
|
1272
|
-
- `loggerIsSet()`: Test whether the application
|
|
1374
|
+
- `loggerIsSet()`: Test whether the application set a logger. The default console logger does not count.
|
|
1273
1375
|
- `setLogger(logger)`: Replaces the logger.
|
|
1274
|
-
- `setLoggerIfUnset(logger)`: Sets the logger only when
|
|
1376
|
+
- `setLoggerIfUnset(logger)`: Sets the logger only when the application has not set one.
|
|
1275
1377
|
- `getCurrentLogger()`: Returns the current JavaScript logger.
|
|
1276
1378
|
- `flushTelemetry()`: Exports pending telemetry.
|
|
1277
1379
|
- `shutdownTelemetry()`: Exports pending telemetry and stops its providers.
|