@k-msg/webhook 0.15.0 → 0.17.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.md CHANGED
@@ -10,6 +10,7 @@ This package provides:
10
10
 
11
11
  Note:
12
12
  - The default `WebhookService` storage is in-memory. For persistence/advanced workflows, see the exported building blocks such as `EndpointManager` and `DeliveryStore`.
13
+ - This package is runtime-neutral (Edge/Web/Node). Node built-ins are not required by default.
13
14
 
14
15
  ## Install
15
16
 
@@ -22,6 +23,7 @@ bun add @k-msg/webhook
22
23
  ## Quickstart (WebhookService)
23
24
 
24
25
  ```ts
26
+ import { readRuntimeEnv } from "@k-msg/core";
25
27
  import {
26
28
  WebhookEventType,
27
29
  WebhookService,
@@ -35,7 +37,7 @@ const config: WebhookConfig = {
35
37
  timeoutMs: 30_000,
36
38
  enableSecurity: true,
37
39
  // Optional: used when an endpoint does not provide its own secret
38
- secretKey: process.env.WEBHOOK_SECRET,
40
+ secretKey: readRuntimeEnv("WEBHOOK_SECRET"),
39
41
  // Optional: algorithm, signatureHeader, signaturePrefix
40
42
  enabledEvents: [
41
43
  WebhookEventType.MESSAGE_SENT,
@@ -53,7 +55,7 @@ const endpoint = await service.registerEndpoint({
53
55
  active: true,
54
56
  events: [WebhookEventType.MESSAGE_SENT, WebhookEventType.MESSAGE_FAILED],
55
57
  // Optional: endpoint-specific secret (preferred over config.secretKey)
56
- secret: process.env.WEBHOOK_SECRET,
58
+ secret: readRuntimeEnv("WEBHOOK_SECRET"),
57
59
  // Optional: per-endpoint retry overrides
58
60
  retryConfig: { maxRetries: 5, retryDelayMs: 1000, backoffMultiplier: 2 },
59
61
  // Optional: metadata-based filters
@@ -109,6 +111,7 @@ To verify a webhook, you must use the exact raw request body string.
109
111
 
110
112
  ```ts
111
113
  import { Hono } from "hono";
114
+ import { readRuntimeEnv } from "@k-msg/core";
112
115
  import { SecurityManager } from "@k-msg/webhook";
113
116
 
114
117
  const app = new Hono();
