@walkeros/server-destination-slack 3.3.0-next-1776098542393
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 +132 -0
- package/dist/dev.d.mts +164 -0
- package/dist/dev.d.ts +164 -0
- package/dist/dev.js +1 -0
- package/dist/dev.js.map +1 -0
- package/dist/dev.mjs +1 -0
- package/dist/dev.mjs.map +1 -0
- package/dist/examples/index.d.mts +113 -0
- package/dist/examples/index.d.ts +113 -0
- package/dist/examples/index.js +278 -0
- package/dist/examples/index.mjs +256 -0
- package/dist/index.d.mts +111 -0
- package/dist/index.d.ts +111 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1 -0
- package/dist/index.mjs.map +1 -0
- package/dist/walkerOS.json +774 -0
- package/package.json +77 -0
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __export = (target, all) => {
|
|
3
|
+
for (var name in all)
|
|
4
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
// src/examples/env.ts
|
|
8
|
+
var env_exports = {};
|
|
9
|
+
__export(env_exports, {
|
|
10
|
+
push: () => push,
|
|
11
|
+
simulation: () => simulation
|
|
12
|
+
});
|
|
13
|
+
var okMessageResponse = {
|
|
14
|
+
ok: true,
|
|
15
|
+
channel: "CMOCK",
|
|
16
|
+
ts: "1700000000.000100"
|
|
17
|
+
};
|
|
18
|
+
var okEphemeralResponse = { ok: true };
|
|
19
|
+
var okOpenResponse = {
|
|
20
|
+
ok: true,
|
|
21
|
+
channel: { id: "D-MOCK-DM" }
|
|
22
|
+
};
|
|
23
|
+
function createMockClient() {
|
|
24
|
+
return {
|
|
25
|
+
chat: {
|
|
26
|
+
postMessage: () => Promise.resolve(okMessageResponse),
|
|
27
|
+
postEphemeral: () => Promise.resolve(okEphemeralResponse)
|
|
28
|
+
},
|
|
29
|
+
conversations: {
|
|
30
|
+
open: () => Promise.resolve(okOpenResponse)
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
var push = {
|
|
35
|
+
slackClient: createMockClient(),
|
|
36
|
+
sendServer: (() => Promise.resolve({ ok: true }))
|
|
37
|
+
};
|
|
38
|
+
var simulation = [
|
|
39
|
+
"call:slackClient.chat.postMessage",
|
|
40
|
+
"call:slackClient.chat.postEphemeral",
|
|
41
|
+
"call:slackClient.conversations.open",
|
|
42
|
+
"call:sendServer"
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
// src/examples/step.ts
|
|
46
|
+
var step_exports = {};
|
|
47
|
+
__export(step_exports, {
|
|
48
|
+
defaultBlocks: () => defaultBlocks,
|
|
49
|
+
deployNotification: () => deployNotification,
|
|
50
|
+
ephemeralMessage: () => ephemeralMessage,
|
|
51
|
+
errorAlert: () => errorAlert,
|
|
52
|
+
purchaseAlert: () => purchaseAlert,
|
|
53
|
+
threadedCheckoutStep: () => threadedCheckoutStep,
|
|
54
|
+
welcomeDM: () => welcomeDM
|
|
55
|
+
});
|
|
56
|
+
import { getEvent } from "@walkeros/core";
|
|
57
|
+
var purchaseAlert = {
|
|
58
|
+
in: getEvent("order complete", {
|
|
59
|
+
timestamp: 1700000100,
|
|
60
|
+
data: {
|
|
61
|
+
id: "ORD-500",
|
|
62
|
+
total: 299.99,
|
|
63
|
+
currency: "EUR",
|
|
64
|
+
product: "Pro Plan"
|
|
65
|
+
},
|
|
66
|
+
user: { id: "buyer-42" }
|
|
67
|
+
}),
|
|
68
|
+
mapping: {
|
|
69
|
+
settings: {
|
|
70
|
+
channel: "#sales",
|
|
71
|
+
text: ":moneybag: New order: ${data.id} - ${data.total} ${data.currency}"
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
out: [
|
|
75
|
+
"slackClient.chat.postMessage",
|
|
76
|
+
{
|
|
77
|
+
channel: "#sales",
|
|
78
|
+
text: ":moneybag: New order: ORD-500 - 299.99 EUR",
|
|
79
|
+
unfurl_links: false,
|
|
80
|
+
unfurl_media: false,
|
|
81
|
+
mrkdwn: true
|
|
82
|
+
}
|
|
83
|
+
]
|
|
84
|
+
};
|
|
85
|
+
var errorAlert = {
|
|
86
|
+
in: getEvent("error occur", {
|
|
87
|
+
timestamp: 1700000200,
|
|
88
|
+
data: {
|
|
89
|
+
message: "Payment gateway timeout",
|
|
90
|
+
code: "PGW_TIMEOUT",
|
|
91
|
+
severity: "critical"
|
|
92
|
+
}
|
|
93
|
+
}),
|
|
94
|
+
mapping: {
|
|
95
|
+
settings: {
|
|
96
|
+
channel: "#engineering-alerts",
|
|
97
|
+
text: ":rotating_light: Error: ${data.message}"
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
out: [
|
|
101
|
+
"slackClient.chat.postMessage",
|
|
102
|
+
{
|
|
103
|
+
channel: "#engineering-alerts",
|
|
104
|
+
text: ":rotating_light: Error: Payment gateway timeout",
|
|
105
|
+
unfurl_links: false,
|
|
106
|
+
unfurl_media: false,
|
|
107
|
+
mrkdwn: true
|
|
108
|
+
}
|
|
109
|
+
]
|
|
110
|
+
};
|
|
111
|
+
var welcomeDM = {
|
|
112
|
+
in: getEvent("user signup", {
|
|
113
|
+
timestamp: 1700000300,
|
|
114
|
+
data: { plan: "enterprise" },
|
|
115
|
+
user: { id: "U-NEW-USER" }
|
|
116
|
+
}),
|
|
117
|
+
mapping: {
|
|
118
|
+
settings: {
|
|
119
|
+
dm: true,
|
|
120
|
+
user: "U-NEW-USER",
|
|
121
|
+
text: ":wave: Welcome aboard! You signed up for the ${data.plan} plan."
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
out: [
|
|
125
|
+
["slackClient.conversations.open", { users: "U-NEW-USER" }],
|
|
126
|
+
[
|
|
127
|
+
"slackClient.chat.postMessage",
|
|
128
|
+
{
|
|
129
|
+
channel: "D-MOCK-DM",
|
|
130
|
+
text: ":wave: Welcome aboard! You signed up for the enterprise plan.",
|
|
131
|
+
unfurl_links: false,
|
|
132
|
+
unfurl_media: false,
|
|
133
|
+
mrkdwn: true
|
|
134
|
+
}
|
|
135
|
+
]
|
|
136
|
+
]
|
|
137
|
+
};
|
|
138
|
+
var threadedCheckoutStep = {
|
|
139
|
+
in: getEvent("checkout step", {
|
|
140
|
+
timestamp: 1700000400,
|
|
141
|
+
data: { step: "payment", sessionTs: "1700000000.000050" }
|
|
142
|
+
}),
|
|
143
|
+
mapping: {
|
|
144
|
+
settings: {
|
|
145
|
+
channel: "#sales",
|
|
146
|
+
text: "Checkout step: ${data.step}",
|
|
147
|
+
threadTs: "1700000000.000050",
|
|
148
|
+
replyBroadcast: true
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
out: [
|
|
152
|
+
"slackClient.chat.postMessage",
|
|
153
|
+
{
|
|
154
|
+
channel: "#sales",
|
|
155
|
+
text: "Checkout step: payment",
|
|
156
|
+
thread_ts: "1700000000.000050",
|
|
157
|
+
reply_broadcast: true,
|
|
158
|
+
unfurl_links: false,
|
|
159
|
+
unfurl_media: false,
|
|
160
|
+
mrkdwn: true
|
|
161
|
+
}
|
|
162
|
+
]
|
|
163
|
+
};
|
|
164
|
+
var ephemeralMessage = {
|
|
165
|
+
in: getEvent("quota warning", {
|
|
166
|
+
timestamp: 1700000500,
|
|
167
|
+
data: { remaining: 5 }
|
|
168
|
+
}),
|
|
169
|
+
mapping: {
|
|
170
|
+
settings: {
|
|
171
|
+
channel: "#admin",
|
|
172
|
+
ephemeral: true,
|
|
173
|
+
user: "U-ADMIN-1",
|
|
174
|
+
text: "Heads up: ${data.remaining} requests remaining"
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
out: [
|
|
178
|
+
"slackClient.chat.postEphemeral",
|
|
179
|
+
{
|
|
180
|
+
channel: "#admin",
|
|
181
|
+
user: "U-ADMIN-1",
|
|
182
|
+
text: "Heads up: 5 requests remaining",
|
|
183
|
+
unfurl_links: false,
|
|
184
|
+
unfurl_media: false,
|
|
185
|
+
mrkdwn: true
|
|
186
|
+
}
|
|
187
|
+
]
|
|
188
|
+
};
|
|
189
|
+
var defaultBlocks = {
|
|
190
|
+
in: getEvent("lead submit", {
|
|
191
|
+
timestamp: 1700000600,
|
|
192
|
+
data: { name: "Acme", email: "sales@acme.test" },
|
|
193
|
+
source: { type: "server", id: "crm", previous_id: "" }
|
|
194
|
+
}),
|
|
195
|
+
mapping: {
|
|
196
|
+
settings: {
|
|
197
|
+
channel: "#growth"
|
|
198
|
+
}
|
|
199
|
+
},
|
|
200
|
+
out: [
|
|
201
|
+
"slackClient.chat.postMessage",
|
|
202
|
+
{
|
|
203
|
+
channel: "#growth",
|
|
204
|
+
text: "lead submit",
|
|
205
|
+
blocks: [
|
|
206
|
+
{
|
|
207
|
+
type: "header",
|
|
208
|
+
text: { type: "plain_text", text: "lead submit" }
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
type: "section",
|
|
212
|
+
fields: [
|
|
213
|
+
{ type: "mrkdwn", text: "*name:*\nAcme" },
|
|
214
|
+
{ type: "mrkdwn", text: "*email:*\nsales@acme.test" }
|
|
215
|
+
]
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
type: "context",
|
|
219
|
+
elements: [{ type: "mrkdwn", text: "Source: crm" }]
|
|
220
|
+
}
|
|
221
|
+
],
|
|
222
|
+
unfurl_links: false,
|
|
223
|
+
unfurl_media: false,
|
|
224
|
+
mrkdwn: true
|
|
225
|
+
}
|
|
226
|
+
]
|
|
227
|
+
};
|
|
228
|
+
var deployNotification = {
|
|
229
|
+
in: getEvent("deploy complete", {
|
|
230
|
+
timestamp: 1700000700,
|
|
231
|
+
data: { version: "1.4.2", environment: "prod" }
|
|
232
|
+
}),
|
|
233
|
+
settings: {
|
|
234
|
+
token: void 0,
|
|
235
|
+
webhookUrl: "https://hooks.slack.com/services/T00/B00/xxx"
|
|
236
|
+
},
|
|
237
|
+
mapping: {
|
|
238
|
+
settings: {
|
|
239
|
+
text: ":rocket: Deployment complete: ${data.version} to ${data.environment}"
|
|
240
|
+
}
|
|
241
|
+
},
|
|
242
|
+
out: [
|
|
243
|
+
"sendServer",
|
|
244
|
+
"https://hooks.slack.com/services/T00/B00/xxx",
|
|
245
|
+
{
|
|
246
|
+
text: ":rocket: Deployment complete: 1.4.2 to prod",
|
|
247
|
+
unfurl_links: false,
|
|
248
|
+
unfurl_media: false,
|
|
249
|
+
mrkdwn: true
|
|
250
|
+
}
|
|
251
|
+
]
|
|
252
|
+
};
|
|
253
|
+
export {
|
|
254
|
+
env_exports as env,
|
|
255
|
+
step_exports as step
|
|
256
|
+
};
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { Destination as Destination$1, Mapping as Mapping$1 } from '@walkeros/core';
|
|
2
|
+
import { DestinationServer, sendServer } from '@walkeros/server-core';
|
|
3
|
+
import { WebClient, ChatPostMessageArguments, ChatPostMessageResponse, ChatPostEphemeralArguments, ChatPostEphemeralResponse, ConversationsOpenArguments, ConversationsOpenResponse } from '@slack/web-api';
|
|
4
|
+
|
|
5
|
+
/** A single Block Kit block (kept loose -- Slack does not export a union type). */
|
|
6
|
+
type SlackBlock = Record<string, unknown>;
|
|
7
|
+
interface Settings {
|
|
8
|
+
/** Slack Bot token (xoxb-...). Enables Web API mode. */
|
|
9
|
+
token?: string;
|
|
10
|
+
/** Incoming Webhook URL. Enables webhook mode. */
|
|
11
|
+
webhookUrl?: string;
|
|
12
|
+
/** Default channel ID or name. Required for Web API mode unless every rule provides one. */
|
|
13
|
+
channel?: string;
|
|
14
|
+
/** Default text template. Supports `${data.field}` interpolation against the walkerOS event. */
|
|
15
|
+
text?: string;
|
|
16
|
+
/** Default Block Kit blocks applied when no mapping override is set. */
|
|
17
|
+
blocks?: SlackBlock[];
|
|
18
|
+
/** Auto-add an event-name header block when generating default blocks. Default: true. */
|
|
19
|
+
includeHeader?: boolean;
|
|
20
|
+
/** Enable link unfurling. Default: false. */
|
|
21
|
+
unfurlLinks?: boolean;
|
|
22
|
+
/** Enable media unfurling. Default: false. */
|
|
23
|
+
unfurlMedia?: boolean;
|
|
24
|
+
/** Use mrkdwn formatting. Default: true. */
|
|
25
|
+
mrkdwn?: boolean;
|
|
26
|
+
/** Static thread_ts to reply to (rarely set at destination level). */
|
|
27
|
+
threadTs?: string;
|
|
28
|
+
/** Retry policy passed to WebClient. Default: 'default'. */
|
|
29
|
+
retryConfig?: 'default' | 'fiveRetriesInFiveMinutes' | 'none';
|
|
30
|
+
_client?: WebClient;
|
|
31
|
+
}
|
|
32
|
+
type InitSettings = Partial<Settings>;
|
|
33
|
+
/**
|
|
34
|
+
* Per-rule mapping settings. Override channel, text, blocks, and route
|
|
35
|
+
* to threads / DMs / ephemeral.
|
|
36
|
+
*/
|
|
37
|
+
interface Mapping {
|
|
38
|
+
/** Override the channel for this rule (Web API mode only). */
|
|
39
|
+
channel?: string;
|
|
40
|
+
/** Override the text template for this rule. */
|
|
41
|
+
text?: string;
|
|
42
|
+
/** Override Block Kit blocks for this rule. */
|
|
43
|
+
blocks?: SlackBlock[];
|
|
44
|
+
/** thread_ts for posting as a reply in a thread. */
|
|
45
|
+
threadTs?: string;
|
|
46
|
+
/** Also broadcast the threaded reply back to the channel. */
|
|
47
|
+
replyBroadcast?: boolean;
|
|
48
|
+
/** Send via chat.postEphemeral instead of chat.postMessage. */
|
|
49
|
+
ephemeral?: boolean;
|
|
50
|
+
/** Slack user ID for ephemeral or DM delivery. */
|
|
51
|
+
user?: string;
|
|
52
|
+
/** Send as DM (conversations.open + chat.postMessage). Requires `user`. */
|
|
53
|
+
dm?: boolean;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Mock-friendly interface for the WebClient methods the destination calls.
|
|
57
|
+
* Tests inject this via env.slackClient.
|
|
58
|
+
*/
|
|
59
|
+
interface SlackClientMock {
|
|
60
|
+
chat: {
|
|
61
|
+
postMessage: (opts: ChatPostMessageArguments) => Promise<ChatPostMessageResponse>;
|
|
62
|
+
postEphemeral: (opts: ChatPostEphemeralArguments) => Promise<ChatPostEphemeralResponse>;
|
|
63
|
+
};
|
|
64
|
+
conversations: {
|
|
65
|
+
open: (opts: ConversationsOpenArguments) => Promise<ConversationsOpenResponse>;
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Env -- optional SDK / transport overrides. Production leaves these undefined.
|
|
70
|
+
* Tests inject `slackClient` (Web API mode) and/or `sendServer` (webhook mode).
|
|
71
|
+
*/
|
|
72
|
+
interface Env extends DestinationServer.Env {
|
|
73
|
+
slackClient?: SlackClientMock;
|
|
74
|
+
sendServer?: typeof sendServer;
|
|
75
|
+
}
|
|
76
|
+
type Types = Destination$1.Types<Settings, Mapping, Env, InitSettings>;
|
|
77
|
+
interface Destination extends DestinationServer.Destination<Types> {
|
|
78
|
+
init: DestinationServer.InitFn<Types>;
|
|
79
|
+
}
|
|
80
|
+
type Config = {
|
|
81
|
+
settings: Settings;
|
|
82
|
+
} & DestinationServer.Config<Types>;
|
|
83
|
+
type InitFn = DestinationServer.InitFn<Types>;
|
|
84
|
+
type PushFn = DestinationServer.PushFn<Types>;
|
|
85
|
+
type PartialConfig = DestinationServer.PartialConfig<Types>;
|
|
86
|
+
type PushEvents = DestinationServer.PushEvents<Mapping>;
|
|
87
|
+
type Rule = Mapping$1.Rule<Mapping>;
|
|
88
|
+
type Rules = Mapping$1.Rules<Rule>;
|
|
89
|
+
|
|
90
|
+
type index_Config = Config;
|
|
91
|
+
type index_Destination = Destination;
|
|
92
|
+
type index_Env = Env;
|
|
93
|
+
type index_InitFn = InitFn;
|
|
94
|
+
type index_InitSettings = InitSettings;
|
|
95
|
+
type index_Mapping = Mapping;
|
|
96
|
+
type index_PartialConfig = PartialConfig;
|
|
97
|
+
type index_PushEvents = PushEvents;
|
|
98
|
+
type index_PushFn = PushFn;
|
|
99
|
+
type index_Rule = Rule;
|
|
100
|
+
type index_Rules = Rules;
|
|
101
|
+
type index_Settings = Settings;
|
|
102
|
+
type index_SlackBlock = SlackBlock;
|
|
103
|
+
type index_SlackClientMock = SlackClientMock;
|
|
104
|
+
type index_Types = Types;
|
|
105
|
+
declare namespace index {
|
|
106
|
+
export type { index_Config as Config, index_Destination as Destination, index_Env as Env, index_InitFn as InitFn, index_InitSettings as InitSettings, index_Mapping as Mapping, index_PartialConfig as PartialConfig, index_PushEvents as PushEvents, index_PushFn as PushFn, index_Rule as Rule, index_Rules as Rules, index_Settings as Settings, index_SlackBlock as SlackBlock, index_SlackClientMock as SlackClientMock, index_Types as Types };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
declare const destinationSlack: Destination;
|
|
110
|
+
|
|
111
|
+
export { index as DestinationSlack, destinationSlack as default, destinationSlack };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { Destination as Destination$1, Mapping as Mapping$1 } from '@walkeros/core';
|
|
2
|
+
import { DestinationServer, sendServer } from '@walkeros/server-core';
|
|
3
|
+
import { WebClient, ChatPostMessageArguments, ChatPostMessageResponse, ChatPostEphemeralArguments, ChatPostEphemeralResponse, ConversationsOpenArguments, ConversationsOpenResponse } from '@slack/web-api';
|
|
4
|
+
|
|
5
|
+
/** A single Block Kit block (kept loose -- Slack does not export a union type). */
|
|
6
|
+
type SlackBlock = Record<string, unknown>;
|
|
7
|
+
interface Settings {
|
|
8
|
+
/** Slack Bot token (xoxb-...). Enables Web API mode. */
|
|
9
|
+
token?: string;
|
|
10
|
+
/** Incoming Webhook URL. Enables webhook mode. */
|
|
11
|
+
webhookUrl?: string;
|
|
12
|
+
/** Default channel ID or name. Required for Web API mode unless every rule provides one. */
|
|
13
|
+
channel?: string;
|
|
14
|
+
/** Default text template. Supports `${data.field}` interpolation against the walkerOS event. */
|
|
15
|
+
text?: string;
|
|
16
|
+
/** Default Block Kit blocks applied when no mapping override is set. */
|
|
17
|
+
blocks?: SlackBlock[];
|
|
18
|
+
/** Auto-add an event-name header block when generating default blocks. Default: true. */
|
|
19
|
+
includeHeader?: boolean;
|
|
20
|
+
/** Enable link unfurling. Default: false. */
|
|
21
|
+
unfurlLinks?: boolean;
|
|
22
|
+
/** Enable media unfurling. Default: false. */
|
|
23
|
+
unfurlMedia?: boolean;
|
|
24
|
+
/** Use mrkdwn formatting. Default: true. */
|
|
25
|
+
mrkdwn?: boolean;
|
|
26
|
+
/** Static thread_ts to reply to (rarely set at destination level). */
|
|
27
|
+
threadTs?: string;
|
|
28
|
+
/** Retry policy passed to WebClient. Default: 'default'. */
|
|
29
|
+
retryConfig?: 'default' | 'fiveRetriesInFiveMinutes' | 'none';
|
|
30
|
+
_client?: WebClient;
|
|
31
|
+
}
|
|
32
|
+
type InitSettings = Partial<Settings>;
|
|
33
|
+
/**
|
|
34
|
+
* Per-rule mapping settings. Override channel, text, blocks, and route
|
|
35
|
+
* to threads / DMs / ephemeral.
|
|
36
|
+
*/
|
|
37
|
+
interface Mapping {
|
|
38
|
+
/** Override the channel for this rule (Web API mode only). */
|
|
39
|
+
channel?: string;
|
|
40
|
+
/** Override the text template for this rule. */
|
|
41
|
+
text?: string;
|
|
42
|
+
/** Override Block Kit blocks for this rule. */
|
|
43
|
+
blocks?: SlackBlock[];
|
|
44
|
+
/** thread_ts for posting as a reply in a thread. */
|
|
45
|
+
threadTs?: string;
|
|
46
|
+
/** Also broadcast the threaded reply back to the channel. */
|
|
47
|
+
replyBroadcast?: boolean;
|
|
48
|
+
/** Send via chat.postEphemeral instead of chat.postMessage. */
|
|
49
|
+
ephemeral?: boolean;
|
|
50
|
+
/** Slack user ID for ephemeral or DM delivery. */
|
|
51
|
+
user?: string;
|
|
52
|
+
/** Send as DM (conversations.open + chat.postMessage). Requires `user`. */
|
|
53
|
+
dm?: boolean;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Mock-friendly interface for the WebClient methods the destination calls.
|
|
57
|
+
* Tests inject this via env.slackClient.
|
|
58
|
+
*/
|
|
59
|
+
interface SlackClientMock {
|
|
60
|
+
chat: {
|
|
61
|
+
postMessage: (opts: ChatPostMessageArguments) => Promise<ChatPostMessageResponse>;
|
|
62
|
+
postEphemeral: (opts: ChatPostEphemeralArguments) => Promise<ChatPostEphemeralResponse>;
|
|
63
|
+
};
|
|
64
|
+
conversations: {
|
|
65
|
+
open: (opts: ConversationsOpenArguments) => Promise<ConversationsOpenResponse>;
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Env -- optional SDK / transport overrides. Production leaves these undefined.
|
|
70
|
+
* Tests inject `slackClient` (Web API mode) and/or `sendServer` (webhook mode).
|
|
71
|
+
*/
|
|
72
|
+
interface Env extends DestinationServer.Env {
|
|
73
|
+
slackClient?: SlackClientMock;
|
|
74
|
+
sendServer?: typeof sendServer;
|
|
75
|
+
}
|
|
76
|
+
type Types = Destination$1.Types<Settings, Mapping, Env, InitSettings>;
|
|
77
|
+
interface Destination extends DestinationServer.Destination<Types> {
|
|
78
|
+
init: DestinationServer.InitFn<Types>;
|
|
79
|
+
}
|
|
80
|
+
type Config = {
|
|
81
|
+
settings: Settings;
|
|
82
|
+
} & DestinationServer.Config<Types>;
|
|
83
|
+
type InitFn = DestinationServer.InitFn<Types>;
|
|
84
|
+
type PushFn = DestinationServer.PushFn<Types>;
|
|
85
|
+
type PartialConfig = DestinationServer.PartialConfig<Types>;
|
|
86
|
+
type PushEvents = DestinationServer.PushEvents<Mapping>;
|
|
87
|
+
type Rule = Mapping$1.Rule<Mapping>;
|
|
88
|
+
type Rules = Mapping$1.Rules<Rule>;
|
|
89
|
+
|
|
90
|
+
type index_Config = Config;
|
|
91
|
+
type index_Destination = Destination;
|
|
92
|
+
type index_Env = Env;
|
|
93
|
+
type index_InitFn = InitFn;
|
|
94
|
+
type index_InitSettings = InitSettings;
|
|
95
|
+
type index_Mapping = Mapping;
|
|
96
|
+
type index_PartialConfig = PartialConfig;
|
|
97
|
+
type index_PushEvents = PushEvents;
|
|
98
|
+
type index_PushFn = PushFn;
|
|
99
|
+
type index_Rule = Rule;
|
|
100
|
+
type index_Rules = Rules;
|
|
101
|
+
type index_Settings = Settings;
|
|
102
|
+
type index_SlackBlock = SlackBlock;
|
|
103
|
+
type index_SlackClientMock = SlackClientMock;
|
|
104
|
+
type index_Types = Types;
|
|
105
|
+
declare namespace index {
|
|
106
|
+
export type { index_Config as Config, index_Destination as Destination, index_Env as Env, index_InitFn as InitFn, index_InitSettings as InitSettings, index_Mapping as Mapping, index_PartialConfig as PartialConfig, index_PushEvents as PushEvents, index_PushFn as PushFn, index_Rule as Rule, index_Rules as Rules, index_Settings as Settings, index_SlackBlock as SlackBlock, index_SlackClientMock as SlackClientMock, index_Types as Types };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
declare const destinationSlack: Destination;
|
|
110
|
+
|
|
111
|
+
export { index as DestinationSlack, destinationSlack as default, destinationSlack };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var e,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,o=Object.prototype.hasOwnProperty,i={};((e,n)=>{for(var r in n)t(e,r,{get:n[r],enumerable:!0})})(i,{DestinationSlack:()=>p,default:()=>g,destinationSlack:()=>b}),module.exports=(e=i,((e,i,l,s)=>{if(i&&"object"==typeof i||"function"==typeof i)for(let a of r(i))o.call(e,a)||a===l||t(e,a,{get:()=>i[a],enumerable:!(s=n(i,a))||s.enumerable});return e})(t({},"__esModule",{value:!0}),e));var l=require("@walkeros/core"),s=require("@walkeros/server-core"),a=/\$\{([^}]+)\}/g;function c(e,t){return e.replace(a,(e,n)=>{const r=function(e,t){return t.split(".").reduce((e,t)=>{if(e&&"object"==typeof e&&t in e)return e[t]},e)}(t,n.trim());return null==r?"":"object"==typeof r?JSON.stringify(r):String(r)})}function u(e,t){return JSON.parse(c(JSON.stringify(e),t))}function k(e,t){var n;const r=[];t&&r.push({type:"header",text:{type:"plain_text",text:e.name}});const o=e.data||{},i=Object.entries(o).filter(([,e])=>null!=e).map(([e,t])=>({type:"mrkdwn",text:`*${e}:*\n${d(t)}`}));i.length>0&&r.push({type:"section",fields:i});const l=null==(n=e.source)?void 0:n.id;return l&&r.push({type:"context",elements:[{type:"mrkdwn",text:`Source: ${l}`}]}),r}function d(e){return null==e?"":"object"==typeof e?JSON.stringify(e):String(e)}var f=async function(e,t){const{config:n,rule:r,env:o,logger:i}=t,a=n.settings,d=(null==r?void 0:r.settings)||{},f=function(e,t,n){var r,o;if(n.blocks&&n.blocks.length>0){const o={blocks:u(n.blocks,e)},i=null!=(r=n.text)?r:t.text;return i&&(o.text=c(i,e)),o}const i=null!=(o=n.text)?o:t.text;return i?{text:c(i,e)}:t.blocks&&t.blocks.length>0?{blocks:u(t.blocks,e)}:{text:e.name,blocks:k(e,!1!==t.includeHeader)}}(e,a,d);return a.webhookUrl?await async function(e,t,n,r){var o,i,a;if(!e.text&&!e.blocks)return void r.warn("Slack message has no text or blocks; skipping");const c={...e.text?{text:e.text}:{},...e.blocks?{blocks:e.blocks}:{},unfurl_links:null!=(o=t.unfurlLinks)&&o,unfurl_media:null!=(i=t.unfurlMedia)&&i,mrkdwn:null==(a=t.mrkdwn)||a},u=(null==n?void 0:n.sendServer)||s.sendServer,k=await u(t.webhookUrl,JSON.stringify(c));(0,l.isObject)(k)&&!1===k.ok&&r.warn(`Slack webhook error: ${JSON.stringify(k)}`)}(f,a,o,i):await async function(e,t,n,r,o){var i,l,s,a,c,u,k;const d=(null==r?void 0:r.slackClient)||t._client;if(!d)return void o.warn("Slack WebClient not initialized");if(!e.text&&!e.blocks)return void o.warn("Slack message has no text or blocks; skipping");let f=null!=(i=n.channel)?i:t.channel;if(n.dm){if(!n.user)return void o.throw("Slack DM requires `mapping.settings.user`");const e=null==(l=(await d.conversations.open({users:n.user})).channel)?void 0:l.id;if(!e)return void o.warn("Slack conversations.open returned no channel id");f=e}if(!f)return void o.throw("Slack push requires a channel (set settings.channel or mapping.settings.channel)");const p={channel:f,...e.text?{text:e.text}:{},...e.blocks?{blocks:e.blocks}:{},unfurl_links:null!=(s=t.unfurlLinks)&&s,unfurl_media:null!=(a=t.unfurlMedia)&&a,mrkdwn:null==(c=t.mrkdwn)||c};(null!=(u=n.threadTs)?u:t.threadTs)&&(p.thread_ts=null!=(k=n.threadTs)?k:t.threadTs);n.replyBroadcast&&(p.reply_broadcast=!0);if(n.ephemeral)return n.user?void await d.chat.postEphemeral({...p,user:n.user}):void o.throw("Slack ephemeral requires `mapping.settings.user`");await d.chat.postMessage(p)}(f,a,d,o,i)};var p={},h={default:void 0,fiveRetriesInFiveMinutes:{retries:5,factor:1.96},none:{retries:0}},b={type:"slack",config:{},init({config:e,logger:t,env:n}){const r=function(e={},t){const n=e.settings||{},{token:r,webhookUrl:o,channel:i}=n;return r||o||t.throw("Slack destination requires either token or webhookUrl"),r&&o&&t.throw("Slack destination accepts either token or webhookUrl, not both"),r&&!i&&t.warn("Slack destination has no default channel; every mapping rule must provide one"),{...e,settings:{...n}}}(e,t),o=r.settings;if(o.webhookUrl)return r;if(null==n?void 0:n.slackClient)return r;try{const{WebClient:e}=require("@slack/web-api"),t=h[o.retryConfig||"default"];o._client=new e(o.token,{...void 0!==t?{retryConfig:t}:{}})}catch(e){t.throw(`Failed to initialize Slack WebClient: ${e}`)}return r},push:async(e,t)=>await f(e,t)},g=b;//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/config.ts","../src/push.ts","../src/message.ts","../src/types/index.ts"],"sourcesContent":["import type { Destination, Settings, SlackClientMock } from './types';\nimport { getConfig } from './config';\nimport { push } from './push';\n\n// Types\nexport * as DestinationSlack from './types';\n\nconst RETRY_PRESETS: Record<string, unknown> = {\n default: undefined,\n fiveRetriesInFiveMinutes: { retries: 5, factor: 1.96 },\n none: { retries: 0 },\n};\n\nexport const destinationSlack: Destination = {\n type: 'slack',\n\n config: {},\n\n init({ config: partialConfig, logger, env }) {\n const config = getConfig(partialConfig, logger);\n const settings = config.settings as Settings;\n\n // Webhook mode -- nothing to initialize.\n if (settings.webhookUrl) return config;\n\n // Web API mode -- prefer mock client from env (testing).\n const envClient = (env as { slackClient?: SlackClientMock } | undefined)\n ?.slackClient;\n if (envClient) return config;\n\n // Production path: lazily load @slack/web-api so webhook-only users\n // do not pay for the SDK import cost.\n try {\n const { WebClient } = require('@slack/web-api');\n const retryConfig = RETRY_PRESETS[settings.retryConfig || 'default'];\n settings._client = new WebClient(settings.token, {\n ...(retryConfig !== undefined ? { retryConfig } : {}),\n });\n } catch (err) {\n logger.throw(`Failed to initialize Slack WebClient: ${err}`);\n }\n\n return config;\n },\n\n async push(event, context) {\n return await push(event, context);\n },\n};\n\nexport default destinationSlack;\n","import type { Logger } from '@walkeros/core';\nimport type { Config, PartialConfig, Settings } from './types';\n\nexport function getConfig(\n partialConfig: PartialConfig = {},\n logger: Logger.Instance,\n): Config {\n const settings = (partialConfig.settings || {}) as Partial<Settings>;\n const { token, webhookUrl, channel } = settings;\n\n if (!token && !webhookUrl)\n logger.throw('Slack destination requires either token or webhookUrl');\n if (token && webhookUrl)\n logger.throw(\n 'Slack destination accepts either token or webhookUrl, not both',\n );\n if (token && !channel)\n logger.warn(\n 'Slack destination has no default channel; every mapping rule must provide one',\n );\n\n return {\n ...partialConfig,\n settings: { ...settings } as Settings,\n };\n}\n","import { isObject } from '@walkeros/core';\nimport { sendServer } from '@walkeros/server-core';\nimport type { Mapping, PushFn, Settings, SlackClientMock } from './types';\nimport { buildMessage } from './message';\n\nexport const push: PushFn = async function (event, ctx) {\n const { config, rule, env, logger } = ctx;\n const settings = config.settings as Settings;\n const ruleSettings = (rule?.settings || {}) as Partial<Mapping>;\n\n const message = buildMessage(event, settings, ruleSettings);\n\n // Webhook mode\n if (settings.webhookUrl) {\n return await pushWebhook(message, settings, env, logger);\n }\n\n // Web API mode\n return await pushWebApi(message, settings, ruleSettings, env, logger);\n};\n\nasync function pushWebhook(\n message: { text?: string; blocks?: unknown[] },\n settings: Settings,\n env: { sendServer?: typeof sendServer } | undefined,\n logger: import('@walkeros/core').Logger.Instance,\n): Promise<void> {\n if (!message.text && !message.blocks) {\n logger.warn('Slack message has no text or blocks; skipping');\n return;\n }\n\n const body = {\n ...(message.text ? { text: message.text } : {}),\n ...(message.blocks ? { blocks: message.blocks } : {}),\n unfurl_links: settings.unfurlLinks ?? false,\n unfurl_media: settings.unfurlMedia ?? false,\n mrkdwn: settings.mrkdwn ?? true,\n };\n\n const send = env?.sendServer || sendServer;\n const result = await send(\n settings.webhookUrl as string,\n JSON.stringify(body),\n );\n\n if (isObject(result) && result.ok === false) {\n logger.warn(`Slack webhook error: ${JSON.stringify(result)}`);\n }\n}\n\nasync function pushWebApi(\n message: { text?: string; blocks?: unknown[] },\n settings: Settings,\n rule: Partial<Mapping>,\n env: { slackClient?: SlackClientMock } | undefined,\n logger: import('@walkeros/core').Logger.Instance,\n): Promise<void> {\n const client =\n (env?.slackClient as SlackClientMock | undefined) ||\n (settings._client as unknown as SlackClientMock | undefined);\n\n if (!client) {\n logger.warn('Slack WebClient not initialized');\n return;\n }\n\n if (!message.text && !message.blocks) {\n logger.warn('Slack message has no text or blocks; skipping');\n return;\n }\n\n // Resolve target channel\n let channel = rule.channel ?? settings.channel;\n\n // DM path: open DM channel first\n if (rule.dm) {\n if (!rule.user) {\n logger.throw('Slack DM requires `mapping.settings.user`');\n return;\n }\n const opened = await client.conversations.open({ users: rule.user });\n const dmChannel = (\n opened as unknown as {\n channel?: { id?: string };\n }\n ).channel?.id;\n if (!dmChannel) {\n logger.warn('Slack conversations.open returned no channel id');\n return;\n }\n channel = dmChannel;\n }\n\n if (!channel) {\n logger.throw(\n 'Slack push requires a channel (set settings.channel or mapping.settings.channel)',\n );\n return;\n }\n\n const baseArgs: Record<string, unknown> = {\n channel,\n ...(message.text ? { text: message.text } : {}),\n ...(message.blocks ? { blocks: message.blocks } : {}),\n unfurl_links: settings.unfurlLinks ?? false,\n unfurl_media: settings.unfurlMedia ?? false,\n mrkdwn: settings.mrkdwn ?? true,\n };\n\n if (rule.threadTs ?? settings.threadTs) {\n baseArgs.thread_ts = rule.threadTs ?? settings.threadTs;\n }\n if (rule.replyBroadcast) baseArgs.reply_broadcast = true;\n\n // Ephemeral path\n if (rule.ephemeral) {\n if (!rule.user) {\n logger.throw('Slack ephemeral requires `mapping.settings.user`');\n return;\n }\n await client.chat.postEphemeral({\n ...baseArgs,\n user: rule.user,\n } as never);\n return;\n }\n\n await client.chat.postMessage(baseArgs as never);\n}\n","import type { WalkerOS } from '@walkeros/core';\nimport type { Mapping, Settings, SlackBlock } from './types';\n\nexport interface BuiltMessage {\n text?: string;\n blocks?: SlackBlock[];\n}\n\nconst TOKEN_RE = /\\$\\{([^}]+)\\}/g;\n\nfunction getPath(obj: unknown, path: string): unknown {\n return path.split('.').reduce<unknown>((acc, key) => {\n if (acc && typeof acc === 'object' && key in (acc as object)) {\n return (acc as Record<string, unknown>)[key];\n }\n return undefined;\n }, obj);\n}\n\nexport function interpolate(template: string, event: WalkerOS.Event): string {\n return template.replace(TOKEN_RE, (_, path: string) => {\n const value = getPath(event, path.trim());\n if (value === undefined || value === null) return '';\n if (typeof value === 'object') return JSON.stringify(value);\n return String(value);\n });\n}\n\nexport function buildMessage(\n event: WalkerOS.Event,\n settings: Partial<Settings>,\n rule: Partial<Mapping>,\n): BuiltMessage {\n // 1. Custom blocks via rule\n if (rule.blocks && rule.blocks.length > 0) {\n const result: BuiltMessage = {\n blocks: interpolateBlocks(rule.blocks, event),\n };\n const text = rule.text ?? settings.text;\n if (text) result.text = interpolate(text, event);\n return result;\n }\n\n // 2. Custom text via rule (no blocks)\n const text = rule.text ?? settings.text;\n if (text) {\n return { text: interpolate(text, event) };\n }\n\n // 3. Destination-level blocks\n if (settings.blocks && settings.blocks.length > 0) {\n return { blocks: interpolateBlocks(settings.blocks, event) };\n }\n\n // 4. Auto-generated default blocks from event data\n return {\n text: event.name,\n blocks: defaultBlocks(event, settings.includeHeader !== false),\n };\n}\n\nfunction interpolateBlocks(\n blocks: SlackBlock[],\n event: WalkerOS.Event,\n): SlackBlock[] {\n return JSON.parse(interpolate(JSON.stringify(blocks), event)) as SlackBlock[];\n}\n\nfunction defaultBlocks(\n event: WalkerOS.Event,\n includeHeader: boolean,\n): SlackBlock[] {\n const blocks: SlackBlock[] = [];\n\n if (includeHeader) {\n blocks.push({\n type: 'header',\n text: { type: 'plain_text', text: event.name },\n });\n }\n\n const data = (event.data || {}) as Record<string, unknown>;\n const fields = Object.entries(data)\n .filter(([, v]) => v !== undefined && v !== null)\n .map(([key, value]) => ({\n type: 'mrkdwn' as const,\n text: `*${key}:*\\n${stringify(value)}`,\n }));\n\n if (fields.length > 0) {\n blocks.push({ type: 'section', fields });\n }\n\n const sourceId = event.source?.id;\n if (sourceId) {\n blocks.push({\n type: 'context',\n elements: [{ type: 'mrkdwn', text: `Source: ${sourceId}` }],\n });\n }\n\n return blocks;\n}\n\nfunction stringify(value: unknown): string {\n if (value === null || value === undefined) return '';\n if (typeof value === 'object') return JSON.stringify(value);\n return String(value);\n}\n","import type {\n Mapping as WalkerOSMapping,\n Destination as CoreDestination,\n} from '@walkeros/core';\nimport type { DestinationServer, sendServer } from '@walkeros/server-core';\nimport type {\n WebClient,\n ChatPostMessageArguments,\n ChatPostMessageResponse,\n ChatPostEphemeralArguments,\n ChatPostEphemeralResponse,\n ConversationsOpenArguments,\n ConversationsOpenResponse,\n} from '@slack/web-api';\n\n/** A single Block Kit block (kept loose -- Slack does not export a union type). */\nexport type SlackBlock = Record<string, unknown>;\n\nexport interface Settings {\n // --- Auth (exactly one required) ---\n /** Slack Bot token (xoxb-...). Enables Web API mode. */\n token?: string;\n /** Incoming Webhook URL. Enables webhook mode. */\n webhookUrl?: string;\n\n // --- Target ---\n /** Default channel ID or name. Required for Web API mode unless every rule provides one. */\n channel?: string;\n\n // --- Message formatting ---\n /** Default text template. Supports `${data.field}` interpolation against the walkerOS event. */\n text?: string;\n /** Default Block Kit blocks applied when no mapping override is set. */\n blocks?: SlackBlock[];\n /** Auto-add an event-name header block when generating default blocks. Default: true. */\n includeHeader?: boolean;\n\n // --- Behavior ---\n /** Enable link unfurling. Default: false. */\n unfurlLinks?: boolean;\n /** Enable media unfurling. Default: false. */\n unfurlMedia?: boolean;\n /** Use mrkdwn formatting. Default: true. */\n mrkdwn?: boolean;\n\n // --- Threading ---\n /** Static thread_ts to reply to (rarely set at destination level). */\n threadTs?: string;\n\n // --- SDK config (Web API only) ---\n /** Retry policy passed to WebClient. Default: 'default'. */\n retryConfig?: 'default' | 'fiveRetriesInFiveMinutes' | 'none';\n\n // --- Runtime (not user-facing) ---\n _client?: WebClient;\n}\n\nexport type InitSettings = Partial<Settings>;\n\n/**\n * Per-rule mapping settings. Override channel, text, blocks, and route\n * to threads / DMs / ephemeral.\n */\nexport interface Mapping {\n /** Override the channel for this rule (Web API mode only). */\n channel?: string;\n /** Override the text template for this rule. */\n text?: string;\n /** Override Block Kit blocks for this rule. */\n blocks?: SlackBlock[];\n /** thread_ts for posting as a reply in a thread. */\n threadTs?: string;\n /** Also broadcast the threaded reply back to the channel. */\n replyBroadcast?: boolean;\n /** Send via chat.postEphemeral instead of chat.postMessage. */\n ephemeral?: boolean;\n /** Slack user ID for ephemeral or DM delivery. */\n user?: string;\n /** Send as DM (conversations.open + chat.postMessage). Requires `user`. */\n dm?: boolean;\n}\n\n/**\n * Mock-friendly interface for the WebClient methods the destination calls.\n * Tests inject this via env.slackClient.\n */\nexport interface SlackClientMock {\n chat: {\n postMessage: (\n opts: ChatPostMessageArguments,\n ) => Promise<ChatPostMessageResponse>;\n postEphemeral: (\n opts: ChatPostEphemeralArguments,\n ) => Promise<ChatPostEphemeralResponse>;\n };\n conversations: {\n open: (\n opts: ConversationsOpenArguments,\n ) => Promise<ConversationsOpenResponse>;\n };\n}\n\n/**\n * Env -- optional SDK / transport overrides. Production leaves these undefined.\n * Tests inject `slackClient` (Web API mode) and/or `sendServer` (webhook mode).\n */\nexport interface Env extends DestinationServer.Env {\n slackClient?: SlackClientMock;\n sendServer?: typeof sendServer;\n}\n\nexport type Types = CoreDestination.Types<Settings, Mapping, Env, InitSettings>;\n\nexport interface Destination extends DestinationServer.Destination<Types> {\n init: DestinationServer.InitFn<Types>;\n}\n\nexport type Config = {\n settings: Settings;\n} & DestinationServer.Config<Types>;\n\nexport type InitFn = DestinationServer.InitFn<Types>;\nexport type PushFn = DestinationServer.PushFn<Types>;\nexport type PartialConfig = DestinationServer.PartialConfig<Types>;\nexport type PushEvents = DestinationServer.PushEvents<Mapping>;\nexport type Rule = WalkerOSMapping.Rule<Mapping>;\nexport type Rules = WalkerOSMapping.Rules<Rule>;\n"],"mappings":"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","names":["text"]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e=(e=>"undefined"!=typeof require?require:"undefined"!=typeof Proxy?new Proxy(e,{get:(e,t)=>("undefined"!=typeof require?require:e)[t]}):e)(function(e){if("undefined"!=typeof require)return require.apply(this,arguments);throw Error('Dynamic require of "'+e+'" is not supported')});import{isObject as t}from"@walkeros/core";import{sendServer as n}from"@walkeros/server-core";var r=/\$\{([^}]+)\}/g;function i(e,t){return e.replace(r,(e,n)=>{const r=function(e,t){return t.split(".").reduce((e,t)=>{if(e&&"object"==typeof e&&t in e)return e[t]},e)}(t,n.trim());return null==r?"":"object"==typeof r?JSON.stringify(r):String(r)})}function o(e,t){return JSON.parse(i(JSON.stringify(e),t))}function l(e,t){var n;const r=[];t&&r.push({type:"header",text:{type:"plain_text",text:e.name}});const i=e.data||{},o=Object.entries(i).filter(([,e])=>null!=e).map(([e,t])=>({type:"mrkdwn",text:`*${e}:*\n${s(t)}`}));o.length>0&&r.push({type:"section",fields:o});const l=null==(n=e.source)?void 0:n.id;return l&&r.push({type:"context",elements:[{type:"mrkdwn",text:`Source: ${l}`}]}),r}function s(e){return null==e?"":"object"==typeof e?JSON.stringify(e):String(e)}var a=async function(e,r){const{config:s,rule:a,env:u,logger:c}=r,d=s.settings,f=(null==a?void 0:a.settings)||{},k=function(e,t,n){var r,s;if(n.blocks&&n.blocks.length>0){const l={blocks:o(n.blocks,e)},s=null!=(r=n.text)?r:t.text;return s&&(l.text=i(s,e)),l}const a=null!=(s=n.text)?s:t.text;return a?{text:i(a,e)}:t.blocks&&t.blocks.length>0?{blocks:o(t.blocks,e)}:{text:e.name,blocks:l(e,!1!==t.includeHeader)}}(e,d,f);return d.webhookUrl?await async function(e,r,i,o){var l,s,a;if(!e.text&&!e.blocks)return void o.warn("Slack message has no text or blocks; skipping");const u={...e.text?{text:e.text}:{},...e.blocks?{blocks:e.blocks}:{},unfurl_links:null!=(l=r.unfurlLinks)&&l,unfurl_media:null!=(s=r.unfurlMedia)&&s,mrkdwn:null==(a=r.mrkdwn)||a},c=(null==i?void 0:i.sendServer)||n,d=await c(r.webhookUrl,JSON.stringify(u));t(d)&&!1===d.ok&&o.warn(`Slack webhook error: ${JSON.stringify(d)}`)}(k,d,u,c):await async function(e,t,n,r,i){var o,l,s,a,u,c,d;const f=(null==r?void 0:r.slackClient)||t._client;if(!f)return void i.warn("Slack WebClient not initialized");if(!e.text&&!e.blocks)return void i.warn("Slack message has no text or blocks; skipping");let k=null!=(o=n.channel)?o:t.channel;if(n.dm){if(!n.user)return void i.throw("Slack DM requires `mapping.settings.user`");const e=null==(l=(await f.conversations.open({users:n.user})).channel)?void 0:l.id;if(!e)return void i.warn("Slack conversations.open returned no channel id");k=e}if(!k)return void i.throw("Slack push requires a channel (set settings.channel or mapping.settings.channel)");const p={channel:k,...e.text?{text:e.text}:{},...e.blocks?{blocks:e.blocks}:{},unfurl_links:null!=(s=t.unfurlLinks)&&s,unfurl_media:null!=(a=t.unfurlMedia)&&a,mrkdwn:null==(u=t.mrkdwn)||u};(null!=(c=n.threadTs)?c:t.threadTs)&&(p.thread_ts=null!=(d=n.threadTs)?d:t.threadTs);n.replyBroadcast&&(p.reply_broadcast=!0);if(n.ephemeral)return n.user?void await f.chat.postEphemeral({...p,user:n.user}):void i.throw("Slack ephemeral requires `mapping.settings.user`");await f.chat.postMessage(p)}(k,d,f,u,c)};var u={},c={default:void 0,fiveRetriesInFiveMinutes:{retries:5,factor:1.96},none:{retries:0}},d={type:"slack",config:{},init({config:t,logger:n,env:r}){const i=function(e={},t){const n=e.settings||{},{token:r,webhookUrl:i,channel:o}=n;return r||i||t.throw("Slack destination requires either token or webhookUrl"),r&&i&&t.throw("Slack destination accepts either token or webhookUrl, not both"),r&&!o&&t.warn("Slack destination has no default channel; every mapping rule must provide one"),{...e,settings:{...n}}}(t,n),o=i.settings;if(o.webhookUrl)return i;if(null==r?void 0:r.slackClient)return i;try{const{WebClient:t}=e("@slack/web-api"),n=c[o.retryConfig||"default"];o._client=new t(o.token,{...void 0!==n?{retryConfig:n}:{}})}catch(e){n.throw(`Failed to initialize Slack WebClient: ${e}`)}return i},push:async(e,t)=>await a(e,t)},f=d;export{u as DestinationSlack,f as default,d as destinationSlack};//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/config.ts","../src/push.ts","../src/message.ts","../src/types/index.ts","../src/index.ts"],"sourcesContent":["import type { Logger } from '@walkeros/core';\nimport type { Config, PartialConfig, Settings } from './types';\n\nexport function getConfig(\n partialConfig: PartialConfig = {},\n logger: Logger.Instance,\n): Config {\n const settings = (partialConfig.settings || {}) as Partial<Settings>;\n const { token, webhookUrl, channel } = settings;\n\n if (!token && !webhookUrl)\n logger.throw('Slack destination requires either token or webhookUrl');\n if (token && webhookUrl)\n logger.throw(\n 'Slack destination accepts either token or webhookUrl, not both',\n );\n if (token && !channel)\n logger.warn(\n 'Slack destination has no default channel; every mapping rule must provide one',\n );\n\n return {\n ...partialConfig,\n settings: { ...settings } as Settings,\n };\n}\n","import { isObject } from '@walkeros/core';\nimport { sendServer } from '@walkeros/server-core';\nimport type { Mapping, PushFn, Settings, SlackClientMock } from './types';\nimport { buildMessage } from './message';\n\nexport const push: PushFn = async function (event, ctx) {\n const { config, rule, env, logger } = ctx;\n const settings = config.settings as Settings;\n const ruleSettings = (rule?.settings || {}) as Partial<Mapping>;\n\n const message = buildMessage(event, settings, ruleSettings);\n\n // Webhook mode\n if (settings.webhookUrl) {\n return await pushWebhook(message, settings, env, logger);\n }\n\n // Web API mode\n return await pushWebApi(message, settings, ruleSettings, env, logger);\n};\n\nasync function pushWebhook(\n message: { text?: string; blocks?: unknown[] },\n settings: Settings,\n env: { sendServer?: typeof sendServer } | undefined,\n logger: import('@walkeros/core').Logger.Instance,\n): Promise<void> {\n if (!message.text && !message.blocks) {\n logger.warn('Slack message has no text or blocks; skipping');\n return;\n }\n\n const body = {\n ...(message.text ? { text: message.text } : {}),\n ...(message.blocks ? { blocks: message.blocks } : {}),\n unfurl_links: settings.unfurlLinks ?? false,\n unfurl_media: settings.unfurlMedia ?? false,\n mrkdwn: settings.mrkdwn ?? true,\n };\n\n const send = env?.sendServer || sendServer;\n const result = await send(\n settings.webhookUrl as string,\n JSON.stringify(body),\n );\n\n if (isObject(result) && result.ok === false) {\n logger.warn(`Slack webhook error: ${JSON.stringify(result)}`);\n }\n}\n\nasync function pushWebApi(\n message: { text?: string; blocks?: unknown[] },\n settings: Settings,\n rule: Partial<Mapping>,\n env: { slackClient?: SlackClientMock } | undefined,\n logger: import('@walkeros/core').Logger.Instance,\n): Promise<void> {\n const client =\n (env?.slackClient as SlackClientMock | undefined) ||\n (settings._client as unknown as SlackClientMock | undefined);\n\n if (!client) {\n logger.warn('Slack WebClient not initialized');\n return;\n }\n\n if (!message.text && !message.blocks) {\n logger.warn('Slack message has no text or blocks; skipping');\n return;\n }\n\n // Resolve target channel\n let channel = rule.channel ?? settings.channel;\n\n // DM path: open DM channel first\n if (rule.dm) {\n if (!rule.user) {\n logger.throw('Slack DM requires `mapping.settings.user`');\n return;\n }\n const opened = await client.conversations.open({ users: rule.user });\n const dmChannel = (\n opened as unknown as {\n channel?: { id?: string };\n }\n ).channel?.id;\n if (!dmChannel) {\n logger.warn('Slack conversations.open returned no channel id');\n return;\n }\n channel = dmChannel;\n }\n\n if (!channel) {\n logger.throw(\n 'Slack push requires a channel (set settings.channel or mapping.settings.channel)',\n );\n return;\n }\n\n const baseArgs: Record<string, unknown> = {\n channel,\n ...(message.text ? { text: message.text } : {}),\n ...(message.blocks ? { blocks: message.blocks } : {}),\n unfurl_links: settings.unfurlLinks ?? false,\n unfurl_media: settings.unfurlMedia ?? false,\n mrkdwn: settings.mrkdwn ?? true,\n };\n\n if (rule.threadTs ?? settings.threadTs) {\n baseArgs.thread_ts = rule.threadTs ?? settings.threadTs;\n }\n if (rule.replyBroadcast) baseArgs.reply_broadcast = true;\n\n // Ephemeral path\n if (rule.ephemeral) {\n if (!rule.user) {\n logger.throw('Slack ephemeral requires `mapping.settings.user`');\n return;\n }\n await client.chat.postEphemeral({\n ...baseArgs,\n user: rule.user,\n } as never);\n return;\n }\n\n await client.chat.postMessage(baseArgs as never);\n}\n","import type { WalkerOS } from '@walkeros/core';\nimport type { Mapping, Settings, SlackBlock } from './types';\n\nexport interface BuiltMessage {\n text?: string;\n blocks?: SlackBlock[];\n}\n\nconst TOKEN_RE = /\\$\\{([^}]+)\\}/g;\n\nfunction getPath(obj: unknown, path: string): unknown {\n return path.split('.').reduce<unknown>((acc, key) => {\n if (acc && typeof acc === 'object' && key in (acc as object)) {\n return (acc as Record<string, unknown>)[key];\n }\n return undefined;\n }, obj);\n}\n\nexport function interpolate(template: string, event: WalkerOS.Event): string {\n return template.replace(TOKEN_RE, (_, path: string) => {\n const value = getPath(event, path.trim());\n if (value === undefined || value === null) return '';\n if (typeof value === 'object') return JSON.stringify(value);\n return String(value);\n });\n}\n\nexport function buildMessage(\n event: WalkerOS.Event,\n settings: Partial<Settings>,\n rule: Partial<Mapping>,\n): BuiltMessage {\n // 1. Custom blocks via rule\n if (rule.blocks && rule.blocks.length > 0) {\n const result: BuiltMessage = {\n blocks: interpolateBlocks(rule.blocks, event),\n };\n const text = rule.text ?? settings.text;\n if (text) result.text = interpolate(text, event);\n return result;\n }\n\n // 2. Custom text via rule (no blocks)\n const text = rule.text ?? settings.text;\n if (text) {\n return { text: interpolate(text, event) };\n }\n\n // 3. Destination-level blocks\n if (settings.blocks && settings.blocks.length > 0) {\n return { blocks: interpolateBlocks(settings.blocks, event) };\n }\n\n // 4. Auto-generated default blocks from event data\n return {\n text: event.name,\n blocks: defaultBlocks(event, settings.includeHeader !== false),\n };\n}\n\nfunction interpolateBlocks(\n blocks: SlackBlock[],\n event: WalkerOS.Event,\n): SlackBlock[] {\n return JSON.parse(interpolate(JSON.stringify(blocks), event)) as SlackBlock[];\n}\n\nfunction defaultBlocks(\n event: WalkerOS.Event,\n includeHeader: boolean,\n): SlackBlock[] {\n const blocks: SlackBlock[] = [];\n\n if (includeHeader) {\n blocks.push({\n type: 'header',\n text: { type: 'plain_text', text: event.name },\n });\n }\n\n const data = (event.data || {}) as Record<string, unknown>;\n const fields = Object.entries(data)\n .filter(([, v]) => v !== undefined && v !== null)\n .map(([key, value]) => ({\n type: 'mrkdwn' as const,\n text: `*${key}:*\\n${stringify(value)}`,\n }));\n\n if (fields.length > 0) {\n blocks.push({ type: 'section', fields });\n }\n\n const sourceId = event.source?.id;\n if (sourceId) {\n blocks.push({\n type: 'context',\n elements: [{ type: 'mrkdwn', text: `Source: ${sourceId}` }],\n });\n }\n\n return blocks;\n}\n\nfunction stringify(value: unknown): string {\n if (value === null || value === undefined) return '';\n if (typeof value === 'object') return JSON.stringify(value);\n return String(value);\n}\n","import type {\n Mapping as WalkerOSMapping,\n Destination as CoreDestination,\n} from '@walkeros/core';\nimport type { DestinationServer, sendServer } from '@walkeros/server-core';\nimport type {\n WebClient,\n ChatPostMessageArguments,\n ChatPostMessageResponse,\n ChatPostEphemeralArguments,\n ChatPostEphemeralResponse,\n ConversationsOpenArguments,\n ConversationsOpenResponse,\n} from '@slack/web-api';\n\n/** A single Block Kit block (kept loose -- Slack does not export a union type). */\nexport type SlackBlock = Record<string, unknown>;\n\nexport interface Settings {\n // --- Auth (exactly one required) ---\n /** Slack Bot token (xoxb-...). Enables Web API mode. */\n token?: string;\n /** Incoming Webhook URL. Enables webhook mode. */\n webhookUrl?: string;\n\n // --- Target ---\n /** Default channel ID or name. Required for Web API mode unless every rule provides one. */\n channel?: string;\n\n // --- Message formatting ---\n /** Default text template. Supports `${data.field}` interpolation against the walkerOS event. */\n text?: string;\n /** Default Block Kit blocks applied when no mapping override is set. */\n blocks?: SlackBlock[];\n /** Auto-add an event-name header block when generating default blocks. Default: true. */\n includeHeader?: boolean;\n\n // --- Behavior ---\n /** Enable link unfurling. Default: false. */\n unfurlLinks?: boolean;\n /** Enable media unfurling. Default: false. */\n unfurlMedia?: boolean;\n /** Use mrkdwn formatting. Default: true. */\n mrkdwn?: boolean;\n\n // --- Threading ---\n /** Static thread_ts to reply to (rarely set at destination level). */\n threadTs?: string;\n\n // --- SDK config (Web API only) ---\n /** Retry policy passed to WebClient. Default: 'default'. */\n retryConfig?: 'default' | 'fiveRetriesInFiveMinutes' | 'none';\n\n // --- Runtime (not user-facing) ---\n _client?: WebClient;\n}\n\nexport type InitSettings = Partial<Settings>;\n\n/**\n * Per-rule mapping settings. Override channel, text, blocks, and route\n * to threads / DMs / ephemeral.\n */\nexport interface Mapping {\n /** Override the channel for this rule (Web API mode only). */\n channel?: string;\n /** Override the text template for this rule. */\n text?: string;\n /** Override Block Kit blocks for this rule. */\n blocks?: SlackBlock[];\n /** thread_ts for posting as a reply in a thread. */\n threadTs?: string;\n /** Also broadcast the threaded reply back to the channel. */\n replyBroadcast?: boolean;\n /** Send via chat.postEphemeral instead of chat.postMessage. */\n ephemeral?: boolean;\n /** Slack user ID for ephemeral or DM delivery. */\n user?: string;\n /** Send as DM (conversations.open + chat.postMessage). Requires `user`. */\n dm?: boolean;\n}\n\n/**\n * Mock-friendly interface for the WebClient methods the destination calls.\n * Tests inject this via env.slackClient.\n */\nexport interface SlackClientMock {\n chat: {\n postMessage: (\n opts: ChatPostMessageArguments,\n ) => Promise<ChatPostMessageResponse>;\n postEphemeral: (\n opts: ChatPostEphemeralArguments,\n ) => Promise<ChatPostEphemeralResponse>;\n };\n conversations: {\n open: (\n opts: ConversationsOpenArguments,\n ) => Promise<ConversationsOpenResponse>;\n };\n}\n\n/**\n * Env -- optional SDK / transport overrides. Production leaves these undefined.\n * Tests inject `slackClient` (Web API mode) and/or `sendServer` (webhook mode).\n */\nexport interface Env extends DestinationServer.Env {\n slackClient?: SlackClientMock;\n sendServer?: typeof sendServer;\n}\n\nexport type Types = CoreDestination.Types<Settings, Mapping, Env, InitSettings>;\n\nexport interface Destination extends DestinationServer.Destination<Types> {\n init: DestinationServer.InitFn<Types>;\n}\n\nexport type Config = {\n settings: Settings;\n} & DestinationServer.Config<Types>;\n\nexport type InitFn = DestinationServer.InitFn<Types>;\nexport type PushFn = DestinationServer.PushFn<Types>;\nexport type PartialConfig = DestinationServer.PartialConfig<Types>;\nexport type PushEvents = DestinationServer.PushEvents<Mapping>;\nexport type Rule = WalkerOSMapping.Rule<Mapping>;\nexport type Rules = WalkerOSMapping.Rules<Rule>;\n","import type { Destination, Settings, SlackClientMock } from './types';\nimport { getConfig } from './config';\nimport { push } from './push';\n\n// Types\nexport * as DestinationSlack from './types';\n\nconst RETRY_PRESETS: Record<string, unknown> = {\n default: undefined,\n fiveRetriesInFiveMinutes: { retries: 5, factor: 1.96 },\n none: { retries: 0 },\n};\n\nexport const destinationSlack: Destination = {\n type: 'slack',\n\n config: {},\n\n init({ config: partialConfig, logger, env }) {\n const config = getConfig(partialConfig, logger);\n const settings = config.settings as Settings;\n\n // Webhook mode -- nothing to initialize.\n if (settings.webhookUrl) return config;\n\n // Web API mode -- prefer mock client from env (testing).\n const envClient = (env as { slackClient?: SlackClientMock } | undefined)\n ?.slackClient;\n if (envClient) return config;\n\n // Production path: lazily load @slack/web-api so webhook-only users\n // do not pay for the SDK import cost.\n try {\n const { WebClient } = require('@slack/web-api');\n const retryConfig = RETRY_PRESETS[settings.retryConfig || 'default'];\n settings._client = new WebClient(settings.token, {\n ...(retryConfig !== undefined ? { retryConfig } : {}),\n });\n } catch (err) {\n logger.throw(`Failed to initialize Slack WebClient: ${err}`);\n }\n\n return config;\n },\n\n async push(event, context) {\n return await push(event, context);\n },\n};\n\nexport default destinationSlack;\n"],"mappings":"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","names":["text"]}
|