@fluojs/discord 1.0.0-beta.2 → 1.0.0-beta.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/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(@Inject(DiscordService) private readonly discord: DiscordService) {}
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(@Inject(DiscordService) private readonly discord: DiscordService) {}
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;
@@ -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;AAqBpB;;;;;;;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;IAuBjG;;;;;;;;;;;;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;YA6Bf,eAAe;IAe7B,OAAO,CAAC,gBAAgB;IAkBxB,OAAO,CAAC,2BAA2B;YAkBrB,kBAAkB;CAejC"}
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-beta.2",
12
+ "version": "1.0.0-beta.3",
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-beta.4",
40
- "@fluojs/di": "^1.0.0-beta.6",
41
- "@fluojs/notifications": "^1.0.0-beta.3",
42
- "@fluojs/runtime": "^1.0.0-beta.11"
39
+ "@fluojs/core": "^1.0.0-beta.5",
40
+ "@fluojs/di": "^1.0.0-beta.7",
41
+ "@fluojs/notifications": "^1.0.0-beta.4",
42
+ "@fluojs/runtime": "^1.0.0-beta.12"
43
43
  },
44
44
  "devDependencies": {
45
45
  "vitest": "^3.2.4"