@fluojs/discord 1.0.5 → 2.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 +23 -3
- package/README.md +23 -3
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/service.d.ts +6 -0
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +43 -9
- package/dist/types.d.ts +8 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/webhook.d.ts.map +1 -1
- package/dist/webhook.js +49 -34
- package/package.json +7 -7
package/README.ko.md
CHANGED
|
@@ -18,6 +18,7 @@ fluo를 위한 webhook-first, transport-agnostic Discord 전달 코어 패키지
|
|
|
18
18
|
- [`@fluojs/notifications`와의 통합](#fluojs-notifications와의-통합)
|
|
19
19
|
- [payload override를 사용하는 template rendering](#payload-override를-사용하는-template-rendering)
|
|
20
20
|
- [명시적 fetch 주입을 사용하는 webhook-first 전달](#명시적-fetch-주입을-사용하는-webhook-first-전달)
|
|
21
|
+
- [구성 가능한 webhook 재시도 정책](#구성-가능한-webhook-재시도-정책)
|
|
21
22
|
- [의도적인 제한 사항](#의도적인-제한-사항)
|
|
22
23
|
- [공개 API 개요](#공개-api-개요)
|
|
23
24
|
- [관련 패키지](#관련-패키지)
|
|
@@ -29,7 +30,7 @@ fluo를 위한 webhook-first, transport-agnostic Discord 전달 코어 패키지
|
|
|
29
30
|
npm install @fluojs/discord @fluojs/notifications
|
|
30
31
|
```
|
|
31
32
|
|
|
32
|
-
이 패키지는
|
|
33
|
+
이 패키지는 패키지 자체의 지원 계약에 따라 Node.js `>=24.0.0 <27`을 요구합니다. Node 20 및 Node 22 host를 Node.js `>=24.0.0 <27`로 올리세요. Node 24 미만과 Node 27 이상은 지원하지 않으므로 process를 지원되는 line으로 옮겨야 합니다. 런타임 전달 계약 자체는 명시적인 fetch-compatible 경계를 통해 계속 transport-agnostic하게 유지됩니다.
|
|
33
34
|
|
|
34
35
|
## 사용 시점
|
|
35
36
|
|
|
@@ -110,8 +111,9 @@ Behavioral contract 메모:
|
|
|
110
111
|
- `DiscordModule.forRoot(...)`와 `DiscordModule.forRootAsync(...)`는 `DiscordService`, `DiscordChannel`, `DISCORD`, `DISCORD_CHANNEL`을 기본 global로 export합니다. fluo 옵션인 `global?: boolean`을 사용하고, migrated code가 Discord provider를 importing module 안에만 유지해야 할 때만 `global: false`를 설정하세요. NestJS `isGlobal`은 지원하지 않습니다.
|
|
111
112
|
- `DiscordService.send(...)`는 전달 전에 `defaultThreadId`를 해석합니다.
|
|
112
113
|
- `DiscordService.sendMany(...)`는 `DiscordMessage[]`를 직접 순차 전송하는 batch API이며 `continueOnError`를 지원합니다. 이는 multi-recipient `@fluojs/notifications` dispatch shortcut이 아닙니다.
|
|
113
|
-
- 서비스는 모듈 bootstrap 시 transport를 초기화하고, bootstrap verification 실패와 애플리케이션 shutdown 전반에서 factory-owned 리소스를 정확히 한 번 닫습니다. shutdown 전에 시작된 factory 생성 transport가 아직 완료되지 않았더라도 이를 기다리며,
|
|
114
|
+
- 서비스는 모듈 bootstrap 시 transport를 초기화하고, bootstrap verification 실패와 애플리케이션 shutdown 전반에서 factory-owned 리소스를 정확히 한 번 닫습니다. shutdown 전에 시작된 factory 생성 transport가 아직 완료되지 않았더라도 이를 기다리며, 진행 중인 bootstrap verification이 settle된 뒤에만 factory-owned transport를 닫습니다. Verification 실패 후 owned cleanup도 실패하면 status diagnostics는 이를 `shutdown-cleanup`으로 재분류하지 않고 최초 `initialization` failure phase를 유지하며, reject된 factory creation도 initialization failure로 유지됩니다.
|
|
114
115
|
- send는 bootstrap이 transport를 `ready`로 표시한 뒤에만 허용됩니다. bootstrap 전, startup 중, bootstrap 실패 후, shutdown 중, shutdown 후 시도는 전달 전에 거부됩니다.
|
|
116
|
+
- `ready` 상태에서 허용된 직접 send 또는 notification dispatch는 factory-owned transport resource를 닫기 전에 renderer와 transport delivery가 끝날 때까지 drain합니다. 전달 실패는 caller의 결과로 남고 shutdown 실패로 전파하지 않습니다.
|
|
115
117
|
- 서비스가 shutdown 중이거나 이미 stopped 상태라면 cached transport를 재사용하지 않고 send를 거부합니다.
|
|
116
118
|
- `DiscordService.sendNotification(...)`은 구성된 renderer를 호출하기 전에 lifecycle readiness를 확인하고, 호출자의 `AbortSignal`을 `DiscordTemplateRenderInput.signal`과 transport delivery 양쪽에 전달합니다.
|
|
117
119
|
- `DiscordService.createPlatformStatusSnapshot()`은 `createDiscordPlatformStatusSnapshot(...)`과 같은 status 계약을 노출합니다. 여기에는 lifecycle/readiness, health, transport kind와 ownership, 기본 thread 구성, bootstrap verification 상태, bootstrap initialization 실패와 shutdown cleanup 실패를 구분하는 diagnostics, notifications channel dependency details가 포함되어, 호출자가 내부 옵션에 접근하지 않고도 Discord wiring을 관찰할 수 있습니다.
|
|
@@ -244,9 +246,26 @@ await discord.send({
|
|
|
244
246
|
|
|
245
247
|
bot 기반 REST 전달처럼 더 풍부한 API 연동이 필요하다면 export된 `DiscordTransport` 계약을 구현해 `DiscordModule.forRoot(...)` 또는 `forRootAsync(...)`에 주입하면 됩니다.
|
|
246
248
|
|
|
249
|
+
### 구성 가능한 webhook 재시도 정책
|
|
250
|
+
|
|
251
|
+
내장 webhook transport는 일시적인 `408`, `429`, `5xx` 응답과 transport-level exception을 재시도합니다. `retry`를 생략하면 총 세 번 시도하고 250ms를 base로 지수 backoff하는 기존 기본값을 유지합니다. workload의 latency 또는 허용 가능한 재시도 수준이 다르다면 transport 생성 시 하나의 정책을 구성하세요.
|
|
252
|
+
|
|
253
|
+
```typescript
|
|
254
|
+
const transport = createDiscordWebhookTransport({
|
|
255
|
+
fetch: runtime.fetch,
|
|
256
|
+
retry: {
|
|
257
|
+
attempts: 5,
|
|
258
|
+
baseDelayMs: 500,
|
|
259
|
+
},
|
|
260
|
+
webhookUrl: config.discordWebhookUrl,
|
|
261
|
+
});
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
`attempts`에는 최초 요청이 포함되며 `1`부터 `10`까지의 정수여야 합니다. `baseDelayMs`는 `0`부터 `60000`까지의 정수여야 하고 이후 대기 시간은 이 값을 기준으로 두 배씩 증가합니다. 이 정책은 이 프로세스 안에서 한 번 보내는 bounded webhook 재시도에만 적용됩니다. 프로세스 재시작 뒤에도 notification을 보존해야 하거나, durable scheduling이 필요하거나, request latency와 독립적으로 delivery를 관리해야 한다면 webhook attempts를 늘리는 대신 queue-backed delivery를 사용하는 `@fluojs/notifications`를 사용하세요.
|
|
265
|
+
|
|
247
266
|
Behavioral contract 메모:
|
|
248
267
|
|
|
249
|
-
- 내장 webhook transport는 `408`, `429`, `5xx` 같은 일시적 응답뿐 아니라 transport-level exception도 bounded exponential backoff로 재시도한 뒤 호출자에게 에러를 노출합니다. 영구적인 upstream 응답은 재시도하지 않습니다.
|
|
268
|
+
- 내장 webhook transport는 `408`, `429`, `5xx` 같은 일시적 응답뿐 아니라 transport-level exception도 구성한 bounded exponential backoff로 재시도한 뒤 호출자에게 에러를 노출합니다. 영구적인 upstream 응답은 재시도하지 않습니다.
|
|
250
269
|
- Retry backoff는 `DiscordSendOptions.signal`을 관찰합니다. 이미 abort된 signal은 다음 backoff timer를 기다리지 않고 즉시 reject됩니다.
|
|
251
270
|
- 성공한 webhook 응답은 `DiscordSendResult.response`로 노출됩니다. rate-limit 재시도가 끝내 실패한 경우를 포함해, 호출자에게 보이는 `DiscordTransportError` 메시지는 기본적으로 raw upstream response body를 포함하지 않습니다.
|
|
252
271
|
- 잘못되었거나 절대 URL이 아닌 `webhookUrl` 값은 전달 실패로 재시도하지 않고 즉시 `DiscordConfigurationError`로 거부됩니다.
|
|
@@ -288,6 +307,7 @@ Discord 패키지는 의도적으로 다음을 **포함하지 않습니다**:
|
|
|
288
307
|
|
|
289
308
|
- `DiscordMessage`
|
|
290
309
|
- `NormalizedDiscordMessage`
|
|
310
|
+
- `DiscordWebhookRetryOptions`
|
|
291
311
|
- `DiscordWebhookTransportOptions`
|
|
292
312
|
- `DiscordFetchLike`
|
|
293
313
|
- `DiscordFetchResponse`
|
package/README.md
CHANGED
|
@@ -18,6 +18,7 @@ Migration boundary: the module API is intentionally Nest-like but not a NestJS d
|
|
|
18
18
|
- [Integration with `@fluojs/notifications`](#integration-with-fluojs-notifications)
|
|
19
19
|
- [Template rendering with payload overrides](#template-rendering-with-payload-overrides)
|
|
20
20
|
- [Webhook-first delivery with explicit fetch injection](#webhook-first-delivery-with-explicit-fetch-injection)
|
|
21
|
+
- [Configurable webhook retry policy](#configurable-webhook-retry-policy)
|
|
21
22
|
- [Intentional limitations](#intentional-limitations)
|
|
22
23
|
- [Public API Overview](#public-api-overview)
|
|
23
24
|
- [Related Packages](#related-packages)
|
|
@@ -29,7 +30,7 @@ Migration boundary: the module API is intentionally Nest-like but not a NestJS d
|
|
|
29
30
|
npm install @fluojs/discord @fluojs/notifications
|
|
30
31
|
```
|
|
31
32
|
|
|
32
|
-
This package
|
|
33
|
+
This package requires Node.js `>=24.0.0 <27` as its package-owned support contract. Upgrade Node 20 and Node 22 hosts to Node.js `>=24.0.0 <27`; Node versions below 24 and Node 27+ are unsupported, so move the process to a supported line. Its delivery contract remains transport-agnostic at runtime through explicit fetch-compatible boundaries.
|
|
33
34
|
|
|
34
35
|
## When to Use
|
|
35
36
|
|
|
@@ -110,8 +111,9 @@ Behavioral contract notes:
|
|
|
110
111
|
- `DiscordModule.forRoot(...)` and `DiscordModule.forRootAsync(...)` export `DiscordService`, `DiscordChannel`, `DISCORD`, and `DISCORD_CHANNEL` globally by default. Use the fluo `global?: boolean` option and set `global: false` only when migrated code must keep Discord providers local to importing modules; NestJS `isGlobal` is not supported.
|
|
111
112
|
- `DiscordService.send(...)` resolves `defaultThreadId` before delivery.
|
|
112
113
|
- `DiscordService.sendMany(...)` is a direct `DiscordMessage[]` batch API that sends messages sequentially and supports `continueOnError`; it is not a multi-recipient `@fluojs/notifications` dispatch shortcut.
|
|
113
|
-
- The service initializes the configured transport during module bootstrap and closes factory-owned resources exactly once across bootstrap verification failure and application shutdown, including any in-flight factory-created transport before shutdown began.
|
|
114
|
+
- The service initializes the configured transport during module bootstrap and closes factory-owned resources exactly once across bootstrap verification failure and application shutdown, including any in-flight factory-created transport before shutdown began. Shutdown waits for in-flight bootstrap verification to settle before closing a factory-owned transport. If verification fails and the subsequent owned cleanup also fails, status diagnostics preserve the first `initialization` failure phase instead of reclassifying it as `shutdown-cleanup`; a rejected factory creation likewise remains an initialization failure.
|
|
114
115
|
- Sends are accepted only after bootstrap marks the transport `ready`; attempts before bootstrap, during startup, after failed bootstrap, while shutting down, or after shutdown are rejected before delivery.
|
|
116
|
+
- A direct send or notification dispatch accepted while `ready` drains its renderer and transport delivery before factory-owned transport resources close. Delivery rejection remains the caller's result and does not make shutdown fail.
|
|
115
117
|
- Sends attempted while the service is shutting down or already stopped are rejected before reusing the cached transport.
|
|
116
118
|
- `DiscordService.sendNotification(...)` checks lifecycle readiness before invoking a configured renderer and passes the caller's `AbortSignal` to both `DiscordTemplateRenderInput.signal` and transport delivery.
|
|
117
119
|
- `DiscordService.createPlatformStatusSnapshot()` exposes the same status contract as `createDiscordPlatformStatusSnapshot(...)`: lifecycle/readiness, health, transport kind and ownership, default thread configuration, bootstrap verification state, distinct bootstrap initialization versus shutdown cleanup failure diagnostics, and notifications channel dependency details, so callers can observe Discord wiring without reaching into internal options.
|
|
@@ -244,9 +246,26 @@ await discord.send({
|
|
|
244
246
|
|
|
245
247
|
For richer API integrations such as bot-backed REST delivery, implement the exported `DiscordTransport` contract and inject it through `DiscordModule.forRoot(...)` or `forRootAsync(...)`.
|
|
246
248
|
|
|
249
|
+
### Configurable webhook retry policy
|
|
250
|
+
|
|
251
|
+
The built-in webhook transport retries transient `408`, `429`, and `5xx` responses and transport-level exceptions. Omit `retry` to preserve the default of three total attempts with a 250ms exponential-backoff base delay. Configure one workload-specific policy at transport construction when its latency or tolerance differs:
|
|
252
|
+
|
|
253
|
+
```typescript
|
|
254
|
+
const transport = createDiscordWebhookTransport({
|
|
255
|
+
fetch: runtime.fetch,
|
|
256
|
+
retry: {
|
|
257
|
+
attempts: 5,
|
|
258
|
+
baseDelayMs: 500,
|
|
259
|
+
},
|
|
260
|
+
webhookUrl: config.discordWebhookUrl,
|
|
261
|
+
});
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
`attempts` includes the initial request and must be an integer from `1` through `10`. `baseDelayMs` must be an integer from `0` through `60000`; later waits double from that base. The retry policy applies only to this bounded in-process webhook send. When a notification must survive process restarts, require durable scheduling, or need delivery managed independently of request latency, use `@fluojs/notifications` with queue-backed delivery instead of increasing webhook attempts.
|
|
265
|
+
|
|
247
266
|
Behavioral contract notes:
|
|
248
267
|
|
|
249
|
-
- 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.
|
|
268
|
+
- The built-in webhook transport retries transient `408`, `429`, and `5xx` responses, and also retries transport-level exceptions, using the configured bounded exponential backoff before surfacing an error. Permanent upstream responses are not retried.
|
|
250
269
|
- Retry backoff observes `DiscordSendOptions.signal`; an already-aborted signal rejects immediately instead of waiting for the next backoff timer.
|
|
251
270
|
- Successful webhook responses are exposed through `DiscordSendResult.response`; caller-visible `DiscordTransportError` messages still omit raw upstream response bodies by default, including after rate-limit retries fail.
|
|
252
271
|
- Malformed or non-absolute `webhookUrl` values are rejected immediately as `DiscordConfigurationError` instead of being retried as delivery failures.
|
|
@@ -288,6 +307,7 @@ The package intentionally keeps `createDiscordProviders(...)`, `DISCORD_OPTIONS`
|
|
|
288
307
|
|
|
289
308
|
- `DiscordMessage`
|
|
290
309
|
- `NormalizedDiscordMessage`
|
|
310
|
+
- `DiscordWebhookRetryOptions`
|
|
291
311
|
- `DiscordWebhookTransportOptions`
|
|
292
312
|
- `DiscordFetchLike`
|
|
293
313
|
- `DiscordFetchResponse`
|
package/dist/index.d.ts
CHANGED
|
@@ -5,6 +5,6 @@ export { DiscordService } from './service.js';
|
|
|
5
5
|
export { createDiscordPlatformStatusSnapshot } from './status.js';
|
|
6
6
|
export type { DiscordLifecycleState, DiscordPlatformStatusSnapshot, DiscordStatusAdapterInput } from './status.js';
|
|
7
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';
|
|
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, DiscordWebhookRetryOptions, DiscordWebhookTransportOptions, NormalizedDiscordMessage, } from './types.js';
|
|
9
9
|
export { createDiscordWebhookTransport } from './webhook.js';
|
|
10
10
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +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"}
|
|
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,0BAA0B,EAC1B,8BAA8B,EAC9B,wBAAwB,GACzB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,6BAA6B,EAAE,MAAM,cAAc,CAAC"}
|
package/dist/service.d.ts
CHANGED
|
@@ -10,12 +10,14 @@ import type { Discord, DiscordMessage, DiscordNotificationDispatchRequest, Disco
|
|
|
10
10
|
*/
|
|
11
11
|
export declare class DiscordService implements Discord, OnModuleInit, OnApplicationShutdown {
|
|
12
12
|
private readonly options;
|
|
13
|
+
private readonly acceptedDeliveryPromises;
|
|
13
14
|
private lifecycleFailurePhase;
|
|
14
15
|
private lifecycleState;
|
|
15
16
|
private ownedTransportCleanupPromise;
|
|
16
17
|
private resolvedTransport;
|
|
17
18
|
private shutdownPromise;
|
|
18
19
|
private transportPromise;
|
|
20
|
+
private transportVerificationPromise;
|
|
19
21
|
constructor(options: NormalizedDiscordModuleOptions);
|
|
20
22
|
onApplicationShutdown(): Promise<void>;
|
|
21
23
|
private closeOwnedTransport;
|
|
@@ -45,6 +47,7 @@ export declare class DiscordService implements Discord, OnModuleInit, OnApplicat
|
|
|
45
47
|
* ```
|
|
46
48
|
*/
|
|
47
49
|
send(message: DiscordMessage, options?: DiscordSendOptions): Promise<DiscordSendResult>;
|
|
50
|
+
private sendAccepted;
|
|
48
51
|
/**
|
|
49
52
|
* Sends multiple Discord messages in input order with optional tolerant failure handling.
|
|
50
53
|
*
|
|
@@ -77,8 +80,11 @@ export declare class DiscordService implements Discord, OnModuleInit, OnApplicat
|
|
|
77
80
|
* ```
|
|
78
81
|
*/
|
|
79
82
|
sendNotification(notification: DiscordNotificationDispatchRequest, options?: DiscordSendOptions): Promise<DiscordSendResult>;
|
|
83
|
+
private sendNotificationAccepted;
|
|
80
84
|
private ensureTransport;
|
|
81
85
|
private clearResolvedTransport;
|
|
86
|
+
private drainAcceptedDeliveries;
|
|
87
|
+
private trackAcceptedDelivery;
|
|
82
88
|
private handleTransportInitializationFailure;
|
|
83
89
|
private assertReadyForSend;
|
|
84
90
|
private normalizeMessage;
|
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;AA+DpB;;;;;;;GAOG;AACH,qBACa,cAAe,YAAW,OAAO,EAAE,YAAY,EAAE,qBAAqB;
|
|
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;AA+DpB;;;;;;;GAOG;AACH,qBACa,cAAe,YAAW,OAAO,EAAE,YAAY,EAAE,qBAAqB;IAUrE,OAAO,CAAC,QAAQ,CAAC,OAAO;IATpC,OAAO,CAAC,QAAQ,CAAC,wBAAwB,CAA+B;IACxE,OAAO,CAAC,qBAAqB,CAAkD;IAC/E,OAAO,CAAC,cAAc,CAA2C;IACjE,OAAO,CAAC,4BAA4B,CAA4B;IAChE,OAAO,CAAC,iBAAiB,CAA+B;IACxD,OAAO,CAAC,eAAe,CAA4B;IACnD,OAAO,CAAC,gBAAgB,CAAwC;IAChE,OAAO,CAAC,4BAA4B,CAA4B;gBAEnC,OAAO,EAAE,8BAA8B;IAE9D,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC;YAgB9B,mBAAmB;IAcjC,OAAO,CAAC,4BAA4B;YAQtB,gCAAgC;YAoBhC,0BAA0B;IAelC,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IA+BnC;;;;OAIG;IACH,4BAA4B;IAY5B;;;;;;;;;;;;;;OAcG;IACG,IAAI,CAAC,OAAO,EAAE,cAAc,EAAE,OAAO,GAAE,kBAAuB,GAAG,OAAO,CAAC,iBAAiB,CAAC;YAUnF,YAAY;IAuB1B;;;;;;;;;;;;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;YAYf,wBAAwB;YAkCxB,eAAe;IAuB7B,OAAO,CAAC,sBAAsB;YAMhB,uBAAuB;IAIrC,OAAO,CAAC,qBAAqB;YA2Bf,oCAAoC;IAuBlD,OAAO,CAAC,kBAAkB;IAU1B,OAAO,CAAC,gBAAgB;IAkBxB,OAAO,CAAC,2BAA2B;YAkBrB,kBAAkB;CAiBjC"}
|
package/dist/service.js
CHANGED
|
@@ -53,12 +53,14 @@ class DiscordService {
|
|
|
53
53
|
static {
|
|
54
54
|
[_DiscordService, _initClass] = _applyDecs(this, [Inject(DISCORD_OPTIONS)], []).c;
|
|
55
55
|
}
|
|
56
|
+
acceptedDeliveryPromises = new Set();
|
|
56
57
|
lifecycleFailurePhase;
|
|
57
58
|
lifecycleState = 'created';
|
|
58
59
|
ownedTransportCleanupPromise;
|
|
59
60
|
resolvedTransport;
|
|
60
61
|
shutdownPromise;
|
|
61
62
|
transportPromise;
|
|
63
|
+
transportVerificationPromise;
|
|
62
64
|
constructor(options) {
|
|
63
65
|
this.options = options;
|
|
64
66
|
}
|
|
@@ -70,18 +72,18 @@ class DiscordService {
|
|
|
70
72
|
return this.shutdownPromise;
|
|
71
73
|
}
|
|
72
74
|
this.lifecycleState = 'stopping';
|
|
73
|
-
this.lifecycleFailurePhase = undefined;
|
|
74
75
|
this.shutdownPromise = this.closeOwnedTransport();
|
|
75
76
|
return this.shutdownPromise;
|
|
76
77
|
}
|
|
77
78
|
async closeOwnedTransport() {
|
|
78
79
|
try {
|
|
80
|
+
await this.drainAcceptedDeliveries();
|
|
79
81
|
await this.closeOwnedTransportResources();
|
|
80
82
|
this.lifecycleState = 'stopped';
|
|
81
83
|
this.lifecycleFailurePhase = undefined;
|
|
82
84
|
} catch (error) {
|
|
83
85
|
this.lifecycleState = 'failed';
|
|
84
|
-
this.lifecycleFailurePhase
|
|
86
|
+
this.lifecycleFailurePhase ??= 'shutdown-cleanup';
|
|
85
87
|
throw new Error('Discord transport failed to close cleanly.', {
|
|
86
88
|
cause: error
|
|
87
89
|
});
|
|
@@ -96,6 +98,10 @@ class DiscordService {
|
|
|
96
98
|
async closeOwnedTransportResourcesOnce() {
|
|
97
99
|
try {
|
|
98
100
|
const transport = await this.resolveTransportForCleanup();
|
|
101
|
+
const verificationPromise = this.transportVerificationPromise;
|
|
102
|
+
if (verificationPromise) {
|
|
103
|
+
await verificationPromise.then(() => undefined, () => undefined);
|
|
104
|
+
}
|
|
99
105
|
if (transport && this.options.transport.ownsResources && transport.close) {
|
|
100
106
|
await transport.close();
|
|
101
107
|
}
|
|
@@ -124,7 +130,8 @@ class DiscordService {
|
|
|
124
130
|
return;
|
|
125
131
|
}
|
|
126
132
|
if (this.options.verifyOnModuleInit && transport.verify) {
|
|
127
|
-
|
|
133
|
+
this.transportVerificationPromise = Promise.resolve(transport.verify());
|
|
134
|
+
await this.transportVerificationPromise;
|
|
128
135
|
}
|
|
129
136
|
if (this.lifecycleState !== 'starting') {
|
|
130
137
|
return;
|
|
@@ -173,8 +180,10 @@ class DiscordService {
|
|
|
173
180
|
throw createAbortError();
|
|
174
181
|
}
|
|
175
182
|
this.assertReadyForSend();
|
|
176
|
-
|
|
177
|
-
|
|
183
|
+
return this.trackAcceptedDelivery(() => this.sendAccepted(message, options, this.ensureTransport()));
|
|
184
|
+
}
|
|
185
|
+
async sendAccepted(message, options, transportPromise) {
|
|
186
|
+
const transport = await transportPromise;
|
|
178
187
|
const normalized = this.normalizeMessage(message);
|
|
179
188
|
assertMessageContent(normalized);
|
|
180
189
|
const result = await transport.send(normalized, options);
|
|
@@ -251,9 +260,12 @@ class DiscordService {
|
|
|
251
260
|
throw createAbortError();
|
|
252
261
|
}
|
|
253
262
|
this.assertReadyForSend();
|
|
263
|
+
return this.trackAcceptedDelivery(() => this.sendNotificationAccepted(notification, options, this.ensureTransport()));
|
|
264
|
+
}
|
|
265
|
+
async sendNotificationAccepted(notification, options, transportPromise) {
|
|
254
266
|
const payload = notification.payload;
|
|
255
267
|
const rendered = await this.renderNotification(notification, options.signal);
|
|
256
|
-
return this.
|
|
268
|
+
return this.sendAccepted({
|
|
257
269
|
allowedMentions: payload.allowedMentions,
|
|
258
270
|
attachments: payload.attachments ?? [],
|
|
259
271
|
avatarUrl: payload.avatarUrl,
|
|
@@ -276,7 +288,7 @@ class DiscordService {
|
|
|
276
288
|
threadName: payload.threadName,
|
|
277
289
|
tts: payload.tts,
|
|
278
290
|
username: payload.username
|
|
279
|
-
}, options);
|
|
291
|
+
}, options, transportPromise);
|
|
280
292
|
}
|
|
281
293
|
async ensureTransport() {
|
|
282
294
|
if (this.lifecycleState === 'stopping' || this.lifecycleState === 'stopped') {
|
|
@@ -299,12 +311,35 @@ class DiscordService {
|
|
|
299
311
|
clearResolvedTransport() {
|
|
300
312
|
this.resolvedTransport = undefined;
|
|
301
313
|
this.transportPromise = undefined;
|
|
314
|
+
this.transportVerificationPromise = undefined;
|
|
315
|
+
}
|
|
316
|
+
async drainAcceptedDeliveries() {
|
|
317
|
+
await Promise.allSettled(this.acceptedDeliveryPromises);
|
|
318
|
+
}
|
|
319
|
+
trackAcceptedDelivery(startDelivery) {
|
|
320
|
+
let rejectDelivery;
|
|
321
|
+
let resolveDelivery;
|
|
322
|
+
const delivery = new Promise((resolve, reject) => {
|
|
323
|
+
rejectDelivery = reject;
|
|
324
|
+
resolveDelivery = resolve;
|
|
325
|
+
});
|
|
326
|
+
this.acceptedDeliveryPromises.add(delivery);
|
|
327
|
+
void delivery.then(() => {
|
|
328
|
+
this.acceptedDeliveryPromises.delete(delivery);
|
|
329
|
+
}, () => {
|
|
330
|
+
this.acceptedDeliveryPromises.delete(delivery);
|
|
331
|
+
});
|
|
332
|
+
if (!resolveDelivery || !rejectDelivery) {
|
|
333
|
+
throw new Error('Discord delivery tracker failed to initialize.');
|
|
334
|
+
}
|
|
335
|
+
void startDelivery().then(resolveDelivery, rejectDelivery);
|
|
336
|
+
return delivery;
|
|
302
337
|
}
|
|
303
338
|
async handleTransportInitializationFailure(error) {
|
|
304
339
|
const interruptedByShutdown = isShutdownLifecycleState(this.lifecycleState);
|
|
340
|
+
this.lifecycleFailurePhase ??= 'initialization';
|
|
305
341
|
if (!interruptedByShutdown) {
|
|
306
342
|
this.lifecycleState = 'failed';
|
|
307
|
-
this.lifecycleFailurePhase = 'initialization';
|
|
308
343
|
}
|
|
309
344
|
let cause = error;
|
|
310
345
|
try {
|
|
@@ -314,7 +349,6 @@ class DiscordService {
|
|
|
314
349
|
}
|
|
315
350
|
if (!interruptedByShutdown && !isShutdownLifecycleState(this.lifecycleState)) {
|
|
316
351
|
this.lifecycleState = 'failed';
|
|
317
|
-
this.lifecycleFailurePhase = 'initialization';
|
|
318
352
|
}
|
|
319
353
|
throw new Error('Discord transport failed to initialize.', {
|
|
320
354
|
cause
|
package/dist/types.d.ts
CHANGED
|
@@ -121,9 +121,17 @@ export type DiscordFetchLike = (input: string, init?: {
|
|
|
121
121
|
method?: string;
|
|
122
122
|
signal?: AbortSignal;
|
|
123
123
|
}) => MaybePromise<DiscordFetchResponse>;
|
|
124
|
+
/** Bounded retry policy accepted by {@link createDiscordWebhookTransport}. */
|
|
125
|
+
export interface DiscordWebhookRetryOptions {
|
|
126
|
+
/** Total delivery attempts, including the initial request. Defaults to `3` and accepts values from `1` through `10`. */
|
|
127
|
+
attempts?: number;
|
|
128
|
+
/** Initial exponential-backoff delay in milliseconds. Defaults to `250` and accepts values from `0` through `60000`. */
|
|
129
|
+
baseDelayMs?: number;
|
|
130
|
+
}
|
|
124
131
|
/** Options accepted by {@link createDiscordWebhookTransport}. */
|
|
125
132
|
export interface DiscordWebhookTransportOptions {
|
|
126
133
|
fetch?: DiscordFetchLike;
|
|
134
|
+
retry?: DiscordWebhookRetryOptions;
|
|
127
135
|
wait?: boolean;
|
|
128
136
|
webhookUrl: string;
|
|
129
137
|
}
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/webhook.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webhook.d.ts","sourceRoot":"","sources":["../src/webhook.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAGV,gBAAgB,EAEhB,8BAA8B,EAE/B,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"webhook.d.ts","sourceRoot":"","sources":["../src/webhook.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAGV,gBAAgB,EAEhB,8BAA8B,EAE/B,MAAM,YAAY,CAAC;AA8JpB;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,8BAA8B,GAAG,gBAAgB,CAwEvG"}
|
package/dist/webhook.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { DiscordConfigurationError, DiscordTransportError } from './errors.js';
|
|
2
2
|
const DEFAULT_RETRY_ATTEMPTS = 3;
|
|
3
3
|
const DEFAULT_RETRY_DELAY_MS = 250;
|
|
4
|
+
const MAX_RETRY_ATTEMPTS = 10;
|
|
5
|
+
const MAX_RETRY_DELAY_MS = 60_000;
|
|
4
6
|
function normalizeOptionalString(value) {
|
|
5
7
|
const trimmed = value?.trim();
|
|
6
8
|
return trimmed && trimmed.length > 0 ? trimmed : undefined;
|
|
@@ -89,13 +91,27 @@ function isAbortError(error) {
|
|
|
89
91
|
function isTransientStatus(status) {
|
|
90
92
|
return status === 408 || status === 429 || status >= 500 && status <= 599;
|
|
91
93
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
94
|
+
function resolveRetryPolicy(retry) {
|
|
95
|
+
const attempts = retry?.attempts ?? DEFAULT_RETRY_ATTEMPTS;
|
|
96
|
+
if (!Number.isInteger(attempts) || attempts < 1 || attempts > MAX_RETRY_ATTEMPTS) {
|
|
97
|
+
throw new DiscordConfigurationError(`Discord webhook transport \`retry.attempts\` must be an integer between 1 and ${String(MAX_RETRY_ATTEMPTS)}.`);
|
|
95
98
|
}
|
|
99
|
+
const baseDelayMs = retry?.baseDelayMs ?? DEFAULT_RETRY_DELAY_MS;
|
|
100
|
+
if (!Number.isInteger(baseDelayMs) || baseDelayMs < 0 || baseDelayMs > MAX_RETRY_DELAY_MS) {
|
|
101
|
+
throw new DiscordConfigurationError(`Discord webhook transport \`retry.baseDelayMs\` must be an integer between 0 and ${String(MAX_RETRY_DELAY_MS)}.`);
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
attempts,
|
|
105
|
+
baseDelayMs
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
async function waitForRetry(delayMs, signal) {
|
|
96
109
|
if (signal?.aborted) {
|
|
97
110
|
throw signal.reason ?? new DOMException('The operation was aborted.', 'AbortError');
|
|
98
111
|
}
|
|
112
|
+
if (delayMs <= 0) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
99
115
|
await new Promise((resolve, reject) => {
|
|
100
116
|
const timer = setTimeout(() => {
|
|
101
117
|
signal?.removeEventListener('abort', onAbort);
|
|
@@ -147,12 +163,14 @@ export function createDiscordWebhookTransport(options) {
|
|
|
147
163
|
}
|
|
148
164
|
const parsedWebhookUrl = parseWebhookUrl(webhookUrl);
|
|
149
165
|
const fetchLike = resolveFetch(options.fetch);
|
|
166
|
+
const retry = resolveRetryPolicy(options.retry);
|
|
150
167
|
const wait = options.wait ?? true;
|
|
151
168
|
return {
|
|
152
169
|
async send(message, context) {
|
|
153
|
-
for (let attempt = 1; attempt <=
|
|
170
|
+
for (let attempt = 1; attempt <= retry.attempts; attempt += 1) {
|
|
171
|
+
let response;
|
|
154
172
|
try {
|
|
155
|
-
|
|
173
|
+
response = await fetchLike(resolveWebhookUrl(parsedWebhookUrl, message, wait), {
|
|
156
174
|
body: JSON.stringify(createWebhookPayload(message)),
|
|
157
175
|
headers: {
|
|
158
176
|
'content-type': 'application/json; charset=utf-8'
|
|
@@ -160,44 +178,41 @@ export function createDiscordWebhookTransport(options) {
|
|
|
160
178
|
method: 'POST',
|
|
161
179
|
signal: context.signal
|
|
162
180
|
});
|
|
163
|
-
const body = await readResponseBody(response);
|
|
164
|
-
if (!response.ok) {
|
|
165
|
-
if (attempt < DEFAULT_RETRY_ATTEMPTS && isTransientStatus(response.status)) {
|
|
166
|
-
await waitForRetry(DEFAULT_RETRY_DELAY_MS * 2 ** (attempt - 1), context.signal);
|
|
167
|
-
continue;
|
|
168
|
-
}
|
|
169
|
-
throw new DiscordTransportError(createStatusFailureMessage(response, attempt));
|
|
170
|
-
}
|
|
171
|
-
const parsed = parseJsonRecord(body);
|
|
172
|
-
const warnings = [];
|
|
173
|
-
if (body && !parsed) {
|
|
174
|
-
warnings.push('Discord webhook returned a non-JSON success body.');
|
|
175
|
-
}
|
|
176
|
-
return {
|
|
177
|
-
channelId: getStringField(parsed, 'channel_id'),
|
|
178
|
-
guildId: getStringField(parsed, 'guild_id'),
|
|
179
|
-
messageId: getStringField(parsed, 'id'),
|
|
180
|
-
ok: true,
|
|
181
|
-
response: body || undefined,
|
|
182
|
-
statusCode: response.status,
|
|
183
|
-
threadId: getStringField(parsed, 'thread_id') ?? message.threadId,
|
|
184
|
-
warnings
|
|
185
|
-
};
|
|
186
181
|
} catch (error) {
|
|
187
182
|
if (isAbortError(error) || context.signal?.aborted) {
|
|
188
183
|
throw error;
|
|
189
184
|
}
|
|
190
|
-
if (
|
|
191
|
-
|
|
192
|
-
}
|
|
193
|
-
if (attempt < DEFAULT_RETRY_ATTEMPTS) {
|
|
194
|
-
await waitForRetry(DEFAULT_RETRY_DELAY_MS * 2 ** (attempt - 1), context.signal);
|
|
185
|
+
if (attempt < retry.attempts) {
|
|
186
|
+
await waitForRetry(retry.baseDelayMs * 2 ** (attempt - 1), context.signal);
|
|
195
187
|
continue;
|
|
196
188
|
}
|
|
197
189
|
throw new DiscordTransportError(createTransportFailureMessage(attempt));
|
|
198
190
|
}
|
|
191
|
+
const body = await readResponseBody(response);
|
|
192
|
+
if (!response.ok) {
|
|
193
|
+
if (attempt < retry.attempts && isTransientStatus(response.status)) {
|
|
194
|
+
await waitForRetry(retry.baseDelayMs * 2 ** (attempt - 1), context.signal);
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
throw new DiscordTransportError(createStatusFailureMessage(response, attempt));
|
|
198
|
+
}
|
|
199
|
+
const parsed = parseJsonRecord(body);
|
|
200
|
+
const warnings = [];
|
|
201
|
+
if (body && !parsed) {
|
|
202
|
+
warnings.push('Discord webhook returned a non-JSON success body.');
|
|
203
|
+
}
|
|
204
|
+
return {
|
|
205
|
+
channelId: getStringField(parsed, 'channel_id'),
|
|
206
|
+
guildId: getStringField(parsed, 'guild_id'),
|
|
207
|
+
messageId: getStringField(parsed, 'id'),
|
|
208
|
+
ok: true,
|
|
209
|
+
response: body || undefined,
|
|
210
|
+
statusCode: response.status,
|
|
211
|
+
threadId: getStringField(parsed, 'thread_id') ?? message.threadId,
|
|
212
|
+
warnings
|
|
213
|
+
};
|
|
199
214
|
}
|
|
200
|
-
throw new DiscordTransportError(createTransportFailureMessage(
|
|
215
|
+
throw new DiscordTransportError(createTransportFailureMessage(retry.attempts));
|
|
201
216
|
}
|
|
202
217
|
};
|
|
203
218
|
}
|
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"portable",
|
|
10
10
|
"fetch"
|
|
11
11
|
],
|
|
12
|
-
"version": "
|
|
12
|
+
"version": "2.0.0",
|
|
13
13
|
"private": false,
|
|
14
14
|
"license": "MIT",
|
|
15
15
|
"repository": {
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"directory": "packages/discord"
|
|
19
19
|
},
|
|
20
20
|
"engines": {
|
|
21
|
-
"node": ">=
|
|
21
|
+
"node": ">=24.0.0 <27"
|
|
22
22
|
},
|
|
23
23
|
"publishConfig": {
|
|
24
24
|
"access": "public"
|
|
@@ -36,13 +36,13 @@
|
|
|
36
36
|
"dist"
|
|
37
37
|
],
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@fluojs/core": "^
|
|
40
|
-
"@fluojs/di": "^
|
|
41
|
-
"@fluojs/notifications": "^
|
|
42
|
-
"@fluojs/runtime": "^
|
|
39
|
+
"@fluojs/core": "^2.0.0",
|
|
40
|
+
"@fluojs/di": "^3.0.0",
|
|
41
|
+
"@fluojs/notifications": "^2.0.0",
|
|
42
|
+
"@fluojs/runtime": "^3.0.0"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
|
-
"vitest": "^
|
|
45
|
+
"vitest": "^4.1.11"
|
|
46
46
|
},
|
|
47
47
|
"scripts": {
|
|
48
48
|
"prebuild": "node ../../tooling/scripts/clean-dist.mjs",
|