@fluojs/slack 1.0.0-beta.4 → 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 CHANGED
@@ -128,6 +128,7 @@ Behavioral contract 메모:
128
128
  - `SlackService.send(...)`는 전달 전에 `defaultChannel`을 해석합니다.
129
129
  - `SlackService.sendMany(...)`는 메시지를 순차적으로 보내며, fail-fast 대신 batch result가 필요한 호출자를 위해 `continueOnError`를 지원합니다.
130
130
  - 서비스는 모듈 bootstrap 시 transport를 초기화하고, factory가 소유한 리소스만 애플리케이션 shutdown 시 닫습니다.
131
+ - shutdown이 시작된 뒤에는 `SlackService.send(...)`와 `SlackService.sendNotification(...)`이 transport를 재사용하거나 lazy 생성하지 않고 `SlackLifecycleError`로 실패합니다. 진행 중인 factory 소유 transport 생성은 shutdown이 기다린 뒤 닫습니다.
131
132
  - `SlackService.createPlatformStatusSnapshot()`은 호출자가 내부 옵션에 접근하지 않아도 lifecycle, readiness, transport 소유권을 보고합니다.
132
133
  - 이 패키지는 절대로 `process.env`를 직접 읽지 않습니다. 모든 설정은 명시적인 옵션 또는 DI를 통해 들어와야 합니다.
133
134
 
@@ -218,7 +219,10 @@ Slack 패키지는 의도적으로 다음을 **포함하지 않습니다**:
218
219
  - `SlackModule.forRoot(options)` / `SlackModule.forRootAsync(options)`
219
220
  - `createSlackProviders(options)`
220
221
  - `SlackService`
222
+ - `SlackService.send(message, options)`
221
223
  - `SlackService.sendMany(messages, options)`
224
+ - `SlackService.sendNotification(notification, options)`
225
+ - `SlackService.createPlatformStatusSnapshot()`
222
226
  - `SlackChannel`
223
227
  - `SLACK`
224
228
  - `SLACK_CHANNEL`
@@ -256,6 +260,7 @@ Slack 패키지는 의도적으로 다음을 **포함하지 않습니다**:
256
260
  - `SlackLifecycleState`
257
261
  - `SlackStatusAdapterInput`
258
262
  - `SlackConfigurationError`
263
+ - `SlackLifecycleError`: lifecycle로 차단된 전달, transport 초기화, 소유 리소스 shutdown 실패에서 발생합니다. 애플리케이션 teardown과 전송이 경합할 수 있다면 이 에러를 catch하세요.
259
264
  - `SlackMessageValidationError`
260
265
  - `SlackTransportError`
261
266
 
package/README.md CHANGED
@@ -128,6 +128,7 @@ Behavioral contract notes:
128
128
  - `SlackService.send(...)` resolves `defaultChannel` before delivery.
129
129
  - `SlackService.sendMany(...)` sends messages sequentially and supports `continueOnError` when callers need a batch result instead of fail-fast behavior.
130
130
  - The service initializes the configured transport during module bootstrap and closes factory-owned resources during application shutdown.
131
+ - Once shutdown starts, `SlackService.send(...)` and `SlackService.sendNotification(...)` fail with `SlackLifecycleError` instead of reusing or lazily creating transports; any in-flight factory-owned transport creation is awaited and closed by shutdown.
131
132
  - `SlackService.createPlatformStatusSnapshot()` reports lifecycle, readiness, and transport ownership without requiring callers to reach into internal options.
132
133
  - The package never reads `process.env` directly. All configuration must enter through explicit options or DI.
133
134
 
@@ -218,7 +219,10 @@ These limitations are part of the package contract so runtime choice, provider c
218
219
  - `SlackModule.forRoot(options)` / `SlackModule.forRootAsync(options)`
219
220
  - `createSlackProviders(options)`
220
221
  - `SlackService`
