@xtandard/webhooks 0.1.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/LICENSE +21 -0
- package/README.md +315 -0
- package/bin/xtandard-webhooks.mjs +3 -0
- package/dist/basic-BIW3Rvuz.cjs +199 -0
- package/dist/basic-BIW3Rvuz.cjs.map +1 -0
- package/dist/basic-DKk0Xfuu.mjs +176 -0
- package/dist/basic-DKk0Xfuu.mjs.map +1 -0
- package/dist/chunk-D7D4PA-g.mjs +13 -0
- package/dist/cli.cjs +655 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/cli.d.cts +42 -0
- package/dist/cli.d.mts +42 -0
- package/dist/cli.mjs +653 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/contract-8h-Azxa5.d.cts +71 -0
- package/dist/contract-9XpcwcCn.mjs +22 -0
- package/dist/contract-9XpcwcCn.mjs.map +1 -0
- package/dist/contract-B2d5dNU3.cjs +33 -0
- package/dist/contract-B2d5dNU3.cjs.map +1 -0
- package/dist/contract-BEhDcd_5.mjs +28 -0
- package/dist/contract-BEhDcd_5.mjs.map +1 -0
- package/dist/contract-Bf1qguwt.cjs +57 -0
- package/dist/contract-Bf1qguwt.cjs.map +1 -0
- package/dist/contract-Bnb3fgRJ.d.cts +177 -0
- package/dist/contract-C2r2Xzwp.d.mts +46 -0
- package/dist/contract-CiPskNvS.d.cts +46 -0
- package/dist/contract-DhQ4JjGG.d.mts +71 -0
- package/dist/contract-T1kcZNdG.d.mts +177 -0
- package/dist/contract-lETlIuXo.d.cts +30 -0
- package/dist/contract-lETlIuXo.d.mts +30 -0
- package/dist/core-CMpnmI5Q.mjs +1605 -0
- package/dist/core-CMpnmI5Q.mjs.map +1 -0
- package/dist/core-DT4ppWh8.d.mts +502 -0
- package/dist/core-KJawHjFF.d.cts +502 -0
- package/dist/core-ZGhH6Vs2.cjs +1790 -0
- package/dist/core-ZGhH6Vs2.cjs.map +1 -0
- package/dist/core.cjs +8 -0
- package/dist/core.d.cts +2 -0
- package/dist/core.d.mts +2 -0
- package/dist/core.mjs +2 -0
- package/dist/create-fetch-handler-BIdk9P30.mjs +1724 -0
- package/dist/create-fetch-handler-BIdk9P30.mjs.map +1 -0
- package/dist/create-fetch-handler-CmooujQo.cjs +1771 -0
- package/dist/create-fetch-handler-CmooujQo.cjs.map +1 -0
- package/dist/create-fetch-handler-Dlkhustu.d.cts +162 -0
- package/dist/create-fetch-handler-jy3hy5nZ.d.mts +162 -0
- package/dist/dispatcher-B0xTEHt1.cjs +212 -0
- package/dist/dispatcher-B0xTEHt1.cjs.map +1 -0
- package/dist/dispatcher-Coubwrka.mjs +196 -0
- package/dist/dispatcher-Coubwrka.mjs.map +1 -0
- package/dist/entry-auth-basic.cjs +5 -0
- package/dist/entry-auth-basic.d.cts +83 -0
- package/dist/entry-auth-basic.d.mts +83 -0
- package/dist/entry-auth-basic.mjs +2 -0
- package/dist/entry-auth-delegated.cjs +28 -0
- package/dist/entry-auth-delegated.cjs.map +1 -0
- package/dist/entry-auth-delegated.d.cts +36 -0
- package/dist/entry-auth-delegated.d.mts +36 -0
- package/dist/entry-auth-delegated.mjs +27 -0
- package/dist/entry-auth-delegated.mjs.map +1 -0
- package/dist/entry-auth-none.cjs +4 -0
- package/dist/entry-auth-none.d.cts +25 -0
- package/dist/entry-auth-none.d.mts +25 -0
- package/dist/entry-auth-none.mjs +2 -0
- package/dist/entry-authorization-delegated.cjs +27 -0
- package/dist/entry-authorization-delegated.cjs.map +1 -0
- package/dist/entry-authorization-delegated.d.cts +31 -0
- package/dist/entry-authorization-delegated.d.mts +31 -0
- package/dist/entry-authorization-delegated.mjs +26 -0
- package/dist/entry-authorization-delegated.mjs.map +1 -0
- package/dist/entry-authorization-none.cjs +3 -0
- package/dist/entry-authorization-none.d.cts +18 -0
- package/dist/entry-authorization-none.d.mts +18 -0
- package/dist/entry-authorization-none.mjs +2 -0
- package/dist/entry-authorization-roles.cjs +6 -0
- package/dist/entry-authorization-roles.d.cts +65 -0
- package/dist/entry-authorization-roles.d.mts +65 -0
- package/dist/entry-authorization-roles.mjs +2 -0
- package/dist/entry-bun.cjs +24 -0
- package/dist/entry-bun.cjs.map +1 -0
- package/dist/entry-bun.d.cts +8 -0
- package/dist/entry-bun.d.mts +8 -0
- package/dist/entry-bun.mjs +23 -0
- package/dist/entry-bun.mjs.map +1 -0
- package/dist/entry-drizzle-mysql.cjs +20 -0
- package/dist/entry-drizzle-mysql.cjs.map +1 -0
- package/dist/entry-drizzle-mysql.d.cts +27 -0
- package/dist/entry-drizzle-mysql.d.mts +27 -0
- package/dist/entry-drizzle-mysql.mjs +19 -0
- package/dist/entry-drizzle-mysql.mjs.map +1 -0
- package/dist/entry-drizzle-pg.cjs +21 -0
- package/dist/entry-drizzle-pg.cjs.map +1 -0
- package/dist/entry-drizzle-pg.d.cts +26 -0
- package/dist/entry-drizzle-pg.d.mts +26 -0
- package/dist/entry-drizzle-pg.mjs +20 -0
- package/dist/entry-drizzle-pg.mjs.map +1 -0
- package/dist/entry-drizzle-sqlite.cjs +21 -0
- package/dist/entry-drizzle-sqlite.cjs.map +1 -0
- package/dist/entry-drizzle-sqlite.d.cts +23 -0
- package/dist/entry-drizzle-sqlite.d.mts +23 -0
- package/dist/entry-drizzle-sqlite.mjs +20 -0
- package/dist/entry-drizzle-sqlite.mjs.map +1 -0
- package/dist/entry-elysia.cjs +125 -0
- package/dist/entry-elysia.cjs.map +1 -0
- package/dist/entry-elysia.d.cts +1017 -0
- package/dist/entry-elysia.d.mts +1017 -0
- package/dist/entry-elysia.mjs +123 -0
- package/dist/entry-elysia.mjs.map +1 -0
- package/dist/entry-express.cjs +57 -0
- package/dist/entry-express.cjs.map +1 -0
- package/dist/entry-express.d.cts +15 -0
- package/dist/entry-express.d.mts +15 -0
- package/dist/entry-express.mjs +56 -0
- package/dist/entry-express.mjs.map +1 -0
- package/dist/entry-hono.cjs +35 -0
- package/dist/entry-hono.cjs.map +1 -0
- package/dist/entry-hono.d.cts +16 -0
- package/dist/entry-hono.d.mts +16 -0
- package/dist/entry-hono.mjs +34 -0
- package/dist/entry-hono.mjs.map +1 -0
- package/dist/entry-hooks-log.cjs +22 -0
- package/dist/entry-hooks-log.cjs.map +1 -0
- package/dist/entry-hooks-log.d.cts +23 -0
- package/dist/entry-hooks-log.d.mts +23 -0
- package/dist/entry-hooks-log.mjs +21 -0
- package/dist/entry-hooks-log.mjs.map +1 -0
- package/dist/entry-storage-cloudflare-kv.cjs +47 -0
- package/dist/entry-storage-cloudflare-kv.cjs.map +1 -0
- package/dist/entry-storage-cloudflare-kv.d.cts +42 -0
- package/dist/entry-storage-cloudflare-kv.d.mts +42 -0
- package/dist/entry-storage-cloudflare-kv.mjs +46 -0
- package/dist/entry-storage-cloudflare-kv.mjs.map +1 -0
- package/dist/entry-storage-drizzle.cjs +78 -0
- package/dist/entry-storage-drizzle.cjs.map +1 -0
- package/dist/entry-storage-drizzle.d.cts +30 -0
- package/dist/entry-storage-drizzle.d.mts +30 -0
- package/dist/entry-storage-drizzle.mjs +77 -0
- package/dist/entry-storage-drizzle.mjs.map +1 -0
- package/dist/entry-storage-file.cjs +4 -0
- package/dist/entry-storage-file.d.cts +30 -0
- package/dist/entry-storage-file.d.mts +30 -0
- package/dist/entry-storage-file.mjs +2 -0
- package/dist/entry-storage-libsql.cjs +3 -0
- package/dist/entry-storage-libsql.d.cts +48 -0
- package/dist/entry-storage-libsql.d.mts +48 -0
- package/dist/entry-storage-libsql.mjs +2 -0
- package/dist/entry-storage-memory.cjs +3 -0
- package/dist/entry-storage-memory.d.cts +2 -0
- package/dist/entry-storage-memory.d.mts +2 -0
- package/dist/entry-storage-memory.mjs +2 -0
- package/dist/entry-storage-mongodb.cjs +3 -0
- package/dist/entry-storage-mongodb.d.cts +55 -0
- package/dist/entry-storage-mongodb.d.mts +55 -0
- package/dist/entry-storage-mongodb.mjs +2 -0
- package/dist/entry-storage-postgres.cjs +3 -0
- package/dist/entry-storage-postgres.d.cts +62 -0
- package/dist/entry-storage-postgres.d.mts +62 -0
- package/dist/entry-storage-postgres.mjs +2 -0
- package/dist/entry-storage-redis.cjs +4 -0
- package/dist/entry-storage-redis.d.cts +77 -0
- package/dist/entry-storage-redis.d.mts +77 -0
- package/dist/entry-storage-redis.mjs +2 -0
- package/dist/entry-storage-sqlite.cjs +3 -0
- package/dist/entry-storage-sqlite.d.cts +36 -0
- package/dist/entry-storage-sqlite.d.mts +36 -0
- package/dist/entry-storage-sqlite.mjs +2 -0
- package/dist/entry-storage-unstorage.cjs +42 -0
- package/dist/entry-storage-unstorage.cjs.map +1 -0
- package/dist/entry-storage-unstorage.d.cts +29 -0
- package/dist/entry-storage-unstorage.d.mts +29 -0
- package/dist/entry-storage-unstorage.mjs +41 -0
- package/dist/entry-storage-unstorage.mjs.map +1 -0
- package/dist/file-COBYZA4Q.cjs +148 -0
- package/dist/file-COBYZA4Q.cjs.map +1 -0
- package/dist/file-fi02eFHk.mjs +131 -0
- package/dist/file-fi02eFHk.mjs.map +1 -0
- package/dist/index.cjs +123 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +368 -0
- package/dist/index.d.mts +366 -0
- package/dist/index.mjs +61 -0
- package/dist/index.mjs.map +1 -0
- package/dist/keys-Byyj4quQ.mjs +111 -0
- package/dist/keys-Byyj4quQ.mjs.map +1 -0
- package/dist/keys-FiKpaVHX.cjs +302 -0
- package/dist/keys-FiKpaVHX.cjs.map +1 -0
- package/dist/libsql-bpVi0bXN.mjs +113 -0
- package/dist/libsql-bpVi0bXN.mjs.map +1 -0
- package/dist/libsql-pPJEo1e4.cjs +124 -0
- package/dist/libsql-pPJEo1e4.cjs.map +1 -0
- package/dist/memory-8Ef-PL5a.cjs +137 -0
- package/dist/memory-8Ef-PL5a.cjs.map +1 -0
- package/dist/memory-BMsSSwqn.mjs +127 -0
- package/dist/memory-BMsSSwqn.mjs.map +1 -0
- package/dist/memory-FnMJWCmB.d.cts +28 -0
- package/dist/memory-qIvANEs_.d.mts +28 -0
- package/dist/mongodb-Cy8yo0uk.cjs +108 -0
- package/dist/mongodb-Cy8yo0uk.cjs.map +1 -0
- package/dist/mongodb-Ddaq9mml.mjs +97 -0
- package/dist/mongodb-Ddaq9mml.mjs.map +1 -0
- package/dist/none-BnZtaGNJ.mjs +23 -0
- package/dist/none-BnZtaGNJ.mjs.map +1 -0
- package/dist/none-CAsxCOWN.cjs +49 -0
- package/dist/none-CAsxCOWN.cjs.map +1 -0
- package/dist/none-CZVrfnmF.cjs +33 -0
- package/dist/none-CZVrfnmF.cjs.map +1 -0
- package/dist/none-GhVIoh_s.mjs +33 -0
- package/dist/none-GhVIoh_s.mjs.map +1 -0
- package/dist/postgres-C8WbchFa.cjs +134 -0
- package/dist/postgres-C8WbchFa.cjs.map +1 -0
- package/dist/postgres-c3pAhmhr.mjs +123 -0
- package/dist/postgres-c3pAhmhr.mjs.map +1 -0
- package/dist/react.css +1 -0
- package/dist/react.js +31465 -0
- package/dist/receiver.cjs +43 -0
- package/dist/receiver.cjs.map +1 -0
- package/dist/receiver.d.cts +36 -0
- package/dist/receiver.d.mts +36 -0
- package/dist/receiver.mjs +40 -0
- package/dist/receiver.mjs.map +1 -0
- package/dist/redis-CFJkuSgB.cjs +270 -0
- package/dist/redis-CFJkuSgB.cjs.map +1 -0
- package/dist/redis-CvLi0KF7.mjs +254 -0
- package/dist/redis-CvLi0KF7.mjs.map +1 -0
- package/dist/roles-D0G9XqBq.cjs +128 -0
- package/dist/roles-D0G9XqBq.cjs.map +1 -0
- package/dist/roles-vp361lTk.mjs +99 -0
- package/dist/roles-vp361lTk.mjs.map +1 -0
- package/dist/schema-mo__wv4P.d.cts +233 -0
- package/dist/schema-mo__wv4P.d.mts +233 -0
- package/dist/schema.cjs +13 -0
- package/dist/schema.cjs.map +1 -0
- package/dist/schema.d.cts +2 -0
- package/dist/schema.d.mts +2 -0
- package/dist/schema.mjs +11 -0
- package/dist/schema.mjs.map +1 -0
- package/dist/signing.cjs +162 -0
- package/dist/signing.cjs.map +1 -0
- package/dist/signing.d.cts +73 -0
- package/dist/signing.d.mts +73 -0
- package/dist/signing.mjs +156 -0
- package/dist/signing.mjs.map +1 -0
- package/dist/sqlite-Cmqnrjes.mjs +67 -0
- package/dist/sqlite-Cmqnrjes.mjs.map +1 -0
- package/dist/sqlite-Dcufk0x3.cjs +78 -0
- package/dist/sqlite-Dcufk0x3.cjs.map +1 -0
- package/dist/table-Ce3Tzwqs.d.cts +11 -0
- package/dist/table-Ce3Tzwqs.d.mts +11 -0
- package/dist/testing.cjs +134 -0
- package/dist/testing.cjs.map +1 -0
- package/dist/testing.d.cts +80 -0
- package/dist/testing.d.mts +80 -0
- package/dist/testing.mjs +131 -0
- package/dist/testing.mjs.map +1 -0
- package/dist/types-react/react.d.ts +98 -0
- package/dist/types-react/schema.d.ts +229 -0
- package/dist/types-react/ui/App.d.ts +22 -0
- package/dist/types-react/ui/api.d.ts +97 -0
- package/dist/types-react/ui/components/JsonCodeEditor.d.ts +12 -0
- package/dist/types-react/ui/components/ThemeToggle.d.ts +2 -0
- package/dist/types-react/ui/components/Toast.d.ts +16 -0
- package/dist/types-react/ui/components/primitives.d.ts +50 -0
- package/dist/types-react/ui/components/ui-bits.d.ts +22 -0
- package/dist/types-react/ui/components/webhook-bits.d.ts +51 -0
- package/dist/types-react/ui/lib/format.d.ts +39 -0
- package/dist/types-react/ui/lib/nav-guard.d.ts +20 -0
- package/dist/types-react/ui/lib/utils.d.ts +3 -0
- package/dist/types-react/ui/theme.d.ts +12 -0
- package/dist/types-react/ui/types.d.ts +80 -0
- package/dist/types-react/ui/views/AuditView.d.ts +6 -0
- package/dist/types-react/ui/views/DeliveriesView.d.ts +12 -0
- package/dist/types-react/ui/views/EndpointsView.d.ts +11 -0
- package/dist/types-react/ui/views/EventTypesView.d.ts +11 -0
- package/dist/types-react/ui/views/MessagesView.d.ts +10 -0
- package/dist/types-react/ui/views/OverviewView.d.ts +12 -0
- package/dist/ui/assets/index-B0eoQX2U.css +1 -0
- package/dist/ui/assets/index-S5t_CLOe.js +209 -0
- package/dist/ui/index.html +14 -0
- package/package.json +487 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
import { _ as WebhookEnvelope, a as AuditEntry, c as DeliveryStatus, d as EndpointSecret, f as EventType, g as WebhookDuration, h as SCHEMA_VERSION, i as AuditAction, l as Endpoint, m as Message, n as Application, o as Delivery, p as JsonValue, r as AttemptTrigger, s as DeliveryAttempt, t as Actor, u as EndpointDisabledReason, v as isTerminalDeliveryStatus } from "./schema-mo__wv4P.mjs";
|
|
2
|
+
import { A as BuildSignedRequestInput, C as createDispatcher, D as emitDelivery, E as DeliveryListener, F as attemptDelivery, I as buildSignedRequest, M as DEFAULT_RESPONSE_BODY_LIMIT, N as SignedRequest, O as AttemptDeliveryInput, P as activeSecrets, S as DispatcherOptions, T as DeliveryEvent, _ as WebhooksCore, a as ListDeliveriesOptions, b as DEFAULT_RETRY_SCHEDULE, c as PayloadTooLargeError, d as ReadonlyError, f as RecordAttemptInput, g as SendExampleResult, h as RetentionRule, i as IdempotencyConflictError, j as DEFAULT_ATTEMPT_TIMEOUT_MS, k as AttemptOutcome, l as PublishInput, m as RetentionOptions, n as ClaimInput, o as ListMessagesOptions, p as RecoverResult, r as ConflictError, s as NotFoundError, t as ActorOptions, u as PublishResult, v as WebhooksCoreOptions, w as DeliveryErrorReporter, x as Dispatcher, y as createWebhooksCore } from "./core-DT4ppWh8.mjs";
|
|
3
|
+
import { a as WatchableWebhooksStorage, c as isCompareAndSwap, d as requirePeer, i as TransactionalWebhooksStorage, l as isTransactional, n as DeliveryQueueStorage, o as WebhooksStorage, r as StorageChangeEvent, s as hasDeliveryQueue, t as CompareAndSwapWebhooksStorage, u as isWatchable } from "./contract-DhQ4JjGG.mjs";
|
|
4
|
+
import { a as HookDeniedError, c as WebhooksHooksInput, d as runAfter, f as runBefore, i as BeforeEventType, l as defaultHookErrorReporter, n as AfterEventType, o as HookErrorReporter, r as BeforeEvent, s as WebhooksHooks, t as AfterEvent, u as normalizeHooks } from "./contract-T1kcZNdG.mjs";
|
|
5
|
+
import { n as Principal, t as AuthProvider } from "./contract-lETlIuXo.mjs";
|
|
6
|
+
import { a as WebhooksResource, i as WebhooksAction, n as AuthorizeInput, o as isMutatingAction, r as MUTATING_ACTIONS, t as AuthorizationProvider } from "./contract-C2r2Xzwp.mjs";
|
|
7
|
+
import { a as WebhooksPortalOptions, i as DEFAULT_PORTAL_ACTIONS, n as WebhooksPanelOptions, o as WebhooksCorsOptions, r as createFetchHandler, t as CreateFetchHandlerResult } from "./create-fetch-handler-jy3hy5nZ.mjs";
|
|
8
|
+
import { SECRET_PREFIX, VerifyInput, WebhookVerificationError, generateSecret, sign, signatureHeader, verify } from "./signing.mjs";
|
|
9
|
+
|
|
10
|
+
//#region src/version.d.ts
|
|
11
|
+
/**
|
|
12
|
+
* Package version, used for the default dispatcher `user-agent`. Bumped by the
|
|
13
|
+
* release flow alongside `package.json`.
|
|
14
|
+
*
|
|
15
|
+
* @module
|
|
16
|
+
*/
|
|
17
|
+
/** The published package version. */
|
|
18
|
+
declare const VERSION = "0.1.0";
|
|
19
|
+
declare namespace keys_d_exports {
|
|
20
|
+
export { DueEntry, RESERVED_APPLICATION_KEYS, ROOT, applicationMetaKey, applicationPrefix, applicationsKey, attemptKey, attemptsPrefix, auditLogKey, byEndpointKey, byEndpointPrefix, byMessageKey, byMessagePrefix, deliveriesPrefix, deliveryKey, dueKey, duePrefix, endpointKey, endpointsKey, eventTypeKey, eventTypesKey, globalAuditLogKey, idempotencyKey, lastSegment, messageKey, messagesPrefix, parseDueKey };
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Storage key layout for `@xtandard/webhooks`.
|
|
24
|
+
*
|
|
25
|
+
* Keys are namespaced by application so a single storage backend can host the
|
|
26
|
+
* whole control plane. Pure string helpers — no dependencies.
|
|
27
|
+
*
|
|
28
|
+
* ```txt
|
|
29
|
+
* whk/applications -> string[] index
|
|
30
|
+
* whk/{app}/metadata -> Application
|
|
31
|
+
* whk/event-types -> string[] index (global)
|
|
32
|
+
* whk/event-types/{name} -> EventType
|
|
33
|
+
* whk/{app}/endpoints -> string[] index
|
|
34
|
+
* whk/{app}/endpoints/{id} -> Endpoint
|
|
35
|
+
* whk/{app}/messages/{id} -> Message
|
|
36
|
+
* whk/{app}/idempotency/{key} -> message id
|
|
37
|
+
* whk/{app}/deliveries/{id} -> Delivery
|
|
38
|
+
* whk/{app}/attempts/{deliveryId}/{n} -> DeliveryAttempt (n zero-padded)
|
|
39
|
+
* whk/{app}/due/{dueAtMillis}~{deliveryId} -> DueEntry (13-digit zero-padded millis)
|
|
40
|
+
* whk/{app}/by-message/{messageId}/{deliveryId} -> 1 (reverse index)
|
|
41
|
+
* whk/{app}/by-endpoint/{endpointId}/{deliveryId} -> 1 (reverse index)
|
|
42
|
+
* whk/{app}/audit-log -> AuditEntry[] (single ordered array)
|
|
43
|
+
* ```
|
|
44
|
+
*
|
|
45
|
+
* The due index is the dispatcher's generic work queue: 13-digit zero-padded
|
|
46
|
+
* milliseconds make lexicographic key order chronological, so a sorted
|
|
47
|
+
* `getKeys(duePrefix(app))` scan yields deliveries in due order.
|
|
48
|
+
*
|
|
49
|
+
* @module
|
|
50
|
+
*/
|
|
51
|
+
/** Root namespace segment for all keys. */
|
|
52
|
+
declare const ROOT = "whk";
|
|
53
|
+
/**
|
|
54
|
+
* Application keys that would collide with global index keys under the shared
|
|
55
|
+
* root. Rejected by validation.
|
|
56
|
+
*/
|
|
57
|
+
declare const RESERVED_APPLICATION_KEYS: readonly ["applications", "event-types", "audit-log"];
|
|
58
|
+
/** Global audit log (event-type actions, which are not application-scoped). */
|
|
59
|
+
declare const globalAuditLogKey: () => string;
|
|
60
|
+
/** Index key listing all known application keys. */
|
|
61
|
+
declare const applicationsKey: () => string;
|
|
62
|
+
/** Metadata for a single application. */
|
|
63
|
+
declare const applicationMetaKey: (applicationKey: string) => string;
|
|
64
|
+
/** Index key listing all event type names (global catalog). */
|
|
65
|
+
declare const eventTypesKey: () => string;
|
|
66
|
+
/** A single event type record. */
|
|
67
|
+
declare const eventTypeKey: (name: string) => string;
|
|
68
|
+
/** Index key listing an application's endpoint ids. */
|
|
69
|
+
declare const endpointsKey: (applicationKey: string) => string;
|
|
70
|
+
/** A single endpoint record. */
|
|
71
|
+
declare const endpointKey: (applicationKey: string, endpointId: string) => string;
|
|
72
|
+
/** Prefix under which an application's messages live. */
|
|
73
|
+
declare const messagesPrefix: (applicationKey: string) => string;
|
|
74
|
+
/** A single message record. */
|
|
75
|
+
declare const messageKey: (applicationKey: string, messageId: string) => string;
|
|
76
|
+
/** Maps a caller-supplied idempotency key to the message id it produced. */
|
|
77
|
+
declare const idempotencyKey: (applicationKey: string, key: string) => string;
|
|
78
|
+
/** Prefix under which an application's deliveries live. */
|
|
79
|
+
declare const deliveriesPrefix: (applicationKey: string) => string;
|
|
80
|
+
/** A single delivery record. */
|
|
81
|
+
declare const deliveryKey: (applicationKey: string, deliveryId: string) => string;
|
|
82
|
+
/** Prefix under which one delivery's attempts live. */
|
|
83
|
+
declare const attemptsPrefix: (applicationKey: string, deliveryId: string) => string;
|
|
84
|
+
/**
|
|
85
|
+
* A single attempt record. The attempt number is zero-padded to four digits so
|
|
86
|
+
* lexicographic key order equals attempt order.
|
|
87
|
+
*/
|
|
88
|
+
declare const attemptKey: (applicationKey: string, deliveryId: string, attemptNumber: number) => string;
|
|
89
|
+
/** Prefix of an application's due index (the dispatcher's work queue). */
|
|
90
|
+
declare const duePrefix: (applicationKey: string) => string;
|
|
91
|
+
/** Value stored at a due-index key. */
|
|
92
|
+
interface DueEntry {
|
|
93
|
+
app: string;
|
|
94
|
+
deliveryId: string;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* A due-index entry: 13-digit zero-padded epoch milliseconds, then the delivery
|
|
98
|
+
* id, `~`-separated. Sorting keys lexicographically sorts entries chronologically.
|
|
99
|
+
*/
|
|
100
|
+
declare const dueKey: (applicationKey: string, dueAtMillis: number, deliveryId: string) => string;
|
|
101
|
+
/** Parse a due-index key back into its due-time and delivery id. */
|
|
102
|
+
declare const parseDueKey: (key: string) => {
|
|
103
|
+
dueAtMillis: number;
|
|
104
|
+
deliveryId: string;
|
|
105
|
+
} | null;
|
|
106
|
+
/** Prefix of the message → deliveries reverse index. */
|
|
107
|
+
declare const byMessagePrefix: (applicationKey: string, messageId: string) => string;
|
|
108
|
+
/** One entry in the message → deliveries reverse index. */
|
|
109
|
+
declare const byMessageKey: (applicationKey: string, messageId: string, deliveryId: string) => string;
|
|
110
|
+
/** Prefix of the endpoint → deliveries reverse index. */
|
|
111
|
+
declare const byEndpointPrefix: (applicationKey: string, endpointId: string) => string;
|
|
112
|
+
/** One entry in the endpoint → deliveries reverse index. */
|
|
113
|
+
declare const byEndpointKey: (applicationKey: string, endpointId: string, deliveryId: string) => string;
|
|
114
|
+
/** Append-only audit log for an application, stored as an ordered `AuditEntry[]`. */
|
|
115
|
+
declare const auditLogKey: (applicationKey: string) => string;
|
|
116
|
+
/** Prefix under which everything belonging to one application lives. */
|
|
117
|
+
declare const applicationPrefix: (applicationKey: string) => string;
|
|
118
|
+
/** Extract the trailing segment (endpoint id / message id / …) from a key. */
|
|
119
|
+
declare const lastSegment: (key: string) => string;
|
|
120
|
+
//#endregion
|
|
121
|
+
//#region src/id.d.ts
|
|
122
|
+
/**
|
|
123
|
+
* Entity id generation: `{prefix}_{22-char base62}`. Zero dependencies — base62
|
|
124
|
+
* over `crypto.getRandomValues`, no ULID. 22 base62 characters encode ~131 bits;
|
|
125
|
+
* ids are generated from 16 random bytes (128 bits) and left-padded to a fixed
|
|
126
|
+
* length so ids are uniform and lexicographically well-behaved.
|
|
127
|
+
*
|
|
128
|
+
* @module
|
|
129
|
+
*/
|
|
130
|
+
/** Prefixes for the four generated entity id kinds. */
|
|
131
|
+
type IdPrefix = "msg" | "ep" | "dlv" | "atp";
|
|
132
|
+
/**
|
|
133
|
+
* Generate a new entity id, e.g. `newId("msg")` → `"msg_0uK9…"` (22-char base62
|
|
134
|
+
* suffix).
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* ```ts
|
|
138
|
+
* import { newId } from "@xtandard/webhooks";
|
|
139
|
+
*
|
|
140
|
+
* const id = newId("ep"); // "ep_…"
|
|
141
|
+
* ```
|
|
142
|
+
*/
|
|
143
|
+
declare function newId(prefix: IdPrefix): string;
|
|
144
|
+
/** Regex an id of the given prefix must match. */
|
|
145
|
+
declare function idPattern(prefix: IdPrefix): RegExp;
|
|
146
|
+
//#endregion
|
|
147
|
+
//#region src/duration.d.ts
|
|
148
|
+
/**
|
|
149
|
+
* Convert a {@link WebhookDuration} (`5000`, `"5s"`, `"30m"`, `"2h"`, `"5d"`)
|
|
150
|
+
* to milliseconds. Throws on malformed strings or negative numbers.
|
|
151
|
+
*
|
|
152
|
+
* @example
|
|
153
|
+
* ```ts
|
|
154
|
+
* import { durationToMs } from "@xtandard/webhooks";
|
|
155
|
+
*
|
|
156
|
+
* durationToMs("5m"); // 300000
|
|
157
|
+
* durationToMs(250); // 250
|
|
158
|
+
* ```
|
|
159
|
+
*/
|
|
160
|
+
declare function durationToMs(duration: WebhookDuration): number;
|
|
161
|
+
/**
|
|
162
|
+
* Parse a comma-separated duration list (the `RETRY_SCHEDULE` env var format,
|
|
163
|
+
* e.g. `"0s,5s,5m,30m,2h,5h,10h"`) into a {@link WebhookDuration} array.
|
|
164
|
+
*/
|
|
165
|
+
declare function parseDurationList(input: string): WebhookDuration[];
|
|
166
|
+
//#endregion
|
|
167
|
+
//#region src/storage/watch.d.ts
|
|
168
|
+
/**
|
|
169
|
+
* A change source. Called with a `notify` callback — wire your source to invoke
|
|
170
|
+
* it (optionally with the changed key) on every change — and return an
|
|
171
|
+
* unsubscribe function (sync or async).
|
|
172
|
+
*/
|
|
173
|
+
type WatchSubscribe = (notify: (key?: string) => void) => (() => void | Promise<void>) | Promise<() => void | Promise<void>>;
|
|
174
|
+
/**
|
|
175
|
+
* Wrap a storage so it implements {@link WatchableWebhooksStorage} using
|
|
176
|
+
* `subscribe` as the change source. The wrapper delegates all reads/writes to
|
|
177
|
+
* `storage` and only adds `watch`; keys not under the watched `prefix` are
|
|
178
|
+
* filtered out. A notification without a key is delivered as a change to the
|
|
179
|
+
* prefix itself (enough to trigger a refresh).
|
|
180
|
+
*/
|
|
181
|
+
declare function withWatch<S extends WebhooksStorage>(storage: S, subscribe: WatchSubscribe): S & WatchableWebhooksStorage;
|
|
182
|
+
/**
|
|
183
|
+
* A dedicated notification client for {@link pgListenNotify} — satisfied by a
|
|
184
|
+
* `pg` `Client`. `Pool` won't do (it rotates connections); use a `Client` you
|
|
185
|
+
* `connect()`.
|
|
186
|
+
*/
|
|
187
|
+
interface PgNotificationClient {
|
|
188
|
+
query(sql: string): Promise<unknown>;
|
|
189
|
+
on(event: "notification", listener: (msg: {
|
|
190
|
+
channel: string;
|
|
191
|
+
payload?: string;
|
|
192
|
+
}) => void): void;
|
|
193
|
+
removeListener(event: "notification", listener: (msg: {
|
|
194
|
+
channel: string;
|
|
195
|
+
payload?: string;
|
|
196
|
+
}) => void): void;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* A {@link WatchSubscribe} backed by Postgres `LISTEN`/`NOTIFY`. Pair with
|
|
200
|
+
* {@link withWatch} for the postgres or drizzle-over-pg adapters. Your migration
|
|
201
|
+
* owns the trigger that `pg_notify`s `channel` with the changed key as payload;
|
|
202
|
+
* this only `LISTEN`s.
|
|
203
|
+
*
|
|
204
|
+
* @example
|
|
205
|
+
* ```ts
|
|
206
|
+
* import { Client } from "pg";
|
|
207
|
+
* const listener = new Client({ connectionString: process.env.DATABASE_URL });
|
|
208
|
+
* await listener.connect();
|
|
209
|
+
* const storage = withWatch(base, pgListenNotify(listener, "xtandard_webhooks"));
|
|
210
|
+
* ```
|
|
211
|
+
*/
|
|
212
|
+
declare function pgListenNotify(client: PgNotificationClient, channel?: string): WatchSubscribe;
|
|
213
|
+
//#endregion
|
|
214
|
+
//#region src/validation.d.ts
|
|
215
|
+
/**
|
|
216
|
+
* Runtime validation for control-plane inputs, built on `valibot`.
|
|
217
|
+
*
|
|
218
|
+
* This is the **admin path** only — `publish()` performs its own minimal
|
|
219
|
+
* checks and the wire/receiver path never imports this module, so `valibot`
|
|
220
|
+
* stays off the hot paths. Validation combines structural parsing (valibot)
|
|
221
|
+
* with semantic checks (URL policy, reserved header names, reserved keys).
|
|
222
|
+
*
|
|
223
|
+
* @module
|
|
224
|
+
*/
|
|
225
|
+
/** Allowed characters for application keys and event type names. */
|
|
226
|
+
declare const KEY_REGEX: RegExp;
|
|
227
|
+
/**
|
|
228
|
+
* Header names owned by the Standard Webhooks wire contract. Endpoints may not
|
|
229
|
+
* override them via static headers.
|
|
230
|
+
*/
|
|
231
|
+
declare const RESERVED_HEADERS: readonly ["webhook-id", "webhook-timestamp", "webhook-signature"];
|
|
232
|
+
/** A single validation problem with a dotted path into the offending data. */
|
|
233
|
+
interface ValidationIssue {
|
|
234
|
+
path: string;
|
|
235
|
+
message: string;
|
|
236
|
+
}
|
|
237
|
+
/** Result of the `validate*` functions. */
|
|
238
|
+
interface ValidationResult {
|
|
239
|
+
valid: boolean;
|
|
240
|
+
errors: ValidationIssue[];
|
|
241
|
+
}
|
|
242
|
+
/** Raised by {@link assertValid} when an input fails validation. Maps to HTTP 422. */
|
|
243
|
+
declare class ValidationError extends Error {
|
|
244
|
+
readonly errors: ValidationIssue[];
|
|
245
|
+
constructor(errors: ValidationIssue[]);
|
|
246
|
+
}
|
|
247
|
+
/** Throw a {@link ValidationError} when `result` is invalid. */
|
|
248
|
+
declare function assertValid(result: ValidationResult): void;
|
|
249
|
+
/**
|
|
250
|
+
* Validate an {@link Application}: structure + reserved-key check.
|
|
251
|
+
*
|
|
252
|
+
* @example
|
|
253
|
+
* ```ts
|
|
254
|
+
* import { validateApplication } from "@xtandard/webhooks";
|
|
255
|
+
*
|
|
256
|
+
* const result = validateApplication({ key: "acme" });
|
|
257
|
+
* // result.valid === true
|
|
258
|
+
* ```
|
|
259
|
+
*/
|
|
260
|
+
declare function validateApplication(input: unknown, basePath?: string): ValidationResult;
|
|
261
|
+
/** Validate an {@link EventType}: structural only (the name regex carries the semantics). */
|
|
262
|
+
declare function validateEventType(input: unknown, basePath?: string): ValidationResult;
|
|
263
|
+
/** Options that relax/extend the endpoint URL policy. */
|
|
264
|
+
interface UrlPolicyOptions {
|
|
265
|
+
/** Allow `http:` for non-localhost hosts (dev only; default `false`). */
|
|
266
|
+
allowInsecureUrls?: boolean;
|
|
267
|
+
/** Extra host-supplied gate; return `false` to reject (e.g. an SSRF denylist). */
|
|
268
|
+
urlPolicy?: (url: string) => boolean;
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Validate an endpoint destination URL: parseable, `https:` (or `http:` for
|
|
272
|
+
* localhost, or anywhere when `allowInsecureUrls`), no embedded credentials,
|
|
273
|
+
* and passing the host's optional `urlPolicy` gate.
|
|
274
|
+
*/
|
|
275
|
+
declare function validateEndpointUrl(url: string, options?: UrlPolicyOptions, basePath?: string): ValidationResult;
|
|
276
|
+
/**
|
|
277
|
+
* Validate an {@link Endpoint} input: structure, URL policy, and static-header
|
|
278
|
+
* restrictions (the Standard Webhooks headers are reserved).
|
|
279
|
+
*
|
|
280
|
+
* @example
|
|
281
|
+
* ```ts
|
|
282
|
+
* import { validateEndpoint } from "@xtandard/webhooks";
|
|
283
|
+
*
|
|
284
|
+
* const result = validateEndpoint({ url: "https://api.example.com/hooks" });
|
|
285
|
+
* // result.valid === true
|
|
286
|
+
* ```
|
|
287
|
+
*/
|
|
288
|
+
declare function validateEndpoint(input: unknown, options?: UrlPolicyOptions, basePath?: string): ValidationResult;
|
|
289
|
+
//#endregion
|
|
290
|
+
//#region src/portal.d.ts
|
|
291
|
+
/** Prefix of every portal token. */
|
|
292
|
+
declare const PORTAL_TOKEN_PREFIX = "whpt_";
|
|
293
|
+
/**
|
|
294
|
+
* Thrown by {@link verifyPortalToken} for any invalid token — malformed,
|
|
295
|
+
* bad signature, or expired. Maps to HTTP 401 at the API layer.
|
|
296
|
+
*/
|
|
297
|
+
declare class PortalTokenError extends Error {
|
|
298
|
+
constructor(message: string);
|
|
299
|
+
}
|
|
300
|
+
/** Options for {@link createPortalToken}. */
|
|
301
|
+
interface PortalTokenOptions {
|
|
302
|
+
/** Token lifetime. Default `"7d"`. */
|
|
303
|
+
expiresIn?: WebhookDuration;
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Mint a portal token granting access to `applicationKey` until `expiresIn`
|
|
307
|
+
* elapses (default 7 days). Call this **server-side** — anyone holding the
|
|
308
|
+
* portal secret can mint tokens for any application.
|
|
309
|
+
*
|
|
310
|
+
* @example
|
|
311
|
+
* ```ts
|
|
312
|
+
* import { createPortalToken } from "@xtandard/webhooks";
|
|
313
|
+
*
|
|
314
|
+
* // In the host app's session-guarded route:
|
|
315
|
+
* const token = await createPortalToken(process.env.PORTAL_SECRET!, "acme", {
|
|
316
|
+
* expiresIn: "1h",
|
|
317
|
+
* });
|
|
318
|
+
* // Hand `token` to the frontend for <WebhooksPortal token={token} />.
|
|
319
|
+
* ```
|
|
320
|
+
*/
|
|
321
|
+
declare function createPortalToken(secret: string, applicationKey: string, options?: PortalTokenOptions): Promise<string>;
|
|
322
|
+
/**
|
|
323
|
+
* Verify a portal token: format, signature (constant time), then expiry.
|
|
324
|
+
* Returns the granted application key on success; throws
|
|
325
|
+
* {@link PortalTokenError} otherwise.
|
|
326
|
+
*
|
|
327
|
+
* @example
|
|
328
|
+
* ```ts
|
|
329
|
+
* import { verifyPortalToken } from "@xtandard/webhooks";
|
|
330
|
+
*
|
|
331
|
+
* const { applicationKey } = await verifyPortalToken(secret, token);
|
|
332
|
+
* ```
|
|
333
|
+
*/
|
|
334
|
+
declare function verifyPortalToken(secret: string, token: string): Promise<{
|
|
335
|
+
applicationKey: string;
|
|
336
|
+
}>;
|
|
337
|
+
//#endregion
|
|
338
|
+
//#region src/server/openapi.d.ts
|
|
339
|
+
/**
|
|
340
|
+
* OpenAPI 3.1 description of the admin JSON API.
|
|
341
|
+
*
|
|
342
|
+
* Exposed two ways, mirroring the flags panel:
|
|
343
|
+
* - served at `GET {basePath}/api/openapi.json` for standalone tools (Scalar,
|
|
344
|
+
* Swagger UI, Postman, codegen);
|
|
345
|
+
* - returned by `createFetchHandler(...).openapi()` so you can MERGE it into
|
|
346
|
+
* your host app's OpenAPI document (e.g. Elysia `@elysiajs/openapi`
|
|
347
|
+
* `references`, or Hono's OpenAPI).
|
|
348
|
+
*
|
|
349
|
+
* Hand-authored — no generation dependency (flags precedent).
|
|
350
|
+
*
|
|
351
|
+
* @module
|
|
352
|
+
*/
|
|
353
|
+
/** Options for {@link buildOpenApiDocument}. */
|
|
354
|
+
interface OpenApiOptions {
|
|
355
|
+
/** Mount prefix used in the `servers` url (e.g. `"/webhooks"`). */
|
|
356
|
+
basePath?: string;
|
|
357
|
+
/** Document title. */
|
|
358
|
+
title?: string;
|
|
359
|
+
/** Document version (defaults to the package's API version). */
|
|
360
|
+
version?: string;
|
|
361
|
+
}
|
|
362
|
+
/** Build the OpenAPI 3.1 document for the admin API. Pure — safe to call anywhere. */
|
|
363
|
+
declare function buildOpenApiDocument(options?: OpenApiOptions): Record<string, unknown>;
|
|
364
|
+
//#endregion
|
|
365
|
+
export { Actor, ActorOptions, type AfterEvent, type AfterEventType, Application, type AttemptDeliveryInput, type AttemptOutcome, AttemptTrigger, AuditAction, AuditEntry, type AuthProvider, type AuthorizationProvider, type AuthorizeInput, type BeforeEvent, type BeforeEventType, type BuildSignedRequestInput, ClaimInput, type CompareAndSwapWebhooksStorage, ConflictError, type CreateFetchHandlerResult, DEFAULT_ATTEMPT_TIMEOUT_MS, DEFAULT_PORTAL_ACTIONS, DEFAULT_RESPONSE_BODY_LIMIT, DEFAULT_RETRY_SCHEDULE, Delivery, DeliveryAttempt, type DeliveryErrorReporter, type DeliveryEvent, type DeliveryListener, type DeliveryQueueStorage, DeliveryStatus, type Dispatcher, type DispatcherOptions, Endpoint, EndpointDisabledReason, EndpointSecret, EventType, HookDeniedError, type HookErrorReporter, type IdPrefix, IdempotencyConflictError, JsonValue, KEY_REGEX, ListDeliveriesOptions, ListMessagesOptions, MUTATING_ACTIONS, Message, NotFoundError, type OpenApiOptions, PORTAL_TOKEN_PREFIX, PayloadTooLargeError, type PgNotificationClient, PortalTokenError, type PortalTokenOptions, type Principal, PublishInput, PublishResult, RESERVED_HEADERS, ReadonlyError, RecordAttemptInput, RecoverResult, RetentionOptions, RetentionRule, SCHEMA_VERSION, SECRET_PREFIX, SendExampleResult, type SignedRequest, type StorageChangeEvent, type TransactionalWebhooksStorage, type UrlPolicyOptions, VERSION, ValidationError, type ValidationIssue, type ValidationResult, type VerifyInput, type WatchSubscribe, type WatchableWebhooksStorage, WebhookDuration, WebhookEnvelope, WebhookVerificationError, type WebhooksAction, WebhooksCore, WebhooksCoreOptions, type WebhooksCorsOptions, type WebhooksHooks, type WebhooksHooksInput, type WebhooksPanelOptions, type WebhooksPortalOptions, type WebhooksResource, type WebhooksStorage, activeSecrets, assertValid, attemptDelivery, buildOpenApiDocument, buildSignedRequest, createDispatcher, createFetchHandler, createPortalToken, createWebhooksCore, defaultHookErrorReporter, durationToMs, emitDelivery, generateSecret, hasDeliveryQueue, idPattern, isCompareAndSwap, isMutatingAction, isTerminalDeliveryStatus, isTransactional, isWatchable, keys_d_exports as keys, newId, normalizeHooks, parseDurationList, pgListenNotify, requirePeer, runAfter, runBefore, sign, signatureHeader, validateApplication, validateEndpoint, validateEndpointUrl, validateEventType, verify, verifyPortalToken, withWatch };
|
|
366
|
+
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { SCHEMA_VERSION, isTerminalDeliveryStatus } from "./schema.mjs";
|
|
2
|
+
import { SECRET_PREFIX, WebhookVerificationError, generateSecret, sign, signatureHeader, verify } from "./signing.mjs";
|
|
3
|
+
import { A as attemptDelivery, C as newId, D as DEFAULT_ATTEMPT_TIMEOUT_MS, E as emitDelivery, O as DEFAULT_RESPONSE_BODY_LIMIT, T as parseDurationList, _ as normalizeHooks, a as ReadonlyError, b as VERSION, c as RESERVED_HEADERS, d as validateApplication, f as validateEndpoint, g as defaultHookErrorReporter, h as HookDeniedError, i as PayloadTooLargeError, j as buildSignedRequest, k as activeSecrets, l as ValidationError, m as validateEventType, n as IdempotencyConflictError, o as createWebhooksCore, p as validateEndpointUrl, r as NotFoundError, s as KEY_REGEX, t as ConflictError, u as assertValid, v as runAfter, w as durationToMs, x as idPattern, y as runBefore } from "./core-CMpnmI5Q.mjs";
|
|
4
|
+
import { x as keys_exports } from "./keys-Byyj4quQ.mjs";
|
|
5
|
+
import { a as requirePeer, i as isWatchable, n as isCompareAndSwap, r as isTransactional, t as hasDeliveryQueue } from "./contract-BEhDcd_5.mjs";
|
|
6
|
+
import { n as createDispatcher, t as DEFAULT_RETRY_SCHEDULE } from "./dispatcher-Coubwrka.mjs";
|
|
7
|
+
import { n as isMutatingAction, t as MUTATING_ACTIONS } from "./contract-9XpcwcCn.mjs";
|
|
8
|
+
import { a as PORTAL_TOKEN_PREFIX, c as verifyPortalToken, i as buildOpenApiDocument, o as PortalTokenError, r as DEFAULT_PORTAL_ACTIONS, s as createPortalToken, t as createFetchHandler } from "./create-fetch-handler-BIdk9P30.mjs";
|
|
9
|
+
//#region src/storage/watch.ts
|
|
10
|
+
/**
|
|
11
|
+
* Wrap a storage so it implements {@link WatchableWebhooksStorage} using
|
|
12
|
+
* `subscribe` as the change source. The wrapper delegates all reads/writes to
|
|
13
|
+
* `storage` and only adds `watch`; keys not under the watched `prefix` are
|
|
14
|
+
* filtered out. A notification without a key is delivered as a change to the
|
|
15
|
+
* prefix itself (enough to trigger a refresh).
|
|
16
|
+
*/
|
|
17
|
+
function withWatch(storage, subscribe) {
|
|
18
|
+
const watchable = Object.create(storage);
|
|
19
|
+
watchable.watch = (prefix, callback) => {
|
|
20
|
+
const notify = (key) => {
|
|
21
|
+
if (key !== void 0 && key !== "" && !key.startsWith(prefix)) return;
|
|
22
|
+
callback({
|
|
23
|
+
type: "update",
|
|
24
|
+
key: key && key !== "" ? key : prefix
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
return Promise.resolve(subscribe(notify));
|
|
28
|
+
};
|
|
29
|
+
return watchable;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* A {@link WatchSubscribe} backed by Postgres `LISTEN`/`NOTIFY`. Pair with
|
|
33
|
+
* {@link withWatch} for the postgres or drizzle-over-pg adapters. Your migration
|
|
34
|
+
* owns the trigger that `pg_notify`s `channel` with the changed key as payload;
|
|
35
|
+
* this only `LISTEN`s.
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```ts
|
|
39
|
+
* import { Client } from "pg";
|
|
40
|
+
* const listener = new Client({ connectionString: process.env.DATABASE_URL });
|
|
41
|
+
* await listener.connect();
|
|
42
|
+
* const storage = withWatch(base, pgListenNotify(listener, "xtandard_webhooks"));
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
function pgListenNotify(client, channel = "xtandard_webhooks") {
|
|
46
|
+
return async (notify) => {
|
|
47
|
+
const listener = (msg) => {
|
|
48
|
+
if (msg.channel === channel) notify(msg.payload);
|
|
49
|
+
};
|
|
50
|
+
client.on("notification", listener);
|
|
51
|
+
await client.query(`LISTEN "${channel.replace(/"/g, "\"\"")}"`);
|
|
52
|
+
return async () => {
|
|
53
|
+
client.removeListener("notification", listener);
|
|
54
|
+
await client.query(`UNLISTEN "${channel.replace(/"/g, "\"\"")}"`);
|
|
55
|
+
};
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
//#endregion
|
|
59
|
+
export { ConflictError, DEFAULT_ATTEMPT_TIMEOUT_MS, DEFAULT_PORTAL_ACTIONS, DEFAULT_RESPONSE_BODY_LIMIT, DEFAULT_RETRY_SCHEDULE, HookDeniedError, IdempotencyConflictError, KEY_REGEX, MUTATING_ACTIONS, NotFoundError, PORTAL_TOKEN_PREFIX, PayloadTooLargeError, PortalTokenError, RESERVED_HEADERS, ReadonlyError, SCHEMA_VERSION, SECRET_PREFIX, VERSION, ValidationError, WebhookVerificationError, activeSecrets, assertValid, attemptDelivery, buildOpenApiDocument, buildSignedRequest, createDispatcher, createFetchHandler, createPortalToken, createWebhooksCore, defaultHookErrorReporter, durationToMs, emitDelivery, generateSecret, hasDeliveryQueue, idPattern, isCompareAndSwap, isMutatingAction, isTerminalDeliveryStatus, isTransactional, isWatchable, keys_exports as keys, newId, normalizeHooks, parseDurationList, pgListenNotify, requirePeer, runAfter, runBefore, sign, signatureHeader, validateApplication, validateEndpoint, validateEndpointUrl, validateEventType, verify, verifyPortalToken, withWatch };
|
|
60
|
+
|
|
61
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/storage/watch.ts"],"sourcesContent":["/**\n * Composable change-notifications. `watch` is **orthogonal** to which storage\n * you use, so rather than bake it into each adapter, {@link withWatch} wraps\n * *any* {@link WebhooksStorage} with a {@link WatchSubscribe} source you provide\n * — Postgres `LISTEN`/`NOTIFY`, Redis pub/sub, an ORM after-write hook, a\n * websocket, an `EventEmitter`, etc.\n *\n * This gives `watch` to adapters that don't implement it themselves (postgres,\n * drizzle, mongodb, unstorage, cloudflare-kv, …), driven by whatever change\n * signal your infrastructure already has.\n *\n * @example\n * ```ts\n * import { withWatch } from \"@xtandard/webhooks\";\n * import { createDrizzleStorage } from \"@xtandard/webhooks/storage/drizzle\";\n *\n * const storage = withWatch(createDrizzleStorage({ db, table }), (notify) => {\n * const sub = redis.subscribe(\"webhook-changes\", (m) => notify(m.key));\n * return () => sub.unsubscribe();\n * });\n * ```\n *\n * @module\n */\n\nimport type { StorageChangeEvent, WatchableWebhooksStorage, WebhooksStorage } from \"./contract.ts\";\n\n/**\n * A change source. Called with a `notify` callback — wire your source to invoke\n * it (optionally with the changed key) on every change — and return an\n * unsubscribe function (sync or async).\n */\nexport type WatchSubscribe = (\n notify: (key?: string) => void,\n) => (() => void | Promise<void>) | Promise<() => void | Promise<void>>;\n\n/**\n * Wrap a storage so it implements {@link WatchableWebhooksStorage} using\n * `subscribe` as the change source. The wrapper delegates all reads/writes to\n * `storage` and only adds `watch`; keys not under the watched `prefix` are\n * filtered out. A notification without a key is delivered as a change to the\n * prefix itself (enough to trigger a refresh).\n */\nexport function withWatch<S extends WebhooksStorage>(\n storage: S,\n subscribe: WatchSubscribe,\n): S & WatchableWebhooksStorage {\n const watchable = Object.create(storage) as S & WatchableWebhooksStorage;\n watchable.watch = (prefix, callback) => {\n const notify = (key?: string): void => {\n if (key !== undefined && key !== \"\" && !key.startsWith(prefix)) return;\n callback({\n type: \"update\",\n key: key && key !== \"\" ? key : prefix,\n } satisfies StorageChangeEvent);\n };\n return Promise.resolve(subscribe(notify));\n };\n return watchable;\n}\n\n/**\n * A dedicated notification client for {@link pgListenNotify} — satisfied by a\n * `pg` `Client`. `Pool` won't do (it rotates connections); use a `Client` you\n * `connect()`.\n */\nexport interface PgNotificationClient {\n query(sql: string): Promise<unknown>;\n on(event: \"notification\", listener: (msg: { channel: string; payload?: string }) => void): void;\n removeListener(\n event: \"notification\",\n listener: (msg: { channel: string; payload?: string }) => void,\n ): void;\n}\n\n/**\n * A {@link WatchSubscribe} backed by Postgres `LISTEN`/`NOTIFY`. Pair with\n * {@link withWatch} for the postgres or drizzle-over-pg adapters. Your migration\n * owns the trigger that `pg_notify`s `channel` with the changed key as payload;\n * this only `LISTEN`s.\n *\n * @example\n * ```ts\n * import { Client } from \"pg\";\n * const listener = new Client({ connectionString: process.env.DATABASE_URL });\n * await listener.connect();\n * const storage = withWatch(base, pgListenNotify(listener, \"xtandard_webhooks\"));\n * ```\n */\nexport function pgListenNotify(\n client: PgNotificationClient,\n channel = \"xtandard_webhooks\",\n): WatchSubscribe {\n return async (notify) => {\n const listener = (msg: { channel: string; payload?: string }): void => {\n if (msg.channel === channel) notify(msg.payload);\n };\n client.on(\"notification\", listener);\n await client.query(`LISTEN \"${channel.replace(/\"/g, '\"\"')}\"`);\n return async () => {\n client.removeListener(\"notification\", listener);\n await client.query(`UNLISTEN \"${channel.replace(/\"/g, '\"\"')}\"`);\n };\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;AA2CA,SAAgB,UACd,SACA,WAC8B;CAC9B,MAAM,YAAY,OAAO,OAAO,OAAO;CACvC,UAAU,SAAS,QAAQ,aAAa;EACtC,MAAM,UAAU,QAAuB;GACrC,IAAI,QAAQ,KAAA,KAAa,QAAQ,MAAM,CAAC,IAAI,WAAW,MAAM,GAAG;GAChE,SAAS;IACP,MAAM;IACN,KAAK,OAAO,QAAQ,KAAK,MAAM;GACjC,CAA8B;EAChC;EACA,OAAO,QAAQ,QAAQ,UAAU,MAAM,CAAC;CAC1C;CACA,OAAO;AACT;;;;;;;;;;;;;;;AA8BA,SAAgB,eACd,QACA,UAAU,qBACM;CAChB,OAAO,OAAO,WAAW;EACvB,MAAM,YAAY,QAAqD;GACrE,IAAI,IAAI,YAAY,SAAS,OAAO,IAAI,OAAO;EACjD;EACA,OAAO,GAAG,gBAAgB,QAAQ;EAClC,MAAM,OAAO,MAAM,WAAW,QAAQ,QAAQ,MAAM,MAAI,EAAE,EAAE;EAC5D,OAAO,YAAY;GACjB,OAAO,eAAe,gBAAgB,QAAQ;GAC9C,MAAM,OAAO,MAAM,aAAa,QAAQ,QAAQ,MAAM,MAAI,EAAE,EAAE;EAChE;CACF;AACF"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { t as __exportAll } from "./chunk-D7D4PA-g.mjs";
|
|
2
|
+
//#region src/keys.ts
|
|
3
|
+
var keys_exports = /* @__PURE__ */ __exportAll({
|
|
4
|
+
RESERVED_APPLICATION_KEYS: () => RESERVED_APPLICATION_KEYS,
|
|
5
|
+
ROOT: () => "whk",
|
|
6
|
+
applicationMetaKey: () => applicationMetaKey,
|
|
7
|
+
applicationPrefix: () => applicationPrefix,
|
|
8
|
+
applicationsKey: () => applicationsKey,
|
|
9
|
+
attemptKey: () => attemptKey,
|
|
10
|
+
attemptsPrefix: () => attemptsPrefix,
|
|
11
|
+
auditLogKey: () => auditLogKey,
|
|
12
|
+
byEndpointKey: () => byEndpointKey,
|
|
13
|
+
byEndpointPrefix: () => byEndpointPrefix,
|
|
14
|
+
byMessageKey: () => byMessageKey,
|
|
15
|
+
byMessagePrefix: () => byMessagePrefix,
|
|
16
|
+
deliveriesPrefix: () => deliveriesPrefix,
|
|
17
|
+
deliveryKey: () => deliveryKey,
|
|
18
|
+
dueKey: () => dueKey,
|
|
19
|
+
duePrefix: () => duePrefix,
|
|
20
|
+
endpointKey: () => endpointKey,
|
|
21
|
+
endpointsKey: () => endpointsKey,
|
|
22
|
+
eventTypeKey: () => eventTypeKey,
|
|
23
|
+
eventTypesKey: () => eventTypesKey,
|
|
24
|
+
globalAuditLogKey: () => globalAuditLogKey,
|
|
25
|
+
idempotencyKey: () => idempotencyKey,
|
|
26
|
+
lastSegment: () => lastSegment,
|
|
27
|
+
messageKey: () => messageKey,
|
|
28
|
+
messagesPrefix: () => messagesPrefix,
|
|
29
|
+
parseDueKey: () => parseDueKey
|
|
30
|
+
});
|
|
31
|
+
/**
|
|
32
|
+
* Application keys that would collide with global index keys under the shared
|
|
33
|
+
* root. Rejected by validation.
|
|
34
|
+
*/
|
|
35
|
+
const RESERVED_APPLICATION_KEYS = [
|
|
36
|
+
"applications",
|
|
37
|
+
"event-types",
|
|
38
|
+
"audit-log"
|
|
39
|
+
];
|
|
40
|
+
/** Global audit log (event-type actions, which are not application-scoped). */
|
|
41
|
+
const globalAuditLogKey = () => `whk/audit-log`;
|
|
42
|
+
/** Index key listing all known application keys. */
|
|
43
|
+
const applicationsKey = () => `whk/applications`;
|
|
44
|
+
/** Metadata for a single application. */
|
|
45
|
+
const applicationMetaKey = (applicationKey) => `whk/${applicationKey}/metadata`;
|
|
46
|
+
/** Index key listing all event type names (global catalog). */
|
|
47
|
+
const eventTypesKey = () => `whk/event-types`;
|
|
48
|
+
/** A single event type record. */
|
|
49
|
+
const eventTypeKey = (name) => `whk/event-types/${name}`;
|
|
50
|
+
/** Index key listing an application's endpoint ids. */
|
|
51
|
+
const endpointsKey = (applicationKey) => `whk/${applicationKey}/endpoints`;
|
|
52
|
+
/** A single endpoint record. */
|
|
53
|
+
const endpointKey = (applicationKey, endpointId) => `whk/${applicationKey}/endpoints/${endpointId}`;
|
|
54
|
+
/** Prefix under which an application's messages live. */
|
|
55
|
+
const messagesPrefix = (applicationKey) => `whk/${applicationKey}/messages/`;
|
|
56
|
+
/** A single message record. */
|
|
57
|
+
const messageKey = (applicationKey, messageId) => `${messagesPrefix(applicationKey)}${messageId}`;
|
|
58
|
+
/** Maps a caller-supplied idempotency key to the message id it produced. */
|
|
59
|
+
const idempotencyKey = (applicationKey, key) => `whk/${applicationKey}/idempotency/${key}`;
|
|
60
|
+
/** Prefix under which an application's deliveries live. */
|
|
61
|
+
const deliveriesPrefix = (applicationKey) => `whk/${applicationKey}/deliveries/`;
|
|
62
|
+
/** A single delivery record. */
|
|
63
|
+
const deliveryKey = (applicationKey, deliveryId) => `${deliveriesPrefix(applicationKey)}${deliveryId}`;
|
|
64
|
+
/** Prefix under which one delivery's attempts live. */
|
|
65
|
+
const attemptsPrefix = (applicationKey, deliveryId) => `whk/${applicationKey}/attempts/${deliveryId}/`;
|
|
66
|
+
/**
|
|
67
|
+
* A single attempt record. The attempt number is zero-padded to four digits so
|
|
68
|
+
* lexicographic key order equals attempt order.
|
|
69
|
+
*/
|
|
70
|
+
const attemptKey = (applicationKey, deliveryId, attemptNumber) => `${attemptsPrefix(applicationKey, deliveryId)}${String(attemptNumber).padStart(4, "0")}`;
|
|
71
|
+
/** Prefix of an application's due index (the dispatcher's work queue). */
|
|
72
|
+
const duePrefix = (applicationKey) => `whk/${applicationKey}/due/`;
|
|
73
|
+
/**
|
|
74
|
+
* A due-index entry: 13-digit zero-padded epoch milliseconds, then the delivery
|
|
75
|
+
* id, `~`-separated. Sorting keys lexicographically sorts entries chronologically.
|
|
76
|
+
*/
|
|
77
|
+
const dueKey = (applicationKey, dueAtMillis, deliveryId) => `${duePrefix(applicationKey)}${String(dueAtMillis).padStart(13, "0")}~${deliveryId}`;
|
|
78
|
+
/** Parse a due-index key back into its due-time and delivery id. */
|
|
79
|
+
const parseDueKey = (key) => {
|
|
80
|
+
const last = lastSegment(key);
|
|
81
|
+
const sep = last.indexOf("~");
|
|
82
|
+
if (sep === -1) return null;
|
|
83
|
+
const millis = Number(last.slice(0, sep));
|
|
84
|
+
const deliveryId = last.slice(sep + 1);
|
|
85
|
+
if (!Number.isFinite(millis) || deliveryId.length === 0) return null;
|
|
86
|
+
return {
|
|
87
|
+
dueAtMillis: millis,
|
|
88
|
+
deliveryId
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
/** Prefix of the message → deliveries reverse index. */
|
|
92
|
+
const byMessagePrefix = (applicationKey, messageId) => `whk/${applicationKey}/by-message/${messageId}/`;
|
|
93
|
+
/** One entry in the message → deliveries reverse index. */
|
|
94
|
+
const byMessageKey = (applicationKey, messageId, deliveryId) => `${byMessagePrefix(applicationKey, messageId)}${deliveryId}`;
|
|
95
|
+
/** Prefix of the endpoint → deliveries reverse index. */
|
|
96
|
+
const byEndpointPrefix = (applicationKey, endpointId) => `whk/${applicationKey}/by-endpoint/${endpointId}/`;
|
|
97
|
+
/** One entry in the endpoint → deliveries reverse index. */
|
|
98
|
+
const byEndpointKey = (applicationKey, endpointId, deliveryId) => `${byEndpointPrefix(applicationKey, endpointId)}${deliveryId}`;
|
|
99
|
+
/** Append-only audit log for an application, stored as an ordered `AuditEntry[]`. */
|
|
100
|
+
const auditLogKey = (applicationKey) => `whk/${applicationKey}/audit-log`;
|
|
101
|
+
/** Prefix under which everything belonging to one application lives. */
|
|
102
|
+
const applicationPrefix = (applicationKey) => `whk/${applicationKey}/`;
|
|
103
|
+
/** Extract the trailing segment (endpoint id / message id / …) from a key. */
|
|
104
|
+
const lastSegment = (key) => {
|
|
105
|
+
const parts = key.split("/");
|
|
106
|
+
return parts[parts.length - 1] ?? "";
|
|
107
|
+
};
|
|
108
|
+
//#endregion
|
|
109
|
+
export { messageKey as C, lastSegment as S, parseDueKey as T, eventTypeKey as _, attemptKey as a, idempotencyKey as b, byEndpointKey as c, byMessagePrefix as d, deliveriesPrefix as f, endpointsKey as g, endpointKey as h, applicationsKey as i, byEndpointPrefix as l, dueKey as m, applicationMetaKey as n, attemptsPrefix as o, deliveryKey as p, applicationPrefix as r, auditLogKey as s, RESERVED_APPLICATION_KEYS as t, byMessageKey as u, eventTypesKey as v, messagesPrefix as w, keys_exports as x, globalAuditLogKey as y };
|
|
110
|
+
|
|
111
|
+
//# sourceMappingURL=keys-Byyj4quQ.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keys-Byyj4quQ.mjs","names":[],"sources":["../src/keys.ts"],"sourcesContent":["/**\n * Storage key layout for `@xtandard/webhooks`.\n *\n * Keys are namespaced by application so a single storage backend can host the\n * whole control plane. Pure string helpers — no dependencies.\n *\n * ```txt\n * whk/applications -> string[] index\n * whk/{app}/metadata -> Application\n * whk/event-types -> string[] index (global)\n * whk/event-types/{name} -> EventType\n * whk/{app}/endpoints -> string[] index\n * whk/{app}/endpoints/{id} -> Endpoint\n * whk/{app}/messages/{id} -> Message\n * whk/{app}/idempotency/{key} -> message id\n * whk/{app}/deliveries/{id} -> Delivery\n * whk/{app}/attempts/{deliveryId}/{n} -> DeliveryAttempt (n zero-padded)\n * whk/{app}/due/{dueAtMillis}~{deliveryId} -> DueEntry (13-digit zero-padded millis)\n * whk/{app}/by-message/{messageId}/{deliveryId} -> 1 (reverse index)\n * whk/{app}/by-endpoint/{endpointId}/{deliveryId} -> 1 (reverse index)\n * whk/{app}/audit-log -> AuditEntry[] (single ordered array)\n * ```\n *\n * The due index is the dispatcher's generic work queue: 13-digit zero-padded\n * milliseconds make lexicographic key order chronological, so a sorted\n * `getKeys(duePrefix(app))` scan yields deliveries in due order.\n *\n * @module\n */\n\n/** Root namespace segment for all keys. */\nexport const ROOT = \"whk\";\n\n/**\n * Application keys that would collide with global index keys under the shared\n * root. Rejected by validation.\n */\nexport const RESERVED_APPLICATION_KEYS = [\"applications\", \"event-types\", \"audit-log\"] as const;\n\n/** Global audit log (event-type actions, which are not application-scoped). */\nexport const globalAuditLogKey = () => `${ROOT}/audit-log`;\n\n/** Index key listing all known application keys. */\nexport const applicationsKey = () => `${ROOT}/applications`;\n\n/** Metadata for a single application. */\nexport const applicationMetaKey = (applicationKey: string) => `${ROOT}/${applicationKey}/metadata`;\n\n/** Index key listing all event type names (global catalog). */\nexport const eventTypesKey = () => `${ROOT}/event-types`;\n\n/** A single event type record. */\nexport const eventTypeKey = (name: string) => `${ROOT}/event-types/${name}`;\n\n/** Index key listing an application's endpoint ids. */\nexport const endpointsKey = (applicationKey: string) => `${ROOT}/${applicationKey}/endpoints`;\n\n/** A single endpoint record. */\nexport const endpointKey = (applicationKey: string, endpointId: string) =>\n `${ROOT}/${applicationKey}/endpoints/${endpointId}`;\n\n/** Prefix under which an application's messages live. */\nexport const messagesPrefix = (applicationKey: string) => `${ROOT}/${applicationKey}/messages/`;\n\n/** A single message record. */\nexport const messageKey = (applicationKey: string, messageId: string) =>\n `${messagesPrefix(applicationKey)}${messageId}`;\n\n/** Maps a caller-supplied idempotency key to the message id it produced. */\nexport const idempotencyKey = (applicationKey: string, key: string) =>\n `${ROOT}/${applicationKey}/idempotency/${key}`;\n\n/** Prefix under which an application's deliveries live. */\nexport const deliveriesPrefix = (applicationKey: string) => `${ROOT}/${applicationKey}/deliveries/`;\n\n/** A single delivery record. */\nexport const deliveryKey = (applicationKey: string, deliveryId: string) =>\n `${deliveriesPrefix(applicationKey)}${deliveryId}`;\n\n/** Prefix under which one delivery's attempts live. */\nexport const attemptsPrefix = (applicationKey: string, deliveryId: string) =>\n `${ROOT}/${applicationKey}/attempts/${deliveryId}/`;\n\n/**\n * A single attempt record. The attempt number is zero-padded to four digits so\n * lexicographic key order equals attempt order.\n */\nexport const attemptKey = (applicationKey: string, deliveryId: string, attemptNumber: number) =>\n `${attemptsPrefix(applicationKey, deliveryId)}${String(attemptNumber).padStart(4, \"0\")}`;\n\n/** Prefix of an application's due index (the dispatcher's work queue). */\nexport const duePrefix = (applicationKey: string) => `${ROOT}/${applicationKey}/due/`;\n\n/** Value stored at a due-index key. */\nexport interface DueEntry {\n app: string;\n deliveryId: string;\n}\n\n/**\n * A due-index entry: 13-digit zero-padded epoch milliseconds, then the delivery\n * id, `~`-separated. Sorting keys lexicographically sorts entries chronologically.\n */\nexport const dueKey = (applicationKey: string, dueAtMillis: number, deliveryId: string) =>\n `${duePrefix(applicationKey)}${String(dueAtMillis).padStart(13, \"0\")}~${deliveryId}`;\n\n/** Parse a due-index key back into its due-time and delivery id. */\nexport const parseDueKey = (key: string): { dueAtMillis: number; deliveryId: string } | null => {\n const last = lastSegment(key);\n const sep = last.indexOf(\"~\");\n if (sep === -1) return null;\n const millis = Number(last.slice(0, sep));\n const deliveryId = last.slice(sep + 1);\n if (!Number.isFinite(millis) || deliveryId.length === 0) return null;\n return { dueAtMillis: millis, deliveryId };\n};\n\n/** Prefix of the message → deliveries reverse index. */\nexport const byMessagePrefix = (applicationKey: string, messageId: string) =>\n `${ROOT}/${applicationKey}/by-message/${messageId}/`;\n\n/** One entry in the message → deliveries reverse index. */\nexport const byMessageKey = (applicationKey: string, messageId: string, deliveryId: string) =>\n `${byMessagePrefix(applicationKey, messageId)}${deliveryId}`;\n\n/** Prefix of the endpoint → deliveries reverse index. */\nexport const byEndpointPrefix = (applicationKey: string, endpointId: string) =>\n `${ROOT}/${applicationKey}/by-endpoint/${endpointId}/`;\n\n/** One entry in the endpoint → deliveries reverse index. */\nexport const byEndpointKey = (applicationKey: string, endpointId: string, deliveryId: string) =>\n `${byEndpointPrefix(applicationKey, endpointId)}${deliveryId}`;\n\n/** Append-only audit log for an application, stored as an ordered `AuditEntry[]`. */\nexport const auditLogKey = (applicationKey: string) => `${ROOT}/${applicationKey}/audit-log`;\n\n/** Prefix under which everything belonging to one application lives. */\nexport const applicationPrefix = (applicationKey: string) => `${ROOT}/${applicationKey}/`;\n\n/** Extract the trailing segment (endpoint id / message id / …) from a key. */\nexport const lastSegment = (key: string): string => {\n const parts = key.split(\"/\");\n return parts[parts.length - 1] ?? \"\";\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqCA,MAAa,4BAA4B;CAAC;CAAgB;CAAe;AAAW;;AAGpF,MAAa,0BAA0B;;AAGvC,MAAa,wBAAwB;;AAGrC,MAAa,sBAAsB,mBAA2B,OAAW,eAAe;;AAGxF,MAAa,sBAAsB;;AAGnC,MAAa,gBAAgB,SAAiB,mBAAuB;;AAGrE,MAAa,gBAAgB,mBAA2B,OAAW,eAAe;;AAGlF,MAAa,eAAe,gBAAwB,eAClD,OAAW,eAAe,aAAa;;AAGzC,MAAa,kBAAkB,mBAA2B,OAAW,eAAe;;AAGpF,MAAa,cAAc,gBAAwB,cACjD,GAAG,eAAe,cAAc,IAAI;;AAGtC,MAAa,kBAAkB,gBAAwB,QACrD,OAAW,eAAe,eAAe;;AAG3C,MAAa,oBAAoB,mBAA2B,OAAW,eAAe;;AAGtF,MAAa,eAAe,gBAAwB,eAClD,GAAG,iBAAiB,cAAc,IAAI;;AAGxC,MAAa,kBAAkB,gBAAwB,eACrD,OAAW,eAAe,YAAY,WAAW;;;;;AAMnD,MAAa,cAAc,gBAAwB,YAAoB,kBACrE,GAAG,eAAe,gBAAgB,UAAU,IAAI,OAAO,aAAa,EAAE,SAAS,GAAG,GAAG;;AAGvF,MAAa,aAAa,mBAA2B,OAAW,eAAe;;;;;AAY/E,MAAa,UAAU,gBAAwB,aAAqB,eAClE,GAAG,UAAU,cAAc,IAAI,OAAO,WAAW,EAAE,SAAS,IAAI,GAAG,EAAE,GAAG;;AAG1E,MAAa,eAAe,QAAoE;CAC9F,MAAM,OAAO,YAAY,GAAG;CAC5B,MAAM,MAAM,KAAK,QAAQ,GAAG;CAC5B,IAAI,QAAQ,IAAI,OAAO;CACvB,MAAM,SAAS,OAAO,KAAK,MAAM,GAAG,GAAG,CAAC;CACxC,MAAM,aAAa,KAAK,MAAM,MAAM,CAAC;CACrC,IAAI,CAAC,OAAO,SAAS,MAAM,KAAK,WAAW,WAAW,GAAG,OAAO;CAChE,OAAO;EAAE,aAAa;EAAQ;CAAW;AAC3C;;AAGA,MAAa,mBAAmB,gBAAwB,cACtD,OAAW,eAAe,cAAc,UAAU;;AAGpD,MAAa,gBAAgB,gBAAwB,WAAmB,eACtE,GAAG,gBAAgB,gBAAgB,SAAS,IAAI;;AAGlD,MAAa,oBAAoB,gBAAwB,eACvD,OAAW,eAAe,eAAe,WAAW;;AAGtD,MAAa,iBAAiB,gBAAwB,YAAoB,eACxE,GAAG,iBAAiB,gBAAgB,UAAU,IAAI;;AAGpD,MAAa,eAAe,mBAA2B,OAAW,eAAe;;AAGjF,MAAa,qBAAqB,mBAA2B,OAAW,eAAe;;AAGvF,MAAa,eAAe,QAAwB;CAClD,MAAM,QAAQ,IAAI,MAAM,GAAG;CAC3B,OAAO,MAAM,MAAM,SAAS,MAAM;AACpC"}
|