@fluojs/discord 1.0.0-beta.1

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 fluo contributors
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.ko.md ADDED
@@ -0,0 +1,216 @@
1
+ # @fluojs/discord
2
+
3
+ <p><a href="./README.md"><kbd>English</kbd></a> <strong><kbd>한국어</kbd></strong></p>
4
+
5
+ fluo를 위한 webhook-first, transport-agnostic Discord 전달 코어 패키지입니다. Nest-like 모듈 API, standalone 사용을 위한 주입 가능한 `DiscordService`, 그리고 Node 전용 Discord SDK를 가정하지 않는 `@fluojs/notifications` 연동용 1st-party `DiscordChannel`을 제공합니다.
6
+
7
+ ## 목차
8
+
9
+ - [설치](#설치)
10
+ - [사용 시점](#사용-시점)
11
+ - [빠른 시작](#빠른-시작)
12
+ - [일반적인 패턴](#일반적인-패턴)
13
+ - [`DiscordService`를 이용한 standalone 전달](#discordservice를-이용한-standalone-전달)
14
+ - [`@fluojs/notifications`와의 통합](#fluojs-notifications와의-통합)
15
+ - [명시적 fetch 주입을 사용하는 webhook-first 전달](#명시적-fetch-주입을-사용하는-webhook-first-전달)
16
+ - [의도적인 제한 사항](#의도적인-제한-사항)
17
+ - [공개 API 개요](#공개-api-개요)
18
+ - [관련 패키지](#관련-패키지)
19
+ - [예제 소스](#예제-소스)
20
+
21
+ ## 설치
22
+
23
+ ```bash
24
+ npm install @fluojs/discord @fluojs/notifications
25
+ ```
26
+
27
+ 이 패키지는 published package metadata에 반영된 저장소 전반의 Node.js 20+ 설치 baseline을 따르지만, 런타임 전달 계약 자체는 명시적인 fetch-compatible 경계를 통해 계속 transport-agnostic하게 유지됩니다.
28
+
29
+ ## 사용 시점
30
+
31
+ - Discord 메시지를 직접 보내는 기능과 `@fluojs/notifications` 채널 연동을 한 패키지에서 처리하고 싶을 때.
32
+ - transport 선택을 Node, Bun, Deno, Cloudflare 호환 애플리케이션 경계 전반에서 명시적이고 이식 가능하게 유지해야 할 때.
33
+ - incoming webhook을 기본 경로로 선호하되, 더 풍부한 REST 또는 bot 기반 연동은 커스텀 transport 계약으로 열어 두고 싶을 때.
34
+ - 설정을 패키지 내부 `process.env` 접근이 아니라 DI 또는 명시적인 옵션으로 주입하고 싶을 때.
35
+
36
+ ## 빠른 시작
37
+
38
+ ### 모듈 등록
39
+
40
+ ```typescript
41
+ import { Module } from '@fluojs/core';
42
+ import { DiscordModule, createDiscordWebhookTransport } from '@fluojs/discord';
43
+
44
+ @Module({
45
+ imports: [
46
+ DiscordModule.forRoot({
47
+ defaultThreadId: 'release-thread-id',
48
+ transport: createDiscordWebhookTransport({
49
+ fetch: globalThis.fetch.bind(globalThis),
50
+ webhookUrl: 'https://discord.com/api/webhooks/123/abc',
51
+ }),
52
+ }),
53
+ ],
54
+ })
55
+ export class AppModule {}
56
+ ```
57
+
58
+ ### 직접 Discord 메시지 보내기
59
+
60
+ ```typescript
61
+ import { Inject } from '@fluojs/core';
62
+ import { DiscordService } from '@fluojs/discord';
63
+
64
+ export class DeployNotifier {
65
+ constructor(@Inject(DiscordService) private readonly discord: DiscordService) {}
66
+
67
+ async announce(version: string) {
68
+ await this.discord.send({
69
+ content: `Deploy ${version} finished successfully.`,
70
+ });
71
+ }
72
+ }
73
+ ```
74
+
75
+ ## 일반적인 패턴
76
+
77
+ ### `DiscordService`를 이용한 standalone 전달
78
+
79
+ notifications foundation을 거치지 않고 직접 Discord 전달을 하고 싶다면 `DiscordService`를 사용합니다.
80
+
81
+ ```typescript
82
+ DiscordModule.forRootAsync({
83
+ inject: [ConfigService],
84
+ useFactory: (config) => ({
85
+ defaultThreadId: config.discord.defaultThreadId,
86
+ transport: createDiscordWebhookTransport({
87
+ fetch: config.runtime.fetch,
88
+ webhookUrl: config.discord.webhookUrl,
89
+ }),
90
+ }),
91
+ });
92
+ ```
93
+
94
+ Behavioral contract 메모:
95
+
96
+ - `DiscordService.send(...)`는 전달 전에 `defaultThreadId`를 해석합니다.
97
+ - 서비스는 모듈 bootstrap 시 transport를 초기화하고, factory가 소유한 리소스만 애플리케이션 shutdown 시 닫습니다.
98
+ - 이 패키지는 절대로 `process.env`를 직접 읽지 않습니다. 모든 설정은 명시적인 옵션 또는 DI를 통해 들어와야 합니다.
99
+
100
+ ### `@fluojs/notifications`와의 통합
101
+
102
+ `DISCORD_CHANNEL`을 `NotificationsModule.forRootAsync(...)`에 주입하여, Discord 전용 payload 필드와 recipient-to-thread 해석 규칙이 모두 `@fluojs/discord` 안에만 남도록 구성합니다.
103
+
104
+ ```typescript
105
+ import { Module } from '@fluojs/core';
106
+ import { NotificationsModule } from '@fluojs/notifications';
107
+ import {
108
+ DISCORD_CHANNEL,
109
+ DiscordModule,
110
+ createDiscordWebhookTransport,
111
+ } from '@fluojs/discord';
112
+
113
+ @Module({
114
+ imports: [
115
+ DiscordModule.forRoot({
116
+ transport: createDiscordWebhookTransport({
117
+ fetch: globalThis.fetch.bind(globalThis),
118
+ webhookUrl: 'https://discord.com/api/webhooks/123/abc',
119
+ }),
120
+ }),
121
+ NotificationsModule.forRootAsync({
122
+ inject: [DISCORD_CHANNEL],
123
+ useFactory: (channel) => ({
124
+ channels: [channel],
125
+ }),
126
+ }),
127
+ ],
128
+ })
129
+ export class AppModule {}
130
+ ```
131
+
132
+ 지원하는 notification payload 필드:
133
+
134
+ - `content`, `embeds`, `components`, `attachments`
135
+ - `allowedMentions`, `username`, `avatarUrl`, `tts`
136
+ - `threadId`, `threadName`, `flags`, `poll`, `metadata`
137
+
138
+ Behavioral contract 메모:
139
+
140
+ - 하나의 notification dispatch는 정확히 하나의 Discord thread 경로로 매핑됩니다. `payload.threadId` 또는 `recipients`의 단일 항목을 사용해야 합니다.
141
+ - `payload.threadId`가 없으면 `DiscordService.sendNotification(...)`는 첫 번째 `recipients` 항목을 사용하고, 그것도 없으면 `defaultThreadId`로 폴백합니다.
142
+ - 여러 Discord thread로 fan-out이 필요하다면 하나의 multi-recipient dispatch 대신 `sendMany(...)`를 사용해야 합니다.
143
+
144
+ ### 명시적 fetch 주입을 사용하는 webhook-first 전달
145
+
146
+ 런타임에 독립적인 1st-party transport가 필요하다면 fetch-compatible HTTP 경계만 의존하는 `createDiscordWebhookTransport(...)`를 사용합니다.
147
+
148
+ ```typescript
149
+ const transport = createDiscordWebhookTransport({
150
+ fetch: runtime.fetch,
151
+ webhookUrl: discordWebhookUrl,
152
+ });
153
+
154
+ await discord.send({
155
+ content: 'Deploy finished',
156
+ embeds: [{ description: 'Build 124 succeeded.' }],
157
+ });
158
+ ```
159
+
160
+ bot 기반 REST 전달처럼 더 풍부한 API 연동이 필요하다면 export된 `DiscordTransport` 계약을 구현해 `DiscordModule.forRoot(...)` 또는 `forRootAsync(...)`에 주입하면 됩니다.
161
+
162
+ Behavioral contract 메모:
163
+
164
+ - 내장 webhook transport는 `408`, `429`, `5xx` 같은 일시적 실패를 호출자에게 에러를 노출하기 전에 bounded exponential backoff로 재시도합니다.
165
+ - 잘못되었거나 절대 URL이 아닌 `webhookUrl` 값은 전달 실패로 재시도하지 않고 즉시 `DiscordConfigurationError`로 거부됩니다.
166
+ - 호출자에게 보이는 `DiscordTransportError` 메시지는 기본적으로 raw upstream response body를 포함하지 않습니다.
167
+
168
+ ### 의도적인 제한 사항
169
+
170
+ Discord 패키지는 의도적으로 다음을 **포함하지 않습니다**:
171
+
172
+ - 자격 증명이나 webhook URL을 `process.env`에서 직접 읽는 동작
173
+ - 공유 루트 패키지 경계에 Node 전용 Discord SDK를 내장하는 것
174
+ - webhook helper와 export된 transport 계약 이상으로 하나의 provider 전략을 강제하는 것
175
+ - 하나의 dispatch 호출 안에서 multi-thread fan-out을 자동 변환하는 것
176
+
177
+ 이 제한 사항은 런타임 선택, provider capability, rollout 전략이 애플리케이션 경계에서 명시적으로 결정되도록 하기 위한 package contract의 일부입니다.
178
+
179
+ ## 공개 API 개요
180
+
181
+ ### 핵심
182
+
183
+ - `DiscordModule.forRoot(options)` / `DiscordModule.forRootAsync(options)`
184
+ - `DiscordService`
185
+ - `DiscordChannel`
186
+ - `DISCORD`
187
+ - `DISCORD_CHANNEL`
188
+
189
+ 애플리케이션 구성은 `DiscordModule`로, notifications 연동은 `DISCORD_CHANNEL`과 export된 transport 계약으로 조합합니다.
190
+
191
+ ### 계약과 헬퍼
192
+
193
+ - `DiscordMessage`
194
+ - `DiscordTransport`
195
+ - `DiscordTransportFactory`
196
+ - `DiscordTemplateRenderer`
197
+ - `createDiscordWebhookTransport(options)`
198
+
199
+ ### 상태 및 에러
200
+
201
+ - `createDiscordPlatformStatusSnapshot(...)`
202
+ - `DiscordConfigurationError`
203
+ - `DiscordMessageValidationError`
204
+ - `DiscordTransportError`
205
+
206
+ ## 관련 패키지
207
+
208
+ - `@fluojs/notifications`: `DISCORD_CHANNEL`을 소비하는 공통 오케스트레이션 계층입니다.
209
+ - `@fluojs/config`: 환경 직접 접근 없이 webhook URL이나 thread id를 해석하려는 경우 권장됩니다.
210
+ - `@fluojs/event-bus`: Discord 알림이 여러 이벤트 기반 부작용 중 하나일 때 유용합니다.
211
+
212
+ ## 예제 소스
213
+
214
+ - `packages/discord/src/module.test.ts`: 모듈 등록, async wiring, webhook transport, notifications integration 예제.
215
+ - `packages/discord/src/public-surface.test.ts`: 공개 export와 TypeScript 계약 검증 예제.
216
+ - `packages/discord/src/status.test.ts`: health/readiness 계약 예제.
package/README.md ADDED
@@ -0,0 +1,216 @@
1
+ # @fluojs/discord
2
+
3
+ <p><strong><kbd>English</kbd></strong> <a href="./README.ko.md"><kbd>한국어</kbd></a></p>
4
+
5
+ Webhook-first, transport-agnostic Discord delivery core for fluo. It provides a Nest-like module API, an injectable `DiscordService` for standalone usage, and a first-party `DiscordChannel` for `@fluojs/notifications` integration without assuming a Node-only Discord SDK.
6
+
7
+ ## Table of Contents
8
+
9
+ - [Installation](#installation)
10
+ - [When to Use](#when-to-use)
11
+ - [Quick Start](#quick-start)
12
+ - [Common Patterns](#common-patterns)
13
+ - [Standalone delivery with `DiscordService`](#standalone-delivery-with-discordservice)
14
+ - [Integration with `@fluojs/notifications`](#integration-with-fluojs-notifications)
15
+ - [Webhook-first delivery with explicit fetch injection](#webhook-first-delivery-with-explicit-fetch-injection)
16
+ - [Intentional limitations](#intentional-limitations)
17
+ - [Public API Overview](#public-api-overview)
18
+ - [Related Packages](#related-packages)
19
+ - [Example Sources](#example-sources)
20
+
21
+ ## Installation
22
+
23
+ ```bash
24
+ npm install @fluojs/discord @fluojs/notifications
25
+ ```
26
+
27
+ This package follows the repo-wide Node.js 20+ install baseline reflected in published package metadata, while keeping its delivery contract transport-agnostic at runtime through explicit fetch-compatible boundaries.
28
+
29
+ ## When to Use
30
+
31
+ - When you want one package that can send Discord messages directly and also plug into `@fluojs/notifications`.
32
+ - When transport choice must stay explicit and portable across Node, Bun, Deno, and Cloudflare-compatible application boundaries.
33
+ - When Discord delivery should prefer incoming webhooks while still allowing richer REST or bot-backed integrations through a custom transport contract.
34
+ - When configuration must enter through DI or explicit options instead of `process.env` reads inside the package.
35
+
36
+ ## Quick Start
37
+
38
+ ### Register the module
39
+
40
+ ```typescript
41
+ import { Module } from '@fluojs/core';
42
+ import { DiscordModule, createDiscordWebhookTransport } from '@fluojs/discord';
43
+
44
+ @Module({
45
+ imports: [
46
+ DiscordModule.forRoot({
47
+ defaultThreadId: 'release-thread-id',
48
+ transport: createDiscordWebhookTransport({
49
+ fetch: globalThis.fetch.bind(globalThis),
50
+ webhookUrl: 'https://discord.com/api/webhooks/123/abc',
51
+ }),
52
+ }),
53
+ ],
54
+ })
55
+ export class AppModule {}
56
+ ```
57
+
58
+ ### Send Discord messages directly
59
+
60
+ ```typescript
61
+ import { Inject } from '@fluojs/core';
62
+ import { DiscordService } from '@fluojs/discord';
63
+
64
+ export class DeployNotifier {
65
+ constructor(@Inject(DiscordService) private readonly discord: DiscordService) {}
66
+
67
+ async announce(version: string) {
68
+ await this.discord.send({
69
+ content: `Deploy ${version} finished successfully.`,
70
+ });
71
+ }
72
+ }
73
+ ```
74
+
75
+ ## Common Patterns
76
+
77
+ ### Standalone delivery with `DiscordService`
78
+
79
+ Use `DiscordService` when your application wants direct Discord delivery without routing through the notifications foundation.
80
+
81
+ ```typescript
82
+ DiscordModule.forRootAsync({
83
+ inject: [ConfigService],
84
+ useFactory: (config) => ({
85
+ defaultThreadId: config.discord.defaultThreadId,
86
+ transport: createDiscordWebhookTransport({
87
+ fetch: config.runtime.fetch,
88
+ webhookUrl: config.discord.webhookUrl,
89
+ }),
90
+ }),
91
+ });
92
+ ```
93
+
94
+ Behavioral contract notes:
95
+
96
+ - `DiscordService.send(...)` resolves `defaultThreadId` before delivery.
97
+ - The service initializes the configured transport during module bootstrap and closes factory-owned resources during application shutdown.
98
+ - The package never reads `process.env` directly. All configuration must enter through explicit options or DI.
99
+
100
+ ### Integration with `@fluojs/notifications`
101
+
102
+ Inject `DISCORD_CHANNEL` into `NotificationsModule.forRootAsync(...)` so the Discord package remains the only place that understands Discord-specific payload fields and recipient-to-thread translation.
103
+
104
+ ```typescript
105
+ import { Module } from '@fluojs/core';
106
+ import { NotificationsModule } from '@fluojs/notifications';
107
+ import {
108
+ DISCORD_CHANNEL,
109
+ DiscordModule,
110
+ createDiscordWebhookTransport,
111
+ } from '@fluojs/discord';
112
+
113
+ @Module({
114
+ imports: [
115
+ DiscordModule.forRoot({
116
+ transport: createDiscordWebhookTransport({
117
+ fetch: globalThis.fetch.bind(globalThis),
118
+ webhookUrl: 'https://discord.com/api/webhooks/123/abc',
119
+ }),
120
+ }),
121
+ NotificationsModule.forRootAsync({
122
+ inject: [DISCORD_CHANNEL],
123
+ useFactory: (channel) => ({
124
+ channels: [channel],
125
+ }),
126
+ }),
127
+ ],
128
+ })
129
+ export class AppModule {}
130
+ ```
131
+
132
+ Supported notification payload fields:
133
+
134
+ - `content`, `embeds`, `components`, `attachments`
135
+ - `allowedMentions`, `username`, `avatarUrl`, `tts`
136
+ - `threadId`, `threadName`, `flags`, `poll`, `metadata`
137
+
138
+ Behavioral contract notes:
139
+
140
+ - One notification dispatch maps to exactly one Discord thread route. Use `payload.threadId` or a single entry in `recipients`.
141
+ - If `payload.threadId` is omitted, `DiscordService.sendNotification(...)` uses the first `recipients` entry or falls back to `defaultThreadId`.
142
+ - If a notification needs fan-out across multiple Discord threads, call `sendMany(...)` instead of one multi-recipient dispatch.
143
+
144
+ ### Webhook-first delivery with explicit fetch injection
145
+
146
+ Use `createDiscordWebhookTransport(...)` when you want a portable first-party transport that only depends on a fetch-compatible HTTP boundary.
147
+
148
+ ```typescript
149
+ const transport = createDiscordWebhookTransport({
150
+ fetch: runtime.fetch,
151
+ webhookUrl: discordWebhookUrl,
152
+ });
153
+
154
+ await discord.send({
155
+ content: 'Deploy finished',
156
+ embeds: [{ description: 'Build 124 succeeded.' }],
157
+ });
158
+ ```
159
+
160
+ For richer API integrations such as bot-backed REST delivery, implement the exported `DiscordTransport` contract and inject it through `DiscordModule.forRoot(...)` or `forRootAsync(...)`.
161
+
162
+ Behavioral contract notes:
163
+
164
+ - The built-in webhook transport retries transient `408`, `429`, and `5xx` failures with bounded exponential backoff before surfacing an error.
165
+ - Malformed or non-absolute `webhookUrl` values are rejected immediately as `DiscordConfigurationError` instead of being retried as delivery failures.
166
+ - Caller-visible `DiscordTransportError` messages omit raw upstream response bodies by default.
167
+
168
+ ### Intentional limitations
169
+
170
+ The Discord package intentionally does **not**:
171
+
172
+ - read credentials or webhook URLs from `process.env`
173
+ - ship a Node-only Discord SDK inside the shared root package boundary
174
+ - force one provider strategy beyond the webhook-first helper and exported transport contract
175
+ - translate one notification into multi-thread fan-out inside a single dispatch call
176
+
177
+ These limitations are part of the package contract so runtime choice, provider capability, and rollout strategy stay explicit at the application boundary.
178
+
179
+ ## Public API Overview
180
+
181
+ ### Core
182
+
183
+ - `DiscordModule.forRoot(options)` / `DiscordModule.forRootAsync(options)`
184
+ - `DiscordService`
185
+ - `DiscordChannel`
186
+ - `DISCORD`
187
+ - `DISCORD_CHANNEL`
188
+
189
+ Compose applications through `DiscordModule` and integrate notifications through `DISCORD_CHANNEL` plus the exported transport contracts.
190
+
191
+ ### Contracts and helpers
192
+
193
+ - `DiscordMessage`
194
+ - `DiscordTransport`
195
+ - `DiscordTransportFactory`
196
+ - `DiscordTemplateRenderer`
197
+ - `createDiscordWebhookTransport(options)`
198
+
199
+ ### Status and errors
200
+
201
+ - `createDiscordPlatformStatusSnapshot(...)`
202
+ - `DiscordConfigurationError`
203
+ - `DiscordMessageValidationError`
204
+ - `DiscordTransportError`
205
+
206
+ ## Related Packages
207
+
208
+ - `@fluojs/notifications`: Shared orchestration layer that consumes `DISCORD_CHANNEL`.
209
+ - `@fluojs/config`: Recommended for resolving webhook URLs or thread ids without direct environment access.
210
+ - `@fluojs/event-bus`: Useful when Discord notifications are one side effect among several event-driven workflows.
211
+
212
+ ## Example Sources
213
+
214
+ - `packages/discord/src/module.test.ts`: Module registration, async wiring, webhook transport, and notifications integration examples.
215
+ - `packages/discord/src/public-surface.test.ts`: Public export and TypeScript contract verification.
216
+ - `packages/discord/src/status.test.ts`: Health/readiness contract examples.
@@ -0,0 +1,24 @@
1
+ import type { NotificationChannel, NotificationChannelContext, NotificationChannelDelivery } from '@fluojs/notifications';
2
+ import { DiscordService } from './service.js';
3
+ import type { DiscordNotificationDispatchRequest, DiscordSendResult, NormalizedDiscordModuleOptions } from './types.js';
4
+ /**
5
+ * Notification channel implementation that bridges `@fluojs/notifications` to {@link DiscordService}.
6
+ *
7
+ * @remarks
8
+ * This class keeps the foundation package channel-agnostic while allowing `@fluojs/discord`
9
+ * to interpret Discord-specific payload fields, webhook delivery, and transport behavior.
10
+ */
11
+ export declare class DiscordChannel implements NotificationChannel<DiscordNotificationDispatchRequest, DiscordSendResult> {
12
+ private readonly discord;
13
+ readonly channel: string;
14
+ constructor(discord: DiscordService, options: NormalizedDiscordModuleOptions);
15
+ /**
16
+ * Sends one notifications foundation request through the configured Discord transport.
17
+ *
18
+ * @param notification Shared notification envelope understood by the Discord package.
19
+ * @param context Optional abort context propagated from the notifications service.
20
+ * @returns A normalized channel delivery result with the provider message id exposed as `externalId` when available.
21
+ */
22
+ send(notification: DiscordNotificationDispatchRequest, context: NotificationChannelContext): Promise<NotificationChannelDelivery<DiscordSendResult>>;
23
+ }
24
+ //# sourceMappingURL=channel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"channel.d.ts","sourceRoot":"","sources":["../src/channel.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,0BAA0B,EAAE,2BAA2B,EAAE,MAAM,uBAAuB,CAAC;AAG1H,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE9C,OAAO,KAAK,EAAE,kCAAkC,EAAE,iBAAiB,EAAE,8BAA8B,EAAE,MAAM,YAAY,CAAC;AAExH;;;;;;GAMG;AACH,qBACa,cAAe,YAAW,mBAAmB,CAAC,kCAAkC,EAAE,iBAAiB,CAAC;IAI7G,OAAO,CAAC,QAAQ,CAAC,OAAO;IAH1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBAGN,OAAO,EAAE,cAAc,EACxC,OAAO,EAAE,8BAA8B;IAKzC;;;;;;OAMG;IACG,IAAI,CACR,YAAY,EAAE,kCAAkC,EAChD,OAAO,EAAE,0BAA0B,GAClC,OAAO,CAAC,2BAA2B,CAAC,iBAAiB,CAAC,CAAC;CAqB3D"}
@@ -0,0 +1,61 @@
1
+ let _initClass;
2
+ function _applyDecs(e, t, n, r, o, i) { var a, c, u, s, f, l, p, d = Symbol.metadata || Symbol.for("Symbol.metadata"), m = Object.defineProperty, h = Object.create, y = [h(null), h(null)], v = t.length; function g(t, n, r) { return function (o, i) { n && (i = o, o = e); for (var a = 0; a < t.length; a++) i = t[a].apply(o, r ? [i] : []); return r ? i : o; }; } function b(e, t, n, r) { if ("function" != typeof e && (r || void 0 !== e)) throw new TypeError(t + " must " + (n || "be") + " a function" + (r ? "" : " or undefined")); return e; } function applyDec(e, t, n, r, o, i, u, s, f, l, p) { function d(e) { if (!p(e)) throw new TypeError("Attempted to access private element on non-instance"); } var h = [].concat(t[0]), v = t[3], w = !u, D = 1 === o, S = 3 === o, j = 4 === o, E = 2 === o; function I(t, n, r) { return function (o, i) { return n && (i = o, o = e), r && r(o), P[t].call(o, i); }; } if (!w) { var P = {}, k = [], F = S ? "get" : j || D ? "set" : "value"; if (f ? (l || D ? P = { get: _setFunctionName(function () { return v(this); }, r, "get"), set: function (e) { t[4](this, e); } } : P[F] = v, l || _setFunctionName(P[F], r, E ? "" : F)) : l || (P = Object.getOwnPropertyDescriptor(e, r)), !l && !f) { if ((c = y[+s][r]) && 7 !== (c ^ o)) throw Error("Decorating two elements with the same name (" + P[F].name + ") is not supported yet"); y[+s][r] = o < 3 ? 1 : o; } } for (var N = e, O = h.length - 1; O >= 0; O -= n ? 2 : 1) { var T = b(h[O], "A decorator", "be", !0), z = n ? h[O - 1] : void 0, A = {}, H = { kind: ["field", "accessor", "method", "getter", "setter", "class"][o], name: r, metadata: a, addInitializer: function (e, t) { if (e.v) throw new TypeError("attempted to call addInitializer after decoration was finished"); b(t, "An initializer", "be", !0), i.push(t); }.bind(null, A) }; if (w) c = T.call(z, N, H), A.v = 1, b(c, "class decorators", "return") && (N = c);else if (H.static = s, H.private = f, c = H.access = { has: f ? p.bind() : function (e) { return r in e; } }, j || (c.get = f ? E ? function (e) { return d(e), P.value; } : I("get", 0, d) : function (e) { return e[r]; }), E || S || (c.set = f ? I("set", 0, d) : function (e, t) { e[r] = t; }), N = T.call(z, D ? { get: P.get, set: P.set } : P[F], H), A.v = 1, D) { if ("object" == typeof N && N) (c = b(N.get, "accessor.get")) && (P.get = c), (c = b(N.set, "accessor.set")) && (P.set = c), (c = b(N.init, "accessor.init")) && k.unshift(c);else if (void 0 !== N) throw new TypeError("accessor decorators must return an object with get, set, or init properties or undefined"); } else b(N, (l ? "field" : "method") + " decorators", "return") && (l ? k.unshift(N) : P[F] = N); } return o < 2 && u.push(g(k, s, 1), g(i, s, 0)), l || w || (f ? D ? u.splice(-1, 0, I("get", s), I("set", s)) : u.push(E ? P[F] : b.call.bind(P[F])) : m(e, r, P)), N; } function w(e) { return m(e, d, { configurable: !0, enumerable: !0, value: a }); } return void 0 !== i && (a = i[d]), a = h(null == a ? null : a), f = [], l = function (e) { e && f.push(g(e)); }, p = function (t, r) { for (var i = 0; i < n.length; i++) { var a = n[i], c = a[1], l = 7 & c; if ((8 & c) == t && !l == r) { var p = a[2], d = !!a[3], m = 16 & c; applyDec(t ? e : e.prototype, a, m, d ? "#" + p : _toPropertyKey(p), l, l < 2 ? [] : t ? s = s || [] : u = u || [], f, !!t, d, r, t && d ? function (t) { return _checkInRHS(t) === e; } : o); } } }, p(8, 0), p(0, 0), p(8, 1), p(0, 1), l(u), l(s), c = f, v || w(e), { e: c, get c() { var n = []; return v && [w(e = applyDec(e, [t], r, e.name, 5, n)), g(n, 1)]; } }; }
3
+ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
4
+ function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
5
+ function _setFunctionName(e, t, n) { "symbol" == typeof t && (t = (t = t.description) ? "[" + t + "]" : ""); try { Object.defineProperty(e, "name", { configurable: !0, value: n ? n + " " + t : t }); } catch (e) {} return e; }
6
+ function _checkInRHS(e) { if (Object(e) !== e) throw TypeError("right-hand side of 'in' should be an object, got " + (null !== e ? typeof e : "null")); return e; }
7
+ import { Inject } from '@fluojs/core';
8
+ import { DiscordTransportError } from './errors.js';
9
+ import { DiscordService } from './service.js';
10
+ import { DISCORD_OPTIONS } from './tokens.js';
11
+ let _DiscordChannel;
12
+ /**
13
+ * Notification channel implementation that bridges `@fluojs/notifications` to {@link DiscordService}.
14
+ *
15
+ * @remarks
16
+ * This class keeps the foundation package channel-agnostic while allowing `@fluojs/discord`
17
+ * to interpret Discord-specific payload fields, webhook delivery, and transport behavior.
18
+ */
19
+ class DiscordChannel {
20
+ static {
21
+ [_DiscordChannel, _initClass] = _applyDecs(this, [Inject(DiscordService, DISCORD_OPTIONS)], []).c;
22
+ }
23
+ channel;
24
+ constructor(discord, options) {
25
+ this.discord = discord;
26
+ this.channel = options.notifications.channel;
27
+ }
28
+
29
+ /**
30
+ * Sends one notifications foundation request through the configured Discord transport.
31
+ *
32
+ * @param notification Shared notification envelope understood by the Discord package.
33
+ * @param context Optional abort context propagated from the notifications service.
34
+ * @returns A normalized channel delivery result with the provider message id exposed as `externalId` when available.
35
+ */
36
+ async send(notification, context) {
37
+ const receipt = await this.discord.sendNotification(notification, {
38
+ signal: context.signal
39
+ });
40
+ if (receipt.ok === false) {
41
+ throw new DiscordTransportError('Discord transport reported an unsuccessful delivery.');
42
+ }
43
+ return {
44
+ externalId: receipt.messageId,
45
+ metadata: {
46
+ channelId: receipt.channelId,
47
+ guildId: receipt.guildId,
48
+ response: receipt.response,
49
+ statusCode: receipt.statusCode,
50
+ threadId: receipt.threadId,
51
+ warnings: receipt.warnings
52
+ },
53
+ receipt,
54
+ status: 'delivered'
55
+ };
56
+ }
57
+ static {
58
+ _initClass();
59
+ }
60
+ }
61
+ export { _DiscordChannel as DiscordChannel };
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Base error type for caller-visible Discord module configuration failures.
3
+ */
4
+ export declare class DiscordConfigurationError extends Error {
5
+ constructor(message: string);
6
+ }
7
+ /**
8
+ * Thrown when a Discord message or notification payload is missing one required contract field.
9
+ */
10
+ export declare class DiscordMessageValidationError extends Error {
11
+ constructor(message: string);
12
+ }
13
+ /**
14
+ * Thrown when one concrete Discord transport reports a caller-visible delivery failure.
15
+ */
16
+ export declare class DiscordTransportError extends Error {
17
+ constructor(message: string);
18
+ }
19
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,qBAAa,yBAA0B,SAAQ,KAAK;gBACtC,OAAO,EAAE,MAAM;CAI5B;AAED;;GAEG;AACH,qBAAa,6BAA8B,SAAQ,KAAK;gBAC1C,OAAO,EAAE,MAAM;CAI5B;AAED;;GAEG;AACH,qBAAa,qBAAsB,SAAQ,KAAK;gBAClC,OAAO,EAAE,MAAM;CAI5B"}
package/dist/errors.js ADDED
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Base error type for caller-visible Discord module configuration failures.
3
+ */
4
+ export class DiscordConfigurationError extends Error {
5
+ constructor(message) {
6
+ super(message);
7
+ this.name = 'DiscordConfigurationError';
8
+ }
9
+ }
10
+
11
+ /**
12
+ * Thrown when a Discord message or notification payload is missing one required contract field.
13
+ */
14
+ export class DiscordMessageValidationError extends Error {
15
+ constructor(message) {
16
+ super(message);
17
+ this.name = 'DiscordMessageValidationError';
18
+ }
19
+ }
20
+
21
+ /**
22
+ * Thrown when one concrete Discord transport reports a caller-visible delivery failure.
23
+ */
24
+ export class DiscordTransportError extends Error {
25
+ constructor(message) {
26
+ super(message);
27
+ this.name = 'DiscordTransportError';
28
+ }
29
+ }
@@ -0,0 +1,10 @@
1
+ export { DiscordConfigurationError, DiscordMessageValidationError, DiscordTransportError, } from './errors.js';
2
+ export { DiscordChannel } from './channel.js';
3
+ export { DiscordModule } from './module.js';
4
+ export { DiscordService } from './service.js';
5
+ export { createDiscordPlatformStatusSnapshot } from './status.js';
6
+ export type { DiscordLifecycleState, DiscordPlatformStatusSnapshot, DiscordStatusAdapterInput } from './status.js';
7
+ export { DISCORD, DISCORD_CHANNEL } from './tokens.js';
8
+ export type { Discord, DiscordAllowedMentions, DiscordAsyncModuleOptions, DiscordAttachment, DiscordComponent, DiscordEmbed, DiscordFetchLike, DiscordFetchResponse, DiscordMessage, DiscordModuleOptions, DiscordNotificationDispatchRequest, DiscordNotificationPayload, DiscordPoll, DiscordSendBatchResult, DiscordSendFailure, DiscordSendManyOptions, DiscordSendOptions, DiscordSendResult, DiscordTemplateRenderInput, DiscordTemplateRenderer, DiscordTemplateRenderResult, DiscordTransport, DiscordTransportContext, DiscordTransportFactory, DiscordTransportReceipt, DiscordWebhookTransportOptions, NormalizedDiscordMessage, } from './types.js';
9
+ export { createDiscordWebhookTransport } from './webhook.js';
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,yBAAyB,EACzB,6BAA6B,EAC7B,qBAAqB,GACtB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,mCAAmC,EAAE,MAAM,aAAa,CAAC;AAClE,YAAY,EAAE,qBAAqB,EAAE,6BAA6B,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAC;AACnH,OAAO,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACvD,YAAY,EACV,OAAO,EACP,sBAAsB,EACtB,yBAAyB,EACzB,iBAAiB,EACjB,gBAAgB,EAChB,YAAY,EACZ,gBAAgB,EAChB,oBAAoB,EACpB,cAAc,EACd,oBAAoB,EACpB,kCAAkC,EAClC,0BAA0B,EAC1B,WAAW,EACX,sBAAsB,EACtB,kBAAkB,EAClB,sBAAsB,EACtB,kBAAkB,EAClB,iBAAiB,EACjB,0BAA0B,EAC1B,uBAAuB,EACvB,2BAA2B,EAC3B,gBAAgB,EAChB,uBAAuB,EACvB,uBAAuB,EACvB,uBAAuB,EACvB,8BAA8B,EAC9B,wBAAwB,GACzB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,6BAA6B,EAAE,MAAM,cAAc,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ export { DiscordConfigurationError, DiscordMessageValidationError, DiscordTransportError } from './errors.js';
2
+ export { DiscordChannel } from './channel.js';
3
+ export { DiscordModule } from './module.js';
4
+ export { DiscordService } from './service.js';
5
+ export { createDiscordPlatformStatusSnapshot } from './status.js';
6
+ export { DISCORD, DISCORD_CHANNEL } from './tokens.js';
7
+ export { createDiscordWebhookTransport } from './webhook.js';
@@ -0,0 +1,41 @@
1
+ import { type ModuleType } from '@fluojs/runtime';
2
+ import type { DiscordAsyncModuleOptions, DiscordModuleOptions } from './types.js';
3
+ /** Runtime module entrypoint for Discord delivery and notifications integration. */
4
+ export declare class DiscordModule {
5
+ /**
6
+ * Registers Discord providers using static options.
7
+ *
8
+ * @param options Static Discord module options including transport wiring and optional template rendering behavior.
9
+ * @returns A global module definition that exports {@link DiscordService}, {@link DiscordChannel}, and compatibility tokens.
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * DiscordModule.forRoot({
14
+ * transport: createDiscordWebhookTransport({ webhookUrl: 'https://discord.com/api/webhooks/...' }),
15
+ * });
16
+ * ```
17
+ */
18
+ static forRoot(options: DiscordModuleOptions): ModuleType;
19
+ /**
20
+ * Registers Discord providers from an async DI factory.
21
+ *
22
+ * @param options Async module options that resolve Discord transport and renderer configuration through DI.
23
+ * @returns A global module definition that memoizes async option resolution per module instance.
24
+ *
25
+ * @example
26
+ * ```ts
27
+ * DiscordModule.forRootAsync({
28
+ * inject: [ConfigService],
29
+ * useFactory: (config) => ({
30
+ * defaultThreadId: config.discord.threadId,
31
+ * transport: createDiscordWebhookTransport({
32
+ * fetch: config.runtime.fetch,
33
+ * webhookUrl: config.discord.webhookUrl,
34
+ * }),
35
+ * }),
36
+ * });
37
+ * ```
38
+ */
39
+ static forRootAsync(options: DiscordAsyncModuleOptions): ModuleType;
40
+ }
41
+ //# sourceMappingURL=module.d.ts.map