@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 +31 -3
- package/README_ko.md +31 -4
- package/dist/dispatcher/batch.dispatcher.d.ts +1 -1
- package/dist/dispatcher/load-balancer.d.ts +1 -1
- package/dist/dispatcher/queue.manager.d.ts +2 -1
- package/dist/dispatcher/types.d.ts +2 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +32 -46
- package/dist/index.js.map +21 -17
- package/dist/index.mjs +32 -46
- package/dist/index.mjs.map +21 -17
- package/dist/registry/delivery.store.d.ts +1 -1
- package/dist/registry/endpoint.manager.d.ts +1 -1
- package/dist/registry/event.store.d.ts +1 -1
- package/dist/registry/types.d.ts +2 -0
- package/dist/security/security.manager.d.ts +2 -0
- package/dist/shared/event-emitter.d.ts +15 -0
- package/dist/shared/file-storage.d.ts +9 -0
- package/package.json +7 -7
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:
|
|
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:
|
|
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 =
|
|
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:
|
|
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:
|
|
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 =
|
|
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 "
|
|
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 "
|
|
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 "
|
|
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";
|