@laserdata/laser-sdk 0.5.3 → 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 +105 -79
- package/dist/agent/agdx.d.ts +8 -3
- package/dist/agent/agdx.d.ts.map +1 -1
- package/dist/agent/agdx.js +39 -22
- package/dist/agent/agdx.js.map +1 -1
- package/dist/agent/builder.d.ts +25 -15
- package/dist/agent/builder.d.ts.map +1 -1
- package/dist/agent/builder.js +103 -59
- package/dist/agent/builder.js.map +1 -1
- package/dist/agent/context.d.ts +3 -6
- package/dist/agent/context.d.ts.map +1 -1
- package/dist/agent/context.js +17 -9
- package/dist/agent/context.js.map +1 -1
- package/dist/agent/contract.d.ts +2 -2
- package/dist/agent/contract.d.ts.map +1 -1
- package/dist/agent/contract.js +10 -6
- package/dist/agent/contract.js.map +1 -1
- package/dist/agent/memory-handler.d.ts +25 -0
- package/dist/agent/memory-handler.d.ts.map +1 -0
- package/dist/agent/memory-handler.js +43 -0
- package/dist/agent/memory-handler.js.map +1 -0
- package/dist/agent/registry.d.ts +3 -10
- package/dist/agent/registry.d.ts.map +1 -1
- package/dist/agent/registry.js +22 -19
- package/dist/agent/registry.js.map +1 -1
- package/dist/agent/reliable-consumer.d.ts +19 -9
- package/dist/agent/reliable-consumer.d.ts.map +1 -1
- package/dist/agent/reliable-consumer.js +193 -54
- package/dist/agent/reliable-consumer.js.map +1 -1
- package/dist/agent/replies.d.ts +6 -0
- package/dist/agent/replies.d.ts.map +1 -1
- package/dist/agent/replies.js +55 -4
- package/dist/agent/replies.js.map +1 -1
- package/dist/agent/router.d.ts +1 -1
- package/dist/agent/router.d.ts.map +1 -1
- package/dist/agent/router.js +5 -19
- package/dist/agent/router.js.map +1 -1
- package/dist/agent/scope.d.ts +7 -3
- package/dist/agent/scope.d.ts.map +1 -1
- package/dist/agent/scope.js +11 -1
- package/dist/agent/scope.js.map +1 -1
- package/dist/agent/timer.d.ts +6 -0
- package/dist/agent/timer.d.ts.map +1 -0
- package/dist/agent/timer.js +21 -0
- package/dist/agent/timer.js.map +1 -0
- package/dist/agent/workflow.d.ts +16 -19
- package/dist/agent/workflow.d.ts.map +1 -1
- package/dist/agent/workflow.js +17 -6
- package/dist/agent/workflow.js.map +1 -1
- package/dist/blob.d.ts +2 -3
- package/dist/blob.d.ts.map +1 -1
- package/dist/blob.js.map +1 -1
- package/dist/bridges/a2a.d.ts +34 -23
- package/dist/bridges/a2a.d.ts.map +1 -1
- package/dist/bridges/a2a.js +29 -19
- package/dist/bridges/a2a.js.map +1 -1
- package/dist/bridges/hops.d.ts +1 -1
- package/dist/bridges/hops.d.ts.map +1 -1
- package/dist/bridges/hops.js +1 -1
- package/dist/bridges/hops.js.map +1 -1
- package/dist/bridges/mcp.d.ts +27 -7
- package/dist/bridges/mcp.d.ts.map +1 -1
- package/dist/bridges/mcp.js +24 -9
- package/dist/bridges/mcp.js.map +1 -1
- package/dist/client/bytes.d.ts +5 -0
- package/dist/client/bytes.d.ts.map +1 -1
- package/dist/client/bytes.js +15 -0
- package/dist/client/bytes.js.map +1 -1
- package/dist/client/capabilities.d.ts +25 -13
- package/dist/client/capabilities.d.ts.map +1 -1
- package/dist/client/capabilities.js +145 -123
- package/dist/client/capabilities.js.map +1 -1
- package/dist/client/error-classify.d.ts +57 -0
- package/dist/client/error-classify.d.ts.map +1 -0
- package/dist/client/error-classify.js +347 -0
- package/dist/client/error-classify.js.map +1 -0
- package/dist/client/errors.d.ts +113 -40
- package/dist/client/errors.d.ts.map +1 -1
- package/dist/client/errors.js +191 -53
- package/dist/client/errors.js.map +1 -1
- package/dist/client/internals.d.ts +1 -0
- package/dist/client/internals.d.ts.map +1 -1
- package/dist/client/internals.js +1 -0
- package/dist/client/internals.js.map +1 -1
- package/dist/client/laser.d.ts +62 -46
- package/dist/client/laser.d.ts.map +1 -1
- package/dist/client/laser.js +206 -120
- package/dist/client/laser.js.map +1 -1
- package/dist/client/managed.d.ts +3 -0
- package/dist/client/managed.d.ts.map +1 -1
- package/dist/client/managed.js +70 -3
- package/dist/client/managed.js.map +1 -1
- package/dist/context-scope.d.ts +25 -9
- package/dist/context-scope.d.ts.map +1 -1
- package/dist/context-scope.js +36 -9
- package/dist/context-scope.js.map +1 -1
- package/dist/context.d.ts +33 -16
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +174 -95
- package/dist/context.js.map +1 -1
- package/dist/conversation-state.d.ts +1 -1
- package/dist/conversation-state.d.ts.map +1 -1
- package/dist/conversation-state.js +35 -16
- package/dist/conversation-state.js.map +1 -1
- package/dist/govern.d.ts +24 -5
- package/dist/govern.d.ts.map +1 -1
- package/dist/govern.js +116 -28
- package/dist/govern.js.map +1 -1
- package/dist/iggy/apache-iggy.d.ts +59 -56
- package/dist/iggy/apache-iggy.d.ts.map +1 -1
- package/dist/iggy/apache-iggy.js +300 -137
- package/dist/iggy/apache-iggy.js.map +1 -1
- package/dist/index.d.ts +97 -65
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +49 -30
- package/dist/index.js.map +1 -1
- package/dist/intent.d.ts +16 -2
- package/dist/intent.d.ts.map +1 -1
- package/dist/intent.js +50 -18
- package/dist/intent.js.map +1 -1
- package/dist/managed/coordination.d.ts +82 -0
- package/dist/managed/coordination.d.ts.map +1 -0
- package/dist/managed/coordination.js +367 -0
- package/dist/managed/coordination.js.map +1 -0
- package/dist/managed/destinations.d.ts +4 -3
- package/dist/managed/destinations.d.ts.map +1 -1
- package/dist/managed/destinations.js +29 -13
- package/dist/managed/destinations.js.map +1 -1
- package/dist/managed/filters.d.ts +5 -5
- package/dist/managed/filters.d.ts.map +1 -1
- package/dist/managed/filters.js +39 -22
- package/dist/managed/filters.js.map +1 -1
- package/dist/managed/forks.d.ts +8 -11
- package/dist/managed/forks.d.ts.map +1 -1
- package/dist/managed/forks.js +33 -13
- package/dist/managed/forks.js.map +1 -1
- package/dist/managed/graph.d.ts +1 -4
- package/dist/managed/graph.d.ts.map +1 -1
- package/dist/managed/graph.js +5 -3
- package/dist/managed/graph.js.map +1 -1
- package/dist/managed/kv.d.ts +26 -14
- package/dist/managed/kv.d.ts.map +1 -1
- package/dist/managed/kv.js +88 -61
- package/dist/managed/kv.js.map +1 -1
- package/dist/managed/projections.d.ts +5 -8
- package/dist/managed/projections.d.ts.map +1 -1
- package/dist/managed/projections.js +22 -2
- package/dist/managed/projections.js.map +1 -1
- package/dist/managed/query.d.ts +20 -6
- package/dist/managed/query.d.ts.map +1 -1
- package/dist/managed/query.js +97 -24
- package/dist/managed/query.js.map +1 -1
- package/dist/managed/runs.d.ts +2 -4
- package/dist/managed/runs.d.ts.map +1 -1
- package/dist/managed/runs.js +9 -1
- package/dist/managed/runs.js.map +1 -1
- package/dist/managed/watch.d.ts +2 -4
- package/dist/managed/watch.d.ts.map +1 -1
- package/dist/managed/watch.js +9 -1
- package/dist/managed/watch.js.map +1 -1
- package/dist/memory/handle.d.ts +75 -22
- package/dist/memory/handle.d.ts.map +1 -1
- package/dist/memory/handle.js +232 -58
- package/dist/memory/handle.js.map +1 -1
- package/dist/memory/log-memory.d.ts +24 -12
- package/dist/memory/log-memory.d.ts.map +1 -1
- package/dist/memory/log-memory.js +214 -100
- package/dist/memory/log-memory.js.map +1 -1
- package/dist/memory/topic.d.ts +2 -2
- package/dist/memory/topic.d.ts.map +1 -1
- package/dist/memory/topic.js +7 -5
- package/dist/memory/topic.js.map +1 -1
- package/dist/memory/types.d.ts +41 -5
- package/dist/memory/types.d.ts.map +1 -1
- package/dist/memory/types.js +44 -18
- package/dist/memory/types.js.map +1 -1
- package/dist/memory/vector-memory.d.ts +8 -6
- package/dist/memory/vector-memory.d.ts.map +1 -1
- package/dist/memory/vector-memory.js +65 -34
- package/dist/memory/vector-memory.js.map +1 -1
- package/dist/memory.d.ts +6 -5
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +4 -4
- package/dist/memory.js.map +1 -1
- package/dist/provenance/agent-topic.d.ts +3 -1
- package/dist/provenance/agent-topic.d.ts.map +1 -1
- package/dist/provenance/agent-topic.js +3 -1
- package/dist/provenance/agent-topic.js.map +1 -1
- package/dist/provenance/provenance.d.ts +3 -3
- package/dist/provenance/provenance.d.ts.map +1 -1
- package/dist/provenance/provenance.js +30 -18
- package/dist/provenance/provenance.js.map +1 -1
- package/dist/runtime/clock.d.ts +3 -0
- package/dist/runtime/clock.d.ts.map +1 -1
- package/dist/runtime/clock.js +16 -3
- package/dist/runtime/clock.js.map +1 -1
- package/dist/schema-codecs.d.ts +7 -3
- package/dist/schema-codecs.d.ts.map +1 -1
- package/dist/schema-codecs.js +80 -12
- package/dist/schema-codecs.js.map +1 -1
- package/dist/session.d.ts +27 -18
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +95 -37
- package/dist/session.js.map +1 -1
- package/dist/signing.d.ts +11 -5
- package/dist/signing.d.ts.map +1 -1
- package/dist/signing.js +64 -21
- package/dist/signing.js.map +1 -1
- package/dist/snapshot.d.ts +10 -2
- package/dist/snapshot.d.ts.map +1 -1
- package/dist/snapshot.js +62 -28
- package/dist/snapshot.js.map +1 -1
- package/dist/state-store.d.ts +1 -1
- package/dist/state-store.d.ts.map +1 -1
- package/dist/state-store.js.map +1 -1
- package/dist/stream/batching.d.ts +81 -0
- package/dist/stream/batching.d.ts.map +1 -0
- package/dist/stream/batching.js +210 -0
- package/dist/stream/batching.js.map +1 -0
- package/dist/stream/bson.d.ts +3 -0
- package/dist/stream/bson.d.ts.map +1 -0
- package/dist/stream/bson.js +245 -0
- package/dist/stream/bson.js.map +1 -0
- package/dist/stream/codecs.d.ts +58 -6
- package/dist/stream/codecs.d.ts.map +1 -1
- package/dist/stream/codecs.js +143 -59
- package/dist/stream/codecs.js.map +1 -1
- package/dist/stream/consumer-group.d.ts +13 -10
- package/dist/stream/consumer-group.d.ts.map +1 -1
- package/dist/stream/consumer-group.js +91 -31
- package/dist/stream/consumer-group.js.map +1 -1
- package/dist/stream/consumer-start.d.ts +17 -0
- package/dist/stream/consumer-start.d.ts.map +1 -0
- package/dist/stream/consumer-start.js +2 -0
- package/dist/stream/consumer-start.js.map +1 -0
- package/dist/stream/consumer.d.ts +166 -24
- package/dist/stream/consumer.d.ts.map +1 -1
- package/dist/stream/consumer.js +451 -68
- package/dist/stream/consumer.js.map +1 -1
- package/dist/stream/cursor.d.ts +5 -12
- package/dist/stream/cursor.d.ts.map +1 -1
- package/dist/stream/cursor.js +34 -8
- package/dist/stream/cursor.js.map +1 -1
- package/dist/stream/header-value.d.ts +61 -15
- package/dist/stream/header-value.d.ts.map +1 -1
- package/dist/stream/header-value.js.map +1 -1
- package/dist/stream/message.d.ts +17 -0
- package/dist/stream/message.d.ts.map +1 -0
- package/dist/stream/message.js +9 -0
- package/dist/stream/message.js.map +1 -0
- package/dist/stream/producer.d.ts +127 -11
- package/dist/stream/producer.d.ts.map +1 -1
- package/dist/stream/producer.js +401 -36
- package/dist/stream/producer.js.map +1 -1
- package/dist/stream/publish.d.ts +6 -15
- package/dist/stream/publish.d.ts.map +1 -1
- package/dist/stream/publish.js +18 -28
- package/dist/stream/publish.js.map +1 -1
- package/dist/stream/record.d.ts +4 -6
- package/dist/stream/record.d.ts.map +1 -1
- package/dist/stream/record.js +4 -3
- package/dist/stream/record.js.map +1 -1
- package/dist/stream/routing.d.ts +12 -0
- package/dist/stream/routing.d.ts.map +1 -1
- package/dist/stream/routing.js +6 -1
- package/dist/stream/routing.js.map +1 -1
- package/dist/stream/stream.d.ts +1 -4
- package/dist/stream/stream.d.ts.map +1 -1
- package/dist/stream/stream.js +5 -1
- package/dist/stream/stream.js.map +1 -1
- package/dist/stream/topic.d.ts +22 -23
- package/dist/stream/topic.d.ts.map +1 -1
- package/dist/stream/topic.js +90 -30
- package/dist/stream/topic.js.map +1 -1
- package/dist/stream/typed-topic.d.ts +9 -13
- package/dist/stream/typed-topic.d.ts.map +1 -1
- package/dist/stream/typed-topic.js +25 -10
- package/dist/stream/typed-topic.js.map +1 -1
- package/dist/swarm.d.ts +0 -1
- package/dist/swarm.d.ts.map +1 -1
- package/dist/swarm.js +1 -0
- package/dist/swarm.js.map +1 -1
- package/dist/testing.d.ts +3 -3
- package/dist/testing.d.ts.map +1 -1
- package/dist/testing.js +3 -3
- package/dist/testing.js.map +1 -1
- package/dist/types/ids.d.ts +17 -2
- package/dist/types/ids.d.ts.map +1 -1
- package/dist/types/ids.js +34 -14
- package/dist/types/ids.js.map +1 -1
- package/dist/wire/agent.d.ts +22 -0
- package/dist/wire/agent.d.ts.map +1 -1
- package/dist/wire/agent.js +80 -25
- package/dist/wire/agent.js.map +1 -1
- package/dist/wire/authz.d.ts +35 -1
- package/dist/wire/authz.d.ts.map +1 -1
- package/dist/wire/authz.js +45 -15
- package/dist/wire/authz.js.map +1 -1
- package/dist/wire/cbor.d.ts.map +1 -1
- package/dist/wire/cbor.js +13 -2
- package/dist/wire/cbor.js.map +1 -1
- package/dist/wire/checkpoint.d.ts +17 -12
- package/dist/wire/checkpoint.d.ts.map +1 -1
- package/dist/wire/checkpoint.js +28 -3
- package/dist/wire/checkpoint.js.map +1 -1
- package/dist/wire/control.d.ts +82 -0
- package/dist/wire/control.d.ts.map +1 -1
- package/dist/wire/control.js +219 -0
- package/dist/wire/control.js.map +1 -1
- package/dist/wire/filter-eval.d.ts +41 -7
- package/dist/wire/filter-eval.d.ts.map +1 -1
- package/dist/wire/filter-eval.js +26 -28
- package/dist/wire/filter-eval.js.map +1 -1
- package/dist/wire/filter.d.ts +51 -17
- package/dist/wire/filter.d.ts.map +1 -1
- package/dist/wire/filter.js +99 -10
- package/dist/wire/filter.js.map +1 -1
- package/dist/wire/fork.d.ts +14 -1
- package/dist/wire/fork.d.ts.map +1 -1
- package/dist/wire/fork.js +19 -8
- package/dist/wire/fork.js.map +1 -1
- package/dist/wire/graph.d.ts +3 -0
- package/dist/wire/graph.d.ts.map +1 -1
- package/dist/wire/graph.js +18 -4
- package/dist/wire/graph.js.map +1 -1
- package/dist/wire/hello.d.ts +4 -1
- package/dist/wire/hello.d.ts.map +1 -1
- package/dist/wire/hello.js +13 -3
- package/dist/wire/hello.js.map +1 -1
- package/dist/wire/index.d.ts +4 -1
- package/dist/wire/index.d.ts.map +1 -1
- package/dist/wire/index.js +4 -1
- package/dist/wire/index.js.map +1 -1
- package/dist/wire/kv.d.ts +37 -2
- package/dist/wire/kv.d.ts.map +1 -1
- package/dist/wire/kv.js +36 -18
- package/dist/wire/kv.js.map +1 -1
- package/dist/wire/query.d.ts +6 -0
- package/dist/wire/query.d.ts.map +1 -1
- package/dist/wire/query.js +38 -12
- package/dist/wire/query.js.map +1 -1
- package/dist/wire/schema.d.ts +29 -6
- package/dist/wire/schema.d.ts.map +1 -1
- package/dist/wire/schema.js +61 -26
- package/dist/wire/schema.js.map +1 -1
- package/dist/wire/snapshot.js +1 -1
- package/dist/wire/snapshot.js.map +1 -1
- package/dist/wire/value.d.ts +10 -2
- package/dist/wire/value.d.ts.map +1 -1
- package/dist/wire/value.js +29 -3
- package/dist/wire/value.js.map +1 -1
- package/package.json +2 -3
- package/dist/stream/polling-strategy.d.ts +0 -14
- package/dist/stream/polling-strategy.d.ts.map +0 -1
- package/dist/stream/polling-strategy.js +0 -2
- package/dist/stream/polling-strategy.js.map +0 -1
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { CodecError, LaserError } from "../client/errors.js";
|
|
2
2
|
import type { Laser } from "../client/laser.js";
|
|
3
|
-
import type {
|
|
3
|
+
import type { HeaderValue } from "../stream/header-value.js";
|
|
4
4
|
import { type Provenance } from "../provenance/provenance.js";
|
|
5
|
-
import { type Clock } from "../runtime/clock.js";
|
|
6
5
|
import type { KeyRegistry, SigningKey } from "../signing.js";
|
|
7
6
|
import { AgentId, type ConsumerGroupName, type MessageId } from "../types/ids.js";
|
|
8
7
|
import { type AgentDeadLetter, type AgentEnvelope, type SignatureContext } from "../wire/agent.js";
|
|
9
8
|
import { type ContentType } from "../wire/content.js";
|
|
10
|
-
import {
|
|
9
|
+
import { AgentCtx } from "./context.js";
|
|
11
10
|
import { type InboxRoute } from "./router.js";
|
|
12
11
|
export declare function provenanceFromEnvelope(envelope: AgentEnvelope): Provenance;
|
|
13
12
|
export interface ReceivedAgentMessage {
|
|
@@ -15,7 +14,7 @@ export interface ReceivedAgentMessage {
|
|
|
15
14
|
readonly partitionId: number;
|
|
16
15
|
readonly offset: bigint;
|
|
17
16
|
readonly timestampMicros?: bigint;
|
|
18
|
-
readonly headers: ReadonlyMap<string,
|
|
17
|
+
readonly headers: ReadonlyMap<string, HeaderValue>;
|
|
19
18
|
}
|
|
20
19
|
export interface ProvenanceAndEnvelope {
|
|
21
20
|
readonly provenance: Provenance;
|
|
@@ -78,7 +77,7 @@ export interface FenceSweepState {
|
|
|
78
77
|
}
|
|
79
78
|
export declare function acceptFence(highWater: Map<string, FenceEntry>, sweepState: FenceSweepState, taskKey: string, fence: bigint, nowMicros: bigint): boolean;
|
|
80
79
|
export interface AgentHandler {
|
|
81
|
-
handle(message: AgentMessage, context:
|
|
80
|
+
handle(message: AgentMessage, context: AgentCtx): Promise<void>;
|
|
82
81
|
}
|
|
83
82
|
export type HandlerResult = {
|
|
84
83
|
readonly kind: "ok";
|
|
@@ -88,7 +87,12 @@ export type HandlerResult = {
|
|
|
88
87
|
};
|
|
89
88
|
export interface AgentMiddleware {
|
|
90
89
|
beforeHandle?(message: AgentMessage): Promise<void>;
|
|
91
|
-
afterHandle?(message: AgentMessage, result:
|
|
90
|
+
afterHandle?(message: AgentMessage, result: {
|
|
91
|
+
readonly kind: "ok";
|
|
92
|
+
} | {
|
|
93
|
+
readonly kind: "error";
|
|
94
|
+
readonly error: LaserError;
|
|
95
|
+
}, attempt: number): Promise<void>;
|
|
92
96
|
}
|
|
93
97
|
export interface DeadLetterSink {
|
|
94
98
|
onDeadLetter(message: AgentMessage | undefined, capsule: AgentDeadLetter, publishError: LaserError | undefined): Promise<void>;
|
|
@@ -100,6 +104,8 @@ export interface ReliableConsumerOptions {
|
|
|
100
104
|
readonly dedupWindow?: number;
|
|
101
105
|
readonly retry?: RetryPolicy;
|
|
102
106
|
readonly pollIntervalMs?: number;
|
|
107
|
+
/** Bounds active work after shutdown is requested. Defaults to 30 seconds. */
|
|
108
|
+
readonly shutdownGraceMs?: number;
|
|
103
109
|
readonly concurrency?: ConcurrencyPolicy;
|
|
104
110
|
readonly maxQueuedRecords?: number;
|
|
105
111
|
readonly maxQueuedBytes?: number;
|
|
@@ -110,8 +116,7 @@ export interface ReliableConsumerOptions {
|
|
|
110
116
|
readonly deduplicator?: Deduplicator;
|
|
111
117
|
readonly warmDedup?: boolean;
|
|
112
118
|
readonly middleware?: readonly AgentMiddleware[];
|
|
113
|
-
readonly
|
|
114
|
-
readonly clock?: Clock;
|
|
119
|
+
readonly onDeadLetter?: DeadLetterSink;
|
|
115
120
|
readonly verifier?: KeyRegistry;
|
|
116
121
|
readonly signingKey?: SigningKey;
|
|
117
122
|
}
|
|
@@ -121,9 +126,14 @@ export interface ReliableConsumerControl {
|
|
|
121
126
|
readonly ready?: () => void;
|
|
122
127
|
readonly hardAborted?: () => boolean;
|
|
123
128
|
}
|
|
129
|
+
/** Whether retrying the same call can succeed, the classifier the Rust
|
|
130
|
+
* `LaserError::is_retryable` defines. A failed publish answers for its cause.
|
|
131
|
+
* Managed failures follow the canonical result-code classifier, while
|
|
132
|
+
* transport, handler, routing, timeout, and deferred-policy failures keep
|
|
133
|
+
* their local semantics. */
|
|
124
134
|
export declare function isRetryable(error: LaserError): boolean;
|
|
125
135
|
export declare class ReliableConsumer {
|
|
126
|
-
readonly options
|
|
136
|
+
private readonly options;
|
|
127
137
|
constructor(options: ReliableConsumerOptions);
|
|
128
138
|
run(laser: Laser, handler: AgentHandler, control?: ReliableConsumerControl): Promise<void>;
|
|
129
139
|
private runSerial;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reliable-consumer.d.ts","sourceRoot":"","sources":["../../src/agent/reliable-consumer.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"reliable-consumer.d.ts","sourceRoot":"","sources":["../../src/agent/reliable-consumer.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { code } from "../client/error-classify.js";
|
|
2
|
+
import { CancelledError, CodecError, ConsumerGroupSetupError, FilterExecutionError, FilterFaultError, FilterOversizedRecordError, HandlerError, InvalidError, LaserError, NoStreamError, TimeoutError, TransportError, publishCause } from "../client/errors.js";
|
|
2
3
|
import { INTERNAL_NATIVE_CONSUMER, INTERNAL_TRANSPORT } from "../client/internals.js";
|
|
3
4
|
import { AgentTopic } from "../provenance/agent-topic.js";
|
|
4
5
|
import { decodeProvenanceHeaders } from "../provenance/provenance.js";
|
|
@@ -6,16 +7,20 @@ import { SystemClock } from "../runtime/clock.js";
|
|
|
6
7
|
import { AgentId, ConversationId } from "../types/ids.js";
|
|
7
8
|
import { AgentKind, OPERATION_TASK, TaskStateName, decodeAgentEnvelope, encodeAgentDeadLetter, taskStateFromCode, unmetRequirements, validateAgentEnvelope } from "../wire/agent.js";
|
|
8
9
|
import { AGENT_OP_VERSION } from "../wire/codes.js";
|
|
10
|
+
import { resultCodeIsRetryable } from "../wire/result.js";
|
|
9
11
|
import { decodeOne, encodeNamed, expectMap } from "../wire/cbor.js";
|
|
10
12
|
import { contentTypeFromCode } from "../wire/content.js";
|
|
11
13
|
import { AGENT_VERSION, CONTENT_TYPE, FENCE } from "../wire/headers.js";
|
|
12
|
-
import {
|
|
14
|
+
import { AgentCtx } from "./context.js";
|
|
13
15
|
import { ADVERTISED_INBOX_ROUTE } from "./router.js";
|
|
14
16
|
const DEDUP_SCOPE_SEP = "\u001f";
|
|
15
17
|
const TEXT_ENCODER = new TextEncoder();
|
|
16
18
|
const FENCE_MAP_SOFT_CAP = 16_384;
|
|
17
19
|
const FENCE_ENTRY_TTL_MICROS = 600000000n;
|
|
18
20
|
const FENCE_SWEEP_INTERVAL_MICROS = 1000000n;
|
|
21
|
+
// How many verified record ids a consumer remembers to refuse a replay of the
|
|
22
|
+
// exact signed bytes, sized like the fence map.
|
|
23
|
+
const VERIFIED_RECORD_WINDOW = 16_384;
|
|
19
24
|
function tryAgentId(value) {
|
|
20
25
|
try {
|
|
21
26
|
return AgentId.new(value);
|
|
@@ -26,7 +31,7 @@ function tryAgentId(value) {
|
|
|
26
31
|
}
|
|
27
32
|
function fenceFromMetadata(metadata) {
|
|
28
33
|
const entry = metadata?.get(FENCE);
|
|
29
|
-
if (entry?.kind !== "int")
|
|
34
|
+
if (entry?.kind !== "int" && entry?.kind !== "uint")
|
|
30
35
|
return undefined;
|
|
31
36
|
const value = entry.value;
|
|
32
37
|
return typeof value === "bigint" && value >= 0n ? value : undefined;
|
|
@@ -141,7 +146,7 @@ export function dedupKey(provenance) {
|
|
|
141
146
|
if (provenance.idempotencyKey === undefined)
|
|
142
147
|
return undefined;
|
|
143
148
|
return provenance.agent !== undefined
|
|
144
|
-
? `${provenance.agent.
|
|
149
|
+
? `${provenance.agent.asStr()}${DEDUP_SCOPE_SEP}${provenance.idempotencyKey}`
|
|
145
150
|
: provenance.idempotencyKey;
|
|
146
151
|
}
|
|
147
152
|
export function acceptFence(highWater, sweepState, taskKey, fence, nowMicros) {
|
|
@@ -165,13 +170,24 @@ function handlerError(error) {
|
|
|
165
170
|
? error
|
|
166
171
|
: new HandlerError(error instanceof Error ? error.message : String(error), { cause: error });
|
|
167
172
|
}
|
|
173
|
+
/** Whether retrying the same call can succeed, the classifier the Rust
|
|
174
|
+
* `LaserError::is_retryable` defines. A failed publish answers for its cause.
|
|
175
|
+
* Managed failures follow the canonical result-code classifier, while
|
|
176
|
+
* transport, handler, routing, timeout, and deferred-policy failures keep
|
|
177
|
+
* their local semantics. */
|
|
168
178
|
export function isRetryable(error) {
|
|
169
|
-
|
|
179
|
+
const cause = publishCause(error);
|
|
180
|
+
if (!(cause instanceof LaserError))
|
|
181
|
+
return false;
|
|
182
|
+
switch (cause.kind) {
|
|
170
183
|
case "config":
|
|
171
184
|
case "no-stream":
|
|
185
|
+
case "no-respond-topic":
|
|
172
186
|
case "ambiguous-mutation":
|
|
173
187
|
case "unsupported":
|
|
174
188
|
case "invalid":
|
|
189
|
+
case "id":
|
|
190
|
+
case "provenance":
|
|
175
191
|
case "codec":
|
|
176
192
|
case "protocol":
|
|
177
193
|
case "handler-config":
|
|
@@ -186,40 +202,91 @@ export function isRetryable(error) {
|
|
|
186
202
|
case "authz":
|
|
187
203
|
case "signature":
|
|
188
204
|
case "budget-exceeded":
|
|
205
|
+
case "fence-violation":
|
|
206
|
+
case "quarantined":
|
|
207
|
+
return false;
|
|
208
|
+
case "publish-failed":
|
|
189
209
|
return false;
|
|
190
210
|
case "transport":
|
|
191
|
-
return
|
|
211
|
+
return cause instanceof TransportError ? cause.retryable : true;
|
|
192
212
|
case "routing":
|
|
193
|
-
return !("reason" in
|
|
194
|
-
|
|
213
|
+
return !("reason" in cause &&
|
|
214
|
+
cause.reason?.kind ===
|
|
195
215
|
"principalMismatch");
|
|
196
216
|
case "query":
|
|
197
217
|
case "kv":
|
|
198
218
|
case "fork":
|
|
199
219
|
case "graph":
|
|
200
220
|
case "agent-workflow":
|
|
201
|
-
|
|
202
|
-
|
|
221
|
+
case "checkpoint":
|
|
222
|
+
return resultCodeIsRetryable(code(cause));
|
|
203
223
|
case "filter":
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
224
|
+
if (cause instanceof ConsumerGroupSetupError) {
|
|
225
|
+
return cause.cause instanceof LaserError && isRetryable(cause.cause);
|
|
226
|
+
}
|
|
227
|
+
if (cause instanceof FilterFaultError || cause instanceof FilterOversizedRecordError) {
|
|
228
|
+
return false;
|
|
229
|
+
}
|
|
230
|
+
return cause instanceof FilterExecutionError && resultCodeIsRetryable(cause.detail.code);
|
|
207
231
|
case "timeout":
|
|
208
232
|
case "handler":
|
|
209
233
|
case "policy-deferred":
|
|
210
234
|
return true;
|
|
211
235
|
}
|
|
212
236
|
}
|
|
213
|
-
|
|
214
|
-
|
|
237
|
+
// The next record, or undefined when none arrived within `waitMs`.
|
|
238
|
+
async function nextOrIdle(consumer, waitMs, signal) {
|
|
239
|
+
try {
|
|
240
|
+
return await consumer.nextWithin(waitMs, signal === undefined ? {} : { signal });
|
|
241
|
+
}
|
|
242
|
+
catch (error) {
|
|
243
|
+
if (error instanceof TimeoutError ||
|
|
244
|
+
(error instanceof CancelledError && signal?.aborted === true))
|
|
245
|
+
return undefined;
|
|
246
|
+
throw error;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
// The worker reads the record's log offset, which a delivery carries in its position.
|
|
250
|
+
function received(message) {
|
|
251
|
+
return {
|
|
252
|
+
payload: message.payload,
|
|
253
|
+
partitionId: message.partitionId,
|
|
254
|
+
offset: message.position.offset,
|
|
255
|
+
timestampMicros: message.timestampMicros,
|
|
256
|
+
headers: message.headers
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
function sleep(ms, signal) {
|
|
260
|
+
if (signal?.aborted === true)
|
|
261
|
+
return Promise.resolve();
|
|
262
|
+
return new Promise((resolve) => {
|
|
263
|
+
let timer;
|
|
264
|
+
const deadline = Date.now() + ms;
|
|
265
|
+
const finish = () => {
|
|
266
|
+
clearTimeout(timer);
|
|
267
|
+
signal?.removeEventListener("abort", finish);
|
|
268
|
+
resolve();
|
|
269
|
+
};
|
|
270
|
+
const tick = () => {
|
|
271
|
+
const remaining = deadline - Date.now();
|
|
272
|
+
if (remaining <= 0)
|
|
273
|
+
finish();
|
|
274
|
+
else
|
|
275
|
+
timer = setTimeout(tick, Math.min(remaining, 2_147_483_647));
|
|
276
|
+
};
|
|
277
|
+
signal?.addEventListener("abort", finish, { once: true });
|
|
278
|
+
timer = setTimeout(tick, Math.min(Math.max(0, ms), 2_147_483_647));
|
|
279
|
+
});
|
|
215
280
|
}
|
|
216
281
|
async function consumeUntilDone(work, hardSignal) {
|
|
217
282
|
if (hardSignal === undefined) {
|
|
218
283
|
await work;
|
|
219
284
|
return true;
|
|
220
285
|
}
|
|
221
|
-
if (hardSignal.aborted)
|
|
286
|
+
if (hardSignal.aborted) {
|
|
287
|
+
void work.catch(() => undefined);
|
|
222
288
|
return false;
|
|
289
|
+
}
|
|
223
290
|
let removeAbort = () => undefined;
|
|
224
291
|
const aborted = new Promise((resolve) => {
|
|
225
292
|
const onAbort = () => {
|
|
@@ -231,9 +298,52 @@ async function consumeUntilDone(work, hardSignal) {
|
|
|
231
298
|
};
|
|
232
299
|
});
|
|
233
300
|
const completed = work.then(() => true);
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
301
|
+
try {
|
|
302
|
+
return await Promise.race([completed, aborted]);
|
|
303
|
+
}
|
|
304
|
+
finally {
|
|
305
|
+
removeAbort();
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
function shutdownControl(control, graceMs) {
|
|
309
|
+
const forced = new AbortController();
|
|
310
|
+
const hardSignal = control.hardSignal === undefined
|
|
311
|
+
? forced.signal
|
|
312
|
+
: AbortSignal.any([forced.signal, control.hardSignal]);
|
|
313
|
+
const signal = control.signal === undefined ? hardSignal : AbortSignal.any([control.signal, hardSignal]);
|
|
314
|
+
let expired = false;
|
|
315
|
+
let timer;
|
|
316
|
+
const onShutdown = () => {
|
|
317
|
+
const deadline = Date.now() + graceMs;
|
|
318
|
+
const tick = () => {
|
|
319
|
+
const remaining = deadline - Date.now();
|
|
320
|
+
if (remaining <= 0) {
|
|
321
|
+
expired = true;
|
|
322
|
+
forced.abort("agent shutdown drain");
|
|
323
|
+
}
|
|
324
|
+
else {
|
|
325
|
+
timer = setTimeout(tick, Math.min(remaining, 2_147_483_647));
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
timer = setTimeout(tick, Math.min(graceMs, 2_147_483_647));
|
|
329
|
+
};
|
|
330
|
+
control.signal?.addEventListener("abort", onShutdown, { once: true });
|
|
331
|
+
if (control.signal?.aborted === true)
|
|
332
|
+
onShutdown();
|
|
333
|
+
return {
|
|
334
|
+
runtime: {
|
|
335
|
+
...control,
|
|
336
|
+
signal,
|
|
337
|
+
hardSignal,
|
|
338
|
+
hardAborted: () => hardSignal.aborted || control.hardAborted?.() === true
|
|
339
|
+
},
|
|
340
|
+
expired: () => expired,
|
|
341
|
+
stopped: () => signal.aborted,
|
|
342
|
+
dispose: () => {
|
|
343
|
+
clearTimeout(timer);
|
|
344
|
+
control.signal?.removeEventListener("abort", onShutdown);
|
|
345
|
+
}
|
|
346
|
+
};
|
|
237
347
|
}
|
|
238
348
|
class ReliableWorker {
|
|
239
349
|
laser;
|
|
@@ -243,6 +353,9 @@ class ReliableWorker {
|
|
|
243
353
|
topicId;
|
|
244
354
|
highWaterFence = new Map();
|
|
245
355
|
fenceSweep = { lastSweepMicros: 0n };
|
|
356
|
+
// A signature binds the envelope to no log position, so the exact signed
|
|
357
|
+
// bytes verify again wherever a writer replays them.
|
|
358
|
+
verifiedRecords = new SlidingWindow(VERIFIED_RECORD_WINDOW);
|
|
246
359
|
constructor(laser, handler, options, streamId, topicId) {
|
|
247
360
|
this.laser = laser;
|
|
248
361
|
this.handler = handler;
|
|
@@ -250,7 +363,12 @@ class ReliableWorker {
|
|
|
250
363
|
this.streamId = streamId;
|
|
251
364
|
this.topicId = topicId;
|
|
252
365
|
}
|
|
366
|
+
cancelled() {
|
|
367
|
+
return this.options.hardSignal.aborted;
|
|
368
|
+
}
|
|
253
369
|
async consume(received) {
|
|
370
|
+
if (this.cancelled())
|
|
371
|
+
return;
|
|
254
372
|
const decoded = decodeAgentMessage(received, this.options.understoodFeatures);
|
|
255
373
|
if (decoded.kind === "error") {
|
|
256
374
|
await this.deadLetterUndecodable(received, decoded.payload);
|
|
@@ -280,6 +398,9 @@ class ReliableWorker {
|
|
|
280
398
|
await this.deadLetter(message, "Rejected", 0, "signature verification failed");
|
|
281
399
|
return;
|
|
282
400
|
}
|
|
401
|
+
const record = message.envelope?.record;
|
|
402
|
+
if (record !== undefined && !(await this.verifiedRecords.observe(record.toString())))
|
|
403
|
+
return;
|
|
283
404
|
}
|
|
284
405
|
const fence = message.provenance.fenceToken;
|
|
285
406
|
if (fence !== undefined &&
|
|
@@ -294,14 +415,18 @@ class ReliableWorker {
|
|
|
294
415
|
await this.deadLetter(message, "DeadlineExceeded", 0, "message past its deadline");
|
|
295
416
|
return;
|
|
296
417
|
}
|
|
418
|
+
if (this.cancelled())
|
|
419
|
+
return;
|
|
297
420
|
await this.ackOnPickup(message);
|
|
298
|
-
const context =
|
|
421
|
+
const context = AgentCtx.create(this.laser, message, {
|
|
299
422
|
...(this.options.agent !== undefined ? { agent: this.options.agent } : {}),
|
|
300
423
|
...(this.options.respondOn !== undefined ? { respondOn: this.options.respondOn } : {}),
|
|
301
424
|
...(this.options.signingKey !== undefined ? { signingKey: this.options.signingKey } : {}),
|
|
302
425
|
inboxRoute: this.options.inboxRoute
|
|
303
426
|
});
|
|
304
427
|
for (const middleware of this.options.middleware) {
|
|
428
|
+
if (this.cancelled())
|
|
429
|
+
return;
|
|
305
430
|
try {
|
|
306
431
|
await middleware.beforeHandle?.(message);
|
|
307
432
|
}
|
|
@@ -312,6 +437,8 @@ class ReliableWorker {
|
|
|
312
437
|
}
|
|
313
438
|
}
|
|
314
439
|
for (let attempt = 0;; attempt += 1) {
|
|
440
|
+
if (this.cancelled())
|
|
441
|
+
return;
|
|
315
442
|
let result;
|
|
316
443
|
try {
|
|
317
444
|
await this.handler.handle(message, context);
|
|
@@ -320,7 +447,11 @@ class ReliableWorker {
|
|
|
320
447
|
catch (error) {
|
|
321
448
|
result = { kind: "error", error: handlerError(error) };
|
|
322
449
|
}
|
|
450
|
+
if (this.cancelled())
|
|
451
|
+
return;
|
|
323
452
|
for (const middleware of this.options.middleware) {
|
|
453
|
+
if (this.cancelled())
|
|
454
|
+
return;
|
|
324
455
|
try {
|
|
325
456
|
await middleware.afterHandle?.(message, result, attempt + 1);
|
|
326
457
|
}
|
|
@@ -338,7 +469,7 @@ class ReliableWorker {
|
|
|
338
469
|
await this.deadLetter(message, "RetryExhausted", attempt + 1, result.error.message);
|
|
339
470
|
return;
|
|
340
471
|
}
|
|
341
|
-
await sleep(retryDelayMs(this.options.retry, attempt));
|
|
472
|
+
await sleep(retryDelayMs(this.options.retry, attempt), this.options.hardSignal);
|
|
342
473
|
}
|
|
343
474
|
}
|
|
344
475
|
async ackOnPickup(message) {
|
|
@@ -404,7 +535,7 @@ class ReliableWorker {
|
|
|
404
535
|
publishError = handlerError(error);
|
|
405
536
|
}
|
|
406
537
|
try {
|
|
407
|
-
await this.options.
|
|
538
|
+
await this.options.onDeadLetter?.onDeadLetter(message, capsule, publishError);
|
|
408
539
|
}
|
|
409
540
|
catch {
|
|
410
541
|
// Dead-letter sinks observe the terminal delivery decision.
|
|
@@ -428,6 +559,9 @@ export class ReliableConsumer {
|
|
|
428
559
|
throw new InvalidError(`${name} must be a positive safe integer`);
|
|
429
560
|
}
|
|
430
561
|
}
|
|
562
|
+
const grace = options.shutdownGraceMs ?? 30_000;
|
|
563
|
+
if (!Number.isFinite(grace) || grace < 0)
|
|
564
|
+
throw new InvalidError("shutdownGraceMs must be a non-negative finite number");
|
|
431
565
|
this.options = options;
|
|
432
566
|
}
|
|
433
567
|
async run(laser, handler, control = {}) {
|
|
@@ -437,75 +571,80 @@ export class ReliableConsumer {
|
|
|
437
571
|
const pollIntervalMs = this.options.pollIntervalMs ?? 10;
|
|
438
572
|
const deduplicator = this.options.deduplicator ?? new SlidingWindow(this.options.dedupWindow ?? 10_000);
|
|
439
573
|
// The runtime owns its delivery contract over the native group consumer.
|
|
440
|
-
const group = laser.topic(this.options.topic).consumerGroup(this.options.group.
|
|
441
|
-
const openConsumer = () => group[INTERNAL_NATIVE_CONSUMER]({
|
|
574
|
+
const group = laser.topic(this.options.topic).consumerGroup(this.options.group.asStr());
|
|
575
|
+
const openConsumer = () => group[INTERNAL_NATIVE_CONSUMER]({ commitPolicy: { kind: "disabled" }, pollIntervalMs }, "propagate");
|
|
442
576
|
let consumer = await openConsumer();
|
|
443
577
|
if (this.options.warmDedup === true) {
|
|
444
578
|
await this.warmDedup(laser, deduplicator, this.options.dedupWindow ?? 10_000);
|
|
445
579
|
}
|
|
446
580
|
const ids = await laserTransportIds(laser, stream, this.options.topic);
|
|
581
|
+
const shutdown = shutdownControl(control, this.options.shutdownGraceMs ?? 30_000);
|
|
582
|
+
const runtime = shutdown.runtime;
|
|
447
583
|
const worker = new ReliableWorker(laser, handler, {
|
|
448
584
|
retry: this.options.retry ?? DEFAULT_RETRY_POLICY,
|
|
449
585
|
understoodFeatures: this.options.understoodFeatures ?? 0n,
|
|
450
586
|
clock: this.options.clock ?? new SystemClock(),
|
|
587
|
+
hardSignal: runtime.hardSignal,
|
|
451
588
|
inboxRoute: this.options.inboxRoute ?? ADVERTISED_INBOX_ROUTE,
|
|
452
589
|
middleware: this.options.middleware ?? [],
|
|
453
590
|
ackOnPickup: this.options.ackOnPickup ?? false,
|
|
454
591
|
deduplicator,
|
|
455
592
|
...(this.options.agent !== undefined ? { agent: this.options.agent } : {}),
|
|
456
593
|
...(this.options.respondOn !== undefined ? { respondOn: this.options.respondOn } : {}),
|
|
457
|
-
...(this.options.
|
|
458
|
-
? {
|
|
594
|
+
...(this.options.onDeadLetter !== undefined
|
|
595
|
+
? { onDeadLetter: this.options.onDeadLetter }
|
|
459
596
|
: {}),
|
|
460
597
|
...(this.options.verifier !== undefined ? { verifier: this.options.verifier } : {}),
|
|
461
598
|
...(this.options.signingKey !== undefined ? { signingKey: this.options.signingKey } : {})
|
|
462
599
|
}, ids.streamId, ids.topicId);
|
|
463
|
-
control.ready?.();
|
|
464
600
|
try {
|
|
601
|
+
control.ready?.();
|
|
465
602
|
for (;;) {
|
|
466
603
|
try {
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
604
|
+
const concurrency = this.options.concurrency ?? SERIAL_CONCURRENCY;
|
|
605
|
+
const running = concurrency.kind === "serial"
|
|
606
|
+
? this.runSerial(consumer, worker, runtime, pollIntervalMs)
|
|
607
|
+
: this.runPerPartition(consumer, worker, concurrency.maxPartitions, this.options.maxQueuedRecords ?? 4_096, this.options.maxQueuedBytes ?? 64 * 1024 * 1024, runtime, pollIntervalMs);
|
|
608
|
+
await consumeUntilDone(running, runtime.hardSignal);
|
|
609
|
+
if (shutdown.expired())
|
|
610
|
+
throw new TimeoutError("agent shutdown drain");
|
|
474
611
|
return;
|
|
475
612
|
}
|
|
476
613
|
catch (error) {
|
|
477
614
|
const failure = handlerError(error);
|
|
478
|
-
if (
|
|
479
|
-
control.hardAborted?.() === true ||
|
|
480
|
-
!isRetryable(failure)) {
|
|
615
|
+
if (runtime.signal.aborted || runtime.hardAborted() || !isRetryable(failure))
|
|
481
616
|
throw failure;
|
|
482
|
-
}
|
|
483
617
|
try {
|
|
484
618
|
await consumer.shutdown();
|
|
485
619
|
}
|
|
486
620
|
catch {
|
|
487
|
-
// Reconnection continues
|
|
621
|
+
// Reconnection continues when the failed consumer cannot leave cleanly.
|
|
488
622
|
}
|
|
489
|
-
await sleep(pollIntervalMs);
|
|
623
|
+
await sleep(pollIntervalMs, runtime.signal);
|
|
624
|
+
if (shutdown.stopped())
|
|
625
|
+
return;
|
|
490
626
|
consumer = await openConsumer();
|
|
491
627
|
}
|
|
492
628
|
}
|
|
493
629
|
}
|
|
494
630
|
finally {
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
631
|
+
shutdown.dispose();
|
|
632
|
+
if (!runtime.hardAborted()) {
|
|
633
|
+
try {
|
|
634
|
+
await consumer.shutdown();
|
|
635
|
+
}
|
|
636
|
+
catch {
|
|
637
|
+
// Preserve the primary consumer failure.
|
|
638
|
+
}
|
|
500
639
|
}
|
|
501
640
|
}
|
|
502
641
|
}
|
|
503
642
|
async runSerial(consumer, worker, control, pollIntervalMs) {
|
|
504
643
|
while (control.signal?.aborted !== true) {
|
|
505
|
-
const message = await consumer.
|
|
506
|
-
if (message ===
|
|
644
|
+
const message = await nextOrIdle(consumer, pollIntervalMs, control.signal);
|
|
645
|
+
if (message === undefined)
|
|
507
646
|
continue;
|
|
508
|
-
if (!(await consumeUntilDone(worker.consume(message), control.hardSignal)))
|
|
647
|
+
if (!(await consumeUntilDone(worker.consume(received(message)), control.hardSignal)))
|
|
509
648
|
return;
|
|
510
649
|
if (control.hardAborted?.() !== true)
|
|
511
650
|
await consumer.commit(message);
|
|
@@ -520,10 +659,10 @@ export class ReliableConsumer {
|
|
|
520
659
|
let failure;
|
|
521
660
|
const currentFailure = () => failure;
|
|
522
661
|
while (control.signal?.aborted !== true && failure === undefined) {
|
|
523
|
-
const message = await consumer.
|
|
524
|
-
if (message ===
|
|
662
|
+
const message = await nextOrIdle(consumer, pollIntervalMs, control.signal);
|
|
663
|
+
if (message === undefined)
|
|
525
664
|
continue;
|
|
526
|
-
const position = `${String(message.partitionId)}:${message.offset.toString()}`;
|
|
665
|
+
const position = `${String(message.partitionId)}:${message.position.offset.toString()}`;
|
|
527
666
|
if (scheduled.has(position))
|
|
528
667
|
continue;
|
|
529
668
|
const messageBytes = message.payload.byteLength + headerBytes(message.headers);
|
|
@@ -544,9 +683,9 @@ export class ReliableConsumer {
|
|
|
544
683
|
queuedBytes += messageBytes;
|
|
545
684
|
const lane = (existing ?? Promise.resolve())
|
|
546
685
|
.then(async () => {
|
|
547
|
-
if (currentFailure() !== undefined)
|
|
686
|
+
if (currentFailure() !== undefined || control.hardAborted?.() === true)
|
|
548
687
|
return;
|
|
549
|
-
await worker.consume(message);
|
|
688
|
+
await worker.consume(received(message));
|
|
550
689
|
if (control.hardAborted?.() !== true)
|
|
551
690
|
await consumer.commit(message);
|
|
552
691
|
})
|
|
@@ -577,7 +716,7 @@ export class ReliableConsumer {
|
|
|
577
716
|
throw new NoStreamError("warm dedup requires a stream");
|
|
578
717
|
const partitions = await transport.getTopicPartitionCount(stream, this.options.topic);
|
|
579
718
|
for (let partitionId = 0; partitionId < partitions; partitionId += 1) {
|
|
580
|
-
const offset = await transport.getConsumerOffset(stream, this.options.topic, { kind: "group", name: this.options.group.
|
|
719
|
+
const offset = await transport.getConsumerOffset(stream, this.options.topic, { kind: "group", name: this.options.group.asStr() }, partitionId);
|
|
581
720
|
if (offset === undefined)
|
|
582
721
|
continue;
|
|
583
722
|
const span = BigInt(Math.max(depth - 1, 0));
|