@storyshelf/notify-chat 0.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Siddhant Gupta
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,30 @@
1
+ # @storyshelf/notify-chat
2
+
3
+ Chat notification providers for StoryShelf: formatted build and admin alerts over Slack and Teams incoming webhooks (plus a log provider for tests/dev). Zero runtime dependencies — all sends go through `@storyshelf/core/utils` (`httpText`: timeout + retry with `Retry-After`).
4
+
5
+ ## Install
6
+
7
+ ```sh
8
+ nub add @storyshelf/notify-chat
9
+ ```
10
+
11
+ ## Quick start
12
+
13
+ ```ts
14
+ import { createShelfApp } from "@storyshelf/app";
15
+ import { chatNotifiers } from "@storyshelf/notify-chat";
16
+
17
+ const app = createShelfApp({ database, storage, notifiers: chatNotifiers });
18
+ ```
19
+
20
+ ## Subpaths
21
+
22
+ Each provider is importable on its own (root aggregates all three):
23
+
24
+ ```ts
25
+ import { slackWebhookNotifier } from "@storyshelf/notify-chat/slack";
26
+ import { teamsWorkflowNotifier } from "@storyshelf/notify-chat/teams";
27
+ import { logNotifier } from "@storyshelf/notify-chat/log";
28
+ ```
29
+
30
+ Project admins then add channels (`slack-webhook`, `teams-workflow`, `log`) with an event filter and display toggles. Webhook URLs are channel secrets (AES-256-GCM at rest, decrypted in memory at send time). See [Notifications](/guides/notifications/).
@@ -0,0 +1,12 @@
1
+ import __tsdown_shims_path from 'node:path';
2
+ import __tsdown_shims_url from 'node:url';
3
+ import { logConfigSchema, logNotifier } from "./log.mjs";
4
+ import { ChatToggles, chatTogglesSchema } from "./types.mjs";
5
+ import { slackWebhookConfigSchema, slackWebhookNotifier } from "./slack.mjs";
6
+ import { teamsWorkflowConfigSchema, teamsWorkflowNotifier } from "./teams.mjs";
7
+ //#region src/index.d.ts
8
+ /** All chat providers in one list for `ShelfOptions.notifiers`. */
9
+ declare const chatNotifiers: import("@storyshelf/core/adapter/notifier").NotifierProvider[];
10
+ //#endregion
11
+ export { type ChatToggles, chatNotifiers, chatTogglesSchema, logConfigSchema, logNotifier, slackWebhookConfigSchema, slackWebhookNotifier, teamsWorkflowConfigSchema, teamsWorkflowNotifier };
12
+ //# sourceMappingURL=index.d.mts.map
package/dist/index.mjs ADDED
@@ -0,0 +1,22 @@
1
+ import "node:path";
2
+ import "node:url";
3
+ import.meta.url;
4
+ import { logConfigSchema, logNotifier } from "./log.mjs";
5
+ import { chatTogglesSchema } from "./types.mjs";
6
+ import { slackWebhookConfigSchema, slackWebhookNotifier } from "./slack.mjs";
7
+ import { teamsWorkflowConfigSchema, teamsWorkflowNotifier } from "./teams.mjs";
8
+ //#region src/index.ts
9
+ /**
10
+ * Chat notification providers: formatted build and admin alerts over
11
+ * Slack and Teams incoming webhooks (plus a log provider for tests/dev).
12
+ */
13
+ /** All chat providers in one list for `ShelfOptions.notifiers`. */
14
+ const chatNotifiers = [
15
+ slackWebhookNotifier,
16
+ teamsWorkflowNotifier,
17
+ logNotifier
18
+ ];
19
+ //#endregion
20
+ export { chatNotifiers, chatTogglesSchema, logConfigSchema, logNotifier, slackWebhookConfigSchema, slackWebhookNotifier, teamsWorkflowConfigSchema, teamsWorkflowNotifier };
21
+
22
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["/**\n * Chat notification providers: formatted build and admin alerts over\n * Slack and Teams incoming webhooks (plus a log provider for tests/dev).\n */\nimport { logNotifier } from \"./log.ts\";\nimport { slackWebhookNotifier } from \"./slack.ts\";\nimport { teamsWorkflowNotifier } from \"./teams.ts\";\n\n/** Slack incoming-webhook provider (`slack-webhook`). */\nexport { slackWebhookNotifier };\n/** Teams workflow-webhook provider (`teams-workflow`). */\nexport { teamsWorkflowNotifier };\n/** Log-only provider for tests, dev, and unconfigured servers (`log`). */\nexport { logNotifier };\n\n/** All chat providers in one list for `ShelfOptions.notifiers`. */\nexport const chatNotifiers = [slackWebhookNotifier, teamsWorkflowNotifier, logNotifier];\n\nexport type { ChatToggles } from \"./types.ts\";\nexport { chatTogglesSchema } from \"./types.ts\";\nexport { logConfigSchema } from \"./log.ts\";\nexport { slackWebhookConfigSchema } from \"./slack.ts\";\nexport { teamsWorkflowConfigSchema } from \"./teams.ts\";\n"],"mappings":";;;;;;;;;;;;;AAgBA,MAAa,gBAAgB;CAAC;CAAsB;CAAuB;AAAW"}
package/dist/log.d.mts ADDED
@@ -0,0 +1,14 @@
1
+ import __tsdown_shims_path from 'node:path';
2
+ import __tsdown_shims_url from 'node:url';
3
+ import { z } from "zod";
4
+ import { NotifierProvider } from "@storyshelf/core/adapter/notifier";
5
+ //#region src/log.d.ts
6
+ /** Validation schema for the log provider config (accepts anything). */
7
+ declare const logConfigSchema: z.ZodObject<{}, z.core.$loose>;
8
+ /** Log-only chat provider for tests, dev, and unconfigured servers. */
9
+ declare const logNotifier: NotifierProvider;
10
+ /** Describe the log provider (name, version, config schema). */
11
+ declare function getMetadata(): NotifierProvider["metadata"];
12
+ //#endregion
13
+ export { getMetadata, logConfigSchema, logNotifier };
14
+ //# sourceMappingURL=log.d.mts.map
package/dist/log.mjs ADDED
@@ -0,0 +1,39 @@
1
+ import "node:path";
2
+ import "node:url";
3
+ import.meta.url;
4
+ import { z } from "zod";
5
+ //#region src/log.ts
6
+ /** Validation schema for the log provider config (accepts anything). */
7
+ const logConfigSchema = z.object({}).loose();
8
+ /** Log-only chat provider for tests, dev, and unconfigured servers. */
9
+ const logNotifier = {
10
+ metadata: getMetadata(),
11
+ create(opts) {
12
+ logConfigSchema.parse(opts.config);
13
+ const logger = opts.logger?.child({ component: "notify-chat-log" }) ?? opts.logger;
14
+ return {
15
+ metadata: getMetadata(),
16
+ async send(input) {
17
+ logger?.info({
18
+ channelId: input.channel.id,
19
+ subject: input.formatted.subject
20
+ }, "chat notification (log)");
21
+ }
22
+ };
23
+ }
24
+ };
25
+ /** Describe the log provider (name, version, config schema). */
26
+ function getMetadata() {
27
+ return {
28
+ name: "Log",
29
+ version: globalThis.__PKG_VERSION__ ?? "0.0.0",
30
+ description: "Chat notifications to the server log (tests and dev)",
31
+ kind: "log",
32
+ category: "notifier",
33
+ schema: logConfigSchema
34
+ };
35
+ }
36
+ //#endregion
37
+ export { getMetadata, logConfigSchema, logNotifier };
38
+
39
+ //# sourceMappingURL=log.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log.mjs","names":[],"sources":["../src/log.ts"],"sourcesContent":["import type { NotifierAdapter, NotifierProvider } from \"@storyshelf/core/adapter/notifier\";\nimport type { Logger } from \"@storyshelf/core/logger\";\nimport { z } from \"zod\";\n\ndeclare const __PKG_VERSION__: string | undefined;\n\n/** Validation schema for the log provider config (accepts anything). */\nexport const logConfigSchema = z.object({}).loose();\n\n/** Log-only chat provider for tests, dev, and unconfigured servers. */\nexport const logNotifier: NotifierProvider = {\n metadata: getMetadata(),\n create(opts: { config: unknown; secret?: string; logger?: Logger }): NotifierAdapter {\n logConfigSchema.parse(opts.config);\n const logger = opts.logger?.child({ component: \"notify-chat-log\" }) ?? opts.logger;\n return {\n metadata: getMetadata(),\n // oxlint-disable-next-line eslint/require-await -- send is async by contract\n async send(input): Promise<void> {\n logger?.info(\n { channelId: input.channel.id, subject: input.formatted.subject },\n \"chat notification (log)\",\n );\n },\n };\n },\n};\n\n/** Describe the log provider (name, version, config schema). */\nexport function getMetadata(): NotifierProvider[\"metadata\"] {\n return {\n name: \"Log\",\n version: (globalThis as unknown as { __PKG_VERSION__?: string }).__PKG_VERSION__ ?? \"0.0.0\",\n description: \"Chat notifications to the server log (tests and dev)\",\n kind: \"log\",\n category: \"notifier\",\n schema: logConfigSchema,\n };\n}\n"],"mappings":";;;;;;AAOA,MAAa,kBAAkB,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM;;AAGlD,MAAa,cAAgC;CAC3C,UAAU,YAAY;CACtB,OAAO,MAA8E;EACnF,gBAAgB,MAAM,KAAK,MAAM;EACjC,MAAM,SAAS,KAAK,QAAQ,MAAM,EAAE,WAAW,kBAAkB,CAAC,KAAK,KAAK;EAC5E,OAAO;GACL,UAAU,YAAY;GAEtB,MAAM,KAAK,OAAsB;IAC/B,QAAQ,KACN;KAAE,WAAW,MAAM,QAAQ;KAAI,SAAS,MAAM,UAAU;IAAQ,GAChE,yBACF;GACF;EACF;CACF;AACF;;AAGA,SAAgB,cAA4C;CAC1D,OAAO;EACL,MAAM;EACN,SAAU,WAAuD,mBAAmB;EACpF,aAAa;EACb,MAAM;EACN,UAAU;EACV,QAAQ;CACV;AACF"}
@@ -0,0 +1,24 @@
1
+ import __tsdown_shims_path from 'node:path';
2
+ import __tsdown_shims_url from 'node:url';
3
+ import { ChatToggles } from "./types.mjs";
4
+ import { NotifierProvider } from "@storyshelf/core/adapter/notifier";
5
+ //#region src/slack.d.ts
6
+ /** Validation schema for the Slack incoming-webhook config. */
7
+ declare const slackWebhookConfigSchema: import("zod").ZodObject<{
8
+ style: import("zod").ZodOptional<import("zod").ZodEnum<{
9
+ compact: "compact";
10
+ verbose: "verbose";
11
+ }>>;
12
+ subjectPrefix: import("zod").ZodOptional<import("zod").ZodString>;
13
+ includeAuthor: import("zod").ZodOptional<import("zod").ZodBoolean>;
14
+ includeMessage: import("zod").ZodOptional<import("zod").ZodBoolean>;
15
+ includeCounts: import("zod").ZodOptional<import("zod").ZodBoolean>;
16
+ username: import("zod").ZodOptional<import("zod").ZodString>;
17
+ }, import("zod/v4/core").$strip>;
18
+ /** Slack incoming-webhook chat provider (Block Kit section + fallback text). */
19
+ declare const slackWebhookNotifier: NotifierProvider;
20
+ /** Describe the Slack provider (name, version, config schema). */
21
+ declare function getMetadata(): NotifierProvider["metadata"];
22
+ //#endregion
23
+ export { type ChatToggles, getMetadata, slackWebhookConfigSchema, slackWebhookNotifier };
24
+ //# sourceMappingURL=slack.d.mts.map
package/dist/slack.mjs ADDED
@@ -0,0 +1,67 @@
1
+ import "node:path";
2
+ import "node:url";
3
+ import.meta.url;
4
+ import { chatTogglesSchema } from "./types.mjs";
5
+ import { httpText } from "@storyshelf/core/utils";
6
+ //#region src/slack.ts
7
+ /** Validation schema for the Slack incoming-webhook config. */
8
+ const slackWebhookConfigSchema = chatTogglesSchema;
9
+ /** Slack incoming-webhook chat provider (Block Kit section + fallback text). */
10
+ const slackWebhookNotifier = {
11
+ metadata: getMetadata(),
12
+ create(opts) {
13
+ return createSlackAdapter({
14
+ config: slackWebhookConfigSchema.parse(opts.config),
15
+ webhookUrl: opts.secret,
16
+ logger: opts.logger
17
+ });
18
+ }
19
+ };
20
+ /** Describe the Slack provider (name, version, config schema). */
21
+ function getMetadata() {
22
+ return {
23
+ name: "Slack",
24
+ version: globalThis.__PKG_VERSION__ ?? "0.0.0",
25
+ description: "Chat messages via Slack incoming webhooks",
26
+ kind: "slack-webhook",
27
+ category: "notifier",
28
+ logo: "slack",
29
+ schema: slackWebhookConfigSchema
30
+ };
31
+ }
32
+ function createSlackAdapter(options) {
33
+ const logger = options.logger?.child({ component: "notify-chat-slack" });
34
+ return {
35
+ metadata: getMetadata(),
36
+ async send(input) {
37
+ const url = requireWebhookUrl(options.webhookUrl);
38
+ const payload = {
39
+ text: input.formatted.subject,
40
+ username: options.config.username,
41
+ blocks: [{
42
+ type: "section",
43
+ text: {
44
+ type: "mrkdwn",
45
+ text: input.formatted.markdown
46
+ }
47
+ }]
48
+ };
49
+ logger?.debug({ channelId: input.channel.id }, "posting slack message");
50
+ await httpText(url, {
51
+ method: "POST",
52
+ json: payload,
53
+ logger
54
+ });
55
+ logger?.info({ channelId: input.channel.id }, "slack message posted");
56
+ }
57
+ };
58
+ }
59
+ /** Incoming-webhook URL arrives as the decrypted channel secret. */
60
+ function requireWebhookUrl(secret) {
61
+ if (!secret) throw new Error("Slack channel is missing its webhook URL secret");
62
+ return secret;
63
+ }
64
+ //#endregion
65
+ export { getMetadata, slackWebhookConfigSchema, slackWebhookNotifier };
66
+
67
+ //# sourceMappingURL=slack.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slack.mjs","names":[],"sources":["../src/slack.ts"],"sourcesContent":["import type { NotifierAdapter, NotifierProvider } from \"@storyshelf/core/adapter/notifier\";\nimport type { Logger } from \"@storyshelf/core/logger\";\nimport { httpText } from \"@storyshelf/core/utils\";\nimport { chatTogglesSchema, type ChatToggles } from \"./types.ts\";\n\nexport type { ChatToggles };\n\ndeclare const __PKG_VERSION__: string | undefined;\n\n/** Validation schema for the Slack incoming-webhook config. */\nexport const slackWebhookConfigSchema = chatTogglesSchema;\n\n/** Slack incoming-webhook chat provider (Block Kit section + fallback text). */\nexport const slackWebhookNotifier: NotifierProvider = {\n metadata: getMetadata(),\n create(opts: { config: unknown; secret?: string; logger?: Logger }): NotifierAdapter {\n const config = slackWebhookConfigSchema.parse(opts.config);\n return createSlackAdapter({ config, webhookUrl: opts.secret, logger: opts.logger });\n },\n};\n\ninterface SlackAdapterOptions {\n config: ChatToggles;\n webhookUrl?: string;\n logger?: Logger;\n}\n\n/** Describe the Slack provider (name, version, config schema). */\nexport function getMetadata(): NotifierProvider[\"metadata\"] {\n return {\n name: \"Slack\",\n version: (globalThis as unknown as { __PKG_VERSION__?: string }).__PKG_VERSION__ ?? \"0.0.0\",\n description: \"Chat messages via Slack incoming webhooks\",\n kind: \"slack-webhook\",\n category: \"notifier\",\n logo: \"slack\",\n schema: slackWebhookConfigSchema,\n };\n}\n\nfunction createSlackAdapter(options: SlackAdapterOptions): NotifierAdapter {\n const logger = options.logger?.child({ component: \"notify-chat-slack\" });\n return {\n metadata: getMetadata(),\n async send(input): Promise<void> {\n const url = requireWebhookUrl(options.webhookUrl);\n const payload = {\n text: input.formatted.subject,\n username: options.config.username,\n blocks: [\n {\n type: \"section\",\n text: { type: \"mrkdwn\", text: input.formatted.markdown },\n },\n ],\n };\n logger?.debug({ channelId: input.channel.id }, \"posting slack message\");\n await httpText(url, { method: \"POST\", json: payload, logger });\n logger?.info({ channelId: input.channel.id }, \"slack message posted\");\n },\n };\n}\n\n/** Incoming-webhook URL arrives as the decrypted channel secret. */\nfunction requireWebhookUrl(secret: string | undefined): string {\n if (!secret) {\n throw new Error(\"Slack channel is missing its webhook URL secret\");\n }\n return secret;\n}\n"],"mappings":";;;;;;;AAUA,MAAa,2BAA2B;;AAGxC,MAAa,uBAAyC;CACpD,UAAU,YAAY;CACtB,OAAO,MAA8E;EAEnF,OAAO,mBAAmB;GAAE,QADb,yBAAyB,MAAM,KAAK,MAClB;GAAG,YAAY,KAAK;GAAQ,QAAQ,KAAK;EAAO,CAAC;CACpF;AACF;;AASA,SAAgB,cAA4C;CAC1D,OAAO;EACL,MAAM;EACN,SAAU,WAAuD,mBAAmB;EACpF,aAAa;EACb,MAAM;EACN,UAAU;EACV,MAAM;EACN,QAAQ;CACV;AACF;AAEA,SAAS,mBAAmB,SAA+C;CACzE,MAAM,SAAS,QAAQ,QAAQ,MAAM,EAAE,WAAW,oBAAoB,CAAC;CACvE,OAAO;EACL,UAAU,YAAY;EACtB,MAAM,KAAK,OAAsB;GAC/B,MAAM,MAAM,kBAAkB,QAAQ,UAAU;GAChD,MAAM,UAAU;IACd,MAAM,MAAM,UAAU;IACtB,UAAU,QAAQ,OAAO;IACzB,QAAQ,CACN;KACE,MAAM;KACN,MAAM;MAAE,MAAM;MAAU,MAAM,MAAM,UAAU;KAAS;IACzD,CACF;GACF;GACA,QAAQ,MAAM,EAAE,WAAW,MAAM,QAAQ,GAAG,GAAG,uBAAuB;GACtE,MAAM,SAAS,KAAK;IAAE,QAAQ;IAAQ,MAAM;IAAS;GAAO,CAAC;GAC7D,QAAQ,KAAK,EAAE,WAAW,MAAM,QAAQ,GAAG,GAAG,sBAAsB;EACtE;CACF;AACF;;AAGA,SAAS,kBAAkB,QAAoC;CAC7D,IAAI,CAAC,QACH,MAAM,IAAI,MAAM,iDAAiD;CAEnE,OAAO;AACT"}
@@ -0,0 +1,24 @@
1
+ import __tsdown_shims_path from 'node:path';
2
+ import __tsdown_shims_url from 'node:url';
3
+ import { ChatToggles } from "./types.mjs";
4
+ import { NotifierProvider } from "@storyshelf/core/adapter/notifier";
5
+ //#region src/teams.d.ts
6
+ /** Validation schema for the Teams workflow-webhook config. */
7
+ declare const teamsWorkflowConfigSchema: import("zod").ZodObject<{
8
+ style: import("zod").ZodOptional<import("zod").ZodEnum<{
9
+ compact: "compact";
10
+ verbose: "verbose";
11
+ }>>;
12
+ subjectPrefix: import("zod").ZodOptional<import("zod").ZodString>;
13
+ includeAuthor: import("zod").ZodOptional<import("zod").ZodBoolean>;
14
+ includeMessage: import("zod").ZodOptional<import("zod").ZodBoolean>;
15
+ includeCounts: import("zod").ZodOptional<import("zod").ZodBoolean>;
16
+ username: import("zod").ZodOptional<import("zod").ZodString>;
17
+ }, import("zod/v4/core").$strip>;
18
+ /** Teams workflow-webhook chat provider (Adaptive Card message). */
19
+ declare const teamsWorkflowNotifier: NotifierProvider;
20
+ /** Describe the Teams provider (name, version, config schema). */
21
+ declare function getMetadata(): NotifierProvider["metadata"];
22
+ //#endregion
23
+ export { type ChatToggles, getMetadata, teamsWorkflowConfigSchema, teamsWorkflowNotifier };
24
+ //# sourceMappingURL=teams.d.mts.map
package/dist/teams.mjs ADDED
@@ -0,0 +1,75 @@
1
+ import "node:path";
2
+ import "node:url";
3
+ import.meta.url;
4
+ import { chatTogglesSchema } from "./types.mjs";
5
+ import { httpText } from "@storyshelf/core/utils";
6
+ //#region src/teams.ts
7
+ /** Validation schema for the Teams workflow-webhook config. */
8
+ const teamsWorkflowConfigSchema = chatTogglesSchema;
9
+ /** Teams workflow-webhook chat provider (Adaptive Card message). */
10
+ const teamsWorkflowNotifier = {
11
+ metadata: getMetadata(),
12
+ create(opts) {
13
+ return createTeamsAdapter({
14
+ config: teamsWorkflowConfigSchema.parse(opts.config),
15
+ webhookUrl: opts.secret,
16
+ logger: opts.logger
17
+ });
18
+ }
19
+ };
20
+ /** Describe the Teams provider (name, version, config schema). */
21
+ function getMetadata() {
22
+ return {
23
+ name: "Microsoft Teams",
24
+ version: globalThis.__PKG_VERSION__ ?? "0.0.0",
25
+ description: "Chat messages via Teams workflow webhooks",
26
+ kind: "teams-workflow",
27
+ category: "notifier",
28
+ logo: "teams",
29
+ schema: teamsWorkflowConfigSchema
30
+ };
31
+ }
32
+ function createTeamsAdapter(options) {
33
+ const logger = options.logger?.child({ component: "notify-chat-teams" });
34
+ return {
35
+ metadata: getMetadata(),
36
+ async send(input) {
37
+ const url = requireWebhookUrl(options.webhookUrl);
38
+ const payload = {
39
+ type: "message",
40
+ attachments: [{
41
+ contentType: "application/vnd.microsoft.card.adaptive",
42
+ content: {
43
+ type: "AdaptiveCard",
44
+ version: "1.4",
45
+ body: [{
46
+ type: "TextBlock",
47
+ text: input.formatted.subject,
48
+ weight: "Bolder"
49
+ }, {
50
+ type: "TextBlock",
51
+ text: input.formatted.text,
52
+ wrap: true
53
+ }]
54
+ }
55
+ }]
56
+ };
57
+ logger?.debug({ channelId: input.channel.id }, "posting teams message");
58
+ await httpText(url, {
59
+ method: "POST",
60
+ json: payload,
61
+ logger
62
+ });
63
+ logger?.info({ channelId: input.channel.id }, "teams message posted");
64
+ }
65
+ };
66
+ }
67
+ /** Workflow URL arrives as the decrypted channel secret. */
68
+ function requireWebhookUrl(secret) {
69
+ if (!secret) throw new Error("Teams channel is missing its workflow URL secret");
70
+ return secret;
71
+ }
72
+ //#endregion
73
+ export { getMetadata, teamsWorkflowConfigSchema, teamsWorkflowNotifier };
74
+
75
+ //# sourceMappingURL=teams.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"teams.mjs","names":[],"sources":["../src/teams.ts"],"sourcesContent":["import type { NotifierAdapter, NotifierProvider } from \"@storyshelf/core/adapter/notifier\";\nimport type { Logger } from \"@storyshelf/core/logger\";\nimport { httpText } from \"@storyshelf/core/utils\";\nimport { chatTogglesSchema, type ChatToggles } from \"./types.ts\";\n\nexport type { ChatToggles };\n\ndeclare const __PKG_VERSION__: string | undefined;\n\n/** Validation schema for the Teams workflow-webhook config. */\nexport const teamsWorkflowConfigSchema = chatTogglesSchema;\n\n/** Teams workflow-webhook chat provider (Adaptive Card message). */\nexport const teamsWorkflowNotifier: NotifierProvider = {\n metadata: getMetadata(),\n create(opts: { config: unknown; secret?: string; logger?: Logger }): NotifierAdapter {\n const config = teamsWorkflowConfigSchema.parse(opts.config);\n return createTeamsAdapter({ config, webhookUrl: opts.secret, logger: opts.logger });\n },\n};\n\ninterface TeamsAdapterOptions {\n config: ChatToggles;\n webhookUrl?: string;\n logger?: Logger;\n}\n\n/** Describe the Teams provider (name, version, config schema). */\nexport function getMetadata(): NotifierProvider[\"metadata\"] {\n return {\n name: \"Microsoft Teams\",\n version: (globalThis as unknown as { __PKG_VERSION__?: string }).__PKG_VERSION__ ?? \"0.0.0\",\n description: \"Chat messages via Teams workflow webhooks\",\n kind: \"teams-workflow\",\n category: \"notifier\",\n logo: \"teams\",\n schema: teamsWorkflowConfigSchema,\n };\n}\n\nfunction createTeamsAdapter(options: TeamsAdapterOptions): NotifierAdapter {\n const logger = options.logger?.child({ component: \"notify-chat-teams\" });\n return {\n metadata: getMetadata(),\n async send(input): Promise<void> {\n const url = requireWebhookUrl(options.webhookUrl);\n const payload = {\n type: \"message\",\n attachments: [\n {\n contentType: \"application/vnd.microsoft.card.adaptive\",\n content: {\n type: \"AdaptiveCard\",\n version: \"1.4\",\n body: [\n { type: \"TextBlock\", text: input.formatted.subject, weight: \"Bolder\" },\n { type: \"TextBlock\", text: input.formatted.text, wrap: true },\n ],\n },\n },\n ],\n };\n logger?.debug({ channelId: input.channel.id }, \"posting teams message\");\n await httpText(url, { method: \"POST\", json: payload, logger });\n logger?.info({ channelId: input.channel.id }, \"teams message posted\");\n },\n };\n}\n\n/** Workflow URL arrives as the decrypted channel secret. */\nfunction requireWebhookUrl(secret: string | undefined): string {\n if (!secret) {\n throw new Error(\"Teams channel is missing its workflow URL secret\");\n }\n return secret;\n}\n"],"mappings":";;;;;;;AAUA,MAAa,4BAA4B;;AAGzC,MAAa,wBAA0C;CACrD,UAAU,YAAY;CACtB,OAAO,MAA8E;EAEnF,OAAO,mBAAmB;GAAE,QADb,0BAA0B,MAAM,KAAK,MACnB;GAAG,YAAY,KAAK;GAAQ,QAAQ,KAAK;EAAO,CAAC;CACpF;AACF;;AASA,SAAgB,cAA4C;CAC1D,OAAO;EACL,MAAM;EACN,SAAU,WAAuD,mBAAmB;EACpF,aAAa;EACb,MAAM;EACN,UAAU;EACV,MAAM;EACN,QAAQ;CACV;AACF;AAEA,SAAS,mBAAmB,SAA+C;CACzE,MAAM,SAAS,QAAQ,QAAQ,MAAM,EAAE,WAAW,oBAAoB,CAAC;CACvE,OAAO;EACL,UAAU,YAAY;EACtB,MAAM,KAAK,OAAsB;GAC/B,MAAM,MAAM,kBAAkB,QAAQ,UAAU;GAChD,MAAM,UAAU;IACd,MAAM;IACN,aAAa,CACX;KACE,aAAa;KACb,SAAS;MACP,MAAM;MACN,SAAS;MACT,MAAM,CACJ;OAAE,MAAM;OAAa,MAAM,MAAM,UAAU;OAAS,QAAQ;MAAS,GACrE;OAAE,MAAM;OAAa,MAAM,MAAM,UAAU;OAAM,MAAM;MAAK,CAC9D;KACF;IACF,CACF;GACF;GACA,QAAQ,MAAM,EAAE,WAAW,MAAM,QAAQ,GAAG,GAAG,uBAAuB;GACtE,MAAM,SAAS,KAAK;IAAE,QAAQ;IAAQ,MAAM;IAAS;GAAO,CAAC;GAC7D,QAAQ,KAAK,EAAE,WAAW,MAAM,QAAQ,GAAG,GAAG,sBAAsB;EACtE;CACF;AACF;;AAGA,SAAS,kBAAkB,QAAoC;CAC7D,IAAI,CAAC,QACH,MAAM,IAAI,MAAM,kDAAkD;CAEpE,OAAO;AACT"}
@@ -0,0 +1,21 @@
1
+ import __tsdown_shims_path from 'node:path';
2
+ import __tsdown_shims_url from 'node:url';
3
+ import { z } from "zod";
4
+ //#region src/types.d.ts
5
+ /** Display toggles shared by chat providers (Brand + toggles v1). */
6
+ declare const chatTogglesSchema: z.ZodObject<{
7
+ style: z.ZodOptional<z.ZodEnum<{
8
+ compact: "compact";
9
+ verbose: "verbose";
10
+ }>>;
11
+ subjectPrefix: z.ZodOptional<z.ZodString>;
12
+ includeAuthor: z.ZodOptional<z.ZodBoolean>;
13
+ includeMessage: z.ZodOptional<z.ZodBoolean>;
14
+ includeCounts: z.ZodOptional<z.ZodBoolean>;
15
+ username: z.ZodOptional<z.ZodString>;
16
+ }, z.core.$strip>;
17
+ /** Display toggles shared by chat providers. */
18
+ type ChatToggles = z.infer<typeof chatTogglesSchema>;
19
+ //#endregion
20
+ export { ChatToggles, chatTogglesSchema };
21
+ //# sourceMappingURL=types.d.mts.map
package/dist/types.mjs ADDED
@@ -0,0 +1,18 @@
1
+ import "node:path";
2
+ import "node:url";
3
+ import.meta.url;
4
+ import { z } from "zod";
5
+ //#region src/types.ts
6
+ /** Display toggles shared by chat providers (Brand + toggles v1). */
7
+ const chatTogglesSchema = z.object({
8
+ style: z.enum(["compact", "verbose"]).optional(),
9
+ subjectPrefix: z.string().min(1).optional(),
10
+ includeAuthor: z.boolean().optional(),
11
+ includeMessage: z.boolean().optional(),
12
+ includeCounts: z.boolean().optional(),
13
+ username: z.string().min(1).optional()
14
+ });
15
+ //#endregion
16
+ export { chatTogglesSchema };
17
+
18
+ //# sourceMappingURL=types.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.mjs","names":[],"sources":["../src/types.ts"],"sourcesContent":["import { z } from \"zod\";\n\n/** Display toggles shared by chat providers (Brand + toggles v1). */\nexport const chatTogglesSchema = z.object({\n style: z.enum([\"compact\", \"verbose\"]).optional(),\n subjectPrefix: z.string().min(1).optional(),\n includeAuthor: z.boolean().optional(),\n includeMessage: z.boolean().optional(),\n includeCounts: z.boolean().optional(),\n username: z.string().min(1).optional(),\n});\n\n/** Display toggles shared by chat providers. */\nexport type ChatToggles = z.infer<typeof chatTogglesSchema>;\n"],"mappings":";;;;;;AAGA,MAAa,oBAAoB,EAAE,OAAO;CACxC,OAAO,EAAE,KAAK,CAAC,WAAW,SAAS,CAAC,CAAC,CAAC,SAAS;CAC/C,eAAe,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;CAC1C,eAAe,EAAE,QAAQ,CAAC,CAAC,SAAS;CACpC,gBAAgB,EAAE,QAAQ,CAAC,CAAC,SAAS;CACrC,eAAe,EAAE,QAAQ,CAAC,CAAC,SAAS;CACpC,UAAU,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;AACvC,CAAC"}
package/package.json ADDED
@@ -0,0 +1,92 @@
1
+ {
2
+ "name": "@storyshelf/notify-chat",
3
+ "version": "0.5.3",
4
+ "description": "Chat notification providers for StoryShelf (Slack and Teams incoming webhooks).",
5
+ "keywords": [
6
+ "notifications",
7
+ "self-hosted",
8
+ "slack",
9
+ "storybook",
10
+ "teams"
11
+ ],
12
+ "homepage": "https://github.com/GuptaSiddhant/StoryShelf#readme",
13
+ "bugs": {
14
+ "url": "https://github.com/GuptaSiddhant/StoryShelf/issues"
15
+ },
16
+ "license": "MIT",
17
+ "author": {
18
+ "name": "Siddhant Gupta",
19
+ "url": "https://guptasiddhant.com"
20
+ },
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "git+https://github.com/GuptaSiddhant/StoryShelf.git",
24
+ "directory": "packages/notify-chat"
25
+ },
26
+ "files": [
27
+ "dist"
28
+ ],
29
+ "type": "module",
30
+ "sideEffects": false,
31
+ "types": "./dist/index.d.mts",
32
+ "exports": {
33
+ ".": {
34
+ "source": "./src/index.ts",
35
+ "default": "./dist/index.mjs"
36
+ },
37
+ "./log": {
38
+ "source": "./src/log.ts",
39
+ "default": "./dist/log.mjs"
40
+ },
41
+ "./slack": {
42
+ "source": "./src/slack.ts",
43
+ "default": "./dist/slack.mjs"
44
+ },
45
+ "./teams": {
46
+ "source": "./src/teams.ts",
47
+ "default": "./dist/teams.mjs"
48
+ },
49
+ "./package.json": "./package.json"
50
+ },
51
+ "publishConfig": {
52
+ "exports": {
53
+ ".": "./dist/index.mjs",
54
+ "./log": "./dist/log.mjs",
55
+ "./slack": "./dist/slack.mjs",
56
+ "./teams": "./dist/teams.mjs",
57
+ "./package.json": "./package.json"
58
+ },
59
+ "access": "public"
60
+ },
61
+ "scripts": {
62
+ "build": "tsdown",
63
+ "dev": "tsdown -w",
64
+ "fmt": "oxfmt ./src",
65
+ "lint": "oxlint --type-aware --type-check ./src",
66
+ "test": "vitest run",
67
+ "prepublishOnly": "nub run build"
68
+ },
69
+ "dependencies": {
70
+ "@storyshelf/core": "0.5.3",
71
+ "zod": "^4.2.0"
72
+ },
73
+ "devDependencies": {
74
+ "@types/node": "^26.0.0",
75
+ "@vitest/coverage-v8": "^4.0.0",
76
+ "oxfmt": "^0.65.0",
77
+ "oxlint": "^1.80.0",
78
+ "oxlint-tsgolint": "^7.0.2001",
79
+ "tsdown": "^0.22.14",
80
+ "typescript": "^7.0.2",
81
+ "vitest": "^5.0.0"
82
+ },
83
+ "jsr": {
84
+ "runtimeCompat": {
85
+ "node": true,
86
+ "deno": true,
87
+ "browser": false,
88
+ "workerd": true,
89
+ "bun": true
90
+ }
91
+ }
92
+ }