222
+ - `SlackService.send(message, options)`
221
223
  - `SlackService.sendMany(messages, options)`
224
+ - `SlackService.sendNotification(notification, options)`
225
+ - `SlackService.createPlatformStatusSnapshot()`
222
226
  - `SlackChannel`
223
227
  - `SLACK`
224
228
  - `SLACK_CHANNEL`
@@ -256,6 +260,7 @@ These limitations are part of the package contract so runtime choice, provider c
256
260
  - `SlackLifecycleState`
257
261
  - `SlackStatusAdapterInput`
258
262
  - `SlackConfigurationError`
263
+ - `SlackLifecycleError`: thrown by lifecycle-gated delivery, transport initialization, and owned-resource shutdown failures. Catch this error when sends can race with application teardown.
259
264
  - `SlackMessageValidationError`
260
265
  - `SlackTransportError`
261
266
 
package/dist/errors.d.ts CHANGED
@@ -10,6 +10,12 @@ export declare class SlackConfigurationError extends Error {
10
10
  export declare class SlackMessageValidationError extends Error {
11
11
  constructor(message: string);
12
12
  }
13
+ /**
14
+ * Thrown when Slack delivery is requested after the service lifecycle has started shutting down.
15
+ */
16
+ export declare class SlackLifecycleError extends Error {
17
+ constructor(message: string, options?: ErrorOptions);
18
+ }
13
19
  /**
14
20
  * Thrown when one concrete Slack transport reports a caller-visible delivery failure.
15
21
  */
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,qBAAa,uBAAwB,SAAQ,KAAK;gBACpC,OAAO,EAAE,MAAM;CAI5B;AAED;;GAEG;AACH,qBAAa,2BAA4B,SAAQ,KAAK;gBACxC,OAAO,EAAE,MAAM;CAI5B;AAED;;GAEG;AACH,qBAAa,mBAAoB,SAAQ,KAAK;gBAChC,OAAO,EAAE,MAAM;CAI5B"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,qBAAa,uBAAwB,SAAQ,KAAK;gBACpC,OAAO,EAAE,MAAM;CAI5B;AAED;;GAEG;AACH,qBAAa,2BAA4B,SAAQ,KAAK;gBACxC,OAAO,EAAE,MAAM;CAI5B;AAED;;GAEG;AACH,qBAAa,mBAAoB,SAAQ,KAAK;gBAChC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY;CAIpD;AAED;;GAEG;AACH,qBAAa,mBAAoB,SAAQ,KAAK;gBAChC,OAAO,EAAE,MAAM;CAI5B"}
package/dist/errors.js CHANGED
@@ -18,6 +18,16 @@ export class SlackMessageValidationError extends Error {
18
18
  }
19
19
  }
20
20
 
21
+ /**
22
+ * Thrown when Slack delivery is requested after the service lifecycle has started shutting down.
23
+ */
24
+ export class SlackLifecycleError extends Error {
25
+ constructor(message, options) {
26
+ super(message, options);
27
+ this.name = 'SlackLifecycleError';
28
+ }
29
+ }
30
+
21
31
  /**
22
32
  * Thrown when one concrete Slack transport reports a caller-visible delivery failure.
23
33
  */
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { SlackConfigurationError, SlackMessageValidationError, SlackTransportError, } from './errors.js';
1
+ export { SlackConfigurationError, SlackLifecycleError, SlackMessageValidationError, SlackTransportError, } from './errors.js';
2
2
  export { SlackChannel } from './channel.js';
3
3
  export { SlackModule, createSlackProviders } from './module.js';
