@hookflo/tern 4.2.1-beta → 4.2.5-beta
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/alerts.d.ts +1 -1
- package/dist/alerts.js +3 -3
- package/dist/notifications/channels/discord/build-payload.d.ts +1 -1
- package/dist/notifications/channels/discord/build-payload.js +29 -19
- package/dist/notifications/channels/slack/build-payload.d.ts +1 -1
- package/dist/notifications/channels/slack/build-payload.js +32 -28
- package/dist/notifications/index.d.ts +2 -2
- package/dist/notifications/index.js +3 -3
- package/dist/notifications/utils.js +32 -1
- package/dist/upstash/controls.js +20 -1
- package/package.json +1 -1
package/dist/alerts.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export * from './notifications';
|
|
2
|
-
export declare const
|
|
2
|
+
export declare const alertInternals: {
|
|
3
3
|
resolveDestinations: typeof import("./notifications/utils").resolveDestinations;
|
|
4
4
|
normalizeAlertOptions: typeof import("./notifications/utils").normalizeAlertOptions;
|
|
5
5
|
buildSlackPayload: typeof import("./notifications/channels/slack").buildSlackPayload;
|
package/dist/alerts.js
CHANGED
|
@@ -14,8 +14,8 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.
|
|
18
|
-
__exportStar(require("./notifications"), exports);
|
|
17
|
+
exports.alertInternals = void 0;
|
|
19
18
|
const notifications_1 = require("./notifications");
|
|
19
|
+
__exportStar(require("./notifications"), exports);
|
|
20
20
|
// Backward-compatible alias used by previous version internals.
|
|
21
|
-
exports.
|
|
21
|
+
exports.alertInternals = notifications_1.notificationInternals;
|
|
@@ -4,34 +4,44 @@ exports.buildDiscordPayload = buildDiscordPayload;
|
|
|
4
4
|
const utils_1 = require("../../utils");
|
|
5
5
|
const constants_1 = require("../../constants");
|
|
6
6
|
function buildDiscordPayload(input) {
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
];
|
|
14
|
-
if (input.eventId) {
|
|
15
|
-
fields.push({ name: 'Event ID', value: `\`${input.eventId}\``, inline: true });
|
|
16
|
-
}
|
|
7
|
+
const isDLQ = input.dlq;
|
|
8
|
+
const title = isDLQ ? "Dead Letter Queue — Event Failed" : "Webhook Received";
|
|
9
|
+
const description = isDLQ
|
|
10
|
+
? "Event exhausted all retries. Manual replay required."
|
|
11
|
+
: "Event verified and queued for processing.";
|
|
12
|
+
const fields = [];
|
|
17
13
|
if (input.source) {
|
|
18
|
-
fields.push({ name:
|
|
14
|
+
fields.push({ name: "Platform", value: input.source, inline: true });
|
|
19
15
|
}
|
|
20
|
-
|
|
21
|
-
|
|
16
|
+
fields.push({
|
|
17
|
+
name: "Severity",
|
|
18
|
+
value: input.severity.toLowerCase(),
|
|
19
|
+
inline: true,
|
|
20
|
+
});
|
|
21
|
+
if (isDLQ) {
|
|
22
|
+
fields.push({ name: "Queue", value: "dlq", inline: true });
|
|
23
|
+
}
|
|
24
|
+
if (input.eventId) {
|
|
25
|
+
fields.push({
|
|
26
|
+
name: isDLQ ? "DLQ ID" : "Event ID",
|
|
27
|
+
value: `\`${input.eventId}\``,
|
|
28
|
+
inline: false,
|
|
29
|
+
});
|
|
22
30
|
}
|
|
23
31
|
const metadataString = (0, utils_1.compactMetadata)(input.metadata);
|
|
24
32
|
if (metadataString) {
|
|
25
|
-
fields.push({ name:
|
|
33
|
+
fields.push({ name: "Details", value: `\`\`\`${metadataString}\`\`\`` });
|
|
26
34
|
}
|
|
27
|
-
const replayLine = input.replayUrl
|
|
28
|
-
|
|
35
|
+
const replayLine = input.replayUrl
|
|
36
|
+
? `\n\n[${input.replayLabel ?? "Replay Event"}](${input.replayUrl})`
|
|
37
|
+
: "";
|
|
38
|
+
const footerText = input.branding === false ? undefined : "Alert from Tern · tern.hookflo.com";
|
|
29
39
|
return {
|
|
30
40
|
embeds: [
|
|
31
41
|
{
|
|
32
|
-
title
|
|
33
|
-
description: `${
|
|
34
|
-
color: parseInt(constants_1.severityColorMap[input.severity].replace(
|
|
42
|
+
title,
|
|
43
|
+
description: `${description}${replayLine}`,
|
|
44
|
+
color: parseInt(constants_1.severityColorMap[input.severity].replace("#", ""), 16),
|
|
35
45
|
fields,
|
|
36
46
|
footer: footerText ? { text: footerText } : undefined,
|
|
37
47
|
timestamp: new Date().toISOString(),
|
|
@@ -4,29 +4,33 @@ exports.buildSlackPayload = buildSlackPayload;
|
|
|
4
4
|
const utils_1 = require("../../utils");
|
|
5
5
|
const constants_1 = require("../../constants");
|
|
6
6
|
function buildSlackPayload(input) {
|
|
7
|
+
const isDLQ = input.dlq;
|
|
8
|
+
const title = isDLQ ? "Dead Letter Queue — Event Failed" : "Webhook Received";
|
|
9
|
+
const message = isDLQ
|
|
10
|
+
? "Event exhausted all retries. Manual replay required."
|
|
11
|
+
: "Event verified and queued for processing.";
|
|
7
12
|
const fields = [
|
|
8
|
-
input.
|
|
9
|
-
|
|
13
|
+
input.source
|
|
14
|
+
? { type: "mrkdwn", text: `*Platform*\n${input.source}` }
|
|
15
|
+
: null,
|
|
10
16
|
{
|
|
11
|
-
type:
|
|
12
|
-
text: `*Severity*\n${input.severity.
|
|
17
|
+
type: "mrkdwn",
|
|
18
|
+
text: `*Severity*\n${input.severity.toLowerCase()}`,
|
|
13
19
|
},
|
|
14
|
-
|
|
20
|
+
isDLQ ? { type: "mrkdwn", text: "*Queue*\ndlq" } : null,
|
|
21
|
+
input.eventId
|
|
22
|
+
? {
|
|
23
|
+
type: "mrkdwn",
|
|
24
|
+
text: `*${isDLQ ? "DLQ ID" : "Event ID"}*\n\`${input.eventId}\``,
|
|
25
|
+
}
|
|
26
|
+
: null,
|
|
15
27
|
].filter(Boolean);
|
|
16
28
|
const blocks = [
|
|
17
29
|
{
|
|
18
|
-
type:
|
|
30
|
+
type: "section",
|
|
19
31
|
text: {
|
|
20
|
-
type:
|
|
21
|
-
text:
|
|
22
|
-
emoji: true,
|
|
23
|
-
},
|
|
24
|
-
},
|
|
25
|
-
{
|
|
26
|
-
type: 'section',
|
|
27
|
-
text: {
|
|
28
|
-
type: 'mrkdwn',
|
|
29
|
-
text: input.message,
|
|
32
|
+
type: "mrkdwn",
|
|
33
|
+
text: `*${title}*\n${message}`,
|
|
30
34
|
},
|
|
31
35
|
fields,
|
|
32
36
|
},
|
|
@@ -34,43 +38,43 @@ function buildSlackPayload(input) {
|
|
|
34
38
|
const metadataString = (0, utils_1.compactMetadata)(input.metadata);
|
|
35
39
|
if (metadataString) {
|
|
36
40
|
blocks.push({
|
|
37
|
-
type:
|
|
41
|
+
type: "section",
|
|
38
42
|
text: {
|
|
39
|
-
type:
|
|
40
|
-
text:
|
|
43
|
+
type: "mrkdwn",
|
|
44
|
+
text: `\`\`\`${metadataString}\`\`\``,
|
|
41
45
|
},
|
|
42
46
|
});
|
|
43
47
|
}
|
|
44
48
|
if (input.replayUrl) {
|
|
45
49
|
blocks.push({
|
|
46
|
-
type:
|
|
50
|
+
type: "actions",
|
|
47
51
|
elements: [
|
|
48
52
|
{
|
|
49
|
-
type:
|
|
53
|
+
type: "button",
|
|
50
54
|
text: {
|
|
51
|
-
type:
|
|
52
|
-
text: input.replayLabel,
|
|
53
|
-
emoji:
|
|
55
|
+
type: "plain_text",
|
|
56
|
+
text: input.replayLabel ?? "Replay Event",
|
|
57
|
+
emoji: false,
|
|
54
58
|
},
|
|
55
59
|
url: input.replayUrl,
|
|
56
|
-
style:
|
|
60
|
+
style: "danger",
|
|
57
61
|
},
|
|
58
62
|
],
|
|
59
63
|
});
|
|
60
64
|
}
|
|
61
65
|
if (input.branding !== false) {
|
|
62
66
|
blocks.push({
|
|
63
|
-
type:
|
|
67
|
+
type: "context",
|
|
64
68
|
elements: [
|
|
65
69
|
{
|
|
66
|
-
type:
|
|
70
|
+
type: "mrkdwn",
|
|
67
71
|
text: `Alert from <${constants_1.TERN_BRAND_URL}|Tern>`,
|
|
68
72
|
},
|
|
69
73
|
],
|
|
70
74
|
});
|
|
71
75
|
}
|
|
72
76
|
return {
|
|
73
|
-
text:
|
|
77
|
+
text: title,
|
|
74
78
|
blocks,
|
|
75
79
|
};
|
|
76
80
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
export * from './types';
|
|
2
1
|
import { normalizeAlertOptions, resolveDestinations } from './utils';
|
|
3
2
|
import { buildSlackPayload } from './channels/slack';
|
|
4
3
|
import { buildDiscordPayload } from './channels/discord';
|
|
5
|
-
export
|
|
4
|
+
export * from './types';
|
|
5
|
+
export declare const notificationInternals: {
|
|
6
6
|
resolveDestinations: typeof resolveDestinations;
|
|
7
7
|
normalizeAlertOptions: typeof normalizeAlertOptions;
|
|
8
8
|
buildSlackPayload: typeof buildSlackPayload;
|
|
@@ -14,12 +14,12 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.
|
|
18
|
-
__exportStar(require("./types"), exports);
|
|
17
|
+
exports.notificationInternals = void 0;
|
|
19
18
|
const utils_1 = require("./utils");
|
|
20
19
|
const slack_1 = require("./channels/slack");
|
|
21
20
|
const discord_1 = require("./channels/discord");
|
|
22
|
-
exports
|
|
21
|
+
__exportStar(require("./types"), exports);
|
|
22
|
+
exports.notificationInternals = {
|
|
23
23
|
resolveDestinations: utils_1.resolveDestinations,
|
|
24
24
|
normalizeAlertOptions: utils_1.normalizeAlertOptions,
|
|
25
25
|
buildSlackPayload: slack_1.buildSlackPayload,
|
|
@@ -10,6 +10,36 @@ function compactMetadata(metadata) {
|
|
|
10
10
|
const entries = Object.entries(metadata).slice(0, 8);
|
|
11
11
|
return entries.map(([key, value]) => `${key}: ${String(value)}`).join('\n');
|
|
12
12
|
}
|
|
13
|
+
function asNonEmptyString(value) {
|
|
14
|
+
return typeof value === 'string' && value.trim().length > 0 ? value : undefined;
|
|
15
|
+
}
|
|
16
|
+
function asObject(value) {
|
|
17
|
+
return value && typeof value === 'object' ? value : undefined;
|
|
18
|
+
}
|
|
19
|
+
function resolveSource(options) {
|
|
20
|
+
const metadata = options.metadata || {};
|
|
21
|
+
return asNonEmptyString(options.source)
|
|
22
|
+
|| asNonEmptyString(metadata.source)
|
|
23
|
+
|| asNonEmptyString(metadata.platform)
|
|
24
|
+
|| asNonEmptyString(metadata.provider);
|
|
25
|
+
}
|
|
26
|
+
function resolveEventId(options) {
|
|
27
|
+
const metadata = options.metadata || {};
|
|
28
|
+
const metadataPayload = asObject(metadata.payload);
|
|
29
|
+
const metadataEvent = asObject(metadata.event);
|
|
30
|
+
const metadataData = asObject(metadata.data);
|
|
31
|
+
return asNonEmptyString(options.eventId)
|
|
32
|
+
|| asNonEmptyString(options.dlqId)
|
|
33
|
+
|| asNonEmptyString(metadata.eventId)
|
|
34
|
+
|| asNonEmptyString(metadata.messageId)
|
|
35
|
+
|| asNonEmptyString(metadata.webhookId)
|
|
36
|
+
|| asNonEmptyString(metadata.id)
|
|
37
|
+
|| asNonEmptyString(metadataPayload?.id)
|
|
38
|
+
|| asNonEmptyString(metadataPayload?.eventId)
|
|
39
|
+
|| asNonEmptyString(metadataPayload?.request_id)
|
|
40
|
+
|| asNonEmptyString(metadataEvent?.id)
|
|
41
|
+
|| asNonEmptyString(metadataData?.id);
|
|
42
|
+
}
|
|
13
43
|
function resolveDestinations(config) {
|
|
14
44
|
const destinations = [];
|
|
15
45
|
if (config.slack?.enabled !== false && config.slack?.webhookUrl) {
|
|
@@ -29,10 +59,11 @@ function normalizeAlertOptions(options) {
|
|
|
29
59
|
return {
|
|
30
60
|
...options,
|
|
31
61
|
dlq: isDlq,
|
|
62
|
+
source: resolveSource(options),
|
|
32
63
|
title,
|
|
33
64
|
message,
|
|
34
65
|
severity,
|
|
35
66
|
replayLabel,
|
|
36
|
-
eventId: options
|
|
67
|
+
eventId: resolveEventId(options),
|
|
37
68
|
};
|
|
38
69
|
}
|
package/dist/upstash/controls.js
CHANGED
|
@@ -117,6 +117,22 @@ function createTernControls(config) {
|
|
|
117
117
|
},
|
|
118
118
|
async alert(options = {}) {
|
|
119
119
|
let replayMeta = {};
|
|
120
|
+
let resolvedSource = options.source;
|
|
121
|
+
let resolvedEventId = options.eventId;
|
|
122
|
+
if (options.dlq && (!resolvedSource || !resolvedEventId)) {
|
|
123
|
+
try {
|
|
124
|
+
const dlqMessages = await this.dlq();
|
|
125
|
+
const matchingMessage = dlqMessages.find((message) => message.dlqId === options.dlqId);
|
|
126
|
+
resolvedSource = resolvedSource || matchingMessage?.platform;
|
|
127
|
+
resolvedEventId = resolvedEventId || matchingMessage?.id;
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
replayMeta = {
|
|
131
|
+
...replayMeta,
|
|
132
|
+
dlqLookupError: error.message,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
}
|
|
120
136
|
if (options.dlq) {
|
|
121
137
|
if (!options.dlqId || options.dlqId.trim() === '') {
|
|
122
138
|
throw new Error('[tern] controls.alert() with dlq: true requires dlqId.');
|
|
@@ -148,9 +164,12 @@ function createTernControls(config) {
|
|
|
148
164
|
: undefined,
|
|
149
165
|
}, {
|
|
150
166
|
...options,
|
|
151
|
-
|
|
167
|
+
source: resolvedSource,
|
|
168
|
+
eventId: resolvedEventId || options.dlqId,
|
|
152
169
|
metadata: {
|
|
153
170
|
...(options.metadata || {}),
|
|
171
|
+
source: resolvedSource || options.metadata?.source,
|
|
172
|
+
eventId: resolvedEventId || options.metadata?.eventId,
|
|
154
173
|
...replayMeta,
|
|
155
174
|
},
|
|
156
175
|
});
|
package/package.json
CHANGED