@walkeros/server-destination-slack 3.4.2 → 4.0.0-next-1777463920154

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/dev.d.mts CHANGED
@@ -114,12 +114,12 @@ type SlackStepExample = Flow.StepExample & {
114
114
  /**
115
115
  * Slack server destination operates in two modes:
116
116
  *
117
- * 1. Web API mode calls the injected `env.slackClient` SDK methods
117
+ * 1. Web API mode - calls the injected `env.slackClient` SDK methods
118
118
  * (`chat.postMessage`, `chat.postEphemeral`, `conversations.open`).
119
119
  * Captured as `[callable, args]` with dotted callable names such as
120
120
  * `'slackClient.chat.postMessage'`.
121
121
  *
122
- * 2. Webhook mode calls `env.sendServer(url, JSON.stringify(body))`.
122
+ * 2. Webhook mode - calls `env.sendServer(url, JSON.stringify(body))`.
123
123
  * Captured as `['sendServer', url, bodyAsString]` where `bodyAsString`
124
124
  * is the already-stringified JSON body. Key insertion order in the
125
125
  * source object matters for string equality.
package/dist/dev.d.ts CHANGED
@@ -114,12 +114,12 @@ type SlackStepExample = Flow.StepExample & {
114
114
  /**
115
115
  * Slack server destination operates in two modes:
116
116
  *
117
- * 1. Web API mode calls the injected `env.slackClient` SDK methods
117
+ * 1. Web API mode - calls the injected `env.slackClient` SDK methods
118
118
  * (`chat.postMessage`, `chat.postEphemeral`, `conversations.open`).
119
119
  * Captured as `[callable, args]` with dotted callable names such as
120
120
  * `'slackClient.chat.postMessage'`.
121
121
  *
122
- * 2. Webhook mode calls `env.sendServer(url, JSON.stringify(body))`.
122
+ * 2. Webhook mode - calls `env.sendServer(url, JSON.stringify(body))`.
123
123
  * Captured as `['sendServer', url, bodyAsString]` where `bodyAsString`
124
124
  * is the already-stringified JSON body. Key insertion order in the
125
125
  * source object matters for string equality.
package/dist/dev.js CHANGED
@@ -1 +1 @@
1
- "use strict";var e,t=Object.defineProperty,a=Object.getOwnPropertyDescriptor,n=Object.getOwnPropertyNames,r=Object.prototype.hasOwnProperty,o=(e,a)=>{for(var n in a)t(e,n,{get:a[n],enumerable:!0})},s={};o(s,{examples:()=>g,schemas:()=>i}),module.exports=(e=s,((e,o,s,i)=>{if(o&&"object"==typeof o||"function"==typeof o)for(let l of n(o))r.call(e,l)||l===s||t(e,l,{get:()=>o[l],enumerable:!(i=a(o,l))||i.enumerable});return e})(t({},"__esModule",{value:!0}),e));var i={};o(i,{MappingSchema:()=>u,SettingsSchema:()=>d,mapping:()=>h,settings:()=>m});var l=require("@walkeros/core/dev"),c=require("@walkeros/core/dev"),d=c.z.object({token:c.z.string().startsWith("xoxb-").describe("Slack Bot token (xoxb-...). Enables Web API mode. Mutually exclusive with webhookUrl.").optional(),webhookUrl:c.z.string().url().describe("Incoming Webhook URL. Enables webhook mode. Mutually exclusive with token.").optional(),channel:c.z.string().describe('Default Slack channel ID or name (e.g. "#alerts" or "C024BE91L"). Required for Web API mode unless every rule supplies one. Ignored in webhook mode.').optional(),text:c.z.string().describe("Default text template. Supports `${data.field}` interpolation against the walkerOS event.").optional(),blocks:c.z.array(c.z.record(c.z.string(),c.z.unknown())).describe("Default Block Kit blocks applied when no mapping override is set.").optional(),includeHeader:c.z.boolean().describe("Auto-add an event-name header block when generating default blocks. Default: true.").optional(),unfurlLinks:c.z.boolean().describe("Enable link unfurling. Default: false (cleaner for automated alerts).").optional(),unfurlMedia:c.z.boolean().describe("Enable media unfurling. Default: false.").optional(),mrkdwn:c.z.boolean().describe("Use mrkdwn formatting in text. Default: true.").optional(),threadTs:c.z.string().describe("Static thread_ts for replies (rarely set at destination level).").optional(),retryConfig:c.z.enum(["default","fiveRetriesInFiveMinutes","none"]).describe('Retry policy passed to WebClient. Default: "default".').optional()}).refine(e=>Boolean(e.token)!==Boolean(e.webhookUrl),{message:"Provide exactly one of `token` or `webhookUrl`."}),p=require("@walkeros/core/dev"),u=p.z.object({channel:p.z.string().describe("Override the destination channel for this rule. Web API mode only -- ignored in webhook mode.").optional(),text:p.z.string().describe("Override the text template for this rule. Supports `${data.field}` interpolation.").optional(),blocks:p.z.array(p.z.record(p.z.string(),p.z.unknown())).describe("Override Block Kit blocks for this rule.").optional(),threadTs:p.z.string().describe("thread_ts for posting as a reply in a thread.").optional(),replyBroadcast:p.z.boolean().describe("Also broadcast the threaded reply back to the channel.").optional(),ephemeral:p.z.boolean().describe("Send via chat.postEphemeral. Requires `user`.").optional(),user:p.z.string().describe("Slack user ID for ephemeral or DM delivery.").optional(),dm:p.z.boolean().describe("Send as DM via conversations.open + chat.postMessage. Requires `user`.").optional()}),m=(0,l.zodToSchema)(d),h=(0,l.zodToSchema)(u),g={};o(g,{env:()=>k,step:()=>x});var k={};o(k,{push:()=>y,simulation:()=>w});var b={ok:!0,channel:"CMOCK",ts:"1700000000.000100"},v={ok:!0},f={ok:!0,channel:{id:"D-MOCK-DM"}};var y={slackClient:{chat:{postMessage:()=>Promise.resolve(b),postEphemeral:()=>Promise.resolve(v)},conversations:{open:()=>Promise.resolve(f)}},sendServer:()=>Promise.resolve({ok:!0})},w=["call:slackClient.chat.postMessage","call:slackClient.chat.postEphemeral","call:slackClient.conversations.open","call:sendServer"],x={};o(x,{defaultBlocks:()=>C,deployNotification:()=>P,ephemeralMessage:()=>_,errorAlert:()=>M,purchaseAlert:()=>E,threadedCheckoutStep:()=>D,welcomeDM:()=>S});var z=require("@walkeros/core"),E={title:"Purchase alert",description:"A completed order posts a templated message to a sales channel via the Slack Web API.",in:(0,z.getEvent)("order complete",{timestamp:1700000100,data:{id:"ORD-500",total:299.99,currency:"EUR",product:"Pro Plan"},user:{id:"buyer-42"}}),mapping:{settings:{channel:"#sales",text:":moneybag: New order: ${data.id} - ${data.total} ${data.currency}"}},out:[["slackClient.chat.postMessage",{channel:"#sales",text:":moneybag: New order: ORD-500 - 299.99 EUR",unfurl_links:!1,unfurl_media:!1,mrkdwn:!0}]]},M={title:"Error alert",description:"An error event posts a critical alert to an engineering channel using a mapping-level channel override.",in:(0,z.getEvent)("error occur",{timestamp:1700000200,data:{message:"Payment gateway timeout",code:"PGW_TIMEOUT",severity:"critical"}}),mapping:{settings:{channel:"#engineering-alerts",text:":rotating_light: Error: ${data.message}"}},out:[["slackClient.chat.postMessage",{channel:"#engineering-alerts",text:":rotating_light: Error: Payment gateway timeout",unfurl_links:!1,unfurl_media:!1,mrkdwn:!0}]]},S={title:"Welcome DM",description:"A user signup opens a Slack DM channel and posts a welcome message with the selected plan.",in:(0,z.getEvent)("user signup",{timestamp:1700000300,data:{plan:"enterprise"},user:{id:"U-NEW-USER"}}),mapping:{settings:{dm:!0,user:"U-NEW-USER",text:":wave: Welcome aboard! You signed up for the ${data.plan} plan."}},out:[["slackClient.conversations.open",{users:"U-NEW-USER"}],["slackClient.chat.postMessage",{channel:"D-MOCK-DM",text:":wave: Welcome aboard! You signed up for the enterprise plan.",unfurl_links:!1,unfurl_media:!1,mrkdwn:!0}]]},D={title:"Threaded reply",description:"A checkout step posts as a threaded reply in Slack via thread_ts with broadcast to the channel.",in:(0,z.getEvent)("checkout step",{timestamp:1700000400,data:{step:"payment",sessionTs:"1700000000.000050"}}),mapping:{settings:{channel:"#sales",text:"Checkout step: ${data.step}",threadTs:"1700000000.000050",replyBroadcast:!0}},out:[["slackClient.chat.postMessage",{channel:"#sales",text:"Checkout step: payment",unfurl_links:!1,unfurl_media:!1,mrkdwn:!0,thread_ts:"1700000000.000050",reply_broadcast:!0}]]},_={title:"Ephemeral message",description:"A quota warning posts an ephemeral Slack message visible only to a target admin user.",in:(0,z.getEvent)("quota warning",{timestamp:1700000500,data:{remaining:5}}),mapping:{settings:{channel:"#admin",ephemeral:!0,user:"U-ADMIN-1",text:"Heads up: ${data.remaining} requests remaining"}},out:[["slackClient.chat.postEphemeral",{channel:"#admin",text:"Heads up: 5 requests remaining",unfurl_links:!1,unfurl_media:!1,mrkdwn:!0,user:"U-ADMIN-1"}]]},C={title:"Default blocks",description:"With no custom text the destination auto-generates a Block Kit message from event data and source.",in:(0,z.getEvent)("lead submit",{timestamp:1700000600,data:{name:"Acme",email:"sales@acme.test"},source:{type:"server",id:"crm",previous_id:""}}),mapping:{settings:{channel:"#growth"}},out:[["slackClient.chat.postMessage",{channel:"#growth",text:"lead submit",blocks:[{type:"header",text:{type:"plain_text",text:"lead submit"}},{type:"section",fields:[{type:"mrkdwn",text:"*name:*\nAcme"},{type:"mrkdwn",text:"*email:*\nsales@acme.test"}]},{type:"context",elements:[{type:"mrkdwn",text:"Source: crm"}]}],unfurl_links:!1,unfurl_media:!1,mrkdwn:!0}]]},P={title:"Webhook deploy",description:"Without a token the destination posts to an incoming Slack webhook URL with the rendered message body.",in:(0,z.getEvent)("deploy complete",{timestamp:1700000700,data:{version:"1.4.2",environment:"prod"}}),settings:{token:void 0,webhookUrl:"https://hooks.slack.com/services/T00/B00/xxx"},mapping:{settings:{text:":rocket: Deployment complete: ${data.version} to ${data.environment}"}},out:[["sendServer","https://hooks.slack.com/services/T00/B00/xxx",JSON.stringify({text:":rocket: Deployment complete: 1.4.2 to prod",unfurl_links:!1,unfurl_media:!1,mrkdwn:!0})]]};//# sourceMappingURL=dev.js.map
1
+ "use strict";var e,t=Object.defineProperty,a=Object.getOwnPropertyDescriptor,n=Object.getOwnPropertyNames,r=Object.prototype.hasOwnProperty,o=(e,a)=>{for(var n in a)t(e,n,{get:a[n],enumerable:!0})},s={};o(s,{examples:()=>g,schemas:()=>i}),module.exports=(e=s,((e,o,s,i)=>{if(o&&"object"==typeof o||"function"==typeof o)for(let l of n(o))r.call(e,l)||l===s||t(e,l,{get:()=>o[l],enumerable:!(i=a(o,l))||i.enumerable});return e})(t({},"__esModule",{value:!0}),e));var i={};o(i,{MappingSchema:()=>u,SettingsSchema:()=>d,mapping:()=>h,settings:()=>m});var l=require("@walkeros/core/dev"),c=require("@walkeros/core/dev"),d=c.z.object({token:c.z.string().startsWith("xoxb-").describe("Slack Bot token (xoxb-...). Enables Web API mode. Mutually exclusive with webhookUrl.").optional(),webhookUrl:c.z.string().url().describe("Incoming Webhook URL. Enables webhook mode. Mutually exclusive with token.").optional(),channel:c.z.string().describe('Default Slack channel ID or name (e.g. "#alerts" or "C024BE91L"). Required for Web API mode unless every rule supplies one. Ignored in webhook mode.').optional(),text:c.z.string().describe("Default text template. Supports `${data.field}` interpolation against the walkerOS event.").optional(),blocks:c.z.array(c.z.record(c.z.string(),c.z.unknown())).describe("Default Block Kit blocks applied when no mapping override is set.").optional(),includeHeader:c.z.boolean().describe("Auto-add an event-name header block when generating default blocks. Default: true.").optional(),unfurlLinks:c.z.boolean().describe("Enable link unfurling. Default: false (cleaner for automated alerts).").optional(),unfurlMedia:c.z.boolean().describe("Enable media unfurling. Default: false.").optional(),mrkdwn:c.z.boolean().describe("Use mrkdwn formatting in text. Default: true.").optional(),threadTs:c.z.string().describe("Static thread_ts for replies (rarely set at destination level).").optional(),retryConfig:c.z.enum(["default","fiveRetriesInFiveMinutes","none"]).describe('Retry policy passed to WebClient. Default: "default".').optional()}).refine(e=>Boolean(e.token)!==Boolean(e.webhookUrl),{message:"Provide exactly one of `token` or `webhookUrl`."}),p=require("@walkeros/core/dev"),u=p.z.object({channel:p.z.string().describe("Override the destination channel for this rule. Web API mode only -- ignored in webhook mode.").optional(),text:p.z.string().describe("Override the text template for this rule. Supports `${data.field}` interpolation.").optional(),blocks:p.z.array(p.z.record(p.z.string(),p.z.unknown())).describe("Override Block Kit blocks for this rule.").optional(),threadTs:p.z.string().describe("thread_ts for posting as a reply in a thread.").optional(),replyBroadcast:p.z.boolean().describe("Also broadcast the threaded reply back to the channel.").optional(),ephemeral:p.z.boolean().describe("Send via chat.postEphemeral. Requires `user`.").optional(),user:p.z.string().describe("Slack user ID for ephemeral or DM delivery.").optional(),dm:p.z.boolean().describe("Send as DM via conversations.open + chat.postMessage. Requires `user`.").optional()}),m=(0,l.zodToSchema)(d),h=(0,l.zodToSchema)(u),g={};o(g,{env:()=>k,step:()=>x});var k={};o(k,{push:()=>y,simulation:()=>w});var b={ok:!0,channel:"CMOCK",ts:"1700000000.000100"},v={ok:!0},f={ok:!0,channel:{id:"D-MOCK-DM"}};var y={slackClient:{chat:{postMessage:()=>Promise.resolve(b),postEphemeral:()=>Promise.resolve(v)},conversations:{open:()=>Promise.resolve(f)}},sendServer:()=>Promise.resolve({ok:!0})},w=["call:slackClient.chat.postMessage","call:slackClient.chat.postEphemeral","call:slackClient.conversations.open","call:sendServer"],x={};o(x,{defaultBlocks:()=>C,deployNotification:()=>P,ephemeralMessage:()=>_,errorAlert:()=>M,purchaseAlert:()=>E,threadedCheckoutStep:()=>D,welcomeDM:()=>S});var z=require("@walkeros/core"),E={title:"Purchase alert",description:"A completed order posts a templated message to a sales channel via the Slack Web API.",in:(0,z.getEvent)("order complete",{timestamp:1700000100,data:{id:"ORD-500",total:299.99,currency:"EUR",product:"Pro Plan"},user:{id:"buyer-42"}}),mapping:{settings:{channel:"#sales",text:":moneybag: New order: ${data.id} - ${data.total} ${data.currency}"}},out:[["slackClient.chat.postMessage",{channel:"#sales",text:":moneybag: New order: ORD-500 - 299.99 EUR",unfurl_links:!1,unfurl_media:!1,mrkdwn:!0}]]},M={title:"Error alert",description:"An error event posts a critical alert to an engineering channel using a mapping-level channel override.",in:(0,z.getEvent)("error occur",{timestamp:1700000200,data:{message:"Payment gateway timeout",code:"PGW_TIMEOUT",severity:"critical"}}),mapping:{settings:{channel:"#engineering-alerts",text:":rotating_light: Error: ${data.message}"}},out:[["slackClient.chat.postMessage",{channel:"#engineering-alerts",text:":rotating_light: Error: Payment gateway timeout",unfurl_links:!1,unfurl_media:!1,mrkdwn:!0}]]},S={title:"Welcome DM",description:"A user signup opens a Slack DM channel and posts a welcome message with the selected plan.",in:(0,z.getEvent)("user signup",{timestamp:1700000300,data:{plan:"enterprise"},user:{id:"U-NEW-USER"}}),mapping:{settings:{dm:!0,user:"U-NEW-USER",text:":wave: Welcome aboard! You signed up for the ${data.plan} plan."}},out:[["slackClient.conversations.open",{users:"U-NEW-USER"}],["slackClient.chat.postMessage",{channel:"D-MOCK-DM",text:":wave: Welcome aboard! You signed up for the enterprise plan.",unfurl_links:!1,unfurl_media:!1,mrkdwn:!0}]]},D={title:"Threaded reply",description:"A checkout step posts as a threaded reply in Slack via thread_ts with broadcast to the channel.",in:(0,z.getEvent)("checkout step",{timestamp:1700000400,data:{step:"payment",sessionTs:"1700000000.000050"}}),mapping:{settings:{channel:"#sales",text:"Checkout step: ${data.step}",threadTs:"1700000000.000050",replyBroadcast:!0}},out:[["slackClient.chat.postMessage",{channel:"#sales",text:"Checkout step: payment",unfurl_links:!1,unfurl_media:!1,mrkdwn:!0,thread_ts:"1700000000.000050",reply_broadcast:!0}]]},_={title:"Ephemeral message",description:"A quota warning posts an ephemeral Slack message visible only to a target admin user.",in:(0,z.getEvent)("quota warning",{timestamp:1700000500,data:{remaining:5}}),mapping:{settings:{channel:"#admin",ephemeral:!0,user:"U-ADMIN-1",text:"Heads up: ${data.remaining} requests remaining"}},out:[["slackClient.chat.postEphemeral",{channel:"#admin",text:"Heads up: 5 requests remaining",unfurl_links:!1,unfurl_media:!1,mrkdwn:!0,user:"U-ADMIN-1"}]]},C={title:"Default blocks",description:"With no custom text the destination auto-generates a Block Kit message from event data and source.",in:(0,z.getEvent)("lead submit",{timestamp:1700000600,data:{name:"Acme",email:"sales@acme.test"},source:{type:"crm",platform:"server"}}),mapping:{settings:{channel:"#growth"}},out:[["slackClient.chat.postMessage",{channel:"#growth",text:"lead submit",blocks:[{type:"header",text:{type:"plain_text",text:"lead submit"}},{type:"section",fields:[{type:"mrkdwn",text:"*name:*\nAcme"},{type:"mrkdwn",text:"*email:*\nsales@acme.test"}]},{type:"context",elements:[{type:"mrkdwn",text:"Source: crm"}]}],unfurl_links:!1,unfurl_media:!1,mrkdwn:!0}]]},P={title:"Webhook deploy",description:"Without a token the destination posts to an incoming Slack webhook URL with the rendered message body.",in:(0,z.getEvent)("deploy complete",{timestamp:1700000700,data:{version:"1.4.2",environment:"prod"}}),settings:{token:void 0,webhookUrl:"https://hooks.slack.com/services/T00/B00/xxx"},mapping:{settings:{text:":rocket: Deployment complete: ${data.version} to ${data.environment}"}},out:[["sendServer","https://hooks.slack.com/services/T00/B00/xxx",JSON.stringify({text:":rocket: Deployment complete: 1.4.2 to prod",unfurl_links:!1,unfurl_media:!1,mrkdwn:!0})]]};//# sourceMappingURL=dev.js.map
package/dist/dev.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/dev.ts","../src/schemas/index.ts","../src/schemas/settings.ts","../src/schemas/mapping.ts","../src/examples/index.ts","../src/examples/env.ts","../src/examples/step.ts"],"sourcesContent":["export * as schemas from './schemas';\nexport * as examples from './examples';\n","import { zodToSchema } from '@walkeros/core/dev';\nimport { SettingsSchema } from './settings';\nimport { MappingSchema } from './mapping';\n\nexport { SettingsSchema, type Settings } from './settings';\nexport { MappingSchema, type Mapping } from './mapping';\n\n// JSON Schema\nexport const settings = zodToSchema(SettingsSchema);\nexport const mapping = zodToSchema(MappingSchema);\n","import { z } from '@walkeros/core/dev';\n\nexport const SettingsSchema = z\n .object({\n token: z\n .string()\n .startsWith('xoxb-')\n .describe(\n 'Slack Bot token (xoxb-...). Enables Web API mode. Mutually exclusive with webhookUrl.',\n )\n .optional(),\n webhookUrl: z\n .string()\n .url()\n .describe(\n 'Incoming Webhook URL. Enables webhook mode. Mutually exclusive with token.',\n )\n .optional(),\n channel: z\n .string()\n .describe(\n 'Default Slack channel ID or name (e.g. \"#alerts\" or \"C024BE91L\"). Required for Web API mode unless every rule supplies one. Ignored in webhook mode.',\n )\n .optional(),\n text: z\n .string()\n .describe(\n 'Default text template. Supports `${data.field}` interpolation against the walkerOS event.',\n )\n .optional(),\n blocks: z\n .array(z.record(z.string(), z.unknown()))\n .describe(\n 'Default Block Kit blocks applied when no mapping override is set.',\n )\n .optional(),\n includeHeader: z\n .boolean()\n .describe(\n 'Auto-add an event-name header block when generating default blocks. Default: true.',\n )\n .optional(),\n unfurlLinks: z\n .boolean()\n .describe(\n 'Enable link unfurling. Default: false (cleaner for automated alerts).',\n )\n .optional(),\n unfurlMedia: z\n .boolean()\n .describe('Enable media unfurling. Default: false.')\n .optional(),\n mrkdwn: z\n .boolean()\n .describe('Use mrkdwn formatting in text. Default: true.')\n .optional(),\n threadTs: z\n .string()\n .describe(\n 'Static thread_ts for replies (rarely set at destination level).',\n )\n .optional(),\n retryConfig: z\n .enum(['default', 'fiveRetriesInFiveMinutes', 'none'])\n .describe('Retry policy passed to WebClient. Default: \"default\".')\n .optional(),\n })\n .refine((v) => Boolean(v.token) !== Boolean(v.webhookUrl), {\n message: 'Provide exactly one of `token` or `webhookUrl`.',\n });\n\nexport type Settings = z.infer<typeof SettingsSchema>;\n","import { z } from '@walkeros/core/dev';\n\nexport const MappingSchema = z.object({\n channel: z\n .string()\n .describe(\n 'Override the destination channel for this rule. Web API mode only -- ignored in webhook mode.',\n )\n .optional(),\n text: z\n .string()\n .describe(\n 'Override the text template for this rule. Supports `${data.field}` interpolation.',\n )\n .optional(),\n blocks: z\n .array(z.record(z.string(), z.unknown()))\n .describe('Override Block Kit blocks for this rule.')\n .optional(),\n threadTs: z\n .string()\n .describe('thread_ts for posting as a reply in a thread.')\n .optional(),\n replyBroadcast: z\n .boolean()\n .describe('Also broadcast the threaded reply back to the channel.')\n .optional(),\n ephemeral: z\n .boolean()\n .describe('Send via chat.postEphemeral. Requires `user`.')\n .optional(),\n user: z\n .string()\n .describe('Slack user ID for ephemeral or DM delivery.')\n .optional(),\n dm: z\n .boolean()\n .describe(\n 'Send as DM via conversations.open + chat.postMessage. Requires `user`.',\n )\n .optional(),\n});\n\nexport type Mapping = z.infer<typeof MappingSchema>;\n","export * as env from './env';\nexport * as step from './step';\n","import type { Env, SlackClientMock } from '../types';\n\nconst okMessageResponse = {\n ok: true as const,\n channel: 'CMOCK',\n ts: '1700000000.000100',\n};\n\nconst okEphemeralResponse = { ok: true as const };\n\nconst okOpenResponse = {\n ok: true as const,\n channel: { id: 'D-MOCK-DM' },\n};\n\nfunction createMockClient(): SlackClientMock {\n return {\n chat: {\n postMessage: () => Promise.resolve(okMessageResponse as never),\n postEphemeral: () => Promise.resolve(okEphemeralResponse as never),\n },\n conversations: {\n open: () => Promise.resolve(okOpenResponse as never),\n },\n };\n}\n\nexport const push: Env = {\n slackClient: createMockClient(),\n sendServer: (() =>\n Promise.resolve({ ok: true } as never)) as Env['sendServer'],\n};\n\nexport const simulation = [\n 'call:slackClient.chat.postMessage',\n 'call:slackClient.chat.postEphemeral',\n 'call:slackClient.conversations.open',\n 'call:sendServer',\n];\n","import type { Flow } from '@walkeros/core';\nimport { getEvent } from '@walkeros/core';\nimport type { Settings } from '../types';\n\n/**\n * Extended step example that may carry destination-level settings overrides.\n */\nexport type SlackStepExample = Flow.StepExample & {\n settings?: Partial<Settings>;\n};\n\n/**\n * Slack server destination operates in two modes:\n *\n * 1. Web API mode — calls the injected `env.slackClient` SDK methods\n * (`chat.postMessage`, `chat.postEphemeral`, `conversations.open`).\n * Captured as `[callable, args]` with dotted callable names such as\n * `'slackClient.chat.postMessage'`.\n *\n * 2. Webhook mode — calls `env.sendServer(url, JSON.stringify(body))`.\n * Captured as `['sendServer', url, bodyAsString]` where `bodyAsString`\n * is the already-stringified JSON body. Key insertion order in the\n * source object matters for string equality.\n *\n * A single push may emit multiple calls (e.g. a DM opens a conversation\n * then posts to the returned channel id), so every `out` is wrapped as\n * `[[callable, ...args], ...]`.\n */\n\n/**\n * Purchase notification -- Web API mode, channel from mapping override,\n * text template interpolated against event.data.\n */\nexport const purchaseAlert: SlackStepExample = {\n title: 'Purchase alert',\n description:\n 'A completed order posts a templated message to a sales channel via the Slack Web API.',\n in: getEvent('order complete', {\n timestamp: 1700000100,\n data: {\n id: 'ORD-500',\n total: 299.99,\n currency: 'EUR',\n product: 'Pro Plan',\n },\n user: { id: 'buyer-42' },\n }),\n mapping: {\n settings: {\n channel: '#sales',\n text: ':moneybag: New order: ${data.id} - ${data.total} ${data.currency}',\n },\n },\n out: [\n [\n 'slackClient.chat.postMessage',\n {\n channel: '#sales',\n text: ':moneybag: New order: ORD-500 - 299.99 EUR',\n unfurl_links: false,\n unfurl_media: false,\n mrkdwn: true,\n },\n ],\n ],\n};\n\n/**\n * Error alert -- routes to a different channel via mapping override.\n */\nexport const errorAlert: SlackStepExample = {\n title: 'Error alert',\n description:\n 'An error event posts a critical alert to an engineering channel using a mapping-level channel override.',\n in: getEvent('error occur', {\n timestamp: 1700000200,\n data: {\n message: 'Payment gateway timeout',\n code: 'PGW_TIMEOUT',\n severity: 'critical',\n },\n }),\n mapping: {\n settings: {\n channel: '#engineering-alerts',\n text: ':rotating_light: Error: ${data.message}',\n },\n },\n out: [\n [\n 'slackClient.chat.postMessage',\n {\n channel: '#engineering-alerts',\n text: ':rotating_light: Error: Payment gateway timeout',\n unfurl_links: false,\n unfurl_media: false,\n mrkdwn: true,\n },\n ],\n ],\n};\n\n/**\n * Welcome DM -- conversations.open(users) -> chat.postMessage(channel: D-id).\n */\nexport const welcomeDM: SlackStepExample = {\n title: 'Welcome DM',\n description:\n 'A user signup opens a Slack DM channel and posts a welcome message with the selected plan.',\n in: getEvent('user signup', {\n timestamp: 1700000300,\n data: { plan: 'enterprise' },\n user: { id: 'U-NEW-USER' },\n }),\n mapping: {\n settings: {\n dm: true,\n user: 'U-NEW-USER',\n text: ':wave: Welcome aboard! You signed up for the ${data.plan} plan.',\n },\n },\n out: [\n ['slackClient.conversations.open', { users: 'U-NEW-USER' }],\n [\n 'slackClient.chat.postMessage',\n {\n channel: 'D-MOCK-DM',\n text: ':wave: Welcome aboard! You signed up for the enterprise plan.',\n unfurl_links: false,\n unfurl_media: false,\n mrkdwn: true,\n },\n ],\n ],\n};\n\n/**\n * Threaded checkout step -- thread_ts override puts the reply into a thread.\n */\nexport const threadedCheckoutStep: SlackStepExample = {\n title: 'Threaded reply',\n description:\n 'A checkout step posts as a threaded reply in Slack via thread_ts with broadcast to the channel.',\n in: getEvent('checkout step', {\n timestamp: 1700000400,\n data: { step: 'payment', sessionTs: '1700000000.000050' },\n }),\n mapping: {\n settings: {\n channel: '#sales',\n text: 'Checkout step: ${data.step}',\n threadTs: '1700000000.000050',\n replyBroadcast: true,\n },\n },\n out: [\n [\n 'slackClient.chat.postMessage',\n {\n channel: '#sales',\n text: 'Checkout step: payment',\n unfurl_links: false,\n unfurl_media: false,\n mrkdwn: true,\n thread_ts: '1700000000.000050',\n reply_broadcast: true,\n },\n ],\n ],\n};\n\n/**\n * Ephemeral message -- visible to one user in the target channel.\n */\nexport const ephemeralMessage: SlackStepExample = {\n title: 'Ephemeral message',\n description:\n 'A quota warning posts an ephemeral Slack message visible only to a target admin user.',\n in: getEvent('quota warning', {\n timestamp: 1700000500,\n data: { remaining: 5 },\n }),\n mapping: {\n settings: {\n channel: '#admin',\n ephemeral: true,\n user: 'U-ADMIN-1',\n text: 'Heads up: ${data.remaining} requests remaining',\n },\n },\n out: [\n [\n 'slackClient.chat.postEphemeral',\n {\n channel: '#admin',\n text: 'Heads up: 5 requests remaining',\n unfurl_links: false,\n unfurl_media: false,\n mrkdwn: true,\n user: 'U-ADMIN-1',\n },\n ],\n ],\n};\n\n/**\n * Default blocks -- no custom text/blocks, destination auto-generates a\n * Block Kit message from the event data.\n */\nexport const defaultBlocks: SlackStepExample = {\n title: 'Default blocks',\n description:\n 'With no custom text the destination auto-generates a Block Kit message from event data and source.',\n in: getEvent('lead submit', {\n timestamp: 1700000600,\n data: { name: 'Acme', email: 'sales@acme.test' },\n source: { type: 'server', id: 'crm', previous_id: '' },\n }),\n mapping: {\n settings: {\n channel: '#growth',\n },\n },\n out: [\n [\n 'slackClient.chat.postMessage',\n {\n channel: '#growth',\n text: 'lead submit',\n blocks: [\n {\n type: 'header',\n text: { type: 'plain_text', text: 'lead submit' },\n },\n {\n type: 'section',\n fields: [\n { type: 'mrkdwn', text: '*name:*\\nAcme' },\n { type: 'mrkdwn', text: '*email:*\\nsales@acme.test' },\n ],\n },\n {\n type: 'context',\n elements: [{ type: 'mrkdwn', text: 'Source: crm' }],\n },\n ],\n unfurl_links: false,\n unfurl_media: false,\n mrkdwn: true,\n },\n ],\n ],\n};\n\n/**\n * Webhook mode -- no token, just webhookUrl. The destination calls\n * `env.sendServer(url, JSON.stringify(body))`. Channel is baked into the\n * URL by Slack.\n */\nexport const deployNotification: SlackStepExample = {\n title: 'Webhook deploy',\n description:\n 'Without a token the destination posts to an incoming Slack webhook URL with the rendered message body.',\n in: getEvent('deploy complete', {\n timestamp: 1700000700,\n data: { version: '1.4.2', environment: 'prod' },\n }),\n settings: {\n token: undefined,\n webhookUrl: 'https://hooks.slack.com/services/T00/B00/xxx',\n },\n mapping: {\n settings: {\n text: ':rocket: Deployment complete: ${data.version} to ${data.environment}',\n },\n },\n out: [\n [\n 'sendServer',\n 'https://hooks.slack.com/services/T00/B00/xxx',\n JSON.stringify({\n text: ':rocket: Deployment complete: 1.4.2 to prod',\n unfurl_links: false,\n unfurl_media: false,\n mrkdwn: true,\n }),\n ],\n ],\n};\n"],"mappings":"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","names":["import_dev","import_dev"]}
1
+ {"version":3,"sources":["../src/dev.ts","../src/schemas/index.ts","../src/schemas/settings.ts","../src/schemas/mapping.ts","../src/examples/index.ts","../src/examples/env.ts","../src/examples/step.ts"],"sourcesContent":["export * as schemas from './schemas';\nexport * as examples from './examples';\n","import { zodToSchema } from '@walkeros/core/dev';\nimport { SettingsSchema } from './settings';\nimport { MappingSchema } from './mapping';\n\nexport { SettingsSchema, type Settings } from './settings';\nexport { MappingSchema, type Mapping } from './mapping';\n\n// JSON Schema\nexport const settings = zodToSchema(SettingsSchema);\nexport const mapping = zodToSchema(MappingSchema);\n","import { z } from '@walkeros/core/dev';\n\nexport const SettingsSchema = z\n .object({\n token: z\n .string()\n .startsWith('xoxb-')\n .describe(\n 'Slack Bot token (xoxb-...). Enables Web API mode. Mutually exclusive with webhookUrl.',\n )\n .optional(),\n webhookUrl: z\n .string()\n .url()\n .describe(\n 'Incoming Webhook URL. Enables webhook mode. Mutually exclusive with token.',\n )\n .optional(),\n channel: z\n .string()\n .describe(\n 'Default Slack channel ID or name (e.g. \"#alerts\" or \"C024BE91L\"). Required for Web API mode unless every rule supplies one. Ignored in webhook mode.',\n )\n .optional(),\n text: z\n .string()\n .describe(\n 'Default text template. Supports `${data.field}` interpolation against the walkerOS event.',\n )\n .optional(),\n blocks: z\n .array(z.record(z.string(), z.unknown()))\n .describe(\n 'Default Block Kit blocks applied when no mapping override is set.',\n )\n .optional(),\n includeHeader: z\n .boolean()\n .describe(\n 'Auto-add an event-name header block when generating default blocks. Default: true.',\n )\n .optional(),\n unfurlLinks: z\n .boolean()\n .describe(\n 'Enable link unfurling. Default: false (cleaner for automated alerts).',\n )\n .optional(),\n unfurlMedia: z\n .boolean()\n .describe('Enable media unfurling. Default: false.')\n .optional(),\n mrkdwn: z\n .boolean()\n .describe('Use mrkdwn formatting in text. Default: true.')\n .optional(),\n threadTs: z\n .string()\n .describe(\n 'Static thread_ts for replies (rarely set at destination level).',\n )\n .optional(),\n retryConfig: z\n .enum(['default', 'fiveRetriesInFiveMinutes', 'none'])\n .describe('Retry policy passed to WebClient. Default: \"default\".')\n .optional(),\n })\n .refine((v) => Boolean(v.token) !== Boolean(v.webhookUrl), {\n message: 'Provide exactly one of `token` or `webhookUrl`.',\n });\n\nexport type Settings = z.infer<typeof SettingsSchema>;\n","import { z } from '@walkeros/core/dev';\n\nexport const MappingSchema = z.object({\n channel: z\n .string()\n .describe(\n 'Override the destination channel for this rule. Web API mode only -- ignored in webhook mode.',\n )\n .optional(),\n text: z\n .string()\n .describe(\n 'Override the text template for this rule. Supports `${data.field}` interpolation.',\n )\n .optional(),\n blocks: z\n .array(z.record(z.string(), z.unknown()))\n .describe('Override Block Kit blocks for this rule.')\n .optional(),\n threadTs: z\n .string()\n .describe('thread_ts for posting as a reply in a thread.')\n .optional(),\n replyBroadcast: z\n .boolean()\n .describe('Also broadcast the threaded reply back to the channel.')\n .optional(),\n ephemeral: z\n .boolean()\n .describe('Send via chat.postEphemeral. Requires `user`.')\n .optional(),\n user: z\n .string()\n .describe('Slack user ID for ephemeral or DM delivery.')\n .optional(),\n dm: z\n .boolean()\n .describe(\n 'Send as DM via conversations.open + chat.postMessage. Requires `user`.',\n )\n .optional(),\n});\n\nexport type Mapping = z.infer<typeof MappingSchema>;\n","export * as env from './env';\nexport * as step from './step';\n","import type { Env, SlackClientMock } from '../types';\n\nconst okMessageResponse = {\n ok: true as const,\n channel: 'CMOCK',\n ts: '1700000000.000100',\n};\n\nconst okEphemeralResponse = { ok: true as const };\n\nconst okOpenResponse = {\n ok: true as const,\n channel: { id: 'D-MOCK-DM' },\n};\n\nfunction createMockClient(): SlackClientMock {\n return {\n chat: {\n postMessage: () => Promise.resolve(okMessageResponse as never),\n postEphemeral: () => Promise.resolve(okEphemeralResponse as never),\n },\n conversations: {\n open: () => Promise.resolve(okOpenResponse as never),\n },\n };\n}\n\nexport const push: Env = {\n slackClient: createMockClient(),\n sendServer: (() =>\n Promise.resolve({ ok: true } as never)) as Env['sendServer'],\n};\n\nexport const simulation = [\n 'call:slackClient.chat.postMessage',\n 'call:slackClient.chat.postEphemeral',\n 'call:slackClient.conversations.open',\n 'call:sendServer',\n];\n","import type { Flow } from '@walkeros/core';\nimport { getEvent } from '@walkeros/core';\nimport type { Settings } from '../types';\n\n/**\n * Extended step example that may carry destination-level settings overrides.\n */\nexport type SlackStepExample = Flow.StepExample & {\n settings?: Partial<Settings>;\n};\n\n/**\n * Slack server destination operates in two modes:\n *\n * 1. Web API mode - calls the injected `env.slackClient` SDK methods\n * (`chat.postMessage`, `chat.postEphemeral`, `conversations.open`).\n * Captured as `[callable, args]` with dotted callable names such as\n * `'slackClient.chat.postMessage'`.\n *\n * 2. Webhook mode - calls `env.sendServer(url, JSON.stringify(body))`.\n * Captured as `['sendServer', url, bodyAsString]` where `bodyAsString`\n * is the already-stringified JSON body. Key insertion order in the\n * source object matters for string equality.\n *\n * A single push may emit multiple calls (e.g. a DM opens a conversation\n * then posts to the returned channel id), so every `out` is wrapped as\n * `[[callable, ...args], ...]`.\n */\n\n/**\n * Purchase notification -- Web API mode, channel from mapping override,\n * text template interpolated against event.data.\n */\nexport const purchaseAlert: SlackStepExample = {\n title: 'Purchase alert',\n description:\n 'A completed order posts a templated message to a sales channel via the Slack Web API.',\n in: getEvent('order complete', {\n timestamp: 1700000100,\n data: {\n id: 'ORD-500',\n total: 299.99,\n currency: 'EUR',\n product: 'Pro Plan',\n },\n user: { id: 'buyer-42' },\n }),\n mapping: {\n settings: {\n channel: '#sales',\n text: ':moneybag: New order: ${data.id} - ${data.total} ${data.currency}',\n },\n },\n out: [\n [\n 'slackClient.chat.postMessage',\n {\n channel: '#sales',\n text: ':moneybag: New order: ORD-500 - 299.99 EUR',\n unfurl_links: false,\n unfurl_media: false,\n mrkdwn: true,\n },\n ],\n ],\n};\n\n/**\n * Error alert -- routes to a different channel via mapping override.\n */\nexport const errorAlert: SlackStepExample = {\n title: 'Error alert',\n description:\n 'An error event posts a critical alert to an engineering channel using a mapping-level channel override.',\n in: getEvent('error occur', {\n timestamp: 1700000200,\n data: {\n message: 'Payment gateway timeout',\n code: 'PGW_TIMEOUT',\n severity: 'critical',\n },\n }),\n mapping: {\n settings: {\n channel: '#engineering-alerts',\n text: ':rotating_light: Error: ${data.message}',\n },\n },\n out: [\n [\n 'slackClient.chat.postMessage',\n {\n channel: '#engineering-alerts',\n text: ':rotating_light: Error: Payment gateway timeout',\n unfurl_links: false,\n unfurl_media: false,\n mrkdwn: true,\n },\n ],\n ],\n};\n\n/**\n * Welcome DM -- conversations.open(users) -> chat.postMessage(channel: D-id).\n */\nexport const welcomeDM: SlackStepExample = {\n title: 'Welcome DM',\n description:\n 'A user signup opens a Slack DM channel and posts a welcome message with the selected plan.',\n in: getEvent('user signup', {\n timestamp: 1700000300,\n data: { plan: 'enterprise' },\n user: { id: 'U-NEW-USER' },\n }),\n mapping: {\n settings: {\n dm: true,\n user: 'U-NEW-USER',\n text: ':wave: Welcome aboard! You signed up for the ${data.plan} plan.',\n },\n },\n out: [\n ['slackClient.conversations.open', { users: 'U-NEW-USER' }],\n [\n 'slackClient.chat.postMessage',\n {\n channel: 'D-MOCK-DM',\n text: ':wave: Welcome aboard! You signed up for the enterprise plan.',\n unfurl_links: false,\n unfurl_media: false,\n mrkdwn: true,\n },\n ],\n ],\n};\n\n/**\n * Threaded checkout step -- thread_ts override puts the reply into a thread.\n */\nexport const threadedCheckoutStep: SlackStepExample = {\n title: 'Threaded reply',\n description:\n 'A checkout step posts as a threaded reply in Slack via thread_ts with broadcast to the channel.',\n in: getEvent('checkout step', {\n timestamp: 1700000400,\n data: { step: 'payment', sessionTs: '1700000000.000050' },\n }),\n mapping: {\n settings: {\n channel: '#sales',\n text: 'Checkout step: ${data.step}',\n threadTs: '1700000000.000050',\n replyBroadcast: true,\n },\n },\n out: [\n [\n 'slackClient.chat.postMessage',\n {\n channel: '#sales',\n text: 'Checkout step: payment',\n unfurl_links: false,\n unfurl_media: false,\n mrkdwn: true,\n thread_ts: '1700000000.000050',\n reply_broadcast: true,\n },\n ],\n ],\n};\n\n/**\n * Ephemeral message -- visible to one user in the target channel.\n */\nexport const ephemeralMessage: SlackStepExample = {\n title: 'Ephemeral message',\n description:\n 'A quota warning posts an ephemeral Slack message visible only to a target admin user.',\n in: getEvent('quota warning', {\n timestamp: 1700000500,\n data: { remaining: 5 },\n }),\n mapping: {\n settings: {\n channel: '#admin',\n ephemeral: true,\n user: 'U-ADMIN-1',\n text: 'Heads up: ${data.remaining} requests remaining',\n },\n },\n out: [\n [\n 'slackClient.chat.postEphemeral',\n {\n channel: '#admin',\n text: 'Heads up: 5 requests remaining',\n unfurl_links: false,\n unfurl_media: false,\n mrkdwn: true,\n user: 'U-ADMIN-1',\n },\n ],\n ],\n};\n\n/**\n * Default blocks -- no custom text/blocks, destination auto-generates a\n * Block Kit message from the event data.\n */\nexport const defaultBlocks: SlackStepExample = {\n title: 'Default blocks',\n description:\n 'With no custom text the destination auto-generates a Block Kit message from event data and source.',\n in: getEvent('lead submit', {\n timestamp: 1700000600,\n data: { name: 'Acme', email: 'sales@acme.test' },\n source: { type: 'crm', platform: 'server' },\n }),\n mapping: {\n settings: {\n channel: '#growth',\n },\n },\n out: [\n [\n 'slackClient.chat.postMessage',\n {\n channel: '#growth',\n text: 'lead submit',\n blocks: [\n {\n type: 'header',\n text: { type: 'plain_text', text: 'lead submit' },\n },\n {\n type: 'section',\n fields: [\n { type: 'mrkdwn', text: '*name:*\\nAcme' },\n { type: 'mrkdwn', text: '*email:*\\nsales@acme.test' },\n ],\n },\n {\n type: 'context',\n elements: [{ type: 'mrkdwn', text: 'Source: crm' }],\n },\n ],\n unfurl_links: false,\n unfurl_media: false,\n mrkdwn: true,\n },\n ],\n ],\n};\n\n/**\n * Webhook mode -- no token, just webhookUrl. The destination calls\n * `env.sendServer(url, JSON.stringify(body))`. Channel is baked into the\n * URL by Slack.\n */\nexport const deployNotification: SlackStepExample = {\n title: 'Webhook deploy',\n description:\n 'Without a token the destination posts to an incoming Slack webhook URL with the rendered message body.',\n in: getEvent('deploy complete', {\n timestamp: 1700000700,\n data: { version: '1.4.2', environment: 'prod' },\n }),\n settings: {\n token: undefined,\n webhookUrl: 'https://hooks.slack.com/services/T00/B00/xxx',\n },\n mapping: {\n settings: {\n text: ':rocket: Deployment complete: ${data.version} to ${data.environment}',\n },\n },\n out: [\n [\n 'sendServer',\n 'https://hooks.slack.com/services/T00/B00/xxx',\n JSON.stringify({\n text: ':rocket: Deployment complete: 1.4.2 to prod',\n unfurl_links: false,\n unfurl_media: false,\n mrkdwn: true,\n }),\n ],\n ],\n};\n"],"mappings":"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","names":["import_dev","import_dev"]}
package/dist/dev.mjs CHANGED
@@ -1 +1 @@
1
- var e=Object.defineProperty,t=(t,a)=>{for(var n in a)e(t,n,{get:a[n],enumerable:!0})},a={};t(a,{MappingSchema:()=>i,SettingsSchema:()=>r,mapping:()=>c,settings:()=>l});import{zodToSchema as n}from"@walkeros/core/dev";import{z as o}from"@walkeros/core/dev";var r=o.object({token:o.string().startsWith("xoxb-").describe("Slack Bot token (xoxb-...). Enables Web API mode. Mutually exclusive with webhookUrl.").optional(),webhookUrl:o.string().url().describe("Incoming Webhook URL. Enables webhook mode. Mutually exclusive with token.").optional(),channel:o.string().describe('Default Slack channel ID or name (e.g. "#alerts" or "C024BE91L"). Required for Web API mode unless every rule supplies one. Ignored in webhook mode.').optional(),text:o.string().describe("Default text template. Supports `${data.field}` interpolation against the walkerOS event.").optional(),blocks:o.array(o.record(o.string(),o.unknown())).describe("Default Block Kit blocks applied when no mapping override is set.").optional(),includeHeader:o.boolean().describe("Auto-add an event-name header block when generating default blocks. Default: true.").optional(),unfurlLinks:o.boolean().describe("Enable link unfurling. Default: false (cleaner for automated alerts).").optional(),unfurlMedia:o.boolean().describe("Enable media unfurling. Default: false.").optional(),mrkdwn:o.boolean().describe("Use mrkdwn formatting in text. Default: true.").optional(),threadTs:o.string().describe("Static thread_ts for replies (rarely set at destination level).").optional(),retryConfig:o.enum(["default","fiveRetriesInFiveMinutes","none"]).describe('Retry policy passed to WebClient. Default: "default".').optional()}).refine(e=>Boolean(e.token)!==Boolean(e.webhookUrl),{message:"Provide exactly one of `token` or `webhookUrl`."});import{z as s}from"@walkeros/core/dev";var i=s.object({channel:s.string().describe("Override the destination channel for this rule. Web API mode only -- ignored in webhook mode.").optional(),text:s.string().describe("Override the text template for this rule. Supports `${data.field}` interpolation.").optional(),blocks:s.array(s.record(s.string(),s.unknown())).describe("Override Block Kit blocks for this rule.").optional(),threadTs:s.string().describe("thread_ts for posting as a reply in a thread.").optional(),replyBroadcast:s.boolean().describe("Also broadcast the threaded reply back to the channel.").optional(),ephemeral:s.boolean().describe("Send via chat.postEphemeral. Requires `user`.").optional(),user:s.string().describe("Slack user ID for ephemeral or DM delivery.").optional(),dm:s.boolean().describe("Send as DM via conversations.open + chat.postMessage. Requires `user`.").optional()}),l=n(r),c=n(i),d={};t(d,{env:()=>p,step:()=>b});var p={};t(p,{push:()=>g,simulation:()=>k});var m={ok:!0,channel:"CMOCK",ts:"1700000000.000100"},u={ok:!0},h={ok:!0,channel:{id:"D-MOCK-DM"}};var g={slackClient:{chat:{postMessage:()=>Promise.resolve(m),postEphemeral:()=>Promise.resolve(u)},conversations:{open:()=>Promise.resolve(h)}},sendServer:()=>Promise.resolve({ok:!0})},k=["call:slackClient.chat.postMessage","call:slackClient.chat.postEphemeral","call:slackClient.conversations.open","call:sendServer"],b={};t(b,{defaultBlocks:()=>D,deployNotification:()=>S,ephemeralMessage:()=>M,errorAlert:()=>y,purchaseAlert:()=>v,threadedCheckoutStep:()=>x,welcomeDM:()=>w});import{getEvent as f}from"@walkeros/core";var v={title:"Purchase alert",description:"A completed order posts a templated message to a sales channel via the Slack Web API.",in:f("order complete",{timestamp:1700000100,data:{id:"ORD-500",total:299.99,currency:"EUR",product:"Pro Plan"},user:{id:"buyer-42"}}),mapping:{settings:{channel:"#sales",text:":moneybag: New order: ${data.id} - ${data.total} ${data.currency}"}},out:[["slackClient.chat.postMessage",{channel:"#sales",text:":moneybag: New order: ORD-500 - 299.99 EUR",unfurl_links:!1,unfurl_media:!1,mrkdwn:!0}]]},y={title:"Error alert",description:"An error event posts a critical alert to an engineering channel using a mapping-level channel override.",in:f("error occur",{timestamp:1700000200,data:{message:"Payment gateway timeout",code:"PGW_TIMEOUT",severity:"critical"}}),mapping:{settings:{channel:"#engineering-alerts",text:":rotating_light: Error: ${data.message}"}},out:[["slackClient.chat.postMessage",{channel:"#engineering-alerts",text:":rotating_light: Error: Payment gateway timeout",unfurl_links:!1,unfurl_media:!1,mrkdwn:!0}]]},w={title:"Welcome DM",description:"A user signup opens a Slack DM channel and posts a welcome message with the selected plan.",in:f("user signup",{timestamp:1700000300,data:{plan:"enterprise"},user:{id:"U-NEW-USER"}}),mapping:{settings:{dm:!0,user:"U-NEW-USER",text:":wave: Welcome aboard! You signed up for the ${data.plan} plan."}},out:[["slackClient.conversations.open",{users:"U-NEW-USER"}],["slackClient.chat.postMessage",{channel:"D-MOCK-DM",text:":wave: Welcome aboard! You signed up for the enterprise plan.",unfurl_links:!1,unfurl_media:!1,mrkdwn:!0}]]},x={title:"Threaded reply",description:"A checkout step posts as a threaded reply in Slack via thread_ts with broadcast to the channel.",in:f("checkout step",{timestamp:1700000400,data:{step:"payment",sessionTs:"1700000000.000050"}}),mapping:{settings:{channel:"#sales",text:"Checkout step: ${data.step}",threadTs:"1700000000.000050",replyBroadcast:!0}},out:[["slackClient.chat.postMessage",{channel:"#sales",text:"Checkout step: payment",unfurl_links:!1,unfurl_media:!1,mrkdwn:!0,thread_ts:"1700000000.000050",reply_broadcast:!0}]]},M={title:"Ephemeral message",description:"A quota warning posts an ephemeral Slack message visible only to a target admin user.",in:f("quota warning",{timestamp:1700000500,data:{remaining:5}}),mapping:{settings:{channel:"#admin",ephemeral:!0,user:"U-ADMIN-1",text:"Heads up: ${data.remaining} requests remaining"}},out:[["slackClient.chat.postEphemeral",{channel:"#admin",text:"Heads up: 5 requests remaining",unfurl_links:!1,unfurl_media:!1,mrkdwn:!0,user:"U-ADMIN-1"}]]},D={title:"Default blocks",description:"With no custom text the destination auto-generates a Block Kit message from event data and source.",in:f("lead submit",{timestamp:1700000600,data:{name:"Acme",email:"sales@acme.test"},source:{type:"server",id:"crm",previous_id:""}}),mapping:{settings:{channel:"#growth"}},out:[["slackClient.chat.postMessage",{channel:"#growth",text:"lead submit",blocks:[{type:"header",text:{type:"plain_text",text:"lead submit"}},{type:"section",fields:[{type:"mrkdwn",text:"*name:*\nAcme"},{type:"mrkdwn",text:"*email:*\nsales@acme.test"}]},{type:"context",elements:[{type:"mrkdwn",text:"Source: crm"}]}],unfurl_links:!1,unfurl_media:!1,mrkdwn:!0}]]},S={title:"Webhook deploy",description:"Without a token the destination posts to an incoming Slack webhook URL with the rendered message body.",in:f("deploy complete",{timestamp:1700000700,data:{version:"1.4.2",environment:"prod"}}),settings:{token:void 0,webhookUrl:"https://hooks.slack.com/services/T00/B00/xxx"},mapping:{settings:{text:":rocket: Deployment complete: ${data.version} to ${data.environment}"}},out:[["sendServer","https://hooks.slack.com/services/T00/B00/xxx",JSON.stringify({text:":rocket: Deployment complete: 1.4.2 to prod",unfurl_links:!1,unfurl_media:!1,mrkdwn:!0})]]};export{d as examples,a as schemas};//# sourceMappingURL=dev.mjs.map
1
+ var e=Object.defineProperty,t=(t,a)=>{for(var n in a)e(t,n,{get:a[n],enumerable:!0})},a={};t(a,{MappingSchema:()=>i,SettingsSchema:()=>r,mapping:()=>c,settings:()=>l});import{zodToSchema as n}from"@walkeros/core/dev";import{z as o}from"@walkeros/core/dev";var r=o.object({token:o.string().startsWith("xoxb-").describe("Slack Bot token (xoxb-...). Enables Web API mode. Mutually exclusive with webhookUrl.").optional(),webhookUrl:o.string().url().describe("Incoming Webhook URL. Enables webhook mode. Mutually exclusive with token.").optional(),channel:o.string().describe('Default Slack channel ID or name (e.g. "#alerts" or "C024BE91L"). Required for Web API mode unless every rule supplies one. Ignored in webhook mode.').optional(),text:o.string().describe("Default text template. Supports `${data.field}` interpolation against the walkerOS event.").optional(),blocks:o.array(o.record(o.string(),o.unknown())).describe("Default Block Kit blocks applied when no mapping override is set.").optional(),includeHeader:o.boolean().describe("Auto-add an event-name header block when generating default blocks. Default: true.").optional(),unfurlLinks:o.boolean().describe("Enable link unfurling. Default: false (cleaner for automated alerts).").optional(),unfurlMedia:o.boolean().describe("Enable media unfurling. Default: false.").optional(),mrkdwn:o.boolean().describe("Use mrkdwn formatting in text. Default: true.").optional(),threadTs:o.string().describe("Static thread_ts for replies (rarely set at destination level).").optional(),retryConfig:o.enum(["default","fiveRetriesInFiveMinutes","none"]).describe('Retry policy passed to WebClient. Default: "default".').optional()}).refine(e=>Boolean(e.token)!==Boolean(e.webhookUrl),{message:"Provide exactly one of `token` or `webhookUrl`."});import{z as s}from"@walkeros/core/dev";var i=s.object({channel:s.string().describe("Override the destination channel for this rule. Web API mode only -- ignored in webhook mode.").optional(),text:s.string().describe("Override the text template for this rule. Supports `${data.field}` interpolation.").optional(),blocks:s.array(s.record(s.string(),s.unknown())).describe("Override Block Kit blocks for this rule.").optional(),threadTs:s.string().describe("thread_ts for posting as a reply in a thread.").optional(),replyBroadcast:s.boolean().describe("Also broadcast the threaded reply back to the channel.").optional(),ephemeral:s.boolean().describe("Send via chat.postEphemeral. Requires `user`.").optional(),user:s.string().describe("Slack user ID for ephemeral or DM delivery.").optional(),dm:s.boolean().describe("Send as DM via conversations.open + chat.postMessage. Requires `user`.").optional()}),l=n(r),c=n(i),d={};t(d,{env:()=>p,step:()=>b});var p={};t(p,{push:()=>g,simulation:()=>k});var m={ok:!0,channel:"CMOCK",ts:"1700000000.000100"},u={ok:!0},h={ok:!0,channel:{id:"D-MOCK-DM"}};var g={slackClient:{chat:{postMessage:()=>Promise.resolve(m),postEphemeral:()=>Promise.resolve(u)},conversations:{open:()=>Promise.resolve(h)}},sendServer:()=>Promise.resolve({ok:!0})},k=["call:slackClient.chat.postMessage","call:slackClient.chat.postEphemeral","call:slackClient.conversations.open","call:sendServer"],b={};t(b,{defaultBlocks:()=>D,deployNotification:()=>S,ephemeralMessage:()=>M,errorAlert:()=>y,purchaseAlert:()=>v,threadedCheckoutStep:()=>x,welcomeDM:()=>w});import{getEvent as f}from"@walkeros/core";var v={title:"Purchase alert",description:"A completed order posts a templated message to a sales channel via the Slack Web API.",in:f("order complete",{timestamp:1700000100,data:{id:"ORD-500",total:299.99,currency:"EUR",product:"Pro Plan"},user:{id:"buyer-42"}}),mapping:{settings:{channel:"#sales",text:":moneybag: New order: ${data.id} - ${data.total} ${data.currency}"}},out:[["slackClient.chat.postMessage",{channel:"#sales",text:":moneybag: New order: ORD-500 - 299.99 EUR",unfurl_links:!1,unfurl_media:!1,mrkdwn:!0}]]},y={title:"Error alert",description:"An error event posts a critical alert to an engineering channel using a mapping-level channel override.",in:f("error occur",{timestamp:1700000200,data:{message:"Payment gateway timeout",code:"PGW_TIMEOUT",severity:"critical"}}),mapping:{settings:{channel:"#engineering-alerts",text:":rotating_light: Error: ${data.message}"}},out:[["slackClient.chat.postMessage",{channel:"#engineering-alerts",text:":rotating_light: Error: Payment gateway timeout",unfurl_links:!1,unfurl_media:!1,mrkdwn:!0}]]},w={title:"Welcome DM",description:"A user signup opens a Slack DM channel and posts a welcome message with the selected plan.",in:f("user signup",{timestamp:1700000300,data:{plan:"enterprise"},user:{id:"U-NEW-USER"}}),mapping:{settings:{dm:!0,user:"U-NEW-USER",text:":wave: Welcome aboard! You signed up for the ${data.plan} plan."}},out:[["slackClient.conversations.open",{users:"U-NEW-USER"}],["slackClient.chat.postMessage",{channel:"D-MOCK-DM",text:":wave: Welcome aboard! You signed up for the enterprise plan.",unfurl_links:!1,unfurl_media:!1,mrkdwn:!0}]]},x={title:"Threaded reply",description:"A checkout step posts as a threaded reply in Slack via thread_ts with broadcast to the channel.",in:f("checkout step",{timestamp:1700000400,data:{step:"payment",sessionTs:"1700000000.000050"}}),mapping:{settings:{channel:"#sales",text:"Checkout step: ${data.step}",threadTs:"1700000000.000050",replyBroadcast:!0}},out:[["slackClient.chat.postMessage",{channel:"#sales",text:"Checkout step: payment",unfurl_links:!1,unfurl_media:!1,mrkdwn:!0,thread_ts:"1700000000.000050",reply_broadcast:!0}]]},M={title:"Ephemeral message",description:"A quota warning posts an ephemeral Slack message visible only to a target admin user.",in:f("quota warning",{timestamp:1700000500,data:{remaining:5}}),mapping:{settings:{channel:"#admin",ephemeral:!0,user:"U-ADMIN-1",text:"Heads up: ${data.remaining} requests remaining"}},out:[["slackClient.chat.postEphemeral",{channel:"#admin",text:"Heads up: 5 requests remaining",unfurl_links:!1,unfurl_media:!1,mrkdwn:!0,user:"U-ADMIN-1"}]]},D={title:"Default blocks",description:"With no custom text the destination auto-generates a Block Kit message from event data and source.",in:f("lead submit",{timestamp:1700000600,data:{name:"Acme",email:"sales@acme.test"},source:{type:"crm",platform:"server"}}),mapping:{settings:{channel:"#growth"}},out:[["slackClient.chat.postMessage",{channel:"#growth",text:"lead submit",blocks:[{type:"header",text:{type:"plain_text",text:"lead submit"}},{type:"section",fields:[{type:"mrkdwn",text:"*name:*\nAcme"},{type:"mrkdwn",text:"*email:*\nsales@acme.test"}]},{type:"context",elements:[{type:"mrkdwn",text:"Source: crm"}]}],unfurl_links:!1,unfurl_media:!1,mrkdwn:!0}]]},S={title:"Webhook deploy",description:"Without a token the destination posts to an incoming Slack webhook URL with the rendered message body.",in:f("deploy complete",{timestamp:1700000700,data:{version:"1.4.2",environment:"prod"}}),settings:{token:void 0,webhookUrl:"https://hooks.slack.com/services/T00/B00/xxx"},mapping:{settings:{text:":rocket: Deployment complete: ${data.version} to ${data.environment}"}},out:[["sendServer","https://hooks.slack.com/services/T00/B00/xxx",JSON.stringify({text:":rocket: Deployment complete: 1.4.2 to prod",unfurl_links:!1,unfurl_media:!1,mrkdwn:!0})]]};export{d as examples,a as schemas};//# sourceMappingURL=dev.mjs.map
package/dist/dev.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/schemas/index.ts","../src/schemas/settings.ts","../src/schemas/mapping.ts","../src/examples/index.ts","../src/examples/env.ts","../src/examples/step.ts"],"sourcesContent":["import { zodToSchema } from '@walkeros/core/dev';\nimport { SettingsSchema } from './settings';\nimport { MappingSchema } from './mapping';\n\nexport { SettingsSchema, type Settings } from './settings';\nexport { MappingSchema, type Mapping } from './mapping';\n\n// JSON Schema\nexport const settings = zodToSchema(SettingsSchema);\nexport const mapping = zodToSchema(MappingSchema);\n","import { z } from '@walkeros/core/dev';\n\nexport const SettingsSchema = z\n .object({\n token: z\n .string()\n .startsWith('xoxb-')\n .describe(\n 'Slack Bot token (xoxb-...). Enables Web API mode. Mutually exclusive with webhookUrl.',\n )\n .optional(),\n webhookUrl: z\n .string()\n .url()\n .describe(\n 'Incoming Webhook URL. Enables webhook mode. Mutually exclusive with token.',\n )\n .optional(),\n channel: z\n .string()\n .describe(\n 'Default Slack channel ID or name (e.g. \"#alerts\" or \"C024BE91L\"). Required for Web API mode unless every rule supplies one. Ignored in webhook mode.',\n )\n .optional(),\n text: z\n .string()\n .describe(\n 'Default text template. Supports `${data.field}` interpolation against the walkerOS event.',\n )\n .optional(),\n blocks: z\n .array(z.record(z.string(), z.unknown()))\n .describe(\n 'Default Block Kit blocks applied when no mapping override is set.',\n )\n .optional(),\n includeHeader: z\n .boolean()\n .describe(\n 'Auto-add an event-name header block when generating default blocks. Default: true.',\n )\n .optional(),\n unfurlLinks: z\n .boolean()\n .describe(\n 'Enable link unfurling. Default: false (cleaner for automated alerts).',\n )\n .optional(),\n unfurlMedia: z\n .boolean()\n .describe('Enable media unfurling. Default: false.')\n .optional(),\n mrkdwn: z\n .boolean()\n .describe('Use mrkdwn formatting in text. Default: true.')\n .optional(),\n threadTs: z\n .string()\n .describe(\n 'Static thread_ts for replies (rarely set at destination level).',\n )\n .optional(),\n retryConfig: z\n .enum(['default', 'fiveRetriesInFiveMinutes', 'none'])\n .describe('Retry policy passed to WebClient. Default: \"default\".')\n .optional(),\n })\n .refine((v) => Boolean(v.token) !== Boolean(v.webhookUrl), {\n message: 'Provide exactly one of `token` or `webhookUrl`.',\n });\n\nexport type Settings = z.infer<typeof SettingsSchema>;\n","import { z } from '@walkeros/core/dev';\n\nexport const MappingSchema = z.object({\n channel: z\n .string()\n .describe(\n 'Override the destination channel for this rule. Web API mode only -- ignored in webhook mode.',\n )\n .optional(),\n text: z\n .string()\n .describe(\n 'Override the text template for this rule. Supports `${data.field}` interpolation.',\n )\n .optional(),\n blocks: z\n .array(z.record(z.string(), z.unknown()))\n .describe('Override Block Kit blocks for this rule.')\n .optional(),\n threadTs: z\n .string()\n .describe('thread_ts for posting as a reply in a thread.')\n .optional(),\n replyBroadcast: z\n .boolean()\n .describe('Also broadcast the threaded reply back to the channel.')\n .optional(),\n ephemeral: z\n .boolean()\n .describe('Send via chat.postEphemeral. Requires `user`.')\n .optional(),\n user: z\n .string()\n .describe('Slack user ID for ephemeral or DM delivery.')\n .optional(),\n dm: z\n .boolean()\n .describe(\n 'Send as DM via conversations.open + chat.postMessage. Requires `user`.',\n )\n .optional(),\n});\n\nexport type Mapping = z.infer<typeof MappingSchema>;\n","export * as env from './env';\nexport * as step from './step';\n","import type { Env, SlackClientMock } from '../types';\n\nconst okMessageResponse = {\n ok: true as const,\n channel: 'CMOCK',\n ts: '1700000000.000100',\n};\n\nconst okEphemeralResponse = { ok: true as const };\n\nconst okOpenResponse = {\n ok: true as const,\n channel: { id: 'D-MOCK-DM' },\n};\n\nfunction createMockClient(): SlackClientMock {\n return {\n chat: {\n postMessage: () => Promise.resolve(okMessageResponse as never),\n postEphemeral: () => Promise.resolve(okEphemeralResponse as never),\n },\n conversations: {\n open: () => Promise.resolve(okOpenResponse as never),\n },\n };\n}\n\nexport const push: Env = {\n slackClient: createMockClient(),\n sendServer: (() =>\n Promise.resolve({ ok: true } as never)) as Env['sendServer'],\n};\n\nexport const simulation = [\n 'call:slackClient.chat.postMessage',\n 'call:slackClient.chat.postEphemeral',\n 'call:slackClient.conversations.open',\n 'call:sendServer',\n];\n","import type { Flow } from '@walkeros/core';\nimport { getEvent } from '@walkeros/core';\nimport type { Settings } from '../types';\n\n/**\n * Extended step example that may carry destination-level settings overrides.\n */\nexport type SlackStepExample = Flow.StepExample & {\n settings?: Partial<Settings>;\n};\n\n/**\n * Slack server destination operates in two modes:\n *\n * 1. Web API mode — calls the injected `env.slackClient` SDK methods\n * (`chat.postMessage`, `chat.postEphemeral`, `conversations.open`).\n * Captured as `[callable, args]` with dotted callable names such as\n * `'slackClient.chat.postMessage'`.\n *\n * 2. Webhook mode — calls `env.sendServer(url, JSON.stringify(body))`.\n * Captured as `['sendServer', url, bodyAsString]` where `bodyAsString`\n * is the already-stringified JSON body. Key insertion order in the\n * source object matters for string equality.\n *\n * A single push may emit multiple calls (e.g. a DM opens a conversation\n * then posts to the returned channel id), so every `out` is wrapped as\n * `[[callable, ...args], ...]`.\n */\n\n/**\n * Purchase notification -- Web API mode, channel from mapping override,\n * text template interpolated against event.data.\n */\nexport const purchaseAlert: SlackStepExample = {\n title: 'Purchase alert',\n description:\n 'A completed order posts a templated message to a sales channel via the Slack Web API.',\n in: getEvent('order complete', {\n timestamp: 1700000100,\n data: {\n id: 'ORD-500',\n total: 299.99,\n currency: 'EUR',\n product: 'Pro Plan',\n },\n user: { id: 'buyer-42' },\n }),\n mapping: {\n settings: {\n channel: '#sales',\n text: ':moneybag: New order: ${data.id} - ${data.total} ${data.currency}',\n },\n },\n out: [\n [\n 'slackClient.chat.postMessage',\n {\n channel: '#sales',\n text: ':moneybag: New order: ORD-500 - 299.99 EUR',\n unfurl_links: false,\n unfurl_media: false,\n mrkdwn: true,\n },\n ],\n ],\n};\n\n/**\n * Error alert -- routes to a different channel via mapping override.\n */\nexport const errorAlert: SlackStepExample = {\n title: 'Error alert',\n description:\n 'An error event posts a critical alert to an engineering channel using a mapping-level channel override.',\n in: getEvent('error occur', {\n timestamp: 1700000200,\n data: {\n message: 'Payment gateway timeout',\n code: 'PGW_TIMEOUT',\n severity: 'critical',\n },\n }),\n mapping: {\n settings: {\n channel: '#engineering-alerts',\n text: ':rotating_light: Error: ${data.message}',\n },\n },\n out: [\n [\n 'slackClient.chat.postMessage',\n {\n channel: '#engineering-alerts',\n text: ':rotating_light: Error: Payment gateway timeout',\n unfurl_links: false,\n unfurl_media: false,\n mrkdwn: true,\n },\n ],\n ],\n};\n\n/**\n * Welcome DM -- conversations.open(users) -> chat.postMessage(channel: D-id).\n */\nexport const welcomeDM: SlackStepExample = {\n title: 'Welcome DM',\n description:\n 'A user signup opens a Slack DM channel and posts a welcome message with the selected plan.',\n in: getEvent('user signup', {\n timestamp: 1700000300,\n data: { plan: 'enterprise' },\n user: { id: 'U-NEW-USER' },\n }),\n mapping: {\n settings: {\n dm: true,\n user: 'U-NEW-USER',\n text: ':wave: Welcome aboard! You signed up for the ${data.plan} plan.',\n },\n },\n out: [\n ['slackClient.conversations.open', { users: 'U-NEW-USER' }],\n [\n 'slackClient.chat.postMessage',\n {\n channel: 'D-MOCK-DM',\n text: ':wave: Welcome aboard! You signed up for the enterprise plan.',\n unfurl_links: false,\n unfurl_media: false,\n mrkdwn: true,\n },\n ],\n ],\n};\n\n/**\n * Threaded checkout step -- thread_ts override puts the reply into a thread.\n */\nexport const threadedCheckoutStep: SlackStepExample = {\n title: 'Threaded reply',\n description:\n 'A checkout step posts as a threaded reply in Slack via thread_ts with broadcast to the channel.',\n in: getEvent('checkout step', {\n timestamp: 1700000400,\n data: { step: 'payment', sessionTs: '1700000000.000050' },\n }),\n mapping: {\n settings: {\n channel: '#sales',\n text: 'Checkout step: ${data.step}',\n threadTs: '1700000000.000050',\n replyBroadcast: true,\n },\n },\n out: [\n [\n 'slackClient.chat.postMessage',\n {\n channel: '#sales',\n text: 'Checkout step: payment',\n unfurl_links: false,\n unfurl_media: false,\n mrkdwn: true,\n thread_ts: '1700000000.000050',\n reply_broadcast: true,\n },\n ],\n ],\n};\n\n/**\n * Ephemeral message -- visible to one user in the target channel.\n */\nexport const ephemeralMessage: SlackStepExample = {\n title: 'Ephemeral message',\n description:\n 'A quota warning posts an ephemeral Slack message visible only to a target admin user.',\n in: getEvent('quota warning', {\n timestamp: 1700000500,\n data: { remaining: 5 },\n }),\n mapping: {\n settings: {\n channel: '#admin',\n ephemeral: true,\n user: 'U-ADMIN-1',\n text: 'Heads up: ${data.remaining} requests remaining',\n },\n },\n out: [\n [\n 'slackClient.chat.postEphemeral',\n {\n channel: '#admin',\n text: 'Heads up: 5 requests remaining',\n unfurl_links: false,\n unfurl_media: false,\n mrkdwn: true,\n user: 'U-ADMIN-1',\n },\n ],\n ],\n};\n\n/**\n * Default blocks -- no custom text/blocks, destination auto-generates a\n * Block Kit message from the event data.\n */\nexport const defaultBlocks: SlackStepExample = {\n title: 'Default blocks',\n description:\n 'With no custom text the destination auto-generates a Block Kit message from event data and source.',\n in: getEvent('lead submit', {\n timestamp: 1700000600,\n data: { name: 'Acme', email: 'sales@acme.test' },\n source: { type: 'server', id: 'crm', previous_id: '' },\n }),\n mapping: {\n settings: {\n channel: '#growth',\n },\n },\n out: [\n [\n 'slackClient.chat.postMessage',\n {\n channel: '#growth',\n text: 'lead submit',\n blocks: [\n {\n type: 'header',\n text: { type: 'plain_text', text: 'lead submit' },\n },\n {\n type: 'section',\n fields: [\n { type: 'mrkdwn', text: '*name:*\\nAcme' },\n { type: 'mrkdwn', text: '*email:*\\nsales@acme.test' },\n ],\n },\n {\n type: 'context',\n elements: [{ type: 'mrkdwn', text: 'Source: crm' }],\n },\n ],\n unfurl_links: false,\n unfurl_media: false,\n mrkdwn: true,\n },\n ],\n ],\n};\n\n/**\n * Webhook mode -- no token, just webhookUrl. The destination calls\n * `env.sendServer(url, JSON.stringify(body))`. Channel is baked into the\n * URL by Slack.\n */\nexport const deployNotification: SlackStepExample = {\n title: 'Webhook deploy',\n description:\n 'Without a token the destination posts to an incoming Slack webhook URL with the rendered message body.',\n in: getEvent('deploy complete', {\n timestamp: 1700000700,\n data: { version: '1.4.2', environment: 'prod' },\n }),\n settings: {\n token: undefined,\n webhookUrl: 'https://hooks.slack.com/services/T00/B00/xxx',\n },\n mapping: {\n settings: {\n text: ':rocket: Deployment complete: ${data.version} to ${data.environment}',\n },\n },\n out: [\n [\n 'sendServer',\n 'https://hooks.slack.com/services/T00/B00/xxx',\n JSON.stringify({\n text: ':rocket: Deployment complete: 1.4.2 to prod',\n unfurl_links: false,\n unfurl_media: false,\n mrkdwn: true,\n }),\n ],\n ],\n};\n"],"mappings":"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","names":["z"]}
1
+ {"version":3,"sources":["../src/schemas/index.ts","../src/schemas/settings.ts","../src/schemas/mapping.ts","../src/examples/index.ts","../src/examples/env.ts","../src/examples/step.ts"],"sourcesContent":["import { zodToSchema } from '@walkeros/core/dev';\nimport { SettingsSchema } from './settings';\nimport { MappingSchema } from './mapping';\n\nexport { SettingsSchema, type Settings } from './settings';\nexport { MappingSchema, type Mapping } from './mapping';\n\n// JSON Schema\nexport const settings = zodToSchema(SettingsSchema);\nexport const mapping = zodToSchema(MappingSchema);\n","import { z } from '@walkeros/core/dev';\n\nexport const SettingsSchema = z\n .object({\n token: z\n .string()\n .startsWith('xoxb-')\n .describe(\n 'Slack Bot token (xoxb-...). Enables Web API mode. Mutually exclusive with webhookUrl.',\n )\n .optional(),\n webhookUrl: z\n .string()\n .url()\n .describe(\n 'Incoming Webhook URL. Enables webhook mode. Mutually exclusive with token.',\n )\n .optional(),\n channel: z\n .string()\n .describe(\n 'Default Slack channel ID or name (e.g. \"#alerts\" or \"C024BE91L\"). Required for Web API mode unless every rule supplies one. Ignored in webhook mode.',\n )\n .optional(),\n text: z\n .string()\n .describe(\n 'Default text template. Supports `${data.field}` interpolation against the walkerOS event.',\n )\n .optional(),\n blocks: z\n .array(z.record(z.string(), z.unknown()))\n .describe(\n 'Default Block Kit blocks applied when no mapping override is set.',\n )\n .optional(),\n includeHeader: z\n .boolean()\n .describe(\n 'Auto-add an event-name header block when generating default blocks. Default: true.',\n )\n .optional(),\n unfurlLinks: z\n .boolean()\n .describe(\n 'Enable link unfurling. Default: false (cleaner for automated alerts).',\n )\n .optional(),\n unfurlMedia: z\n .boolean()\n .describe('Enable media unfurling. Default: false.')\n .optional(),\n mrkdwn: z\n .boolean()\n .describe('Use mrkdwn formatting in text. Default: true.')\n .optional(),\n threadTs: z\n .string()\n .describe(\n 'Static thread_ts for replies (rarely set at destination level).',\n )\n .optional(),\n retryConfig: z\n .enum(['default', 'fiveRetriesInFiveMinutes', 'none'])\n .describe('Retry policy passed to WebClient. Default: \"default\".')\n .optional(),\n })\n .refine((v) => Boolean(v.token) !== Boolean(v.webhookUrl), {\n message: 'Provide exactly one of `token` or `webhookUrl`.',\n });\n\nexport type Settings = z.infer<typeof SettingsSchema>;\n","import { z } from '@walkeros/core/dev';\n\nexport const MappingSchema = z.object({\n channel: z\n .string()\n .describe(\n 'Override the destination channel for this rule. Web API mode only -- ignored in webhook mode.',\n )\n .optional(),\n text: z\n .string()\n .describe(\n 'Override the text template for this rule. Supports `${data.field}` interpolation.',\n )\n .optional(),\n blocks: z\n .array(z.record(z.string(), z.unknown()))\n .describe('Override Block Kit blocks for this rule.')\n .optional(),\n threadTs: z\n .string()\n .describe('thread_ts for posting as a reply in a thread.')\n .optional(),\n replyBroadcast: z\n .boolean()\n .describe('Also broadcast the threaded reply back to the channel.')\n .optional(),\n ephemeral: z\n .boolean()\n .describe('Send via chat.postEphemeral. Requires `user`.')\n .optional(),\n user: z\n .string()\n .describe('Slack user ID for ephemeral or DM delivery.')\n .optional(),\n dm: z\n .boolean()\n .describe(\n 'Send as DM via conversations.open + chat.postMessage. Requires `user`.',\n )\n .optional(),\n});\n\nexport type Mapping = z.infer<typeof MappingSchema>;\n","export * as env from './env';\nexport * as step from './step';\n","import type { Env, SlackClientMock } from '../types';\n\nconst okMessageResponse = {\n ok: true as const,\n channel: 'CMOCK',\n ts: '1700000000.000100',\n};\n\nconst okEphemeralResponse = { ok: true as const };\n\nconst okOpenResponse = {\n ok: true as const,\n channel: { id: 'D-MOCK-DM' },\n};\n\nfunction createMockClient(): SlackClientMock {\n return {\n chat: {\n postMessage: () => Promise.resolve(okMessageResponse as never),\n postEphemeral: () => Promise.resolve(okEphemeralResponse as never),\n },\n conversations: {\n open: () => Promise.resolve(okOpenResponse as never),\n },\n };\n}\n\nexport const push: Env = {\n slackClient: createMockClient(),\n sendServer: (() =>\n Promise.resolve({ ok: true } as never)) as Env['sendServer'],\n};\n\nexport const simulation = [\n 'call:slackClient.chat.postMessage',\n 'call:slackClient.chat.postEphemeral',\n 'call:slackClient.conversations.open',\n 'call:sendServer',\n];\n","import type { Flow } from '@walkeros/core';\nimport { getEvent } from '@walkeros/core';\nimport type { Settings } from '../types';\n\n/**\n * Extended step example that may carry destination-level settings overrides.\n */\nexport type SlackStepExample = Flow.StepExample & {\n settings?: Partial<Settings>;\n};\n\n/**\n * Slack server destination operates in two modes:\n *\n * 1. Web API mode - calls the injected `env.slackClient` SDK methods\n * (`chat.postMessage`, `chat.postEphemeral`, `conversations.open`).\n * Captured as `[callable, args]` with dotted callable names such as\n * `'slackClient.chat.postMessage'`.\n *\n * 2. Webhook mode - calls `env.sendServer(url, JSON.stringify(body))`.\n * Captured as `['sendServer', url, bodyAsString]` where `bodyAsString`\n * is the already-stringified JSON body. Key insertion order in the\n * source object matters for string equality.\n *\n * A single push may emit multiple calls (e.g. a DM opens a conversation\n * then posts to the returned channel id), so every `out` is wrapped as\n * `[[callable, ...args], ...]`.\n */\n\n/**\n * Purchase notification -- Web API mode, channel from mapping override,\n * text template interpolated against event.data.\n */\nexport const purchaseAlert: SlackStepExample = {\n title: 'Purchase alert',\n description:\n 'A completed order posts a templated message to a sales channel via the Slack Web API.',\n in: getEvent('order complete', {\n timestamp: 1700000100,\n data: {\n id: 'ORD-500',\n total: 299.99,\n currency: 'EUR',\n product: 'Pro Plan',\n },\n user: { id: 'buyer-42' },\n }),\n mapping: {\n settings: {\n channel: '#sales',\n text: ':moneybag: New order: ${data.id} - ${data.total} ${data.currency}',\n },\n },\n out: [\n [\n 'slackClient.chat.postMessage',\n {\n channel: '#sales',\n text: ':moneybag: New order: ORD-500 - 299.99 EUR',\n unfurl_links: false,\n unfurl_media: false,\n mrkdwn: true,\n },\n ],\n ],\n};\n\n/**\n * Error alert -- routes to a different channel via mapping override.\n */\nexport const errorAlert: SlackStepExample = {\n title: 'Error alert',\n description:\n 'An error event posts a critical alert to an engineering channel using a mapping-level channel override.',\n in: getEvent('error occur', {\n timestamp: 1700000200,\n data: {\n message: 'Payment gateway timeout',\n code: 'PGW_TIMEOUT',\n severity: 'critical',\n },\n }),\n mapping: {\n settings: {\n channel: '#engineering-alerts',\n text: ':rotating_light: Error: ${data.message}',\n },\n },\n out: [\n [\n 'slackClient.chat.postMessage',\n {\n channel: '#engineering-alerts',\n text: ':rotating_light: Error: Payment gateway timeout',\n unfurl_links: false,\n unfurl_media: false,\n mrkdwn: true,\n },\n ],\n ],\n};\n\n/**\n * Welcome DM -- conversations.open(users) -> chat.postMessage(channel: D-id).\n */\nexport const welcomeDM: SlackStepExample = {\n title: 'Welcome DM',\n description:\n 'A user signup opens a Slack DM channel and posts a welcome message with the selected plan.',\n in: getEvent('user signup', {\n timestamp: 1700000300,\n data: { plan: 'enterprise' },\n user: { id: 'U-NEW-USER' },\n }),\n mapping: {\n settings: {\n dm: true,\n user: 'U-NEW-USER',\n text: ':wave: Welcome aboard! You signed up for the ${data.plan} plan.',\n },\n },\n out: [\n ['slackClient.conversations.open', { users: 'U-NEW-USER' }],\n [\n 'slackClient.chat.postMessage',\n {\n channel: 'D-MOCK-DM',\n text: ':wave: Welcome aboard! You signed up for the enterprise plan.',\n unfurl_links: false,\n unfurl_media: false,\n mrkdwn: true,\n },\n ],\n ],\n};\n\n/**\n * Threaded checkout step -- thread_ts override puts the reply into a thread.\n */\nexport const threadedCheckoutStep: SlackStepExample = {\n title: 'Threaded reply',\n description:\n 'A checkout step posts as a threaded reply in Slack via thread_ts with broadcast to the channel.',\n in: getEvent('checkout step', {\n timestamp: 1700000400,\n data: { step: 'payment', sessionTs: '1700000000.000050' },\n }),\n mapping: {\n settings: {\n channel: '#sales',\n text: 'Checkout step: ${data.step}',\n threadTs: '1700000000.000050',\n replyBroadcast: true,\n },\n },\n out: [\n [\n 'slackClient.chat.postMessage',\n {\n channel: '#sales',\n text: 'Checkout step: payment',\n unfurl_links: false,\n unfurl_media: false,\n mrkdwn: true,\n thread_ts: '1700000000.000050',\n reply_broadcast: true,\n },\n ],\n ],\n};\n\n/**\n * Ephemeral message -- visible to one user in the target channel.\n */\nexport const ephemeralMessage: SlackStepExample = {\n title: 'Ephemeral message',\n description:\n 'A quota warning posts an ephemeral Slack message visible only to a target admin user.',\n in: getEvent('quota warning', {\n timestamp: 1700000500,\n data: { remaining: 5 },\n }),\n mapping: {\n settings: {\n channel: '#admin',\n ephemeral: true,\n user: 'U-ADMIN-1',\n text: 'Heads up: ${data.remaining} requests remaining',\n },\n },\n out: [\n [\n 'slackClient.chat.postEphemeral',\n {\n channel: '#admin',\n text: 'Heads up: 5 requests remaining',\n unfurl_links: false,\n unfurl_media: false,\n mrkdwn: true,\n user: 'U-ADMIN-1',\n },\n ],\n ],\n};\n\n/**\n * Default blocks -- no custom text/blocks, destination auto-generates a\n * Block Kit message from the event data.\n */\nexport const defaultBlocks: SlackStepExample = {\n title: 'Default blocks',\n description:\n 'With no custom text the destination auto-generates a Block Kit message from event data and source.',\n in: getEvent('lead submit', {\n timestamp: 1700000600,\n data: { name: 'Acme', email: 'sales@acme.test' },\n source: { type: 'crm', platform: 'server' },\n }),\n mapping: {\n settings: {\n channel: '#growth',\n },\n },\n out: [\n [\n 'slackClient.chat.postMessage',\n {\n channel: '#growth',\n text: 'lead submit',\n blocks: [\n {\n type: 'header',\n text: { type: 'plain_text', text: 'lead submit' },\n },\n {\n type: 'section',\n fields: [\n { type: 'mrkdwn', text: '*name:*\\nAcme' },\n { type: 'mrkdwn', text: '*email:*\\nsales@acme.test' },\n ],\n },\n {\n type: 'context',\n elements: [{ type: 'mrkdwn', text: 'Source: crm' }],\n },\n ],\n unfurl_links: false,\n unfurl_media: false,\n mrkdwn: true,\n },\n ],\n ],\n};\n\n/**\n * Webhook mode -- no token, just webhookUrl. The destination calls\n * `env.sendServer(url, JSON.stringify(body))`. Channel is baked into the\n * URL by Slack.\n */\nexport const deployNotification: SlackStepExample = {\n title: 'Webhook deploy',\n description:\n 'Without a token the destination posts to an incoming Slack webhook URL with the rendered message body.',\n in: getEvent('deploy complete', {\n timestamp: 1700000700,\n data: { version: '1.4.2', environment: 'prod' },\n }),\n settings: {\n token: undefined,\n webhookUrl: 'https://hooks.slack.com/services/T00/B00/xxx',\n },\n mapping: {\n settings: {\n text: ':rocket: Deployment complete: ${data.version} to ${data.environment}',\n },\n },\n out: [\n [\n 'sendServer',\n 'https://hooks.slack.com/services/T00/B00/xxx',\n JSON.stringify({\n text: ':rocket: Deployment complete: 1.4.2 to prod',\n unfurl_links: false,\n unfurl_media: false,\n mrkdwn: true,\n }),\n ],\n ],\n};\n"],"mappings":"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","names":["z"]}
@@ -69,12 +69,12 @@ type SlackStepExample = Flow.StepExample & {
69
69
  /**
70
70
  * Slack server destination operates in two modes:
71
71
  *
72
- * 1. Web API mode calls the injected `env.slackClient` SDK methods
72
+ * 1. Web API mode - calls the injected `env.slackClient` SDK methods
73
73
  * (`chat.postMessage`, `chat.postEphemeral`, `conversations.open`).
74
74
  * Captured as `[callable, args]` with dotted callable names such as
75
75
  * `'slackClient.chat.postMessage'`.
76
76
  *
77
- * 2. Webhook mode calls `env.sendServer(url, JSON.stringify(body))`.
77
+ * 2. Webhook mode - calls `env.sendServer(url, JSON.stringify(body))`.
78
78
  * Captured as `['sendServer', url, bodyAsString]` where `bodyAsString`
79
79
  * is the already-stringified JSON body. Key insertion order in the
80
80
  * source object matters for string equality.
@@ -69,12 +69,12 @@ type SlackStepExample = Flow.StepExample & {
69
69
  /**
70
70
  * Slack server destination operates in two modes:
71
71
  *
72
- * 1. Web API mode calls the injected `env.slackClient` SDK methods
72
+ * 1. Web API mode - calls the injected `env.slackClient` SDK methods
73
73
  * (`chat.postMessage`, `chat.postEphemeral`, `conversations.open`).
74
74
  * Captured as `[callable, args]` with dotted callable names such as
75
75
  * `'slackClient.chat.postMessage'`.
76
76
  *
77
- * 2. Webhook mode calls `env.sendServer(url, JSON.stringify(body))`.
77
+ * 2. Webhook mode - calls `env.sendServer(url, JSON.stringify(body))`.
78
78
  * Captured as `['sendServer', url, bodyAsString]` where `bodyAsString`
79
79
  * is the already-stringified JSON body. Key insertion order in the
80
80
  * source object matters for string equality.
@@ -231,7 +231,7 @@ var defaultBlocks = {
231
231
  in: (0, import_core.getEvent)("lead submit", {
232
232
  timestamp: 1700000600,
233
233
  data: { name: "Acme", email: "sales@acme.test" },
234
- source: { type: "server", id: "crm", previous_id: "" }
234
+ source: { type: "crm", platform: "server" }
235
235
  }),
236
236
  mapping: {
237
237
  settings: {
@@ -210,7 +210,7 @@ var defaultBlocks = {
210
210
  in: getEvent("lead submit", {
211
211
  timestamp: 1700000600,
212
212
  data: { name: "Acme", email: "sales@acme.test" },
213
- source: { type: "server", id: "crm", previous_id: "" }
213
+ source: { type: "crm", platform: "server" }
214
214
  }),
215
215
  mapping: {
216
216
  settings: {
package/dist/index.js CHANGED
@@ -1 +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
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:()=>d,default:()=>g,destinationSlack:()=>b}),module.exports=(e=i,((e,i,s,a)=>{if(i&&"object"==typeof i||"function"==typeof i)for(let c of r(i))o.call(e,c)||c===s||t(e,c,{get:()=>i[c],enumerable:!(a=n(i,c))||a.enumerable});return e})(t({},"__esModule",{value:!0}),e));var s=require("@walkeros/core"),a=require("@walkeros/server-core"),c=/\$\{([^}]+)\}/g;function l(e,t){return e.replace(c,(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(l(JSON.stringify(e),t))}function k(e,t){const n=[];t&&n.push({type:"header",text:{type:"plain_text",text:e.name}});const r=e.data||{},o=Object.entries(r).filter(([,e])=>null!=e).map(([e,t])=>({type:"mrkdwn",text:`*${e}:*\n${f(t)}`}));o.length>0&&n.push({type:"section",fields:o});const i=e.source?.type;return i&&n.push({type:"context",elements:[{type:"mrkdwn",text:`Source: ${i}`}]}),n}function f(e){return null==e?"":"object"==typeof e?JSON.stringify(e):String(e)}var p=async function(e,t){const{config:n,rule:r,env:o,logger:i}=t,c=n.settings,f=r?.settings||{},p=function(e,t,n){if(n.blocks&&n.blocks.length>0){const r={blocks:u(n.blocks,e)},o=n.text??t.text;return o&&(r.text=l(o,e)),r}const r=n.text??t.text;return r?{text:l(r,e)}:t.blocks&&t.blocks.length>0?{blocks:u(t.blocks,e)}:{text:e.name,blocks:k(e,!1!==t.includeHeader)}}(e,c,f);return c.webhookUrl?await async function(e,t,n,r){if(!e.text&&!e.blocks)return void r.warn("Slack message has no text or blocks; skipping");const o={...e.text?{text:e.text}:{},...e.blocks?{blocks:e.blocks}:{},unfurl_links:t.unfurlLinks??!1,unfurl_media:t.unfurlMedia??!1,mrkdwn:t.mrkdwn??!0},i=n?.sendServer||a.sendServer,c=await i(t.webhookUrl,JSON.stringify(o));(0,s.isObject)(c)&&!1===c.ok&&r.warn(`Slack webhook error: ${JSON.stringify(c)}`)}(p,c,o,i):await async function(e,t,n,r,o){const i=r?.slackClient||t._client;if(!i)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 s=n.channel??t.channel;if(n.dm){if(!n.user)return void o.throw("Slack DM requires `mapping.settings.user`");const e=await i.conversations.open({users:n.user}),t=e.channel?.id;if(!t)return void o.warn("Slack conversations.open returned no channel id");s=t}if(!s)return void o.throw("Slack push requires a channel (set settings.channel or mapping.settings.channel)");const a={channel:s,...e.text?{text:e.text}:{},...e.blocks?{blocks:e.blocks}:{},unfurl_links:t.unfurlLinks??!1,unfurl_media:t.unfurlMedia??!1,mrkdwn:t.mrkdwn??!0};(n.threadTs??t.threadTs)&&(a.thread_ts=n.threadTs??t.threadTs);n.replyBroadcast&&(a.reply_broadcast=!0);if(n.ephemeral)return n.user?void await i.chat.postEphemeral({...a,user:n.user}):void o.throw("Slack ephemeral requires `mapping.settings.user`");await i.chat.postMessage(a)}(p,c,f,o,i)};var d={},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;const i=n?.slackClient;if(i)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 p(e,t)},g=b;//# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +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"]}
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 sourceType = event.source?.type;\n if (sourceType) {\n blocks.push({\n type: 'context',\n elements: [{ type: 'mrkdwn', text: `Source: ${sourceType}` }],\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 CHANGED
@@ -1 +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
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 s(e,t){const n=[];t&&n.push({type:"header",text:{type:"plain_text",text:e.name}});const r=e.data||{},i=Object.entries(r).filter(([,e])=>null!=e).map(([e,t])=>({type:"mrkdwn",text:`*${e}:*\n${a(t)}`}));i.length>0&&n.push({type:"section",fields:i});const o=e.source?.type;return o&&n.push({type:"context",elements:[{type:"mrkdwn",text:`Source: ${o}`}]}),n}function a(e){return null==e?"":"object"==typeof e?JSON.stringify(e):String(e)}var c=async function(e,r){const{config:a,rule:c,env:l,logger:u}=r,f=a.settings,k=c?.settings||{},d=function(e,t,n){if(n.blocks&&n.blocks.length>0){const r={blocks:o(n.blocks,e)},s=n.text??t.text;return s&&(r.text=i(s,e)),r}const r=n.text??t.text;return r?{text:i(r,e)}:t.blocks&&t.blocks.length>0?{blocks:o(t.blocks,e)}:{text:e.name,blocks:s(e,!1!==t.includeHeader)}}(e,f,k);return f.webhookUrl?await async function(e,r,i,o){if(!e.text&&!e.blocks)return void o.warn("Slack message has no text or blocks; skipping");const s={...e.text?{text:e.text}:{},...e.blocks?{blocks:e.blocks}:{},unfurl_links:r.unfurlLinks??!1,unfurl_media:r.unfurlMedia??!1,mrkdwn:r.mrkdwn??!0},a=i?.sendServer||n,c=await a(r.webhookUrl,JSON.stringify(s));t(c)&&!1===c.ok&&o.warn(`Slack webhook error: ${JSON.stringify(c)}`)}(d,f,l,u):await async function(e,t,n,r,i){const o=r?.slackClient||t._client;if(!o)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 s=n.channel??t.channel;if(n.dm){if(!n.user)return void i.throw("Slack DM requires `mapping.settings.user`");const e=await o.conversations.open({users:n.user}),t=e.channel?.id;if(!t)return void i.warn("Slack conversations.open returned no channel id");s=t}if(!s)return void i.throw("Slack push requires a channel (set settings.channel or mapping.settings.channel)");const a={channel:s,...e.text?{text:e.text}:{},...e.blocks?{blocks:e.blocks}:{},unfurl_links:t.unfurlLinks??!1,unfurl_media:t.unfurlMedia??!1,mrkdwn:t.mrkdwn??!0};(n.threadTs??t.threadTs)&&(a.thread_ts=n.threadTs??t.threadTs);n.replyBroadcast&&(a.reply_broadcast=!0);if(n.ephemeral)return n.user?void await o.chat.postEphemeral({...a,user:n.user}):void i.throw("Slack ephemeral requires `mapping.settings.user`");await o.chat.postMessage(a)}(d,f,k,l,u)};var l={},u={default:void 0,fiveRetriesInFiveMinutes:{retries:5,factor:1.96},none:{retries:0}},f={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;const s=r?.slackClient;if(s)return i;try{const{WebClient:t}=e("@slack/web-api"),n=u[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 c(e,t)},k=f;export{l as DestinationSlack,k as default,f as destinationSlack};//# sourceMappingURL=index.mjs.map
@@ -1 +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"]}
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 sourceType = event.source?.type;\n if (sourceType) {\n blocks.push({\n type: 'context',\n elements: [{ type: 'mrkdwn', text: `Source: ${sourceType}` }],\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":";;;;;;;;AAGO,SAAS,UACd,gBAA+B,CAAC,GAChC,QACQ;AACR,QAAM,WAAY,cAAc,YAAY,CAAC;AAC7C,QAAM,EAAE,OAAO,YAAY,QAAQ,IAAI;AAEvC,MAAI,CAAC,SAAS,CAAC;AACb,WAAO,MAAM,uDAAuD;AACtE,MAAI,SAAS;AACX,WAAO;AAAA,MACL;AAAA,IACF;AACF,MAAI,SAAS,CAAC;AACZ,WAAO;AAAA,MACL;AAAA,IACF;AAEF,SAAO;AAAA,IACL,GAAG;AAAA,IACH,UAAU,EAAE,GAAG,SAAS;AAAA,EAC1B;AACF;;;ACzBA,SAAS,gBAAgB;AACzB,SAAS,kBAAkB;;;ACO3B,IAAM,WAAW;AAEjB,SAAS,QAAQ,KAAc,MAAuB;AACpD,SAAO,KAAK,MAAM,GAAG,EAAE,OAAgB,CAAC,KAAK,QAAQ;AACnD,QAAI,OAAO,OAAO,QAAQ,YAAY,OAAQ,KAAgB;AAC5D,aAAQ,IAAgC,GAAG;AAAA,IAC7C;AACA,WAAO;AAAA,EACT,GAAG,GAAG;AACR;AAEO,SAAS,YAAY,UAAkB,OAA+B;AAC3E,SAAO,SAAS,QAAQ,UAAU,CAAC,GAAG,SAAiB;AACrD,UAAM,QAAQ,QAAQ,OAAO,KAAK,KAAK,CAAC;AACxC,QAAI,UAAU,UAAa,UAAU,KAAM,QAAO;AAClD,QAAI,OAAO,UAAU,SAAU,QAAO,KAAK,UAAU,KAAK;AAC1D,WAAO,OAAO,KAAK;AAAA,EACrB,CAAC;AACH;AAEO,SAAS,aACd,OACA,UACA,MACc;AAEd,MAAI,KAAK,UAAU,KAAK,OAAO,SAAS,GAAG;AACzC,UAAM,SAAuB;AAAA,MAC3B,QAAQ,kBAAkB,KAAK,QAAQ,KAAK;AAAA,IAC9C;AACA,UAAMA,QAAO,KAAK,QAAQ,SAAS;AACnC,QAAIA,MAAM,QAAO,OAAO,YAAYA,OAAM,KAAK;AAC/C,WAAO;AAAA,EACT;AAGA,QAAM,OAAO,KAAK,QAAQ,SAAS;AACnC,MAAI,MAAM;AACR,WAAO,EAAE,MAAM,YAAY,MAAM,KAAK,EAAE;AAAA,EAC1C;AAGA,MAAI,SAAS,UAAU,SAAS,OAAO,SAAS,GAAG;AACjD,WAAO,EAAE,QAAQ,kBAAkB,SAAS,QAAQ,KAAK,EAAE;AAAA,EAC7D;AAGA,SAAO;AAAA,IACL,MAAM,MAAM;AAAA,IACZ,QAAQ,cAAc,OAAO,SAAS,kBAAkB,KAAK;AAAA,EAC/D;AACF;AAEA,SAAS,kBACP,QACA,OACc;AACd,SAAO,KAAK,MAAM,YAAY,KAAK,UAAU,MAAM,GAAG,KAAK,CAAC;AAC9D;AAEA,SAAS,cACP,OACA,eACc;AACd,QAAM,SAAuB,CAAC;AAE9B,MAAI,eAAe;AACjB,WAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,MAAM,EAAE,MAAM,cAAc,MAAM,MAAM,KAAK;AAAA,IAC/C,CAAC;AAAA,EACH;AAEA,QAAM,OAAQ,MAAM,QAAQ,CAAC;AAC7B,QAAM,SAAS,OAAO,QAAQ,IAAI,EAC/B,OAAO,CAAC,CAAC,EAAE,CAAC,MAAM,MAAM,UAAa,MAAM,IAAI,EAC/C,IAAI,CAAC,CAAC,KAAK,KAAK,OAAO;AAAA,IACtB,MAAM;AAAA,IACN,MAAM,IAAI,GAAG;AAAA,EAAO,UAAU,KAAK,CAAC;AAAA,EACtC,EAAE;AAEJ,MAAI,OAAO,SAAS,GAAG;AACrB,WAAO,KAAK,EAAE,MAAM,WAAW,OAAO,CAAC;AAAA,EACzC;AAEA,QAAM,aAAa,MAAM,QAAQ;AACjC,MAAI,YAAY;AACd,WAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,UAAU,CAAC,EAAE,MAAM,UAAU,MAAM,WAAW,UAAU,GAAG,CAAC;AAAA,IAC9D,CAAC;AAAA,EACH;AAEA,SAAO;AACT;AAEA,SAAS,UAAU,OAAwB;AACzC,MAAI,UAAU,QAAQ,UAAU,OAAW,QAAO;AAClD,MAAI,OAAO,UAAU,SAAU,QAAO,KAAK,UAAU,KAAK;AAC1D,SAAO,OAAO,KAAK;AACrB;;;ADvGO,IAAM,OAAe,eAAgB,OAAO,KAAK;AACtD,QAAM,EAAE,QAAQ,MAAM,KAAK,OAAO,IAAI;AACtC,QAAM,WAAW,OAAO;AACxB,QAAM,eAAgB,MAAM,YAAY,CAAC;AAEzC,QAAM,UAAU,aAAa,OAAO,UAAU,YAAY;AAG1D,MAAI,SAAS,YAAY;AACvB,WAAO,MAAM,YAAY,SAAS,UAAU,KAAK,MAAM;AAAA,EACzD;AAGA,SAAO,MAAM,WAAW,SAAS,UAAU,cAAc,KAAK,MAAM;AACtE;AAEA,eAAe,YACb,SACA,UACA,KACA,QACe;AACf,MAAI,CAAC,QAAQ,QAAQ,CAAC,QAAQ,QAAQ;AACpC,WAAO,KAAK,+CAA+C;AAC3D;AAAA,EACF;AAEA,QAAM,OAAO;AAAA,IACX,GAAI,QAAQ,OAAO,EAAE,MAAM,QAAQ,KAAK,IAAI,CAAC;AAAA,IAC7C,GAAI,QAAQ,SAAS,EAAE,QAAQ,QAAQ,OAAO,IAAI,CAAC;AAAA,IACnD,cAAc,SAAS,eAAe;AAAA,IACtC,cAAc,SAAS,eAAe;AAAA,IACtC,QAAQ,SAAS,UAAU;AAAA,EAC7B;AAEA,QAAM,OAAO,KAAK,cAAc;AAChC,QAAM,SAAS,MAAM;AAAA,IACnB,SAAS;AAAA,IACT,KAAK,UAAU,IAAI;AAAA,EACrB;AAEA,MAAI,SAAS,MAAM,KAAK,OAAO,OAAO,OAAO;AAC3C,WAAO,KAAK,wBAAwB,KAAK,UAAU,MAAM,CAAC,EAAE;AAAA,EAC9D;AACF;AAEA,eAAe,WACb,SACA,UACA,MACA,KACA,QACe;AACf,QAAM,SACH,KAAK,eACL,SAAS;AAEZ,MAAI,CAAC,QAAQ;AACX,WAAO,KAAK,iCAAiC;AAC7C;AAAA,EACF;AAEA,MAAI,CAAC,QAAQ,QAAQ,CAAC,QAAQ,QAAQ;AACpC,WAAO,KAAK,+CAA+C;AAC3D;AAAA,EACF;AAGA,MAAI,UAAU,KAAK,WAAW,SAAS;AAGvC,MAAI,KAAK,IAAI;AACX,QAAI,CAAC,KAAK,MAAM;AACd,aAAO,MAAM,2CAA2C;AACxD;AAAA,IACF;AACA,UAAM,SAAS,MAAM,OAAO,cAAc,KAAK,EAAE,OAAO,KAAK,KAAK,CAAC;AACnE,UAAM,YACJ,OAGA,SAAS;AACX,QAAI,CAAC,WAAW;AACd,aAAO,KAAK,iDAAiD;AAC7D;AAAA,IACF;AACA,cAAU;AAAA,EACZ;AAEA,MAAI,CAAC,SAAS;AACZ,WAAO;AAAA,MACL;AAAA,IACF;AACA;AAAA,EACF;AAEA,QAAM,WAAoC;AAAA,IACxC;AAAA,IACA,GAAI,QAAQ,OAAO,EAAE,MAAM,QAAQ,KAAK,IAAI,CAAC;AAAA,IAC7C,GAAI,QAAQ,SAAS,EAAE,QAAQ,QAAQ,OAAO,IAAI,CAAC;AAAA,IACnD,cAAc,SAAS,eAAe;AAAA,IACtC,cAAc,SAAS,eAAe;AAAA,IACtC,QAAQ,SAAS,UAAU;AAAA,EAC7B;AAEA,MAAI,KAAK,YAAY,SAAS,UAAU;AACtC,aAAS,YAAY,KAAK,YAAY,SAAS;AAAA,EACjD;AACA,MAAI,KAAK,eAAgB,UAAS,kBAAkB;AAGpD,MAAI,KAAK,WAAW;AAClB,QAAI,CAAC,KAAK,MAAM;AACd,aAAO,MAAM,kDAAkD;AAC/D;AAAA,IACF;AACA,UAAM,OAAO,KAAK,cAAc;AAAA,MAC9B,GAAG;AAAA,MACH,MAAM,KAAK;AAAA,IACb,CAAU;AACV;AAAA,EACF;AAEA,QAAM,OAAO,KAAK,YAAY,QAAiB;AACjD;;;AEjIA;;;ACOA,IAAM,gBAAyC;AAAA,EAC7C,SAAS;AAAA,EACT,0BAA0B,EAAE,SAAS,GAAG,QAAQ,KAAK;AAAA,EACrD,MAAM,EAAE,SAAS,EAAE;AACrB;AAEO,IAAM,mBAAgC;AAAA,EAC3C,MAAM;AAAA,EAEN,QAAQ,CAAC;AAAA,EAET,KAAK,EAAE,QAAQ,eAAe,QAAQ,IAAI,GAAG;AAC3C,UAAM,SAAS,UAAU,eAAe,MAAM;AAC9C,UAAM,WAAW,OAAO;AAGxB,QAAI,SAAS,WAAY,QAAO;AAGhC,UAAM,YAAa,KACf;AACJ,QAAI,UAAW,QAAO;AAItB,QAAI;AACF,YAAM,EAAE,UAAU,IAAI,UAAQ,gBAAgB;AAC9C,YAAM,cAAc,cAAc,SAAS,eAAe,SAAS;AACnE,eAAS,UAAU,IAAI,UAAU,SAAS,OAAO;AAAA,QAC/C,GAAI,gBAAgB,SAAY,EAAE,YAAY,IAAI,CAAC;AAAA,MACrD,CAAC;AAAA,IACH,SAAS,KAAK;AACZ,aAAO,MAAM,yCAAyC,GAAG,EAAE;AAAA,IAC7D;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,KAAK,OAAO,SAAS;AACzB,WAAO,MAAM,KAAK,OAAO,OAAO;AAAA,EAClC;AACF;AAEA,IAAO,gBAAQ;","names":["text"]}
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$meta": {
3
3
  "package": "@walkeros/server-destination-slack",
4
- "version": "3.4.2",
4
+ "version": "4.0.0-next-1777463920154",
5
5
  "type": "destination",
6
6
  "platform": [
7
7
  "server"
@@ -183,35 +183,21 @@
183
183
  "entity": "child",
184
184
  "data": {
185
185
  "is": "subordinated"
186
- },
187
- "nested": [],
188
- "context": {
189
- "element": [
190
- "child",
191
- 0
192
- ]
193
186
  }
194
187
  }
195
188
  ],
196
189
  "consent": {
197
190
  "functional": true
198
191
  },
199
- "id": "1700000600-gr0up-1",
192
+ "id": "dd77eeff5382e19d",
200
193
  "trigger": "test",
201
194
  "entity": "lead",
202
195
  "action": "submit",
203
196
  "timestamp": 1700000600,
204
197
  "timing": 3.14,
205
- "group": "gr0up",
206
- "count": 1,
207
- "version": {
208
- "source": "3.4.2",
209
- "tagging": 1
210
- },
211
198
  "source": {
212
- "type": "server",
213
- "id": "crm",
214
- "previous_id": ""
199
+ "type": "crm",
200
+ "platform": "server"
215
201
  }
216
202
  },
217
203
  "mapping": {
@@ -294,35 +280,21 @@
294
280
  "entity": "child",
295
281
  "data": {
296
282
  "is": "subordinated"
297
- },
298
- "nested": [],
299
- "context": {
300
- "element": [
301
- "child",
302
- 0
303
- ]
304
283
  }
305
284
  }
306
285
  ],
307
286
  "consent": {
308
287
  "functional": true
309
288
  },
310
- "id": "1700000700-gr0up-1",
289
+ "id": "1bc8d24d77720f48",
311
290
  "trigger": "test",
312
291
  "entity": "deploy",
313
292
  "action": "complete",
314
293
  "timestamp": 1700000700,
315
294
  "timing": 3.14,
316
- "group": "gr0up",
317
- "count": 1,
318
- "version": {
319
- "source": "3.4.2",
320
- "tagging": 1
321
- },
322
295
  "source": {
323
- "type": "web",
324
- "id": "https://localhost:80",
325
- "previous_id": "http://remotehost:9001"
296
+ "type": "collector",
297
+ "schema": "4"
326
298
  }
327
299
  },
328
300
  "settings": {
@@ -371,35 +343,21 @@
371
343
  "entity": "child",
372
344
  "data": {
373
345
  "is": "subordinated"
374
- },
375
- "nested": [],
376
- "context": {
377
- "element": [
378
- "child",
379
- 0
380
- ]
381
346
  }
382
347
  }
383
348
  ],
384
349
  "consent": {
385
350
  "functional": true
386
351
  },
387
- "id": "1700000500-gr0up-1",
352
+ "id": "9a25fecb3fa79eff",
388
353
  "trigger": "test",
389
354
  "entity": "quota",
390
355
  "action": "warning",
391
356
  "timestamp": 1700000500,
392
357
  "timing": 3.14,
393
- "group": "gr0up",
394
- "count": 1,
395
- "version": {
396
- "source": "3.4.2",
397
- "tagging": 1
398
- },
399
358
  "source": {
400
- "type": "web",
401
- "id": "https://localhost:80",
402
- "previous_id": "http://remotehost:9001"
359
+ "type": "collector",
360
+ "schema": "4"
403
361
  }
404
362
  },
405
363
  "mapping": {
@@ -456,35 +414,21 @@
456
414
  "entity": "child",
457
415
  "data": {
458
416
  "is": "subordinated"
459
- },
460
- "nested": [],
461
- "context": {
462
- "element": [
463
- "child",
464
- 0
465
- ]
466
417
  }
467
418
  }
468
419
  ],
469
420
  "consent": {
470
421
  "functional": true
471
422
  },
472
- "id": "1700000200-gr0up-1",
423
+ "id": "7fc14eb400b3b9c4",
473
424
  "trigger": "test",
474
425
  "entity": "error",
475
426
  "action": "occur",
476
427
  "timestamp": 1700000200,
477
428
  "timing": 3.14,
478
- "group": "gr0up",
479
- "count": 1,
480
- "version": {
481
- "source": "3.4.2",
482
- "tagging": 1
483
- },
484
429
  "source": {
485
- "type": "web",
486
- "id": "https://localhost:80",
487
- "previous_id": "http://remotehost:9001"
430
+ "type": "collector",
431
+ "schema": "4"
488
432
  }
489
433
  },
490
434
  "mapping": {
@@ -583,22 +527,15 @@
583
527
  "consent": {
584
528
  "functional": true
585
529
  },
586
- "id": "1700000100-gr0up-1",
530
+ "id": "ad9a061b1a250617",
587
531
  "trigger": "load",
588
532
  "entity": "order",
589
533
  "action": "complete",
590
534
  "timestamp": 1700000100,
591
535
  "timing": 3.14,
592
- "group": "gr0up",
593
- "count": 1,
594
- "version": {
595
- "source": "3.4.2",
596
- "tagging": 1
597
- },
598
536
  "source": {
599
- "type": "web",
600
- "id": "https://localhost:80",
601
- "previous_id": "http://remotehost:9001"
537
+ "type": "collector",
538
+ "schema": "4"
602
539
  }
603
540
  },
604
541
  "mapping": {
@@ -651,35 +588,21 @@
651
588
  "entity": "child",
652
589
  "data": {
653
590
  "is": "subordinated"
654
- },
655
- "nested": [],
656
- "context": {
657
- "element": [
658
- "child",
659
- 0
660
- ]
661
591
  }
662
592
  }
663
593
  ],
664
594
  "consent": {
665
595
  "functional": true
666
596
  },
667
- "id": "1700000400-gr0up-1",
597
+ "id": "302a1f14efe570d8",
668
598
  "trigger": "test",
669
599
  "entity": "checkout",
670
600
  "action": "step",
671
601
  "timestamp": 1700000400,
672
602
  "timing": 3.14,
673
- "group": "gr0up",
674
- "count": 1,
675
- "version": {
676
- "source": "3.4.2",
677
- "tagging": 1
678
- },
679
603
  "source": {
680
- "type": "web",
681
- "id": "https://localhost:80",
682
- "previous_id": "http://remotehost:9001"
604
+ "type": "collector",
605
+ "schema": "4"
683
606
  }
684
607
  },
685
608
  "mapping": {
@@ -733,35 +656,21 @@
733
656
  "entity": "child",
734
657
  "data": {
735
658
  "is": "subordinated"
736
- },
737
- "nested": [],
738
- "context": {
739
- "element": [
740
- "child",
741
- 0
742
- ]
743
659
  }
744
660
  }
745
661
  ],
746
662
  "consent": {
747
663
  "functional": true
748
664
  },
749
- "id": "1700000300-gr0up-1",
665
+ "id": "ed77681882fec7b8",
750
666
  "trigger": "test",
751
667
  "entity": "user",
752
668
  "action": "signup",
753
669
  "timestamp": 1700000300,
754
670
  "timing": 3.14,
755
- "group": "gr0up",
756
- "count": 1,
757
- "version": {
758
- "source": "3.4.2",
759
- "tagging": 1
760
- },
761
671
  "source": {
762
- "type": "web",
763
- "id": "https://localhost:80",
764
- "previous_id": "http://remotehost:9001"
672
+ "type": "collector",
673
+ "schema": "4"
765
674
  }
766
675
  },
767
676
  "mapping": {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@walkeros/server-destination-slack",
3
3
  "description": "Slack server destination for walkerOS (Incoming Webhook + @slack/web-api, Block Kit, channel routing, threading, DMs)",
4
- "version": "3.4.2",
4
+ "version": "4.0.0-next-1777463920154",
5
5
  "license": "MIT",
6
6
  "exports": {
7
7
  ".": {
@@ -35,11 +35,11 @@
35
35
  },
36
36
  "dependencies": {
37
37
  "@slack/web-api": "^7.0.0",
38
- "@walkeros/core": "3.4.2",
39
- "@walkeros/server-core": "3.4.2"
38
+ "@walkeros/core": "4.0.0-next-1777463920154",
39
+ "@walkeros/server-core": "4.0.0-next-1777463920154"
40
40
  },
41
41
  "devDependencies": {
42
- "@walkeros/collector": "3.4.2"
42
+ "@walkeros/collector": "4.0.0-next-1777463920154"
43
43
  },
44
44
  "repository": {
45
45
  "url": "git+https://github.com/elbwalker/walkerOS.git",