4
4
  export { SlackService } from './service.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,uBAAuB,EACvB,2BAA2B,EAC3B,mBAAmB,GACpB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,iCAAiC,EAAE,MAAM,aAAa,CAAC;AAChE,YAAY,EAAE,mBAAmB,EAAE,2BAA2B,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAC7G,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACnD,YAAY,EACV,sBAAsB,EACtB,KAAK,EACL,uBAAuB,EACvB,eAAe,EACf,UAAU,EACV,cAAc,EACd,kBAAkB,EAClB,YAAY,EACZ,kBAAkB,EAClB,gCAAgC,EAChC,wBAAwB,EACxB,oBAAoB,EACpB,gBAAgB,EAChB,oBAAoB,EACpB,gBAAgB,EAChB,eAAe,EACf,wBAAwB,EACxB,qBAAqB,EACrB,yBAAyB,EACzB,cAAc,EACd,qBAAqB,EACrB,qBAAqB,EACrB,qBAAqB,EACrB,4BAA4B,GAC7B,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,2BAA2B,EAAE,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,uBAAuB,EACvB,mBAAmB,EACnB,2BAA2B,EAC3B,mBAAmB,GACpB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,iCAAiC,EAAE,MAAM,aAAa,CAAC;AAChE,YAAY,EAAE,mBAAmB,EAAE,2BAA2B,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAC7G,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACnD,YAAY,EACV,sBAAsB,EACtB,KAAK,EACL,uBAAuB,EACvB,eAAe,EACf,UAAU,EACV,cAAc,EACd,kBAAkB,EAClB,YAAY,EACZ,kBAAkB,EAClB,gCAAgC,EAChC,wBAAwB,EACxB,oBAAoB,EACpB,gBAAgB,EAChB,oBAAoB,EACpB,gBAAgB,EAChB,eAAe,EACf,wBAAwB,EACxB,qBAAqB,EACrB,yBAAyB,EACzB,cAAc,EACd,qBAAqB,EACrB,qBAAqB,EACrB,qBAAqB,EACrB,4BAA4B,GAC7B,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,2BAA2B,EAAE,MAAM,cAAc,CAAC"}
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- export { SlackConfigurationError, SlackMessageValidationError, SlackTransportError } from './errors.js';
1
+ export { SlackConfigurationError, SlackLifecycleError, SlackMessageValidationError, SlackTransportError } from './errors.js';
2
2
  export { SlackChannel } from './channel.js';
3
3
  export { SlackModule, createSlackProviders } from './module.js';
4
4
  export { SlackService } from './service.js';
package/dist/service.d.ts CHANGED
@@ -72,6 +72,8 @@ export declare class SlackService implements Slack, OnModuleInit, OnApplicationS
72
72
  */
73
73
  sendNotification(notification: SlackNotificationDispatchRequest, options?: SlackSendOptions): Promise<SlackSendResult>;
74
74
  private ensureTransport;
75
+ private assertCanCreateOrUseTransport;
76
+ private assertCanDeliver;
75
77
  private normalizeMessage;
76
78
  private resolveNotificationChannel;
