@fbsm/saga-core 0.1.0-beta.1 → 0.1.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +91 -56
- package/dist/index.cjs +122 -47
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -3
- package/dist/index.d.ts +12 -3
- package/dist/index.js +120 -46
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,20 +10,20 @@ npm install @fbsm/saga-core
|
|
|
10
10
|
|
|
11
11
|
## Overview
|
|
12
12
|
|
|
13
|
-
| Export
|
|
14
|
-
|
|
15
|
-
| `SagaPublisher` | Creates sagas and publishes events
|
|
16
|
-
| `SagaRunner`
|
|
17
|
-
| `SagaRegistry`
|
|
18
|
-
| `SagaParser`
|
|
19
|
-
| `SagaContext`
|
|
13
|
+
| Export | Description |
|
|
14
|
+
| --------------- | -------------------------------------------------------- |
|
|
15
|
+
| `SagaPublisher` | Creates sagas and publishes events |
|
|
16
|
+
| `SagaRunner` | Consumes events, dispatches to handlers with retry logic |
|
|
17
|
+
| `SagaRegistry` | Stores participant and handler mappings |
|
|
18
|
+
| `SagaParser` | Parses inbound messages (3-layer fallback) |
|
|
19
|
+
| `SagaContext` | AsyncLocalStorage-based context propagation |
|
|
20
20
|
|
|
21
21
|
## SagaContext
|
|
22
22
|
|
|
23
23
|
Uses `AsyncLocalStorage` to propagate saga metadata through async call chains.
|
|
24
24
|
|
|
25
25
|
```typescript
|
|
26
|
-
import { SagaContext } from
|
|
26
|
+
import { SagaContext } from "@fbsm/saga-core";
|
|
27
27
|
|
|
28
28
|
// Read current context (or undefined)
|
|
29
29
|
const ctx = SagaContext.current();
|
|
@@ -34,6 +34,7 @@ const ctx = SagaContext.require();
|
|
|
34
34
|
```
|
|
35
35
|
|
|
36
36
|
**`SagaContextData`**:
|
|
37
|
+
|
|
37
38
|
```typescript
|
|
38
39
|
interface SagaContextData {
|
|
39
40
|
sagaId: string;
|
|
@@ -47,6 +48,7 @@ interface SagaContextData {
|
|
|
47
48
|
```
|
|
48
49
|
|
|
49
50
|
Context is set automatically by:
|
|
51
|
+
|
|
50
52
|
1. **`SagaRunner`** — wraps every handler execution with `SagaContext.run()`
|
|
51
53
|
2. **`SagaPublisher.start(fn)` / `startChild(fn)` / `emitToParent(fn)`** — wraps callbacks with `SagaContext.run()`
|
|
52
54
|
|
|
@@ -55,20 +57,21 @@ Context is set automatically by:
|
|
|
55
57
|
Creates sagas and publishes events. See [Core Functions](../doc/core-functions.md) for detailed usage of each method.
|
|
56
58
|
|
|
57
59
|
```typescript
|
|
58
|
-
import { SagaPublisher } from
|
|
60
|
+
import { SagaPublisher } from "@fbsm/saga-core";
|
|
59
61
|
|
|
60
62
|
const publisher = new SagaPublisher(transport, otelContext, topicPrefix);
|
|
61
63
|
```
|
|
62
64
|
|
|
63
|
-
| Method
|
|
64
|
-
|
|
65
|
-
| `start(fn, opts?)`
|
|
66
|
-
| `startChild(fn, opts?)`
|
|
67
|
-
| `emit(params)`
|
|
68
|
-
| `emitToParent(params \| fn)`
|
|
69
|
-
| `forSaga(sagaId, parentCtx?, causationId?, key?)` | Get bound `Emit` function (no ALS)
|
|
65
|
+
| Method | Description |
|
|
66
|
+
| ------------------------------------------------- | ------------------------------------- |
|
|
67
|
+
| `start(fn, opts?)` | Create a root saga with ALS context |
|
|
68
|
+
| `startChild(fn, opts?)` | Create a child saga linked to current |
|
|
69
|
+
| `emit(params)` | Publish event in current context |
|
|
70
|
+
| `emitToParent(params \| fn)` | Emit to parent saga |
|
|
71
|
+
| `forSaga(sagaId, parentCtx?, causationId?, key?)` | Get bound `Emit` function (no ALS) |
|
|
70
72
|
|
|
71
73
|
**`SagaStartOptions`**:
|
|
74
|
+
|
|
72
75
|
```typescript
|
|
73
76
|
interface SagaStartOptions {
|
|
74
77
|
sagaName?: string;
|
|
@@ -82,36 +85,42 @@ interface SagaStartOptions {
|
|
|
82
85
|
Consumes events from transport, routes to handlers, and applies retry logic.
|
|
83
86
|
|
|
84
87
|
```typescript
|
|
85
|
-
import { SagaRunner } from
|
|
88
|
+
import { SagaRunner } from "@fbsm/saga-core";
|
|
86
89
|
|
|
87
90
|
const runner = new SagaRunner(
|
|
88
|
-
registry,
|
|
89
|
-
transport,
|
|
90
|
-
publisher,
|
|
91
|
-
parser,
|
|
92
|
-
options,
|
|
93
|
-
otelContext,
|
|
94
|
-
logger,
|
|
91
|
+
registry, // SagaRegistry
|
|
92
|
+
transport, // SagaTransport
|
|
93
|
+
publisher, // SagaPublisher
|
|
94
|
+
parser, // SagaParser
|
|
95
|
+
options, // RunnerOptions
|
|
96
|
+
otelContext, // OtelContext (optional)
|
|
97
|
+
logger, // SagaLogger (optional)
|
|
95
98
|
);
|
|
96
99
|
|
|
97
100
|
await runner.start(); // Subscribe and begin consuming
|
|
98
|
-
await runner.stop();
|
|
101
|
+
await runner.stop(); // Disconnect
|
|
102
|
+
|
|
103
|
+
// Health check (delegates to transport if it implements HealthCheckable)
|
|
104
|
+
const health = await runner.healthCheck();
|
|
105
|
+
// { status: 'up' | 'down', details?: { consumerGroupState, groupId, memberCount } }
|
|
99
106
|
```
|
|
100
107
|
|
|
101
108
|
**`RunnerOptions`**:
|
|
109
|
+
|
|
102
110
|
```typescript
|
|
103
111
|
interface RunnerOptions {
|
|
104
|
-
|
|
112
|
+
groupId: string;
|
|
105
113
|
fromBeginning?: boolean;
|
|
106
114
|
topicPrefix?: string;
|
|
107
115
|
retryPolicy?: {
|
|
108
|
-
maxRetries?: number;
|
|
109
|
-
initialDelayMs?: number;
|
|
116
|
+
maxRetries?: number; // default: 3
|
|
117
|
+
initialDelayMs?: number; // default: 200
|
|
110
118
|
};
|
|
111
119
|
}
|
|
112
120
|
```
|
|
113
121
|
|
|
114
122
|
**Handler execution flow**:
|
|
123
|
+
|
|
115
124
|
1. Parse inbound message via `SagaParser`
|
|
116
125
|
2. Look up handler in route map
|
|
117
126
|
3. Wrap emit with `final` hint (if `{ final: true }`)
|
|
@@ -132,51 +141,56 @@ Parses inbound messages using a 3-layer fallback strategy:
|
|
|
132
141
|
|
|
133
142
|
When using the header-based format (default with `@fbsm/saga-transport-kafka`):
|
|
134
143
|
|
|
135
|
-
| Header
|
|
136
|
-
|
|
137
|
-
| `saga-id`
|
|
138
|
-
| `saga-event-id`
|
|
139
|
-
| `saga-causation-id`
|
|
140
|
-
| `saga-step-name`
|
|
141
|
-
| `saga-published-at`
|
|
142
|
-
| `saga-schema-version`
|
|
143
|
-
| `saga-root-id`
|
|
144
|
-
| `saga-parent-id`
|
|
145
|
-
| `saga-event-hint`
|
|
146
|
-
| `saga-name`
|
|
147
|
-
| `saga-description`
|
|
148
|
-
| `saga-step-description` | Step description (optional)
|
|
149
|
-
| `saga-key`
|
|
144
|
+
| Header | Description |
|
|
145
|
+
| ----------------------- | ------------------------------------------------------ |
|
|
146
|
+
| `saga-id` | Saga instance ID |
|
|
147
|
+
| `saga-event-id` | Unique event ID |
|
|
148
|
+
| `saga-causation-id` | ID of the event that caused this one |
|
|
149
|
+
| `saga-step-name` | Logical step name |
|
|
150
|
+
| `saga-published-at` | ISO timestamp of publication |
|
|
151
|
+
| `saga-schema-version` | Schema version (currently `1`) |
|
|
152
|
+
| `saga-root-id` | Root saga ID (top-level ancestor) |
|
|
153
|
+
| `saga-parent-id` | Parent saga ID (for sub-sagas, optional) |
|
|
154
|
+
| `saga-event-hint` | Event hint: `compensation`, `final`, `fork` (optional) |
|
|
155
|
+
| `saga-name` | Saga name (optional) |
|
|
156
|
+
| `saga-description` | Saga description (optional) |
|
|
157
|
+
| `saga-step-description` | Step description (optional) |
|
|
158
|
+
| `saga-key` | Partition key (optional) |
|
|
150
159
|
|
|
151
160
|
## Errors
|
|
152
161
|
|
|
153
|
-
| Error
|
|
154
|
-
|
|
155
|
-
| `SagaError`
|
|
156
|
-
| `SagaRetryableError`
|
|
157
|
-
| `SagaDuplicateHandlerError`
|
|
158
|
-
| `SagaParseError`
|
|
159
|
-
| `SagaTransportNotConnectedError` | Publishing to a disconnected transport
|
|
160
|
-
| `SagaContextNotFoundError`
|
|
161
|
-
| `SagaNoParentError`
|
|
162
|
-
| `SagaInvalidHandlerConfigError`
|
|
162
|
+
| Error | Description |
|
|
163
|
+
| -------------------------------- | ----------------------------------------------------------------------------------------------------------- |
|
|
164
|
+
| `SagaError` | Base error class for all saga errors |
|
|
165
|
+
| `SagaRetryableError` | Throw in handlers to trigger retry with exponential backoff. `new SagaRetryableError(message, maxRetries?)` |
|
|
166
|
+
| `SagaDuplicateHandlerError` | Two handlers registered for the same event type |
|
|
167
|
+
| `SagaParseError` | Message parsing failed |
|
|
168
|
+
| `SagaTransportNotConnectedError` | Publishing to a disconnected transport |
|
|
169
|
+
| `SagaContextNotFoundError` | `emit()`/`startChild()`/`emitToParent()` called outside a saga context |
|
|
170
|
+
| `SagaNoParentError` | `emitToParent()` called in a saga without `parentSagaId` |
|
|
171
|
+
| `SagaInvalidHandlerConfigError` | Handler has conflicting options (e.g., both `final` and `fork`) |
|
|
163
172
|
|
|
164
173
|
**Retry behavior**: `SagaRetryableError` triggers exponential backoff: `initialDelayMs * 2^attempt`. After `maxRetries` attempts, `onRetryExhausted()` is called if defined. Non-retryable errors are logged and skipped.
|
|
165
174
|
|
|
166
175
|
## OTel Integration
|
|
167
176
|
|
|
168
177
|
```typescript
|
|
169
|
-
import {
|
|
178
|
+
import {
|
|
179
|
+
createOtelContext,
|
|
180
|
+
W3cOtelContext,
|
|
181
|
+
NoopOtelContext,
|
|
182
|
+
} from "@fbsm/saga-core";
|
|
170
183
|
|
|
171
184
|
// Auto-detect: uses W3cOtelContext if @opentelemetry/api is available, NoopOtelContext otherwise
|
|
172
185
|
const otelCtx = createOtelContext();
|
|
173
186
|
|
|
174
187
|
// Or explicitly:
|
|
175
|
-
const otelCtx = new W3cOtelContext();
|
|
176
|
-
const otelCtx = new NoopOtelContext();
|
|
188
|
+
const otelCtx = new W3cOtelContext(); // requires @opentelemetry/api
|
|
189
|
+
const otelCtx = new NoopOtelContext(); // no-op (no tracing)
|
|
177
190
|
```
|
|
178
191
|
|
|
179
192
|
The OTel context:
|
|
193
|
+
|
|
180
194
|
- Injects W3C baggage with saga context into outgoing messages
|
|
181
195
|
- Extracts trace context from incoming message headers
|
|
182
196
|
- Creates spans for publish and handle operations
|
|
@@ -199,6 +213,27 @@ interface SagaTransport {
|
|
|
199
213
|
}
|
|
200
214
|
```
|
|
201
215
|
|
|
216
|
+
## Health Checks
|
|
217
|
+
|
|
218
|
+
Transports can optionally implement the `HealthCheckable` interface to support health checks.
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
import { isHealthCheckable } from "@fbsm/saga-core";
|
|
222
|
+
import type { HealthCheckable, TransportHealthResult } from "@fbsm/saga-core";
|
|
223
|
+
|
|
224
|
+
// Check if a transport supports health checks
|
|
225
|
+
if (isHealthCheckable(transport)) {
|
|
226
|
+
const result: TransportHealthResult = await transport.healthCheck();
|
|
227
|
+
// result.status: 'up' | 'down'
|
|
228
|
+
// result.details: transport-specific details
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Or use SagaRunner.healthCheck() which delegates automatically
|
|
232
|
+
const health = await runner.healthCheck();
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
`KafkaTransport` from `@fbsm/saga-transport-kafka` implements `HealthCheckable` using `consumer.describeGroup()`. Healthy states: `Stable`, `CompletingRebalance`, `PreparingRebalance`.
|
|
236
|
+
|
|
202
237
|
## SagaLogger
|
|
203
238
|
|
|
204
239
|
```typescript
|
package/dist/index.cjs
CHANGED
|
@@ -36,10 +36,16 @@ __export(index_exports, {
|
|
|
36
36
|
SagaRunner: () => SagaRunner,
|
|
37
37
|
SagaTransportNotConnectedError: () => SagaTransportNotConnectedError,
|
|
38
38
|
W3cOtelContext: () => W3cOtelContext,
|
|
39
|
-
createOtelContext: () => createOtelContext
|
|
39
|
+
createOtelContext: () => createOtelContext,
|
|
40
|
+
isHealthCheckable: () => isHealthCheckable
|
|
40
41
|
});
|
|
41
42
|
module.exports = __toCommonJS(index_exports);
|
|
42
43
|
|
|
44
|
+
// src/transport/transport.interface.ts
|
|
45
|
+
function isHealthCheckable(transport) {
|
|
46
|
+
return typeof transport.healthCheck === "function";
|
|
47
|
+
}
|
|
48
|
+
|
|
43
49
|
// src/errors/saga.error.ts
|
|
44
50
|
var SagaError = class extends Error {
|
|
45
51
|
isSagaError = true;
|
|
@@ -118,26 +124,41 @@ var SagaRunner = class {
|
|
|
118
124
|
async start() {
|
|
119
125
|
this.routeMap = this.registry.buildRouteMap();
|
|
120
126
|
const prefix = this.options.topicPrefix ?? "";
|
|
121
|
-
const topics = Array.from(this.routeMap.keys()).map(
|
|
127
|
+
const topics = Array.from(this.routeMap.keys()).map(
|
|
128
|
+
(et) => `${prefix}${et}`
|
|
129
|
+
);
|
|
122
130
|
await this.transport.connect();
|
|
123
131
|
if (topics.length > 0) {
|
|
124
|
-
this.logger.info(
|
|
132
|
+
this.logger.info(
|
|
133
|
+
`[SagaRunner] Subscribing to ${topics.length} topic(s): [${topics.join(", ")}]`
|
|
134
|
+
);
|
|
125
135
|
await this.transport.subscribe(
|
|
126
136
|
topics,
|
|
127
137
|
(message) => this.handleMessage(message),
|
|
128
138
|
{
|
|
129
139
|
fromBeginning: this.options.fromBeginning,
|
|
130
|
-
groupId:
|
|
140
|
+
groupId: this.options.groupId
|
|
131
141
|
}
|
|
132
142
|
);
|
|
133
143
|
this.logger.info("[SagaRunner] Consumer running");
|
|
134
144
|
} else {
|
|
135
|
-
this.logger.warn(
|
|
145
|
+
this.logger.warn(
|
|
146
|
+
"[SagaRunner] No handlers registered \u2014 nothing to subscribe"
|
|
147
|
+
);
|
|
136
148
|
}
|
|
137
149
|
}
|
|
138
150
|
async stop() {
|
|
139
151
|
await this.transport.disconnect();
|
|
140
152
|
}
|
|
153
|
+
async healthCheck() {
|
|
154
|
+
if (isHealthCheckable(this.transport)) {
|
|
155
|
+
return this.transport.healthCheck();
|
|
156
|
+
}
|
|
157
|
+
return {
|
|
158
|
+
status: "up",
|
|
159
|
+
details: { reason: "Transport does not support health checks" }
|
|
160
|
+
};
|
|
161
|
+
}
|
|
141
162
|
async handleMessage(message) {
|
|
142
163
|
const event = this.parser.parse(message);
|
|
143
164
|
if (!event) return;
|
|
@@ -159,10 +180,15 @@ var SagaRunner = class {
|
|
|
159
180
|
sagaName: event.sagaName,
|
|
160
181
|
sagaDescription: event.sagaDescription
|
|
161
182
|
};
|
|
162
|
-
const emit = this.publisher.forSaga(
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
183
|
+
const emit = this.publisher.forSaga(
|
|
184
|
+
event.sagaId,
|
|
185
|
+
{
|
|
186
|
+
parentSagaId: event.parentSagaId,
|
|
187
|
+
rootSagaId: event.rootSagaId
|
|
188
|
+
},
|
|
189
|
+
event.eventId,
|
|
190
|
+
event.key
|
|
191
|
+
);
|
|
166
192
|
const wrappedEmit = async (params) => {
|
|
167
193
|
const finalParams = isFinalHandler ? { ...params, hint: "final" } : params;
|
|
168
194
|
return emit(finalParams);
|
|
@@ -170,10 +196,15 @@ var SagaRunner = class {
|
|
|
170
196
|
const forkConfig = route.options?.fork;
|
|
171
197
|
const finalEmit = forkConfig ? async (params) => {
|
|
172
198
|
const subSagaId = (0, import_uuid.v7)();
|
|
173
|
-
const subEmit = this.publisher.forSaga(
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
199
|
+
const subEmit = this.publisher.forSaga(
|
|
200
|
+
subSagaId,
|
|
201
|
+
{
|
|
202
|
+
parentSagaId: event.sagaId,
|
|
203
|
+
rootSagaId: event.rootSagaId
|
|
204
|
+
},
|
|
205
|
+
event.eventId,
|
|
206
|
+
event.key
|
|
207
|
+
);
|
|
177
208
|
const forkMeta = typeof forkConfig === "object" ? forkConfig : {};
|
|
178
209
|
const forkCtx = {
|
|
179
210
|
sagaId: subSagaId,
|
|
@@ -184,7 +215,10 @@ var SagaRunner = class {
|
|
|
184
215
|
sagaName: forkMeta.sagaName,
|
|
185
216
|
sagaDescription: forkMeta.sagaDescription
|
|
186
217
|
};
|
|
187
|
-
await SagaContext.run(
|
|
218
|
+
await SagaContext.run(
|
|
219
|
+
forkCtx,
|
|
220
|
+
() => subEmit({ ...params, hint: "fork" })
|
|
221
|
+
);
|
|
188
222
|
} : wrappedEmit;
|
|
189
223
|
const spanAttrs = {
|
|
190
224
|
"saga.id": event.sagaId,
|
|
@@ -195,8 +229,10 @@ var SagaRunner = class {
|
|
|
195
229
|
"saga.handler.service": route.participant.serviceId
|
|
196
230
|
};
|
|
197
231
|
if (event.sagaName) spanAttrs["saga.name"] = event.sagaName;
|
|
198
|
-
if (event.sagaDescription)
|
|
199
|
-
|
|
232
|
+
if (event.sagaDescription)
|
|
233
|
+
spanAttrs["saga.description"] = event.sagaDescription;
|
|
234
|
+
if (event.stepDescription)
|
|
235
|
+
spanAttrs["saga.step.description"] = event.stepDescription;
|
|
200
236
|
if (event.parentSagaId) spanAttrs["saga.parent.id"] = event.parentSagaId;
|
|
201
237
|
const sagaCtxData = {
|
|
202
238
|
sagaId: event.sagaId,
|
|
@@ -209,10 +245,20 @@ var SagaRunner = class {
|
|
|
209
245
|
};
|
|
210
246
|
const runHandler = () => SagaContext.run(
|
|
211
247
|
sagaCtxData,
|
|
212
|
-
() => this.runWithRetry(
|
|
248
|
+
() => this.runWithRetry(
|
|
249
|
+
route.handler,
|
|
250
|
+
route.participant,
|
|
251
|
+
incoming,
|
|
252
|
+
finalEmit
|
|
253
|
+
)
|
|
213
254
|
);
|
|
214
255
|
if (this.otelCtx) {
|
|
215
|
-
await this.otelCtx.withExtractedSpan(
|
|
256
|
+
await this.otelCtx.withExtractedSpan(
|
|
257
|
+
`saga.handle ${event.eventType}`,
|
|
258
|
+
spanAttrs,
|
|
259
|
+
message.headers,
|
|
260
|
+
runHandler
|
|
261
|
+
);
|
|
216
262
|
} else {
|
|
217
263
|
await runHandler();
|
|
218
264
|
}
|
|
@@ -321,10 +367,15 @@ var SagaPublisher = class {
|
|
|
321
367
|
}
|
|
322
368
|
async emit(params) {
|
|
323
369
|
const ctx = SagaContext.require();
|
|
324
|
-
const boundEmit = this.forSaga(
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
370
|
+
const boundEmit = this.forSaga(
|
|
371
|
+
ctx.sagaId,
|
|
372
|
+
{
|
|
373
|
+
parentSagaId: ctx.parentSagaId,
|
|
374
|
+
rootSagaId: ctx.rootSagaId
|
|
375
|
+
},
|
|
376
|
+
ctx.causationId,
|
|
377
|
+
ctx.key
|
|
378
|
+
);
|
|
328
379
|
return boundEmit(params);
|
|
329
380
|
}
|
|
330
381
|
async startChild(fn, opts) {
|
|
@@ -358,10 +409,15 @@ var SagaPublisher = class {
|
|
|
358
409
|
await SagaContext.run(parentCtx, paramsOrFn);
|
|
359
410
|
return;
|
|
360
411
|
}
|
|
361
|
-
const parentEmit = this.forSaga(
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
412
|
+
const parentEmit = this.forSaga(
|
|
413
|
+
ctx.parentSagaId,
|
|
414
|
+
{
|
|
415
|
+
parentSagaId: ctx.parentSagaId,
|
|
416
|
+
rootSagaId: ctx.rootSagaId
|
|
417
|
+
},
|
|
418
|
+
ctx.causationId,
|
|
419
|
+
ctx.key
|
|
420
|
+
);
|
|
365
421
|
return parentEmit(paramsOrFn);
|
|
366
422
|
}
|
|
367
423
|
forSaga(sagaId, parentCtx, causationId, baseKey) {
|
|
@@ -401,7 +457,11 @@ var SagaPublisher = class {
|
|
|
401
457
|
};
|
|
402
458
|
}
|
|
403
459
|
async publish(event) {
|
|
404
|
-
this.otelCtx.injectBaggage(
|
|
460
|
+
this.otelCtx.injectBaggage(
|
|
461
|
+
event.sagaId,
|
|
462
|
+
event.rootSagaId,
|
|
463
|
+
event.parentSagaId
|
|
464
|
+
);
|
|
405
465
|
const attrs = {
|
|
406
466
|
"saga.id": event.sagaId,
|
|
407
467
|
"saga.event.type": event.eventType,
|
|
@@ -627,7 +687,10 @@ var W3cOtelContext = class {
|
|
|
627
687
|
entries["saga.parent.id"] = { value: parentSagaId };
|
|
628
688
|
}
|
|
629
689
|
const baggage = this.api.propagation.createBaggage(entries);
|
|
630
|
-
const ctx = this.api.propagation.setBaggage(
|
|
690
|
+
const ctx = this.api.propagation.setBaggage(
|
|
691
|
+
this.api.context.active(),
|
|
692
|
+
baggage
|
|
693
|
+
);
|
|
631
694
|
this.api.context.with(ctx, () => {
|
|
632
695
|
});
|
|
633
696
|
}
|
|
@@ -659,7 +722,9 @@ var W3cOtelContext = class {
|
|
|
659
722
|
code: this.api.SpanStatusCode.ERROR,
|
|
660
723
|
message: error instanceof Error ? error.message : String(error)
|
|
661
724
|
});
|
|
662
|
-
span.recordException(
|
|
725
|
+
span.recordException(
|
|
726
|
+
error instanceof Error ? error : new Error(String(error))
|
|
727
|
+
);
|
|
663
728
|
throw error;
|
|
664
729
|
} finally {
|
|
665
730
|
span.end();
|
|
@@ -670,27 +735,36 @@ var W3cOtelContext = class {
|
|
|
670
735
|
this.api.propagation.inject(this.api.context.active(), headers);
|
|
671
736
|
}
|
|
672
737
|
async withExtractedSpan(name, attrs, headers, fn) {
|
|
673
|
-
const parentCtx = this.api.propagation.extract(
|
|
738
|
+
const parentCtx = this.api.propagation.extract(
|
|
739
|
+
this.api.ROOT_CONTEXT,
|
|
740
|
+
headers
|
|
741
|
+
);
|
|
674
742
|
const tracer = this.api.trace.getTracer("@fbsm/saga-core");
|
|
675
743
|
return this.api.context.with(
|
|
676
744
|
parentCtx,
|
|
677
|
-
() => tracer.startActiveSpan(
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
span.
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
745
|
+
() => tracer.startActiveSpan(
|
|
746
|
+
name,
|
|
747
|
+
{ kind: this.api.SpanKind.CONSUMER },
|
|
748
|
+
async (span) => {
|
|
749
|
+
span.setAttributes(attrs);
|
|
750
|
+
try {
|
|
751
|
+
const result = await fn();
|
|
752
|
+
span.setStatus({ code: this.api.SpanStatusCode.OK });
|
|
753
|
+
return result;
|
|
754
|
+
} catch (error) {
|
|
755
|
+
span.setStatus({
|
|
756
|
+
code: this.api.SpanStatusCode.ERROR,
|
|
757
|
+
message: error instanceof Error ? error.message : String(error)
|
|
758
|
+
});
|
|
759
|
+
span.recordException(
|
|
760
|
+
error instanceof Error ? error : new Error(String(error))
|
|
761
|
+
);
|
|
762
|
+
throw error;
|
|
763
|
+
} finally {
|
|
764
|
+
span.end();
|
|
765
|
+
}
|
|
692
766
|
}
|
|
693
|
-
|
|
767
|
+
)
|
|
694
768
|
);
|
|
695
769
|
}
|
|
696
770
|
};
|
|
@@ -721,6 +795,7 @@ function createOtelContext(enabled) {
|
|
|
721
795
|
SagaRunner,
|
|
722
796
|
SagaTransportNotConnectedError,
|
|
723
797
|
W3cOtelContext,
|
|
724
|
-
createOtelContext
|
|
798
|
+
createOtelContext,
|
|
799
|
+
isHealthCheckable
|
|
725
800
|
});
|
|
726
801
|
//# sourceMappingURL=index.cjs.map
|