@@ -124,7 +127,7 @@ app.post("/webhooks/k-msg", async (c) => {
124
127
 
125
128
  const signature = c.req.header("X-Webhook-Signature") ?? "";
126
129
  const timestamp = c.req.header("X-Webhook-Timestamp") ?? "";
127
- const secret = process.env.WEBHOOK_SECRET ?? "";
130
+ const secret = readRuntimeEnv("WEBHOOK_SECRET") ?? "";
128
131
 
129
132
  if (!security.verifyTimestamp(timestamp, 300)) {
130
133
  return c.json({ error: "Request too old" }, 401);
@@ -164,6 +167,31 @@ await service.registerEndpoint({
164
167
  });
165
168
  ```
166
169
 
170
+ ## File Storage Adapter (for `type: "file"`)
171
+
172
+ `EndpointManager`, `EventStore`, `DeliveryStore`, and `QueueManager` no longer import Node `fs/path` directly.
173
+ When using file persistence, provide `fileAdapter`.
174
+
175
+ ```ts
176
+ import { DeliveryStore, type FileStorageAdapter } from "@k-msg/webhook";
177
+ import * as fs from "node:fs/promises";
178
+ import * as path from "node:path";
179
+
180
+ const nodeFileAdapter: FileStorageAdapter = {
181
+ appendFile: (filePath, data) => fs.appendFile(filePath, data, "utf8"),
182
+ readFile: (filePath) => fs.readFile(filePath, "utf8"),
183
+ writeFile: (filePath, data) => fs.writeFile(filePath, data, "utf8"),
184
+ ensureDirForFile: (filePath) =>
185
+ fs.mkdir(path.dirname(filePath), { recursive: true }),
186
+ };
187
+
188
+ const store = new DeliveryStore({
189
+ type: "file",
190
+ filePath: "./data/deliveries.log",
191
+ fileAdapter: nodeFileAdapter,
192
+ });
193
+ ```
194
+
167
195
  ## Zod Schemas
168
196
 
169
197
  This package exports Zod schemas for validation:
package/README_ko.md CHANGED
@@ -10,6 +10,7 @@ HTTP 엔드포인트로 실시간 메시지 이벤트를 전달하기 위한 웹
10
10
 
11
11
  참고:
12
12
  - 기본 `WebhookService` 저장소는 in-memory입니다. 영속 저장/고급 워크플로우가 필요하면 `EndpointManager`, `DeliveryStore` 같은 빌딩 블록을 활용하세요.
13
+ - 이 패키지는 런타임 중립(Edge/Web/Node)으로 동작하며, 기본적으로 Node 내장 모듈에 의존하지 않습니다.
13
14
 
14
15
  ## 설치
15
16
 
@@ -22,6 +23,7 @@ bun add @k-msg/webhook
22
23
  ## 빠른 시작 (WebhookService)
23
24
 
24
25
  ```ts
26
+ import { readRuntimeEnv } from "@k-msg/core";
25
27
  import {
26
28
  WebhookEventType,
27
29
  WebhookService,
@@ -35,7 +37,7 @@ const config: WebhookConfig = {
35
37
  timeoutMs: 30_000,
36
38
  enableSecurity: true,
37
39
  // Optional: 엔드포인트에 secret이 없을 때 fallback으로 사용됩니다.
38
- secretKey: process.env.WEBHOOK_SECRET,
40
+ secretKey: readRuntimeEnv("WEBHOOK_SECRET"),
39
41
  // Optional: algorithm, signatureHeader, signaturePrefix
40
42
  enabledEvents: [
41
43
  WebhookEventType.MESSAGE_SENT,
@@ -53,7 +55,7 @@ const endpoint = await service.registerEndpoint({
53
55
  active: true,
54
56
  events: [WebhookEventType.MESSAGE_SENT, WebhookEventType.MESSAGE_FAILED],
55
57
  // Optional: 엔드포인트별 secret (config.secretKey보다 우선)
56
- secret: process.env.WEBHOOK_SECRET,
58
+ secret: readRuntimeEnv("WEBHOOK_SECRET"),
57
59
  // Optional: 엔드포인트별 재시도 설정
58
60
  retryConfig: { maxRetries: 5, retryDelayMs: 1000, backoffMultiplier: 2 },
59
61
  // Optional: 메타데이터 기반 필터
@@ -109,6 +111,7 @@ ${timestamp}.${rawBody}
109
111
 
110
112
  ```ts
111
113
  import { Hono } from "hono";
114
+ import { readRuntimeEnv } from "@k-msg/core";
112
115
  import { SecurityManager } from "@k-msg/webhook";
113
116
 
114
117
  const app = new Hono();
@@ -123,7 +126,7 @@ app.post("/webhooks/k-msg", async (c) => {
123
126
 
124
127
  const signature = c.req.header("X-Webhook-Signature") ?? "";
125
128
  const timestamp = c.req.header("X-Webhook-Timestamp") ?? "";
126
- const secret = process.env.WEBHOOK_SECRET ?? "";
129
+ const secret = readRuntimeEnv("WEBHOOK_SECRET") ?? "";
127
130
 
128
131
  if (!security.verifyTimestamp(timestamp, 300)) {
129
132
  return c.json({ error: "Request too old" }, 401);
@@ -163,6 +166,31 @@ await service.registerEndpoint({
163
166
  });
164
167
  ```
165
168
 
169
+ ## File Storage Adapter (`type: "file"` 사용 시)
170
+
171
+ `EndpointManager`, `EventStore`, `DeliveryStore`, `QueueManager`는 Node `fs/path`를 직접 import하지 않습니다.
172
+ 파일 영속화를 사용하려면 `fileAdapter`를 주입해야 합니다.
173
+
174
+ ```ts
175
+ import { DeliveryStore, type FileStorageAdapter } from "@k-msg/webhook";
176
+ import * as fs from "node:fs/promises";
177
+ import * as path from "node:path";
178
+
179
+ const nodeFileAdapter: FileStorageAdapter = {
180
+ appendFile: (filePath, data) => fs.appendFile(filePath, data, "utf8"),
181
+ readFile: (filePath) => fs.readFile(filePath, "utf8"),
182
+ writeFile: (filePath, data) => fs.writeFile(filePath, data, "utf8"),
183
+ ensureDirForFile: (filePath) =>
184
+ fs.mkdir(path.dirname(filePath), { recursive: true }),
185
+ };
186
+
187
+ const store = new DeliveryStore({
188
+ type: "file",
189
+ filePath: "./data/deliveries.log",
190
+ fileAdapter: nodeFileAdapter,
191
+ });
192
+ ```
193
+
166
194
  ## Zod 스키마
167
195
 
168
196
  이 패키지는 검증용 Zod 스키마를 export 합니다.
@@ -173,4 +201,3 @@ await service.registerEndpoint({
173
201
  ## License
174
202
 
175
203
  MIT
176
-
@@ -2,7 +2,7 @@
2
2
  * Batch Webhook Dispatcher
3
3
  * 대량 웹훅 요청을 효율적으로 처리하는 배치 디스패처
4
4
  */
5
- import { EventEmitter } from "events";
5
+ import { EventEmitter } from "../shared/event-emitter";
6
6
  import type { WebhookBatch } from "../types/webhook.types";
7
7
  import type { BatchConfig, DispatchJob } from "./types";
8
8
  export declare class BatchDispatcher extends EventEmitter {
@@ -2,7 +2,7 @@
2
2
  * Load Balancer
3
3
  * 웹훅 엔드포인트 간의 부하 분산 관리
4
4
  */
5
- import { EventEmitter } from "events";
5
+ import { EventEmitter } from "../shared/event-emitter";
6
6
  import type { WebhookEndpoint } from "../types/webhook.types";
7
7
  import type { LoadBalancerConfig } from "./types";
8
8
  interface EndpointHealth {
@@ -2,7 +2,7 @@
2
2
  * Queue Manager
3
3
  * 웹훅 작업 큐 관리 시스템
4
4
  */
5
- import { EventEmitter } from "events";
5
+ import { EventEmitter } from "../shared/event-emitter";
6
6
  import type { DispatchJob, QueueConfig } from "./types";
7
7
  export declare class QueueManager extends EventEmitter {
8
8
  private config;
@@ -11,6 +11,7 @@ export declare class QueueManager extends EventEmitter {
11
11
  private mediumPriorityQueue;
12
12
  private lowPriorityQueue;
13
13
  private delayedJobs;
14
+ private ttlCleanupInterval;
14
15
  private totalJobs;
15
16
  private defaultConfig;
16
17
  constructor(config?: Partial<QueueConfig>);
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * Dispatcher Type Definitions
3
3
  */
4
+ import type { FileStorageAdapter } from "../shared/file-storage";
4
5
  import type { WebhookEndpoint, WebhookEvent } from "../types/webhook.types";
5
6
  export interface DispatchConfig {
6
7
  maxConcurrentRequests: number;
@@ -20,6 +21,7 @@ export interface QueueConfig {
20
21
  maxQueueSize: number;
21
22
  persistToDisk: boolean;
22
23
  diskPath?: string;
24
+ fileAdapter?: FileStorageAdapter;
23
25
  compressionEnabled: boolean;
24
26
  ttlMs: number;
25
27
  }
package/dist/index.d.ts CHANGED
@@ -15,5 +15,6 @@ export { SecurityManager } from "./security/security.manager";
15
15
  export { DefaultHttpClient, type HttpClient, MockHttpClient, WebhookDispatcher, } from "./services/webhook.dispatcher";
16
16
  export { WebhookRegistry } from "./services/webhook.registry";
17
17
  export { WebhookService } from "./services/webhook.service";
18
+ export type { FileStorageAdapter } from "./shared/file-storage";
18
19
  export type { WebhookAttempt, WebhookBatch, WebhookConfig, WebhookDelivery, WebhookDeliveryData, WebhookEndpoint, WebhookEndpointData, WebhookEvent, WebhookEventData, WebhookSecurity, WebhookStats, WebhookTestResult, } from "./types/webhook.types";
19
20
  export { WebhookDeliverySchema, WebhookEndpointSchema, WebhookEventSchema, WebhookEventType, } from "./types/webhook.types";