77
79
  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,EAEV,4BAA4B,EAC5B,KAAK,EACL,YAAY,EACZ,gCAAgC,EAChC,oBAAoB,EAEpB,oBAAoB,EACpB,gBAAgB,EAChB,eAAe,EAGhB,MAAM,YAAY,CAAC;AAqBpB;;;;;;;GAOG;AACH,qBACa,YAAa,YAAW,KAAK,EAAE,YAAY,EAAE,qBAAqB;IAKjE,OAAO,CAAC,QAAQ,CAAC,OAAO;IAJpC,OAAO,CAAC,cAAc,CAAmF;IACzG,OAAO,CAAC,iBAAiB,CAA6B;IACtD,OAAO,CAAC,gBAAgB,CAAsC;gBAEjC,OAAO,EAAE,4BAA4B;IAE5D,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC;IAetC,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAiBnC;;;;OAIG;IACH,4BAA4B;IAW5B;;;;;;;;;;;;;;;OAeG;IACG,IAAI,CAAC,OAAO,EAAE,YAAY,EAAE,OAAO,GAAE,gBAAqB,GAAG,OAAO,CAAC,eAAe,CAAC;IAqB3F;;;;;;;;;;;;OAYG;IACG,QAAQ,CAAC,QAAQ,EAAE,SAAS,YAAY,EAAE,EAAE,OAAO,GAAE,oBAAyB,GAAG,OAAO,CAAC,oBAAoB,CAAC;IA6BpH;;;;;;;;;;;;;;;;OAgBG;IACG,gBAAgB,CACpB,YAAY,EAAE,gCAAgC,EAC9C,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,eAAe,CAAC;YA6Bb,eAAe;IAe7B,OAAO,CAAC,gBAAgB;IAkBxB,OAAO,CAAC,0BAA0B;YAkBpB,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,EAEV,4BAA4B,EAC5B,KAAK,EACL,YAAY,EACZ,gCAAgC,EAChC,oBAAoB,EAEpB,oBAAoB,EACpB,gBAAgB,EAChB,eAAe,EAGhB,MAAM,YAAY,CAAC;AA2CpB;;;;;;;GAOG;AACH,qBACa,YAAa,YAAW,KAAK,EAAE,YAAY,EAAE,qBAAqB;IAKjE,OAAO,CAAC,QAAQ,CAAC,OAAO;IAJpC,OAAO,CAAC,cAAc,CAAyC;IAC/D,OAAO,CAAC,iBAAiB,CAA6B;IACtD,OAAO,CAAC,gBAAgB,CAAsC;gBAEjC,OAAO,EAAE,4BAA4B;IAE5D,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC;IAiBtC,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAiCnC;;;;OAIG;IACH,4BAA4B;IAW5B;;;;;;;;;;;;;;;OAeG;IACG,IAAI,CAAC,OAAO,EAAE,YAAY,EAAE,OAAO,GAAE,gBAAqB,GAAG,OAAO,CAAC,eAAe,CAAC;IAqB3F;;;;;;;;;;;;OAYG;IACG,QAAQ,CAAC,QAAQ,EAAE,SAAS,YAAY,EAAE,EAAE,OAAO,GAAE,oBAAyB,GAAG,OAAO,CAAC,oBAAoB,CAAC;IA6BpH;;;;;;;;;;;;;;;;OAgBG;IACG,gBAAgB,CACpB,YAAY,EAAE,gCAAgC,EAC9C,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,eAAe,CAAC;YAgCb,eAAe;IAiB7B,OAAO,CAAC,6BAA6B;IAMrC,OAAO,CAAC,gBAAgB;IAIxB,OAAO,CAAC,gBAAgB;IAkBxB,OAAO,CAAC,0BAA0B;YAkBpB,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 { SlackMessageValidationError } from './errors.js';
8
+ import { SlackLifecycleError, SlackMessageValidationError } from './errors.js';
9
9
  import { createSlackPlatformStatusSnapshot } from './status.js';
10
10
  import { SLACK_OPTIONS } from './tokens.js';
