@geekmidas/events 10.0.0-alpha.78 → 10.0.0-alpha.80
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{SQSSubscriber-BXLE_1L3.mjs → SQSSubscriber-BEJBfNTe.mjs} +94 -10
- package/dist/SQSSubscriber-BEJBfNTe.mjs.map +1 -0
- package/dist/{SQSSubscriber-DtRUaZB3.cjs → SQSSubscriber-COTqWvOk.cjs} +101 -10
- package/dist/SQSSubscriber-COTqWvOk.cjs.map +1 -0
- package/dist/basic/index.cjs +16 -4
- package/dist/basic/index.cjs.map +1 -1
- package/dist/basic/index.d.cts.map +1 -1
- package/dist/basic/index.d.mts.map +1 -1
- package/dist/basic/index.mjs +16 -4
- package/dist/basic/index.mjs.map +1 -1
- package/dist/index.cjs +11 -4
- package/dist/index.d.cts +60 -1
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +60 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +3 -3
- package/dist/pgboss/index.cjs +50 -22
- package/dist/pgboss/index.cjs.map +1 -1
- package/dist/pgboss/index.d.cts.map +1 -1
- package/dist/pgboss/index.d.mts.map +1 -1
- package/dist/pgboss/index.mjs +50 -22
- package/dist/pgboss/index.mjs.map +1 -1
- package/dist/rabbitmq/index.cjs +32 -15
- package/dist/rabbitmq/index.cjs.map +1 -1
- package/dist/rabbitmq/index.d.cts.map +1 -1
- package/dist/rabbitmq/index.d.mts.map +1 -1
- package/dist/rabbitmq/index.mjs +32 -15
- package/dist/rabbitmq/index.mjs.map +1 -1
- package/dist/{registry-DLnflkMm.mjs → registry-DXLY1Xf6.mjs} +2 -2
- package/dist/{registry-DLnflkMm.mjs.map → registry-DXLY1Xf6.mjs.map} +1 -1
- package/dist/{registry-DNk5GnGq.cjs → registry-DlT36229.cjs} +3 -3
- package/dist/{registry-DNk5GnGq.cjs.map → registry-DlT36229.cjs.map} +1 -1
- package/dist/sns/index.cjs +17 -8
- package/dist/sns/index.cjs.map +1 -1
- package/dist/sns/index.d.cts.map +1 -1
- package/dist/sns/index.d.mts.map +1 -1
- package/dist/sns/index.mjs +17 -8
- package/dist/sns/index.mjs.map +1 -1
- package/dist/sqs/index.cjs +7 -72
- package/dist/sqs/index.cjs.map +1 -1
- package/dist/sqs/index.d.cts +1 -0
- package/dist/sqs/index.d.cts.map +1 -1
- package/dist/sqs/index.d.mts +1 -0
- package/dist/sqs/index.d.mts.map +1 -1
- package/dist/sqs/index.mjs +3 -66
- package/dist/sqs/index.mjs.map +1 -1
- package/dist/telemetry-DG3l807O.cjs +309 -0
- package/dist/telemetry-DG3l807O.cjs.map +1 -0
- package/dist/telemetry-DjjgJMul.mjs +244 -0
- package/dist/telemetry-DjjgJMul.mjs.map +1 -0
- package/package.json +5 -3
- package/dist/SQSSubscriber-BXLE_1L3.mjs.map +0 -1
- package/dist/SQSSubscriber-DtRUaZB3.cjs.map +0 -1
- package/dist/types-BcZjLxVV.cjs +0 -51
- package/dist/types-BcZjLxVV.cjs.map +0 -1
- package/dist/types-Bk5JM6MP.mjs +0 -40
- package/dist/types-Bk5JM6MP.mjs.map +0 -1
package/dist/sqs/index.mjs
CHANGED
|
@@ -1,70 +1,7 @@
|
|
|
1
|
-
import "../
|
|
2
|
-
import { n as eventsDriverFor } from "../registry-
|
|
1
|
+
import "../telemetry-DjjgJMul.mjs";
|
|
2
|
+
import { n as eventsDriverFor } from "../registry-DXLY1Xf6.mjs";
|
|
3
3
|
import { t as sqsUrl_exports } from "./sqsUrl.mjs";
|
|
4
|
-
import {
|
|
5
|
-
import { SendMessageBatchCommand } from "@aws-sdk/client-sqs";
|
|
6
|
-
import chunk from "lodash.chunk";
|
|
7
|
-
//#region src/sqs/SQSPublisher.ts
|
|
8
|
-
var SQSPublisher = class SQSPublisher {
|
|
9
|
-
connection;
|
|
10
|
-
options;
|
|
11
|
-
constructor(connection, options = {}) {
|
|
12
|
-
this.connection = connection;
|
|
13
|
-
this.options = { maxBatchSize: options.maxBatchSize ?? 10 };
|
|
14
|
-
}
|
|
15
|
-
/**
|
|
16
|
-
* Create an SQSPublisher from a connection string
|
|
17
|
-
* Format: sqs://?queueUrl=https://sqs.region.amazonaws.com/accountId/queueName®ion=us-east-1&endpoint=http://localhost:4566&maxBatchSize=10
|
|
18
|
-
*/
|
|
19
|
-
static async fromConnectionString(connectionString) {
|
|
20
|
-
const params = new URL(connectionString).searchParams;
|
|
21
|
-
const { SQSConnection } = await import("../SQSSubscriber-BXLE_1L3.mjs").then((n) => n.r);
|
|
22
|
-
const connection = await SQSConnection.fromConnectionString(connectionString);
|
|
23
|
-
const options = { maxBatchSize: params.get("maxBatchSize") ? Number.parseInt(params.get("maxBatchSize"), 10) : void 0 };
|
|
24
|
-
return new SQSPublisher(connection, options);
|
|
25
|
-
}
|
|
26
|
-
async publish(messages) {
|
|
27
|
-
if (messages.length === 0) return;
|
|
28
|
-
if (!this.connection.isConnected()) await this.connection.connect();
|
|
29
|
-
const batches = chunk(messages, this.options.maxBatchSize);
|
|
30
|
-
await Promise.all(batches.map((batch) => this.sendBatch(batch)));
|
|
31
|
-
}
|
|
32
|
-
async sendBatch(messages) {
|
|
33
|
-
const entries = messages.map((message, index) => ({
|
|
34
|
-
Id: `${index}`,
|
|
35
|
-
MessageBody: JSON.stringify({
|
|
36
|
-
type: message.type,
|
|
37
|
-
payload: message.payload
|
|
38
|
-
}),
|
|
39
|
-
MessageAttributes: { type: {
|
|
40
|
-
DataType: "String",
|
|
41
|
-
StringValue: message.type
|
|
42
|
-
} }
|
|
43
|
-
}));
|
|
44
|
-
const input = {
|
|
45
|
-
QueueUrl: this.connection.queueUrl,
|
|
46
|
-
Entries: entries
|
|
47
|
-
};
|
|
48
|
-
const command = new SendMessageBatchCommand(input);
|
|
49
|
-
const response = await this.connection.sqsClient.send(command);
|
|
50
|
-
if (response.Failed && response.Failed.length > 0) throw new SqsBatchPartlyFailed(response.Failed.map((f) => ({
|
|
51
|
-
id: f.Id,
|
|
52
|
-
code: f.Code,
|
|
53
|
-
message: f.Message
|
|
54
|
-
})));
|
|
55
|
-
}
|
|
56
|
-
async close() {}
|
|
57
|
-
};
|
|
58
|
-
/** SQS accepted the batch but refused some of its messages; the rest were sent. */
|
|
59
|
-
var SqsBatchPartlyFailed = class extends Error {
|
|
60
|
-
failed;
|
|
61
|
-
constructor(failed) {
|
|
62
|
-
super(`Failed to send ${failed.length} messages: ${failed.map((f) => `${f.id}: ${f.code} - ${f.message}`).join(", ")}. The others in the batch were sent; retry only these.`);
|
|
63
|
-
this.failed = failed;
|
|
64
|
-
this.name = "SqsBatchPartlyFailed";
|
|
65
|
-
}
|
|
66
|
-
};
|
|
67
|
-
//#endregion
|
|
4
|
+
import { i as SQSConnection, n as SQSPublisher, r as SqsBatchPartlyFailed, t as SQSSubscriber } from "../SQSSubscriber-BEJBfNTe.mjs";
|
|
68
5
|
//#region src/sqs/driver.ts
|
|
69
6
|
/**
|
|
70
7
|
* `sqs://` — an SQS queue. Register it with
|
package/dist/sqs/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/sqs/
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/sqs/driver.ts"],"sourcesContent":["import { type EventsDriver, eventsDriverFor } from '../registry';\nimport { EventPublisherType } from '../types';\nimport { SQSConnection } from './SQSConnection';\nimport { SQSPublisher } from './SQSPublisher';\nimport { SQSSubscriber } from './SQSSubscriber';\n\n/**\n * `sqs://` — an SQS queue. Register it with\n * `registerEventsDriver(sqsEventsDriver)`; this subpath is the only one that\n * imports `@aws-sdk/client-sqs` on its own.\n */\nexport const sqsEventsDriver: EventsDriver = {\n\tscheme: EventPublisherType.SQS,\n\tconnect: (url) => SQSConnection.fromConnectionString(url),\n\tpublisher: (url) => SQSPublisher.fromConnectionString(url),\n\tpublisherFor: (connection) => new SQSPublisher(connection as SQSConnection),\n\tasync subscriber(url) {\n\t\t// A queue named with the topic it is subscribed to is a managed SNS→SQS\n\t\t// subscription, which is the SNS driver's to build — reached through the\n\t\t// registry, so this subpath never imports the SNS client.\n\t\tif (new URL(url).searchParams.get('topicArn')) {\n\t\t\treturn eventsDriverFor(EventPublisherType.SNS, 'subscriber').subscriber(\n\t\t\t\turl.replace(/^sqs:/, 'sns:'),\n\t\t\t);\n\t\t}\n\t\treturn new SQSSubscriber(await SQSConnection.fromConnectionString(url));\n\t},\n\tsubscriberFor: (connection) => new SQSSubscriber(connection as SQSConnection),\n};\n"],"mappings":";;;;;;;;;;AAWA,MAAa,kBAAgC;CAC5C,QAAA;CACA,UAAU,QAAQ,cAAc,qBAAqB,GAAG;CACxD,YAAY,QAAQ,aAAa,qBAAqB,GAAG;CACzD,eAAe,eAAe,IAAI,aAAa,UAA2B;CAC1E,MAAM,WAAW,KAAK;EAIrB,IAAI,IAAI,IAAI,GAAG,CAAC,CAAC,aAAa,IAAI,UAAU,GAC3C,OAAO,gBAAA,OAAwC,YAAY,CAAC,CAAC,WAC5D,IAAI,QAAQ,SAAS,MAAM,CAC5B;EAED,OAAO,IAAI,cAAc,MAAM,cAAc,qBAAqB,GAAG,CAAC;CACvE;CACA,gBAAgB,eAAe,IAAI,cAAc,UAA2B;AAC7E"}
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
let _opentelemetry_api = require("@opentelemetry/api");
|
|
2
|
+
//#region src/types.ts
|
|
3
|
+
/**
|
|
4
|
+
* Event publisher/subscriber types
|
|
5
|
+
*/
|
|
6
|
+
let EventPublisherType = /* @__PURE__ */ function(EventPublisherType) {
|
|
7
|
+
EventPublisherType["Basic"] = "basic";
|
|
8
|
+
EventPublisherType["EventBridge"] = "eventbridge";
|
|
9
|
+
EventPublisherType["SQS"] = "sqs";
|
|
10
|
+
EventPublisherType["SNS"] = "sns";
|
|
11
|
+
EventPublisherType["Kafka"] = "kafka";
|
|
12
|
+
EventPublisherType["RabbitMQ"] = "rabbitmq";
|
|
13
|
+
EventPublisherType["PgBoss"] = "pgboss";
|
|
14
|
+
return EventPublisherType;
|
|
15
|
+
}({});
|
|
16
|
+
/** The transports a publisher, subscriber or connection can be built for. */
|
|
17
|
+
const SUPPORTED = [
|
|
18
|
+
"basic",
|
|
19
|
+
"sqs",
|
|
20
|
+
"sns",
|
|
21
|
+
"rabbitmq",
|
|
22
|
+
"pgboss"
|
|
23
|
+
];
|
|
24
|
+
/**
|
|
25
|
+
* A connection string or connection whose transport nothing here implements —
|
|
26
|
+
* a typo'd scheme, or one of the listed-but-unbuilt ones (EventBridge, Kafka).
|
|
27
|
+
*/
|
|
28
|
+
var UnsupportedEventTransport = class extends Error {
|
|
29
|
+
transport;
|
|
30
|
+
role;
|
|
31
|
+
constructor(transport, role) {
|
|
32
|
+
super(`No event ${role} for "${transport}". Use one of: ${SUPPORTED.join(", ")} — e.g. pgboss://… or sns://….`);
|
|
33
|
+
this.transport = transport;
|
|
34
|
+
this.role = role;
|
|
35
|
+
this.name = "UnsupportedEventTransport";
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
//#endregion
|
|
39
|
+
//#region src/telemetry.ts
|
|
40
|
+
/**
|
|
41
|
+
* Trace context across a broker: what makes request → queue → worker one trace.
|
|
42
|
+
*
|
|
43
|
+
* On publish, each message gets a PRODUCER span, and that span's context is
|
|
44
|
+
* written into the message through the global propagator — into the broker's
|
|
45
|
+
* own header or attribute field where it has one. On consume, the context is
|
|
46
|
+
* read back and the handler runs inside a CONSUMER span that is its child. A
|
|
47
|
+
* message carrying no context (an older publisher, a process with telemetry
|
|
48
|
+
* off) starts a new trace instead.
|
|
49
|
+
*
|
|
50
|
+
* Everything goes through `@opentelemetry/api`'s globals. Without a registered
|
|
51
|
+
* provider and propagator the spans are no-ops and nothing is written into a
|
|
52
|
+
* message, so a process that never asked for telemetry publishes exactly what
|
|
53
|
+
* it did before.
|
|
54
|
+
*
|
|
55
|
+
* @module
|
|
56
|
+
*/
|
|
57
|
+
/**
|
|
58
|
+
* The key a trace context travels under where a broker has no header field of
|
|
59
|
+
* its own — a pg-boss job's data. Reserved: it is removed before a handler sees
|
|
60
|
+
* the payload, and a payload of the application's own should not use it.
|
|
61
|
+
*/
|
|
62
|
+
const TRACE_CONTEXT_KEY = "__gkmTrace";
|
|
63
|
+
const TRACER = "@geekmidas/events";
|
|
64
|
+
function tracer() {
|
|
65
|
+
return _opentelemetry_api.trace.getTracer(TRACER);
|
|
66
|
+
}
|
|
67
|
+
function attributes(target, operation) {
|
|
68
|
+
return {
|
|
69
|
+
"messaging.system": target.system,
|
|
70
|
+
"messaging.destination.name": target.destination,
|
|
71
|
+
"messaging.operation": operation,
|
|
72
|
+
"messaging.operation.type": operation === "publish" ? "send" : "process",
|
|
73
|
+
...target.type && { "gkm.event.type": target.type },
|
|
74
|
+
...target.messageId && { "messaging.message.id": target.messageId }
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Start the PRODUCER span for one message, as a child of whatever is active,
|
|
79
|
+
* and write its context into a carrier for the broker.
|
|
80
|
+
*/
|
|
81
|
+
function startProducer(target) {
|
|
82
|
+
const active = _opentelemetry_api.context.active();
|
|
83
|
+
const span = tracer().startSpan(`${target.destination} publish`, {
|
|
84
|
+
kind: _opentelemetry_api.SpanKind.PRODUCER,
|
|
85
|
+
attributes: attributes(target, "publish")
|
|
86
|
+
}, active);
|
|
87
|
+
const carrier = {};
|
|
88
|
+
_opentelemetry_api.propagation.inject(_opentelemetry_api.trace.setSpan(active, span), carrier);
|
|
89
|
+
return {
|
|
90
|
+
span,
|
|
91
|
+
carrier: Object.keys(carrier).length > 0 ? carrier : void 0
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
/** End producer spans, recording `error` on each when the send failed. */
|
|
95
|
+
function endProducers(spans, error) {
|
|
96
|
+
for (const span of spans) {
|
|
97
|
+
if (error !== void 0) recordError(span, error);
|
|
98
|
+
span.end();
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Publish `items`, each with a PRODUCER span of its own, through `send` —
|
|
103
|
+
* which receives the carrier to attach to each item, in order.
|
|
104
|
+
*/
|
|
105
|
+
async function publishTraced(items, describe, send) {
|
|
106
|
+
const produced = items.map((item) => startProducer(describe(item)));
|
|
107
|
+
const spans = produced.map((p) => p.span);
|
|
108
|
+
try {
|
|
109
|
+
const result = await send(produced.map((p) => p.carrier));
|
|
110
|
+
endProducers(spans);
|
|
111
|
+
return result;
|
|
112
|
+
} catch (error) {
|
|
113
|
+
endProducers(spans, error);
|
|
114
|
+
throw error;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* The context a carrier holds, or undefined when it holds none — no carrier, a
|
|
119
|
+
* malformed `traceparent`, or a propagator that throws.
|
|
120
|
+
*/
|
|
121
|
+
function extractTraceContext(carrier) {
|
|
122
|
+
if (!carrier || Object.keys(carrier).length === 0) return void 0;
|
|
123
|
+
try {
|
|
124
|
+
const extracted = _opentelemetry_api.propagation.extract(_opentelemetry_api.ROOT_CONTEXT, carrier);
|
|
125
|
+
return _opentelemetry_api.trace.getSpanContext(extracted) ? extracted : void 0;
|
|
126
|
+
} catch {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Run `fn` inside a CONSUMER span for one message: a child of the context the
|
|
132
|
+
* message carried, or a new trace when it carried none. The active context of
|
|
133
|
+
* the poller that received it is never the parent — a job is not part of
|
|
134
|
+
* whatever happened to start the poll loop.
|
|
135
|
+
*/
|
|
136
|
+
function consumeTraced(target, carrier, fn) {
|
|
137
|
+
return runConsumer(target, extractTraceContext(carrier) ?? _opentelemetry_api.ROOT_CONTEXT, [], fn);
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* One CONSUMER span for a batch that arrived together — a Lambda's records.
|
|
141
|
+
* One message with a context is its child; several start a trace of their own
|
|
142
|
+
* that links to each message's producer, since a span has one parent.
|
|
143
|
+
*/
|
|
144
|
+
function consumeBatchTraced(target, carriers, fn) {
|
|
145
|
+
const contexts = carriers.map(extractTraceContext).filter((c) => c !== void 0);
|
|
146
|
+
if (carriers.length === 1 && contexts.length === 1) return runConsumer(target, contexts[0], [], fn);
|
|
147
|
+
const links = contexts.map((c) => ({ context: _opentelemetry_api.trace.getSpanContext(c) }));
|
|
148
|
+
return runConsumer(target, _opentelemetry_api.ROOT_CONTEXT, links, fn);
|
|
149
|
+
}
|
|
150
|
+
function runConsumer(target, parent, links, fn) {
|
|
151
|
+
const span = tracer().startSpan(`${target.destination} process`, {
|
|
152
|
+
kind: _opentelemetry_api.SpanKind.CONSUMER,
|
|
153
|
+
attributes: attributes(target, "process"),
|
|
154
|
+
links
|
|
155
|
+
}, parent);
|
|
156
|
+
return _opentelemetry_api.context.with(_opentelemetry_api.trace.setSpan(parent, span), async () => {
|
|
157
|
+
try {
|
|
158
|
+
return await fn();
|
|
159
|
+
} catch (error) {
|
|
160
|
+
recordError(span, error);
|
|
161
|
+
throw error;
|
|
162
|
+
} finally {
|
|
163
|
+
span.end();
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
function recordError(span, error) {
|
|
168
|
+
if (error instanceof Error) span.recordException(error);
|
|
169
|
+
span.setStatus({
|
|
170
|
+
code: _opentelemetry_api.SpanStatusCode.ERROR,
|
|
171
|
+
...error instanceof Error && { message: error.message }
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* A carrier from broker message attributes, in any of the shapes they arrive
|
|
176
|
+
* in: SQS's `{ StringValue }`, a Lambda SQS record's `{ stringValue }`, an SNS
|
|
177
|
+
* envelope's `{ Value }`, or plain strings (RabbitMQ headers).
|
|
178
|
+
*/
|
|
179
|
+
function carrierFromAttributes(attributes) {
|
|
180
|
+
if (!attributes) return void 0;
|
|
181
|
+
const carrier = {};
|
|
182
|
+
for (const field of PROPAGATION_FIELDS) {
|
|
183
|
+
const value = attributeValue(attributes[field]);
|
|
184
|
+
if (value) carrier[field] = value;
|
|
185
|
+
}
|
|
186
|
+
return Object.keys(carrier).length > 0 ? carrier : void 0;
|
|
187
|
+
}
|
|
188
|
+
/** The W3C fields; what the default propagator reads and writes. */
|
|
189
|
+
const PROPAGATION_FIELDS = ["traceparent", "tracestate"];
|
|
190
|
+
function attributeValue(value) {
|
|
191
|
+
if (typeof value === "string") return value;
|
|
192
|
+
if (Buffer.isBuffer(value)) return value.toString();
|
|
193
|
+
if (value && typeof value === "object") {
|
|
194
|
+
const v = value;
|
|
195
|
+
const found = v.StringValue ?? v.stringValue ?? v.Value;
|
|
196
|
+
return typeof found === "string" ? found : void 0;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
/** A carrier as SQS/SNS message attributes. */
|
|
200
|
+
function carrierAsAttributes(carrier) {
|
|
201
|
+
const attributes = {};
|
|
202
|
+
if (!carrier) return attributes;
|
|
203
|
+
for (const [key, value] of Object.entries(carrier)) attributes[key] = {
|
|
204
|
+
DataType: "String",
|
|
205
|
+
StringValue: value
|
|
206
|
+
};
|
|
207
|
+
return attributes;
|
|
208
|
+
}
|
|
209
|
+
function isPlainObject(value) {
|
|
210
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
211
|
+
const proto = Object.getPrototypeOf(value);
|
|
212
|
+
return proto === Object.prototype || proto === null;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* `data` with the carrier under {@link TRACE_CONTEXT_KEY}, for a broker with no
|
|
216
|
+
* header field. Unchanged when there is no carrier, or when `data` is not a
|
|
217
|
+
* plain object a key can be added to — such a message starts its own trace.
|
|
218
|
+
*/
|
|
219
|
+
function withTraceKey(data, carrier) {
|
|
220
|
+
if (!carrier || !isPlainObject(data)) return data;
|
|
221
|
+
return {
|
|
222
|
+
...data,
|
|
223
|
+
[TRACE_CONTEXT_KEY]: carrier
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* `data` without {@link TRACE_CONTEXT_KEY}, and the carrier that was under it.
|
|
228
|
+
* What a handler sees never has the key.
|
|
229
|
+
*/
|
|
230
|
+
function withoutTraceKey(data) {
|
|
231
|
+
if (!isPlainObject(data) || !("__gkmTrace" in data)) return {
|
|
232
|
+
data,
|
|
233
|
+
carrier: void 0
|
|
234
|
+
};
|
|
235
|
+
const { [TRACE_CONTEXT_KEY]: raw, ...rest } = data;
|
|
236
|
+
return {
|
|
237
|
+
data: rest,
|
|
238
|
+
carrier: isPlainObject(raw) ? carrierFromAttributes(raw) : void 0
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
//#endregion
|
|
242
|
+
Object.defineProperty(exports, "EventPublisherType", {
|
|
243
|
+
enumerable: true,
|
|
244
|
+
get: function() {
|
|
245
|
+
return EventPublisherType;
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
Object.defineProperty(exports, "TRACE_CONTEXT_KEY", {
|
|
249
|
+
enumerable: true,
|
|
250
|
+
get: function() {
|
|
251
|
+
return TRACE_CONTEXT_KEY;
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
Object.defineProperty(exports, "UnsupportedEventTransport", {
|
|
255
|
+
enumerable: true,
|
|
256
|
+
get: function() {
|
|
257
|
+
return UnsupportedEventTransport;
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
Object.defineProperty(exports, "carrierAsAttributes", {
|
|
261
|
+
enumerable: true,
|
|
262
|
+
get: function() {
|
|
263
|
+
return carrierAsAttributes;
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
Object.defineProperty(exports, "carrierFromAttributes", {
|
|
267
|
+
enumerable: true,
|
|
268
|
+
get: function() {
|
|
269
|
+
return carrierFromAttributes;
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
Object.defineProperty(exports, "consumeBatchTraced", {
|
|
273
|
+
enumerable: true,
|
|
274
|
+
get: function() {
|
|
275
|
+
return consumeBatchTraced;
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
Object.defineProperty(exports, "consumeTraced", {
|
|
279
|
+
enumerable: true,
|
|
280
|
+
get: function() {
|
|
281
|
+
return consumeTraced;
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
Object.defineProperty(exports, "extractTraceContext", {
|
|
285
|
+
enumerable: true,
|
|
286
|
+
get: function() {
|
|
287
|
+
return extractTraceContext;
|
|
288
|
+
}
|
|
289
|
+
});
|
|
290
|
+
Object.defineProperty(exports, "publishTraced", {
|
|
291
|
+
enumerable: true,
|
|
292
|
+
get: function() {
|
|
293
|
+
return publishTraced;
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
Object.defineProperty(exports, "withTraceKey", {
|
|
297
|
+
enumerable: true,
|
|
298
|
+
get: function() {
|
|
299
|
+
return withTraceKey;
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
Object.defineProperty(exports, "withoutTraceKey", {
|
|
303
|
+
enumerable: true,
|
|
304
|
+
get: function() {
|
|
305
|
+
return withoutTraceKey;
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
//# sourceMappingURL=telemetry-DG3l807O.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telemetry-DG3l807O.cjs","names":["trace","context","SpanKind","propagation","ROOT_CONTEXT","SpanStatusCode"],"sources":["../src/types.ts","../src/telemetry.ts"],"sourcesContent":["import type { InferStandardSchema } from '@geekmidas/schema';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\n\n/**\n * Represents a publishable event message with a type and payload.\n *\n * @template TType - The event type/name (e.g., 'user.created')\n * @template TPayload - The event payload data\n */\nexport type PublishableMessage<TType extends string, TPayload> = {\n\ttype: TType;\n\tpayload: TPayload;\n};\n\n/**\n * Event publisher/subscriber types\n */\nexport enum EventPublisherType {\n\tBasic = 'basic',\n\tEventBridge = 'eventbridge',\n\tSQS = 'sqs',\n\tSNS = 'sns',\n\tKafka = 'kafka',\n\tRabbitMQ = 'rabbitmq',\n\tPgBoss = 'pgboss',\n}\n\n/** The transports a publisher, subscriber or connection can be built for. */\nconst SUPPORTED: readonly string[] = [\n\tEventPublisherType.Basic,\n\tEventPublisherType.SQS,\n\tEventPublisherType.SNS,\n\tEventPublisherType.RabbitMQ,\n\tEventPublisherType.PgBoss,\n];\n\n/**\n * A connection string or connection whose transport nothing here implements —\n * a typo'd scheme, or one of the listed-but-unbuilt ones (EventBridge, Kafka).\n */\nexport class UnsupportedEventTransport extends Error {\n\tconstructor(\n\t\treadonly transport: string,\n\t\treadonly role: 'publisher' | 'subscriber' | 'connection',\n\t) {\n\t\tsuper(\n\t\t\t`No event ${role} for \"${transport}\". Use one of: ${SUPPORTED.join(', ')} — e.g. pgboss://… or sns://….`,\n\t\t);\n\t\tthis.name = 'UnsupportedEventTransport';\n\t}\n}\n\n/**\n * Base interface for event connections\n * Connections manage the underlying transport (RabbitMQ channel, SQS client, etc.)\n */\nexport interface EventConnection {\n\treadonly type: EventPublisherType;\n\tconnect(): Promise<void>;\n\tclose(): Promise<void>;\n\tisConnected(): boolean;\n}\n\n/**\n * Interface for event publishers that handle the actual publishing of events.\n * Implementations can send to EventBridge, SQS, Kafka, or any other event system.\n *\n * @template TMessage - The union type of all publishable messages\n */\nexport type EventPublisher<TMessage extends PublishableMessage<string, any>> = {\n\tpublish: (message: TMessage[]) => Promise<void>;\n};\n\n/**\n * Interface for event subscribers that handle receiving and processing events.\n *\n * @template TMessage - The union type of all publishable messages\n */\nexport type EventSubscriber<TMessage extends PublishableMessage<string, any>> =\n\t{\n\t\tsubscribe: (\n\t\t\tmessages: TMessage['type'][],\n\t\t\tlistener: (payload: TMessage) => Promise<void>,\n\t\t) => Promise<void>;\n\t};\n/**\n * Utility type to extract the message from EventPublisher\n */\nexport type ExtractPublisherMessage<T> =\n\tT extends EventPublisher<infer M> ? M : never;\n\nexport type MappedEvent<\n\tT extends EventPublisher<any> | undefined,\n\tOutSchema extends StandardSchemaV1 | undefined = undefined,\n> = {\n\ttype: ExtractPublisherMessage<T>['type'];\n\tpayload: (\n\t\tctx: InferStandardSchema<OutSchema>,\n\t) => ExtractPublisherMessage<T>['payload'];\n\twhen?: (ctx: InferStandardSchema<OutSchema>) => boolean;\n};\n","/**\n * Trace context across a broker: what makes request → queue → worker one trace.\n *\n * On publish, each message gets a PRODUCER span, and that span's context is\n * written into the message through the global propagator — into the broker's\n * own header or attribute field where it has one. On consume, the context is\n * read back and the handler runs inside a CONSUMER span that is its child. A\n * message carrying no context (an older publisher, a process with telemetry\n * off) starts a new trace instead.\n *\n * Everything goes through `@opentelemetry/api`'s globals. Without a registered\n * provider and propagator the spans are no-ops and nothing is written into a\n * message, so a process that never asked for telemetry publishes exactly what\n * it did before.\n *\n * @module\n */\nimport {\n\ttype Attributes,\n\ttype Context,\n\tcontext,\n\ttype Link,\n\tpropagation,\n\tROOT_CONTEXT,\n\ttype Span,\n\tSpanKind,\n\tSpanStatusCode,\n\ttrace,\n} from '@opentelemetry/api';\n\n/**\n * The key a trace context travels under where a broker has no header field of\n * its own — a pg-boss job's data. Reserved: it is removed before a handler sees\n * the payload, and a payload of the application's own should not use it.\n */\nexport const TRACE_CONTEXT_KEY = '__gkmTrace';\n\n/** A propagated trace context: `traceparent`, and `tracestate` when set. */\nexport type TraceCarrier = Record<string, string>;\n\n/** What a message is, as its spans describe it. */\nexport interface MessagingTarget {\n\t/** `messaging.system`: `pgboss`, `aws_sqs`, `aws_sns`, `rabbitmq`, `basic`. */\n\tsystem: string;\n\t/** The queue or topic — `messaging.destination.name`. */\n\tdestination: string;\n\t/** The event's type, when the message has one. */\n\ttype?: string;\n\t/** The broker's id for the message, when it has one. */\n\tmessageId?: string;\n}\n\nconst TRACER = '@geekmidas/events';\n\nfunction tracer() {\n\treturn trace.getTracer(TRACER);\n}\n\nfunction attributes(\n\ttarget: MessagingTarget,\n\toperation: 'publish' | 'process',\n): Attributes {\n\treturn {\n\t\t'messaging.system': target.system,\n\t\t'messaging.destination.name': target.destination,\n\t\t'messaging.operation': operation,\n\t\t'messaging.operation.type': operation === 'publish' ? 'send' : 'process',\n\t\t...(target.type && { 'gkm.event.type': target.type }),\n\t\t...(target.messageId && { 'messaging.message.id': target.messageId }),\n\t};\n}\n\n/** A PRODUCER span for one message, and the context to send with it. */\nexport interface ProducedMessage {\n\tspan: Span;\n\t/** Undefined when there is nothing to propagate: no propagator registered. */\n\tcarrier: TraceCarrier | undefined;\n}\n\n/**\n * Start the PRODUCER span for one message, as a child of whatever is active,\n * and write its context into a carrier for the broker.\n */\nexport function startProducer(target: MessagingTarget): ProducedMessage {\n\tconst active = context.active();\n\tconst span = tracer().startSpan(\n\t\t`${target.destination} publish`,\n\t\t{ kind: SpanKind.PRODUCER, attributes: attributes(target, 'publish') },\n\t\tactive,\n\t);\n\n\tconst carrier: TraceCarrier = {};\n\tpropagation.inject(trace.setSpan(active, span), carrier);\n\n\treturn {\n\t\tspan,\n\t\tcarrier: Object.keys(carrier).length > 0 ? carrier : undefined,\n\t};\n}\n\n/** End producer spans, recording `error` on each when the send failed. */\nexport function endProducers(spans: readonly Span[], error?: unknown): void {\n\tfor (const span of spans) {\n\t\tif (error !== undefined) recordError(span, error);\n\t\tspan.end();\n\t}\n}\n\n/**\n * Publish `items`, each with a PRODUCER span of its own, through `send` —\n * which receives the carrier to attach to each item, in order.\n */\nexport async function publishTraced<T, R>(\n\titems: readonly T[],\n\tdescribe: (item: T) => MessagingTarget,\n\tsend: (carriers: (TraceCarrier | undefined)[]) => Promise<R>,\n): Promise<R> {\n\tconst produced = items.map((item) => startProducer(describe(item)));\n\tconst spans = produced.map((p) => p.span);\n\ttry {\n\t\tconst result = await send(produced.map((p) => p.carrier));\n\t\tendProducers(spans);\n\t\treturn result;\n\t} catch (error) {\n\t\tendProducers(spans, error);\n\t\tthrow error;\n\t}\n}\n\n/**\n * The context a carrier holds, or undefined when it holds none — no carrier, a\n * malformed `traceparent`, or a propagator that throws.\n */\nexport function extractTraceContext(\n\tcarrier: TraceCarrier | undefined,\n): Context | undefined {\n\tif (!carrier || Object.keys(carrier).length === 0) return undefined;\n\ttry {\n\t\tconst extracted = propagation.extract(ROOT_CONTEXT, carrier);\n\t\treturn trace.getSpanContext(extracted) ? extracted : undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\n/**\n * Run `fn` inside a CONSUMER span for one message: a child of the context the\n * message carried, or a new trace when it carried none. The active context of\n * the poller that received it is never the parent — a job is not part of\n * whatever happened to start the poll loop.\n */\nexport function consumeTraced<R>(\n\ttarget: MessagingTarget,\n\tcarrier: TraceCarrier | undefined,\n\tfn: () => Promise<R>,\n): Promise<R> {\n\tconst parent = extractTraceContext(carrier) ?? ROOT_CONTEXT;\n\treturn runConsumer(target, parent, [], fn);\n}\n\n/**\n * One CONSUMER span for a batch that arrived together — a Lambda's records.\n * One message with a context is its child; several start a trace of their own\n * that links to each message's producer, since a span has one parent.\n */\nexport function consumeBatchTraced<R>(\n\ttarget: MessagingTarget,\n\tcarriers: readonly (TraceCarrier | undefined)[],\n\tfn: () => Promise<R>,\n): Promise<R> {\n\tconst contexts = carriers\n\t\t.map(extractTraceContext)\n\t\t.filter((c): c is Context => c !== undefined);\n\n\tif (carriers.length === 1 && contexts.length === 1) {\n\t\treturn runConsumer(target, contexts[0]!, [], fn);\n\t}\n\n\tconst links: Link[] = contexts.map((c) => ({\n\t\tcontext: trace.getSpanContext(c)!,\n\t}));\n\treturn runConsumer(target, ROOT_CONTEXT, links, fn);\n}\n\nfunction runConsumer<R>(\n\ttarget: MessagingTarget,\n\tparent: Context,\n\tlinks: Link[],\n\tfn: () => Promise<R>,\n): Promise<R> {\n\tconst span = tracer().startSpan(\n\t\t`${target.destination} process`,\n\t\t{\n\t\t\tkind: SpanKind.CONSUMER,\n\t\t\tattributes: attributes(target, 'process'),\n\t\t\tlinks,\n\t\t},\n\t\tparent,\n\t);\n\n\treturn context.with(trace.setSpan(parent, span), async () => {\n\t\ttry {\n\t\t\treturn await fn();\n\t\t} catch (error) {\n\t\t\trecordError(span, error);\n\t\t\tthrow error;\n\t\t} finally {\n\t\t\tspan.end();\n\t\t}\n\t});\n}\n\nfunction recordError(span: Span, error: unknown): void {\n\tif (error instanceof Error) span.recordException(error);\n\tspan.setStatus({\n\t\tcode: SpanStatusCode.ERROR,\n\t\t...(error instanceof Error && { message: error.message }),\n\t});\n}\n\n/**\n * A carrier from broker message attributes, in any of the shapes they arrive\n * in: SQS's `{ StringValue }`, a Lambda SQS record's `{ stringValue }`, an SNS\n * envelope's `{ Value }`, or plain strings (RabbitMQ headers).\n */\nexport function carrierFromAttributes(\n\tattributes: Record<string, unknown> | undefined | null,\n): TraceCarrier | undefined {\n\tif (!attributes) return undefined;\n\tconst carrier: TraceCarrier = {};\n\tfor (const field of PROPAGATION_FIELDS) {\n\t\tconst value = attributeValue(attributes[field]);\n\t\tif (value) carrier[field] = value;\n\t}\n\treturn Object.keys(carrier).length > 0 ? carrier : undefined;\n}\n\n/** The W3C fields; what the default propagator reads and writes. */\nconst PROPAGATION_FIELDS = ['traceparent', 'tracestate'] as const;\n\nfunction attributeValue(value: unknown): string | undefined {\n\tif (typeof value === 'string') return value;\n\tif (Buffer.isBuffer(value)) return value.toString();\n\tif (value && typeof value === 'object') {\n\t\tconst v = value as {\n\t\t\tValue?: unknown;\n\t\t\tStringValue?: unknown;\n\t\t\tstringValue?: unknown;\n\t\t};\n\t\tconst found = v.StringValue ?? v.stringValue ?? v.Value;\n\t\treturn typeof found === 'string' ? found : undefined;\n\t}\n\treturn undefined;\n}\n\n/** A carrier as SQS/SNS message attributes. */\nexport function carrierAsAttributes(\n\tcarrier: TraceCarrier | undefined,\n): Record<string, { DataType: 'String'; StringValue: string }> {\n\tconst attributes: Record<\n\t\tstring,\n\t\t{ DataType: 'String'; StringValue: string }\n\t> = {};\n\tif (!carrier) return attributes;\n\tfor (const [key, value] of Object.entries(carrier)) {\n\t\tattributes[key] = { DataType: 'String', StringValue: value };\n\t}\n\treturn attributes;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n\tif (!value || typeof value !== 'object' || Array.isArray(value)) return false;\n\tconst proto = Object.getPrototypeOf(value);\n\treturn proto === Object.prototype || proto === null;\n}\n\n/**\n * `data` with the carrier under {@link TRACE_CONTEXT_KEY}, for a broker with no\n * header field. Unchanged when there is no carrier, or when `data` is not a\n * plain object a key can be added to — such a message starts its own trace.\n */\nexport function withTraceKey<T>(data: T, carrier: TraceCarrier | undefined): T {\n\tif (!carrier || !isPlainObject(data)) return data;\n\treturn { ...data, [TRACE_CONTEXT_KEY]: carrier } as T;\n}\n\n/**\n * `data` without {@link TRACE_CONTEXT_KEY}, and the carrier that was under it.\n * What a handler sees never has the key.\n */\nexport function withoutTraceKey<T>(data: T): {\n\tdata: T;\n\tcarrier: TraceCarrier | undefined;\n} {\n\tif (!isPlainObject(data) || !(TRACE_CONTEXT_KEY in data)) {\n\t\treturn { data, carrier: undefined };\n\t}\n\tconst { [TRACE_CONTEXT_KEY]: raw, ...rest } = data;\n\treturn {\n\t\tdata: rest as T,\n\t\tcarrier: isPlainObject(raw) ? carrierFromAttributes(raw) : undefined,\n\t};\n}\n"],"mappings":"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"}
|