@nimbusnexus/webhooks-sdk 0.3.0 → 0.5.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/dist/index.d.ts CHANGED
@@ -105,8 +105,12 @@ interface OutboxRecord {
105
105
  id: string;
106
106
  eventType: string;
107
107
  payload: Record<string, unknown>;
108
- environment: string;
109
- application: string;
108
+ /**
109
+ * The id of the project the event is published into (`prj_…`), or `null` for the workspace's default
110
+ * project. `null` means the `project_id` field is OMITTED from the publish body — the id is opaque
111
+ * and per-workspace, so only the server can resolve the default.
112
+ */
113
+ projectId: string | null;
110
114
  source: string | null;
111
115
  /** Epoch ms the record was first enqueued; `listPending` orders by this, oldest first. */
112
116
  createdAt: number;
@@ -186,12 +190,12 @@ declare class SqliteStore implements Store {
186
190
  }
187
191
 
188
192
  /** The published event, as returned by `POST /v1/events` (webhookd's `EventOut`). */
189
- interface WebhookdEvent {
193
+ interface WebhooksEvent {
190
194
  id: string;
191
195
  eventUid: string;
192
196
  eventType: string;
193
- application: string;
194
- environment: string;
197
+ /** The id of the project the event was published into (always resolved server-side). */
198
+ projectId: string;
195
199
  deliveriesCreated: number;
196
200
  source: string | null;
197
201
  }
@@ -208,8 +212,8 @@ interface Subscription {
208
212
  interface Endpoint {
209
213
  id: string;
210
214
  url: string;
211
- environment: string;
212
- application: string;
215
+ /** The id of the project the endpoint belongs to. */
216
+ project_id: string;
213
217
  status: string;
214
218
  subscriptions: Subscription[];
215
219
  /** The signing secret — returned ONCE, on create + rotate-secret only. */
@@ -253,20 +257,20 @@ interface Page<T> {
253
257
  }
254
258
  interface ClientOptions {
255
259
  baseUrl: string;
256
- /** A per-tenant API key (`whsk_…`) or a service token. */
260
+ /** A per-workspace API key (`whsk_…`) or a service token. */
257
261
  apiKey: string;
258
262
  timeoutMs?: number;
259
263
  maxRetries?: number;
260
264
  /** Inject a `fetch` implementation (defaults to the global `fetch`); used for tests. */
261
265
  fetchImpl?: typeof fetch;
262
266
  /**
263
- * Durable outbox store. When set, {@link WebhookdClient.enqueue} / {@link WebhookdClient.drain}
264
- * (and the background drainer) become available. Omit to use only the live {@link WebhookdClient.publish}.
267
+ * Durable outbox store. When set, {@link WebhooksClient.enqueue} / {@link WebhooksClient.drain}
268
+ * (and the background drainer) become available. Omit to use only the live {@link WebhooksClient.publish}.
265
269
  */
266
270
  store?: Store;
267
271
  /** Max delivery attempts before a record is parked dead (default 10). */
268
272
  maxAttempts?: number;
269
- /** How many records a single {@link WebhookdClient.drain} pulls from the store (default 100). */
273
+ /** How many records a single {@link WebhooksClient.drain} pulls from the store (default 100). */
270
274
  drainBatchLimit?: number;
271
275
  /** Invoked once when `drain` parks a record dead (retry budget exhausted). */
272
276
  onDead?: (record: OutboxRecord) => void;
@@ -274,8 +278,11 @@ interface ClientOptions {
274
278
  onDrainError?: (error: unknown) => void;
275
279
  }
276
280
  interface EnqueueOptions {
277
- environment?: string;
278
- application?: string;
281
+ /**
282
+ * The id of the project to publish into (`prj_…`). Omit (or pass an empty string) to target the
283
+ * workspace's default project — the server resolves it, there is no client-side sentinel.
284
+ */
285
+ projectId?: string;
279
286
  source?: string;
280
287
  /** The Idempotency-Key / record id. Defaults to a generated UUID v4. Re-enqueuing the same key is a no-op overwrite. */
281
288
  idempotencyKey?: string;
@@ -295,16 +302,22 @@ interface DrainResult {
295
302
  remaining: number;
296
303
  }
297
304
  interface PublishOptions {
298
- environment?: string;
299
- application?: string;
305
+ /**
306
+ * The id of the project to publish into (`prj_…`). Omit (or pass an empty string) to target the
307
+ * workspace's default project — the server resolves it, there is no client-side sentinel.
308
+ */
309
+ projectId?: string;
300
310
  source?: string;
301
311
  /** Makes the publish safe to retry — a replay returns the original event without re-fanning-out. */
302
312
  idempotencyKey?: string;
303
313
  }
304
314
  interface CreateEndpointOptions {
305
- environment?: string;
315
+ /**
316
+ * The id of the project to create the endpoint in (`prj_…`). Omit (or pass an empty string) for
317
+ * the workspace's default project.
318
+ */
319
+ projectId?: string;
306
320
  subscriptions?: Subscription[];
307
- application?: string;
308
321
  secret?: string;
309
322
  maxAttempts?: number;
310
323
  retrySchedule?: number[];
@@ -313,7 +326,11 @@ interface CreateEndpointOptions {
313
326
  deliveryTimeoutMs?: number;
314
327
  }
315
328
  interface ListEndpointsOptions {
316
- environment?: string;
329
+ /**
330
+ * The id of the project to list endpoints for (`prj_…`). Omit (or pass an empty string) for the
331
+ * workspace's default project.
332
+ */
333
+ projectId?: string;
317
334
  offset?: number;
318
335
  limit?: number;
319
336
  }
@@ -345,7 +362,7 @@ interface ListDeliveriesOptions {
345
362
  offset?: number;
346
363
  limit?: number;
347
364
  }
348
- declare class WebhookdClient {
365
+ declare class WebhooksClient {
349
366
  private readonly baseUrl;
350
367
  private readonly apiKey;
351
368
  private readonly timeoutMs;
@@ -359,7 +376,7 @@ declare class WebhookdClient {
359
376
  private drainTimer?;
360
377
  private draining;
361
378
  constructor(opts: ClientOptions);
362
- publish(eventType: string, payload: Record<string, unknown>, opts?: PublishOptions): Promise<WebhookdEvent>;
379
+ publish(eventType: string, payload: Record<string, unknown>, opts?: PublishOptions): Promise<WebhooksEvent>;
363
380
  /**
364
381
  * Durably buffer an event and return IMMEDIATELY — NO network call. Builds an {@link OutboxRecord}
365
382
  * (id = `opts.idempotencyKey` or a fresh UUID v4), saves it to the configured store, and returns the
@@ -388,7 +405,7 @@ declare class WebhookdClient {
388
405
  private requireStore;
389
406
  /** Create an endpoint. The response includes the signing `secret` exactly once — persist it. */
390
407
  createEndpoint(url: string, opts?: CreateEndpointOptions): Promise<Endpoint>;
391
- /** List endpoints for an environment (defaults to `prod`). */
408
+ /** List endpoints for a project. Omit `projectId` for the workspace's default project. */
392
409
  listEndpoints(opts?: ListEndpointsOptions): Promise<Page<Endpoint>>;
393
410
  /** Fetch a single endpoint by id. */
394
411
  getEndpoint(id: string): Promise<Endpoint>;
@@ -416,13 +433,13 @@ declare class WebhookdClient {
416
433
  /**
417
434
  * The single request path used by every method: builds the URL (+ query), attaches auth, and runs
418
435
  * the shared retry loop (connection errors + 429 honoring Retry-After + 5xx, capped exponential
419
- * backoff), raising a typed {@link WebhookdApiError} on other 4xx/5xx. The caller reads the body.
436
+ * backoff), raising a typed {@link WebhooksApiError} on other 4xx/5xx. The caller reads the body.
420
437
  */
421
438
  private request;
422
439
  }
423
440
 
424
441
  /** Base class for all webhookd SDK errors (network failures, etc.). */
425
- declare class WebhookdError extends Error {
442
+ declare class WebhooksError extends Error {
426
443
  constructor(message: string);
427
444
  }
428
445
  /**
@@ -430,7 +447,7 @@ declare class WebhookdError extends Error {
430
447
  * `code` (e.g. `"rate_limited"`, `"not_found"`, `"validation_error"`) and a human `message`,
431
448
  * plus the HTTP `statusCode`.
432
449
  */
433
- declare class WebhookdApiError extends WebhookdError {
450
+ declare class WebhooksApiError extends WebhooksError {
434
451
  readonly statusCode: number;
435
452
  readonly code: string;
436
453
  constructor(statusCode: number, code: string, message: string);
@@ -440,9 +457,9 @@ declare class WebhookdApiError extends WebhookdError {
440
457
  * @nimbusnexus/webhooks-sdk — the official TypeScript client for NimbusNexus Webhooks.
441
458
  *
442
459
  * - `verify` — verify an incoming webhook's HMAC signature (for subscribers).
443
- * - `WebhookdClient` — publish events to webhookd (for producers).
460
+ * - `WebhooksClient` — publish events to webhookd (for producers).
444
461
  */
445
462
 
446
- declare const VERSION = "0.3.0";
463
+ declare const VERSION = "0.5.0";
447
464
 
448
- export { type ApiKey, type ClientOptions, type CreateApiKeyOptions, type CreateEndpointOptions, DEAD_NEXT_ATTEMPT_MS, DEFAULT_TOLERANCE_SECONDS, type Delivery, type DrainOptions, type DrainResult, type Endpoint, type EndpointPatch, type EnqueueOptions, FileStore, type ListDeliveriesOptions, type ListEndpointsOptions, MemoryStore, type OutboxRecord, type Page, PostgresStore, type PostgresStoreOptions, type PublishOptions, RedisStore, type RedisStoreOptions, SqliteStore, type Store, type Subscription, VERSION, type VerifyOptions, WebhookdApiError, WebhookdClient, WebhookdError, type WebhookdEvent, isDead, sign, verify };
465
+ export { type ApiKey, type ClientOptions, type CreateApiKeyOptions, type CreateEndpointOptions, DEAD_NEXT_ATTEMPT_MS, DEFAULT_TOLERANCE_SECONDS, type Delivery, type DrainOptions, type DrainResult, type Endpoint, type EndpointPatch, type EnqueueOptions, FileStore, type ListDeliveriesOptions, type ListEndpointsOptions, MemoryStore, type OutboxRecord, type Page, PostgresStore, type PostgresStoreOptions, type PublishOptions, RedisStore, type RedisStoreOptions, SqliteStore, type Store, type Subscription, VERSION, type VerifyOptions, WebhooksApiError, WebhooksClient, WebhooksError, type WebhooksEvent, isDead, sign, verify };
package/dist/index.js CHANGED
@@ -41,18 +41,18 @@ function verify(secret, rawBody, signature, opts = {}) {
41
41
  import { randomUUID } from "crypto";
42
42
 
43
43
  // src/errors.ts
44
- var WebhookdError = class extends Error {
44
+ var WebhooksError = class extends Error {
45
45
  constructor(message) {
46
46
  super(message);
47
- this.name = "WebhookdError";
47
+ this.name = "WebhooksError";
48
48
  }
49
49
  };
50
- var WebhookdApiError = class extends WebhookdError {
50
+ var WebhooksApiError = class extends WebhooksError {
51
51
  statusCode;
52
52
  code;
53
53
  constructor(statusCode, code, message) {
54
54
  super(`[${statusCode} ${code}] ${message}`);
55
- this.name = "WebhookdApiError";
55
+ this.name = "WebhooksApiError";
56
56
  this.statusCode = statusCode;
57
57
  this.code = code;
58
58
  }
@@ -159,8 +159,7 @@ function rowToRecord(row) {
159
159
  id: row.id,
160
160
  eventType: row.event_type,
161
161
  payload: row.payload,
162
- environment: row.environment,
163
- application: row.application,
162
+ projectId: row.project_id,
164
163
  source: row.source,
165
164
  createdAt: Number(row.created_at),
166
165
  attempts: row.attempts,
@@ -197,8 +196,7 @@ var PostgresStore = class {
197
196
  id TEXT PRIMARY KEY,
198
197
  event_type TEXT NOT NULL,
199
198
  payload JSONB NOT NULL,
200
- environment TEXT NOT NULL,
201
- application TEXT NOT NULL,
199
+ project_id TEXT,
202
200
  source TEXT,
203
201
  sent BOOLEAN NOT NULL DEFAULT FALSE,
204
202
  attempts INTEGER NOT NULL,
@@ -215,13 +213,12 @@ var PostgresStore = class {
215
213
  const pool = await this.ensure();
216
214
  await pool.query(
217
215
  `INSERT INTO ${this.table}
218
- (id, event_type, payload, environment, application, source, sent, attempts, last_error, next_attempt_at, created_at)
219
- VALUES ($1, $2, $3, $4, $5, $6, FALSE, $7, $8, $9, $10)
216
+ (id, event_type, payload, project_id, source, sent, attempts, last_error, next_attempt_at, created_at)
217
+ VALUES ($1, $2, $3, $4, $5, FALSE, $6, $7, $8, $9)
220
218
  ON CONFLICT (id) DO UPDATE SET
221
219
  event_type = EXCLUDED.event_type,
222
220
  payload = EXCLUDED.payload,
223
- environment = EXCLUDED.environment,
224
- application = EXCLUDED.application,
221
+ project_id = EXCLUDED.project_id,
225
222
  source = EXCLUDED.source,
226
223
  sent = EXCLUDED.sent,
227
224
  attempts = EXCLUDED.attempts,
@@ -232,8 +229,7 @@ var PostgresStore = class {
232
229
  record.id,
233
230
  record.eventType,
234
231
  JSON.stringify(record.payload),
235
- record.environment,
236
- record.application,
232
+ record.projectId,
237
233
  record.source,
238
234
  record.attempts,
239
235
  record.lastError,
@@ -389,8 +385,7 @@ function rowToRecord2(row) {
389
385
  id: row.id,
390
386
  eventType: row.event_type,
391
387
  payload: JSON.parse(row.payload),
392
- environment: row.environment,
393
- application: row.application,
388
+ projectId: row.project_id,
394
389
  source: row.source,
395
390
  createdAt: row.created_at,
396
391
  attempts: row.attempts,
@@ -417,12 +412,14 @@ var SqliteStore = class {
417
412
  }
418
413
  this.db = new DatabaseSync(path);
419
414
  this.db.exec(
415
+ // `project_id` is NULLABLE: NULL = the workspace's default project (the publish body simply
416
+ // omits the field). Renamed from the legacy `project` slug column — this store has no
417
+ // schema-versioning mechanism, so a table created by an older SDK build is not upgraded.
420
418
  `CREATE TABLE IF NOT EXISTS webhookd_outbox (
421
419
  id TEXT PRIMARY KEY,
422
420
  event_type TEXT NOT NULL,
423
421
  payload TEXT NOT NULL,
424
- environment TEXT NOT NULL,
425
- application TEXT NOT NULL,
422
+ project_id TEXT,
426
423
  source TEXT,
427
424
  created_at INTEGER NOT NULL,
428
425
  attempts INTEGER NOT NULL,
@@ -434,13 +431,12 @@ var SqliteStore = class {
434
431
  save(record) {
435
432
  this.db.prepare(
436
433
  `INSERT INTO webhookd_outbox
437
- (id, event_type, payload, environment, application, source, created_at, attempts, last_error, next_attempt_at)
438
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
434
+ (id, event_type, payload, project_id, source, created_at, attempts, last_error, next_attempt_at)
435
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
439
436
  ON CONFLICT(id) DO UPDATE SET
440
437
  event_type = excluded.event_type,
441
438
  payload = excluded.payload,
442
- environment = excluded.environment,
443
- application = excluded.application,
439
+ project_id = excluded.project_id,
444
440
  source = excluded.source,
445
441
  created_at = excluded.created_at,
446
442
  attempts = excluded.attempts,
@@ -450,8 +446,7 @@ var SqliteStore = class {
450
446
  record.id,
451
447
  record.eventType,
452
448
  JSON.stringify(record.payload),
453
- record.environment,
454
- record.application,
449
+ record.projectId,
455
450
  record.source,
456
451
  record.createdAt,
457
452
  record.attempts,
@@ -496,7 +491,7 @@ var SqliteStore = class {
496
491
 
497
492
  // src/client.ts
498
493
  var RETRY_STATUSES = /* @__PURE__ */ new Set([429, 500, 502, 503, 504]);
499
- var WebhookdClient = class {
494
+ var WebhooksClient = class {
500
495
  baseUrl;
501
496
  apiKey;
502
497
  timeoutMs;
@@ -524,10 +519,10 @@ var WebhookdClient = class {
524
519
  async publish(eventType, payload, opts = {}) {
525
520
  const body = {
526
521
  event_type: eventType,
527
- payload,
528
- environment: opts.environment ?? "prod",
529
- application: opts.application ?? "default"
522
+ payload
530
523
  };
524
+ const projectId = normalizeProjectId(opts.projectId);
525
+ if (projectId !== null) body.project_id = projectId;
531
526
  if (opts.source !== void 0) body.source = opts.source;
532
527
  const headers = {};
533
528
  if (opts.idempotencyKey !== void 0) headers["Idempotency-Key"] = opts.idempotencyKey;
@@ -537,8 +532,7 @@ var WebhookdClient = class {
537
532
  id: String(data.id),
538
533
  eventUid: String(data.event_uid),
539
534
  eventType: String(data.event_type),
540
- application: String(data.application ?? "default"),
541
- environment: String(data.environment ?? "prod"),
535
+ projectId: String(data.project_id),
542
536
  deliveriesCreated: Number(data.deliveries_created ?? 0),
543
537
  source: data.source ?? null
544
538
  };
@@ -556,8 +550,8 @@ var WebhookdClient = class {
556
550
  id: opts.idempotencyKey ?? randomUUID(),
557
551
  eventType,
558
552
  payload,
559
- environment: opts.environment ?? "prod",
560
- application: opts.application ?? "default",
553
+ // null = the workspace's default project (the field is omitted from the publish body on drain).
554
+ projectId: normalizeProjectId(opts.projectId),
561
555
  source: opts.source ?? null,
562
556
  createdAt: now,
563
557
  attempts: 0,
@@ -584,10 +578,9 @@ var WebhookdClient = class {
584
578
  for (const record of rows) {
585
579
  const body = {
586
580
  event_type: record.eventType,
587
- payload: record.payload,
588
- environment: record.environment,
589
- application: record.application
581
+ payload: record.payload
590
582
  };
583
+ if (record.projectId !== null) body.project_id = record.projectId;
591
584
  if (record.source !== null) body.source = record.source;
592
585
  try {
593
586
  await this.request("POST", "/v1/events", {
@@ -647,18 +640,16 @@ var WebhookdClient = class {
647
640
  }
648
641
  requireStore() {
649
642
  if (!this.store) {
650
- throw new WebhookdError("no outbox store configured \u2014 pass `store` in ClientOptions");
643
+ throw new WebhooksError("no outbox store configured \u2014 pass `store` in ClientOptions");
651
644
  }
652
645
  return this.store;
653
646
  }
654
647
  // ── Endpoints ──────────────────────────────────────────────────────────────
655
648
  /** Create an endpoint. The response includes the signing `secret` exactly once — persist it. */
656
649
  async createEndpoint(url, opts = {}) {
657
- const body = {
658
- url,
659
- environment: opts.environment ?? "prod",
660
- application: opts.application ?? "default"
661
- };
650
+ const body = { url };
651
+ const projectId = normalizeProjectId(opts.projectId);
652
+ if (projectId !== null) body.project_id = projectId;
662
653
  if (opts.subscriptions !== void 0) body.subscriptions = opts.subscriptions;
663
654
  if (opts.secret !== void 0) body.secret = opts.secret;
664
655
  if (opts.maxAttempts !== void 0) body.max_attempts = opts.maxAttempts;
@@ -668,9 +659,11 @@ var WebhookdClient = class {
668
659
  if (opts.deliveryTimeoutMs !== void 0) body.delivery_timeout_ms = opts.deliveryTimeoutMs;
669
660
  return this.requestJson("POST", "/v1/endpoints", { body });
670
661
  }
671
- /** List endpoints for an environment (defaults to `prod`). */
662
+ /** List endpoints for a project. Omit `projectId` for the workspace's default project. */
672
663
  async listEndpoints(opts = {}) {
673
- const query = { environment: opts.environment ?? "prod" };
664
+ const query = {};
665
+ const projectId = normalizeProjectId(opts.projectId);
666
+ if (projectId !== null) query.project_id = projectId;
674
667
  if (opts.offset !== void 0) query.offset = opts.offset;
675
668
  if (opts.limit !== void 0) query.limit = opts.limit;
676
669
  return this.requestJson("GET", "/v1/endpoints", { query });
@@ -744,7 +737,7 @@ var WebhookdClient = class {
744
737
  /**
745
738
  * The single request path used by every method: builds the URL (+ query), attaches auth, and runs
746
739
  * the shared retry loop (connection errors + 429 honoring Retry-After + 5xx, capped exponential
747
- * backoff), raising a typed {@link WebhookdApiError} on other 4xx/5xx. The caller reads the body.
740
+ * backoff), raising a typed {@link WebhooksApiError} on other 4xx/5xx. The caller reads the body.
748
741
  */
749
742
  async request(method, path, opts = {}) {
750
743
  let url = `${this.baseUrl}${path}`;
@@ -781,7 +774,7 @@ var WebhookdClient = class {
781
774
  await sleep(backoffMs(attempt));
782
775
  continue;
783
776
  }
784
- throw new WebhookdError(`request failed: ${String(err)}`);
777
+ throw new WebhooksError(`request failed: ${String(err)}`);
785
778
  }
786
779
  if (RETRY_STATUSES.has(resp.status) && attempt < this.maxRetries) {
787
780
  await sleep(retryAfterMs(resp) ?? backoffMs(attempt));
@@ -790,9 +783,12 @@ var WebhookdClient = class {
790
783
  if (resp.status >= 400) throw await apiError(resp);
791
784
  return resp;
792
785
  }
793
- throw new WebhookdError(`request failed after retries: ${String(lastErr)}`);
786
+ throw new WebhooksError(`request failed after retries: ${String(lastErr)}`);
794
787
  }
795
788
  };
789
+ function normalizeProjectId(projectId) {
790
+ return projectId === void 0 || projectId === null || projectId === "" ? null : projectId;
791
+ }
796
792
  function backoffMs(attempt) {
797
793
  return Math.min(2e3, 200 * 2 ** attempt);
798
794
  }
@@ -811,14 +807,14 @@ async function apiError(resp) {
811
807
  message = data.error?.message ?? message;
812
808
  } catch {
813
809
  }
814
- return new WebhookdApiError(resp.status, code, message);
810
+ return new WebhooksApiError(resp.status, code, message);
815
811
  }
816
812
  function sleep(ms) {
817
813
  return new Promise((resolve) => setTimeout(resolve, ms));
818
814
  }
819
815
 
820
816
  // src/index.ts
821
- var VERSION = "0.3.0";
817
+ var VERSION = "0.5.0";
822
818
  export {
823
819
  DEAD_NEXT_ATTEMPT_MS,
824
820
  DEFAULT_TOLERANCE_SECONDS,
@@ -828,9 +824,9 @@ export {
828
824
  RedisStore,
829
825
  SqliteStore,
830
826
  VERSION,
831
- WebhookdApiError,
832
- WebhookdClient,
833
- WebhookdError,
827
+ WebhooksApiError,
828
+ WebhooksClient,
829
+ WebhooksError,
834
830
  isDead,
835
831
  sign,
836
832
  verify