@fluojs/discord 1.0.0-beta.2 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ko.md +4 -2
- package/README.md +4 -2
- package/dist/service.d.ts +1 -0
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +21 -5
- package/dist/webhook.js +3 -3
- package/package.json +5 -5
package/README.ko.md
CHANGED
|
@@ -61,8 +61,9 @@ export class AppModule {}
|
|
|
61
61
|
import { Inject } from '@fluojs/core';
|
|
62
62
|
import { DiscordService } from '@fluojs/discord';
|
|
63
63
|
|
|
64
|
+
@Inject(DiscordService)
|
|
64
65
|
export class DeployNotifier {
|
|
65
|
-
constructor(
|
|
66
|
+
constructor(private readonly discord: DiscordService) {}
|
|
66
67
|
|
|
67
68
|
async announce(version: string) {
|
|
68
69
|
await this.discord.send({
|
|
@@ -95,6 +96,7 @@ Behavioral contract 메모:
|
|
|
95
96
|
|
|
96
97
|
- `DiscordService.send(...)`는 전달 전에 `defaultThreadId`를 해석합니다.
|
|
97
98
|
- 서비스는 모듈 bootstrap 시 transport를 초기화하고, factory가 소유한 리소스만 애플리케이션 shutdown 시 닫습니다.
|
|
99
|
+
- 서비스가 shutdown 중이거나 이미 stopped 상태라면 cached transport를 재사용하지 않고 send를 거부합니다.
|
|
98
100
|
- 빈 `defaultThreadId`와 `notifications.channel` 값은 trim 후 무시됩니다. notifications channel은 기본적으로 `discord`입니다.
|
|
99
101
|
- 이 패키지는 절대로 `process.env`를 직접 읽지 않습니다. 모든 설정은 명시적인 옵션 또는 DI를 통해 들어와야 합니다.
|
|
100
102
|
|
|
@@ -163,7 +165,7 @@ bot 기반 REST 전달처럼 더 풍부한 API 연동이 필요하다면 export
|
|
|
163
165
|
|
|
164
166
|
Behavioral contract 메모:
|
|
165
167
|
|
|
166
|
-
- 내장 webhook transport는 `408`, `429`, `5xx` 같은 일시적 응답뿐 아니라 transport-level exception도 bounded exponential backoff로 재시도한 뒤 호출자에게 에러를 노출합니다.
|
|
168
|
+
- 내장 webhook transport는 `408`, `429`, `5xx` 같은 일시적 응답뿐 아니라 transport-level exception도 bounded exponential backoff로 재시도한 뒤 호출자에게 에러를 노출합니다. 영구적인 upstream 응답은 재시도하지 않습니다.
|
|
167
169
|
- 잘못되었거나 절대 URL이 아닌 `webhookUrl` 값은 전달 실패로 재시도하지 않고 즉시 `DiscordConfigurationError`로 거부됩니다.
|
|
168
170
|
- 호출자에게 보이는 `DiscordTransportError` 메시지는 기본적으로 raw upstream response body를 포함하지 않습니다.
|
|
169
171
|
|
package/README.md
CHANGED
|
@@ -61,8 +61,9 @@ export class AppModule {}
|
|
|
61
61
|
import { Inject } from '@fluojs/core';
|
|
62
62
|
import { DiscordService } from '@fluojs/discord';
|
|
63
63
|
|
|
64
|
+
@Inject(DiscordService)
|
|
64
65
|
export class DeployNotifier {
|
|
65
|
-
constructor(
|
|
66
|
+
constructor(private readonly discord: DiscordService) {}
|
|
66
67
|
|
|
67
68
|
async announce(version: string) {
|
|
68
69
|
await this.discord.send({
|
|
@@ -95,6 +96,7 @@ Behavioral contract notes:
|
|
|
95
96
|
|
|
96
97
|
- `DiscordService.send(...)` resolves `defaultThreadId` before delivery.
|
|
97
98
|
- The service initializes the configured transport during module bootstrap and closes factory-owned resources during application shutdown.
|
|
99
|
+
- Sends attempted while the service is shutting down or already stopped are rejected before reusing the cached transport.
|
|
98
100
|
- Blank `defaultThreadId` and `notifications.channel` values are trimmed and ignored; the notifications channel defaults to `discord`.
|
|
99
101
|
- The package never reads `process.env` directly. All configuration must enter through explicit options or DI.
|
|
100
102
|
|
|
@@ -163,7 +165,7 @@ For richer API integrations such as bot-backed REST delivery, implement the expo
|
|
|
163
165
|
|
|
164
166
|
Behavioral contract notes:
|
|
165
167
|
|
|
166
|
-
- The built-in webhook transport retries transient `408`, `429`, and `5xx` responses, and also retries transport-level exceptions, using bounded exponential backoff before surfacing an error.
|
|
168
|
+
- The built-in webhook transport retries transient `408`, `429`, and `5xx` responses, and also retries transport-level exceptions, using bounded exponential backoff before surfacing an error. Permanent upstream responses are not retried.
|
|
167
169
|
- Malformed or non-absolute `webhookUrl` values are rejected immediately as `DiscordConfigurationError` instead of being retried as delivery failures.
|
|
168
170
|
- Caller-visible `DiscordTransportError` messages omit raw upstream response bodies by default.
|
|
169
171
|
|
package/dist/service.d.ts
CHANGED
|
@@ -71,6 +71,7 @@ export declare class DiscordService implements Discord, OnModuleInit, OnApplicat
|
|
|
71
71
|
*/
|
|
72
72
|
sendNotification(notification: DiscordNotificationDispatchRequest, options?: DiscordSendOptions): Promise<DiscordSendResult>;
|
|
73
73
|
private ensureTransport;
|
|
74
|
+
private assertReadyForSend;
|
|
74
75
|
private normalizeMessage;
|
|
75
76
|
private resolveNotificationThreadId;
|
|
76
77
|
private renderNotification;
|
package/dist/service.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,qBAAqB,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAK3E,OAAO,KAAK,EACV,OAAO,EACP,cAAc,EACd,kCAAkC,EAClC,sBAAsB,EAEtB,sBAAsB,EACtB,kBAAkB,EAClB,iBAAiB,EAIjB,8BAA8B,EAC/B,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,qBAAqB,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAK3E,OAAO,KAAK,EACV,OAAO,EACP,cAAc,EACd,kCAAkC,EAClC,sBAAsB,EAEtB,sBAAsB,EACtB,kBAAkB,EAClB,iBAAiB,EAIjB,8BAA8B,EAC/B,MAAM,YAAY,CAAC;AAyBpB;;;;;;;GAOG;AACH,qBACa,cAAe,YAAW,OAAO,EAAE,YAAY,EAAE,qBAAqB;IAKrE,OAAO,CAAC,QAAQ,CAAC,OAAO;IAJpC,OAAO,CAAC,cAAc,CAAmF;IACzG,OAAO,CAAC,iBAAiB,CAA+B;IACxD,OAAO,CAAC,gBAAgB,CAAwC;gBAEnC,OAAO,EAAE,8BAA8B;IAE9D,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC;IAetC,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAiBnC;;;;OAIG;IACH,4BAA4B;IAW5B;;;;;;;;;;;;;;OAcG;IACG,IAAI,CAAC,OAAO,EAAE,cAAc,EAAE,OAAO,GAAE,kBAAuB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAyBjG;;;;;;;;;;;;OAYG;IACG,QAAQ,CAAC,QAAQ,EAAE,SAAS,cAAc,EAAE,EAAE,OAAO,GAAE,sBAA2B,GAAG,OAAO,CAAC,sBAAsB,CAAC;IA6B1H;;;;;;;;;;;;;;;;OAgBG;IACG,gBAAgB,CACpB,YAAY,EAAE,kCAAkC,EAChD,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,iBAAiB,CAAC;YAiCf,eAAe;IAe7B,OAAO,CAAC,kBAAkB;IAM1B,OAAO,CAAC,gBAAgB;IAkBxB,OAAO,CAAC,2BAA2B;YAkBrB,kBAAkB;CAejC"}
|
package/dist/service.js
CHANGED
|
@@ -5,7 +5,7 @@ function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e =
|
|
|
5
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
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
7
|
import { Inject } from '@fluojs/core';
|
|
8
|
-
import { DiscordMessageValidationError } from './errors.js';
|
|
8
|
+
import { DiscordMessageValidationError, DiscordTransportError } from './errors.js';
|
|
9
9
|
import { createDiscordPlatformStatusSnapshot } from './status.js';
|
|
10
10
|
import { DISCORD_OPTIONS } from './tokens.js';
|
|
11
11
|
function createAbortError() {
|
|
@@ -13,6 +13,9 @@ function createAbortError() {
|
|
|
13
13
|
error.name = 'AbortError';
|
|
14
14
|
return error;
|
|
15
15
|
}
|
|
16
|
+
function createStoppedTransportError() {
|
|
17
|
+
return new DiscordTransportError('Discord transport is shutting down or already stopped.');
|
|
18
|
+
}
|
|
16
19
|
function normalizeOptionalString(value) {
|
|
17
20
|
const trimmed = value?.trim();
|
|
18
21
|
return trimmed && trimmed.length > 0 ? trimmed : undefined;
|
|
@@ -49,9 +52,11 @@ class DiscordService {
|
|
|
49
52
|
await this.resolvedTransport.close();
|
|
50
53
|
}
|
|
51
54
|
this.lifecycleState = 'stopped';
|
|
52
|
-
} catch {
|
|
55
|
+
} catch (error) {
|
|
53
56
|
this.lifecycleState = 'failed';
|
|
54
|
-
throw new Error('Discord transport failed to close cleanly.'
|
|
57
|
+
throw new Error('Discord transport failed to close cleanly.', {
|
|
58
|
+
cause: error
|
|
59
|
+
});
|
|
55
60
|
}
|
|
56
61
|
}
|
|
57
62
|
async onModuleInit() {
|
|
@@ -62,9 +67,11 @@ class DiscordService {
|
|
|
62
67
|
await transport.verify();
|
|
63
68
|
}
|
|
64
69
|
this.lifecycleState = 'ready';
|
|
65
|
-
} catch {
|
|
70
|
+
} catch (error) {
|
|
66
71
|
this.lifecycleState = 'failed';
|
|
67
|
-
throw new Error('Discord transport failed to initialize.'
|
|
72
|
+
throw new Error('Discord transport failed to initialize.', {
|
|
73
|
+
cause: error
|
|
74
|
+
});
|
|
68
75
|
}
|
|
69
76
|
}
|
|
70
77
|
|
|
@@ -103,6 +110,7 @@ class DiscordService {
|
|
|
103
110
|
if (options.signal?.aborted) {
|
|
104
111
|
throw createAbortError();
|
|
105
112
|
}
|
|
113
|
+
this.assertReadyForSend();
|
|
106
114
|
const transport = await this.ensureTransport();
|
|
107
115
|
const normalized = this.normalizeMessage(message);
|
|
108
116
|
assertMessageContent(normalized);
|
|
@@ -176,6 +184,9 @@ class DiscordService {
|
|
|
176
184
|
* ```
|
|
177
185
|
*/
|
|
178
186
|
async sendNotification(notification, options = {}) {
|
|
187
|
+
if (options.signal?.aborted) {
|
|
188
|
+
throw createAbortError();
|
|
189
|
+
}
|
|
179
190
|
const payload = notification.payload;
|
|
180
191
|
const rendered = await this.renderNotification(notification);
|
|
181
192
|
return this.send({
|
|
@@ -215,6 +226,11 @@ class DiscordService {
|
|
|
215
226
|
}
|
|
216
227
|
return this.transportPromise;
|
|
217
228
|
}
|
|
229
|
+
assertReadyForSend() {
|
|
230
|
+
if (this.lifecycleState === 'stopping' || this.lifecycleState === 'stopped') {
|
|
231
|
+
throw createStoppedTransportError();
|
|
232
|
+
}
|
|
233
|
+
}
|
|
218
234
|
normalizeMessage(message) {
|
|
219
235
|
return {
|
|
220
236
|
allowedMentions: message.allowedMentions,
|
package/dist/webhook.js
CHANGED
|
@@ -184,13 +184,13 @@ export function createDiscordWebhookTransport(options) {
|
|
|
184
184
|
if (isAbortError(error) || context.signal?.aborted) {
|
|
185
185
|
throw error;
|
|
186
186
|
}
|
|
187
|
+
if (error instanceof DiscordTransportError) {
|
|
188
|
+
throw error;
|
|
189
|
+
}
|
|
187
190
|
if (attempt < DEFAULT_RETRY_ATTEMPTS) {
|
|
188
191
|
await waitForRetry(DEFAULT_RETRY_DELAY_MS * 2 ** (attempt - 1), context.signal);
|
|
189
192
|
continue;
|
|
190
193
|
}
|
|
191
|
-
if (error instanceof DiscordTransportError) {
|
|
192
|
-
throw error;
|
|
193
|
-
}
|
|
194
194
|
throw new DiscordTransportError(createTransportFailureMessage(attempt));
|
|
195
195
|
}
|
|
196
196
|
}
|
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"portable",
|
|
10
10
|
"fetch"
|
|
11
11
|
],
|
|
12
|
-
"version": "1.0.0
|
|
12
|
+
"version": "1.0.0",
|
|
13
13
|
"private": false,
|
|
14
14
|
"license": "MIT",
|
|
15
15
|
"repository": {
|
|
@@ -36,10 +36,10 @@
|
|
|
36
36
|
"dist"
|
|
37
37
|
],
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@fluojs/core": "^1.0.0
|
|
40
|
-
"@fluojs/di": "^1.0.0
|
|
41
|
-
"@fluojs/notifications": "^1.0.0
|
|
42
|
-
"@fluojs/runtime": "^1.0.0
|
|
39
|
+
"@fluojs/core": "^1.0.0",
|
|
40
|
+
"@fluojs/di": "^1.0.0",
|
|
41
|
+
"@fluojs/notifications": "^1.0.0",
|
|
42
|
+
"@fluojs/runtime": "^1.0.0"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"vitest": "^3.2.4"
|