11
11
  function createAbortError() {
@@ -13,6 +13,22 @@ function createAbortError() {
13
13
  error.name = 'AbortError';
14
14
  return error;
15
15
  }
16
+ function assertNotAborted(signal) {
17
+ if (signal?.aborted) {
18
+ throw createAbortError();
19
+ }
20
+ }
21
+ function createLifecycleError(message, cause) {
22
+ return new SlackLifecycleError(message, {
23
+ cause
24
+ });
25
+ }
26
+ function createDeliveryLifecycleError(state) {
27
+ return new SlackLifecycleError(`Slack delivery cannot start while the service lifecycle is ${state}.`);
28
+ }
29
+ function isShutdownLifecycleState(state) {
30
+ return state === 'stopping' || state === 'stopped';
31
+ }
16
32
  function normalizeOptionalString(value) {
17
33
  const trimmed = value?.trim();
18
34
  return trimmed && trimmed.length > 0 ? trimmed : undefined;
@@ -45,26 +61,39 @@ class SlackService {
45
61
  async onApplicationShutdown() {
46
62
  this.lifecycleState = 'stopping';
47
63
  try {
48
- if (this.resolvedTransport && this.options.transport.ownsResources && this.resolvedTransport.close) {
49
- await this.resolvedTransport.close();
64
+ const transport = this.resolvedTransport ?? (this.transportPromise ? await this.transportPromise : undefined);
65
+ if (transport && this.options.transport.ownsResources && transport.close) {
66
+ await transport.close();
50
67
  }
51
68
  this.lifecycleState = 'stopped';
52
- } catch {
69
+ } catch (error) {
53
70
  this.lifecycleState = 'failed';
54
- throw new Error('Slack transport failed to close cleanly.');
71
+ throw createLifecycleError('Slack transport failed to close cleanly.', error);
55
72
  }
56
73
  }
57
74
  async onModuleInit() {
75
+ if (this.lifecycleState === 'stopping' || this.lifecycleState === 'stopped') {
76
+ return;
77
+ }
58
78
  this.lifecycleState = 'starting';
59
79
  try {
60
80
  const transport = await this.ensureTransport();
81
+ if (this.lifecycleState !== 'starting') {
82
+ return;
83
+ }
61
84
  if (this.options.verifyOnModuleInit && transport.verify) {
62
85
  await transport.verify();
63
86
  }
87
+ if (this.lifecycleState !== 'starting') {
88
+ return;
89
+ }
64
90
  this.lifecycleState = 'ready';
65
- } catch {
91
+ } catch (error) {
92
+ if (isShutdownLifecycleState(this.lifecycleState)) {
93
+ throw error;
94
+ }
66
95
  this.lifecycleState = 'failed';
67
- throw new Error('Slack transport failed to initialize.');
96
+ throw createLifecycleError('Slack transport failed to initialize.', error);
68
97
  }
69
98
  }
70
99
 
@@ -101,12 +130,12 @@ class SlackService {
101
130
  * ```
102
131
  */
103
132
  async send(message, options = {}) {
104
- if (options.signal?.aborted) {
105
- throw createAbortError();
106
- }
133
+ assertNotAborted(options.signal);
134
+ this.assertCanDeliver();
107
135
  const transport = await this.ensureTransport();
108
136
  const normalized = this.normalizeMessage(message);
109
137
  assertMessageContent(normalized);
138
+ this.assertCanDeliver();
110
139
  const result = await transport.send(normalized, options);
111
140
  return {
112
141
  channel: result.channel ?? normalized.channel,
@@ -175,6 +204,8 @@ class SlackService {
175
204
  * ```
176
205
  */
177
206
  async sendNotification(notification, options = {}) {
207
+ assertNotAborted(options.signal);
208
+ this.assertCanDeliver();
178
209
  const payload = notification.payload;
179
210
  const rendered = await this.renderNotification(notification);
180
211
  return this.send({
@@ -203,6 +234,7 @@ class SlackService {
203
234
  }, options);
204
235
  }
205
236
  async ensureTransport() {
237
+ this.assertCanCreateOrUseTransport();
206
238
  if (this.resolvedTransport) {
207
239
  return this.resolvedTransport;
208
240
  }
@@ -214,6 +246,14 @@ class SlackService {
214
246
  }
215
247
  return this.transportPromise;
216
248
  }
249
+ assertCanCreateOrUseTransport() {
250
+ if (this.lifecycleState === 'stopping' || this.lifecycleState === 'stopped' || this.lifecycleState === 'failed') {
251
+ throw createDeliveryLifecycleError(this.lifecycleState);
252
+ }
253
+ }
254
+ assertCanDeliver() {
255
+ this.assertCanCreateOrUseTransport();
256
+ }
217
257
  normalizeMessage(message) {
218
258
  return {
219
259
  attachments: message.attachments ?? [],
package/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  "portable",
10
10
  "fetch"
11
11
  ],
12
- "version": "1.0.0-beta.4",
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-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"
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"