@nimbusnexus/webhooks-sdk 0.2.0 → 0.4.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 +73 -2
- package/dist/index.cjs +593 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +265 -13
- package/dist/index.d.ts +265 -13
- package/dist/index.js +582 -13
- package/dist/index.js.map +1 -1
- package/package.json +22 -3
package/README.md
CHANGED
|
@@ -35,15 +35,84 @@ try {
|
|
|
35
35
|
{ orderId: "ord_123", total: 4200 },
|
|
36
36
|
{ idempotencyKey: "order-123" }, // makes the publish safe to retry
|
|
37
37
|
);
|
|
38
|
-
console.log(event.eventUid, event.deliveriesCreated);
|
|
38
|
+
console.log(event.eventUid, event.projectId, event.deliveriesCreated);
|
|
39
39
|
} catch (e) {
|
|
40
40
|
if (e instanceof WebhookdApiError) console.error(e.statusCode, e.code, e.message);
|
|
41
41
|
}
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
+
**Projects.** A project is addressed by its **id** (`prj_…`), not a slug. Omit `projectId` — as
|
|
45
|
+
above — to publish into your workspace's **default** project; the server resolves it. Pass one only to
|
|
46
|
+
target a specific project:
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
await wh.publish("order.created", { orderId: "ord_123" }, { projectId: "prj_3f9a…" });
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
The id is opaque and per-workspace, so there is no client-side sentinel for "the default project":
|
|
53
|
+
leaving `projectId` unset omits the field entirely. The response (`event.projectId`) always carries
|
|
54
|
+
the id the event actually landed in.
|
|
55
|
+
|
|
44
56
|
Transient failures (network errors, `429`, `5xx`) are retried with backoff (a `429` honours
|
|
45
57
|
`Retry-After`); other `4xx` throw `WebhookdApiError` carrying the `{error:{code,message}}` envelope.
|
|
46
58
|
|
|
59
|
+
## Outbox / durable buffering (producers)
|
|
60
|
+
|
|
61
|
+
`publish()` calls webhookd synchronously — if webhookd is unreachable it rejects and the event is
|
|
62
|
+
lost. The **write-first outbox** decouples the two: `enqueue()` durably persists the event to a
|
|
63
|
+
pluggable `Store` and resolves IMMEDIATELY (no network); `drain()` (or a background drainer) ships the
|
|
64
|
+
buffered events later. Every send carries `Idempotency-Key = record.id`, so a re-drain after a crash
|
|
65
|
+
or a lost response never double-publishes — webhookd dedupes. Delivery is **at-least-once**: nothing
|
|
66
|
+
is lost while webhookd is down.
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
import { WebhookdClient, SqliteStore } from "@nimbusnexus/webhooks-sdk";
|
|
70
|
+
|
|
71
|
+
// 1. Configure a durable store (survives process restarts; needs Node >= 22.5 for node:sqlite).
|
|
72
|
+
const store = new SqliteStore("outbox.db");
|
|
73
|
+
|
|
74
|
+
const wh = new WebhookdClient({
|
|
75
|
+
baseUrl: "https://webhooks.example.com",
|
|
76
|
+
apiKey: "whsk_…",
|
|
77
|
+
store,
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// 2. enqueue() instead of publish() — writes to the store and resolves at once, NO network call.
|
|
81
|
+
const { id } = await wh.enqueue("order.created", { orderId: "ord_123", total: 4200 });
|
|
82
|
+
|
|
83
|
+
// 3a. Drain on demand (resolves to { sent, failed, remaining }):
|
|
84
|
+
await wh.drain();
|
|
85
|
+
|
|
86
|
+
// 3b. …or run a background drainer that calls drain() every 5s until you stop it.
|
|
87
|
+
wh.startDrainer(5);
|
|
88
|
+
// ... your app keeps enqueuing; the drainer ships in the background ...
|
|
89
|
+
wh.stopDrainer();
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
**Idempotency guarantee.** `id` is the `idempotencyKey` you pass (or a generated UUID v4) and becomes
|
|
93
|
+
the `Idempotency-Key` header on every delivery attempt for that record. If the process crashes after
|
|
94
|
+
a send but before the response is recorded, the next `drain()` re-sends with the *same* key and
|
|
95
|
+
webhookd returns the original event without re-fanning-out. A record that keeps failing is retried
|
|
96
|
+
with capped exponential backoff up to `maxAttempts` (default 10), then parked **dead** (never retried
|
|
97
|
+
again, retrievable via `store.listDead()`) and passed to the optional `onDead` callback.
|
|
98
|
+
|
|
99
|
+
`enqueue` takes the same optional `projectId` as `publish` (omit it for the default project). It is
|
|
100
|
+
persisted on the buffered record as a nullable `project_id` — `null` means "the workspace's default
|
|
101
|
+
project", and `drain` then omits the field from the publish body.
|
|
102
|
+
|
|
103
|
+
**Built-in stores** — pass one as `store` in `ClientOptions`:
|
|
104
|
+
|
|
105
|
+
| Store | Durable? | Extra needed |
|
|
106
|
+
| --- | --- | --- |
|
|
107
|
+
| `MemoryStore` | No (in-process) | — (built-in) |
|
|
108
|
+
| `FileStore(dir)` | Yes (per-record JSON files) | — (built-in) |
|
|
109
|
+
| `SqliteStore(path)` | Yes (transactional) | — (built-in `node:sqlite`, Node ≥ 22.5) |
|
|
110
|
+
| `RedisStore({ url })` | Yes | `npm install redis` |
|
|
111
|
+
| `PostgresStore({ connectionString })` | Yes | `npm install pg` |
|
|
112
|
+
|
|
113
|
+
The core SDK stays zero-dependency; `redis` / `pg` are `optionalDependencies`, imported lazily only
|
|
114
|
+
when you construct `RedisStore` / `PostgresStore`.
|
|
115
|
+
|
|
47
116
|
## Manage endpoints, keys & deliveries (operators)
|
|
48
117
|
|
|
49
118
|
The same client wraps the control-plane API — register receivers, mint keys, and drain the
|
|
@@ -58,13 +127,15 @@ const wh = new WebhookdClient({ baseUrl: "https://webhooks.example.com", apiKey:
|
|
|
58
127
|
|
|
59
128
|
// --- Endpoints ---------------------------------------------------------------
|
|
60
129
|
// Create a receiver — its signing secret is in the response exactly once, so persist it now.
|
|
130
|
+
// Omit `projectId` (here and on listEndpoints) for the workspace's default project.
|
|
61
131
|
const ep = await wh.createEndpoint("https://your-app.example/webhooks", {
|
|
62
132
|
subscriptions: [{ match_kind: "prefix", pattern: "order." }],
|
|
63
133
|
description: "orders service",
|
|
64
134
|
});
|
|
65
135
|
const { id: endpointId, secret: signingSecret } = ep;
|
|
66
136
|
|
|
67
|
-
await wh.listEndpoints(
|
|
137
|
+
await wh.listEndpoints(); // default project — { items, next_offset }
|
|
138
|
+
await wh.listEndpoints({ projectId: "prj_3f9a…" }); // a specific project, by id
|
|
68
139
|
await wh.getEndpoint(endpointId);
|
|
69
140
|
|
|
70
141
|
// PATCH — send only the keys you want to change (omitted = unchanged, null = cleared):
|