@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.
Files changed (279) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +315 -0
  3. package/bin/xtandard-webhooks.mjs +3 -0
  4. package/dist/basic-BIW3Rvuz.cjs +199 -0
  5. package/dist/basic-BIW3Rvuz.cjs.map +1 -0
  6. package/dist/basic-DKk0Xfuu.mjs +176 -0
  7. package/dist/basic-DKk0Xfuu.mjs.map +1 -0
  8. package/dist/chunk-D7D4PA-g.mjs +13 -0
  9. package/dist/cli.cjs +655 -0
  10. package/dist/cli.cjs.map +1 -0
  11. package/dist/cli.d.cts +42 -0
  12. package/dist/cli.d.mts +42 -0
  13. package/dist/cli.mjs +653 -0
  14. package/dist/cli.mjs.map +1 -0
  15. package/dist/contract-8h-Azxa5.d.cts +71 -0
  16. package/dist/contract-9XpcwcCn.mjs +22 -0
  17. package/dist/contract-9XpcwcCn.mjs.map +1 -0
  18. package/dist/contract-B2d5dNU3.cjs +33 -0
  19. package/dist/contract-B2d5dNU3.cjs.map +1 -0
  20. package/dist/contract-BEhDcd_5.mjs +28 -0
  21. package/dist/contract-BEhDcd_5.mjs.map +1 -0
  22. package/dist/contract-Bf1qguwt.cjs +57 -0
  23. package/dist/contract-Bf1qguwt.cjs.map +1 -0
  24. package/dist/contract-Bnb3fgRJ.d.cts +177 -0
  25. package/dist/contract-C2r2Xzwp.d.mts +46 -0
  26. package/dist/contract-CiPskNvS.d.cts +46 -0
  27. package/dist/contract-DhQ4JjGG.d.mts +71 -0
  28. package/dist/contract-T1kcZNdG.d.mts +177 -0
  29. package/dist/contract-lETlIuXo.d.cts +30 -0
  30. package/dist/contract-lETlIuXo.d.mts +30 -0
  31. package/dist/core-CMpnmI5Q.mjs +1605 -0
  32. package/dist/core-CMpnmI5Q.mjs.map +1 -0
  33. package/dist/core-DT4ppWh8.d.mts +502 -0
  34. package/dist/core-KJawHjFF.d.cts +502 -0
  35. package/dist/core-ZGhH6Vs2.cjs +1790 -0
  36. package/dist/core-ZGhH6Vs2.cjs.map +1 -0
  37. package/dist/core.cjs +8 -0
  38. package/dist/core.d.cts +2 -0
  39. package/dist/core.d.mts +2 -0
  40. package/dist/core.mjs +2 -0
  41. package/dist/create-fetch-handler-BIdk9P30.mjs +1724 -0
  42. package/dist/create-fetch-handler-BIdk9P30.mjs.map +1 -0
  43. package/dist/create-fetch-handler-CmooujQo.cjs +1771 -0
  44. package/dist/create-fetch-handler-CmooujQo.cjs.map +1 -0
  45. package/dist/create-fetch-handler-Dlkhustu.d.cts +162 -0
  46. package/dist/create-fetch-handler-jy3hy5nZ.d.mts +162 -0
  47. package/dist/dispatcher-B0xTEHt1.cjs +212 -0
  48. package/dist/dispatcher-B0xTEHt1.cjs.map +1 -0
  49. package/dist/dispatcher-Coubwrka.mjs +196 -0
  50. package/dist/dispatcher-Coubwrka.mjs.map +1 -0
  51. package/dist/entry-auth-basic.cjs +5 -0
  52. package/dist/entry-auth-basic.d.cts +83 -0
  53. package/dist/entry-auth-basic.d.mts +83 -0
  54. package/dist/entry-auth-basic.mjs +2 -0
  55. package/dist/entry-auth-delegated.cjs +28 -0
  56. package/dist/entry-auth-delegated.cjs.map +1 -0
  57. package/dist/entry-auth-delegated.d.cts +36 -0
  58. package/dist/entry-auth-delegated.d.mts +36 -0
  59. package/dist/entry-auth-delegated.mjs +27 -0
  60. package/dist/entry-auth-delegated.mjs.map +1 -0
  61. package/dist/entry-auth-none.cjs +4 -0
  62. package/dist/entry-auth-none.d.cts +25 -0
  63. package/dist/entry-auth-none.d.mts +25 -0
  64. package/dist/entry-auth-none.mjs +2 -0
  65. package/dist/entry-authorization-delegated.cjs +27 -0
  66. package/dist/entry-authorization-delegated.cjs.map +1 -0
  67. package/dist/entry-authorization-delegated.d.cts +31 -0
  68. package/dist/entry-authorization-delegated.d.mts +31 -0
  69. package/dist/entry-authorization-delegated.mjs +26 -0
  70. package/dist/entry-authorization-delegated.mjs.map +1 -0
  71. package/dist/entry-authorization-none.cjs +3 -0
  72. package/dist/entry-authorization-none.d.cts +18 -0
  73. package/dist/entry-authorization-none.d.mts +18 -0
  74. package/dist/entry-authorization-none.mjs +2 -0
  75. package/dist/entry-authorization-roles.cjs +6 -0
  76. package/dist/entry-authorization-roles.d.cts +65 -0
  77. package/dist/entry-authorization-roles.d.mts +65 -0
  78. package/dist/entry-authorization-roles.mjs +2 -0
  79. package/dist/entry-bun.cjs +24 -0
  80. package/dist/entry-bun.cjs.map +1 -0
  81. package/dist/entry-bun.d.cts +8 -0
  82. package/dist/entry-bun.d.mts +8 -0
  83. package/dist/entry-bun.mjs +23 -0
  84. package/dist/entry-bun.mjs.map +1 -0
  85. package/dist/entry-drizzle-mysql.cjs +20 -0
  86. package/dist/entry-drizzle-mysql.cjs.map +1 -0
  87. package/dist/entry-drizzle-mysql.d.cts +27 -0
  88. package/dist/entry-drizzle-mysql.d.mts +27 -0
  89. package/dist/entry-drizzle-mysql.mjs +19 -0
  90. package/dist/entry-drizzle-mysql.mjs.map +1 -0
  91. package/dist/entry-drizzle-pg.cjs +21 -0
  92. package/dist/entry-drizzle-pg.cjs.map +1 -0
  93. package/dist/entry-drizzle-pg.d.cts +26 -0
  94. package/dist/entry-drizzle-pg.d.mts +26 -0
  95. package/dist/entry-drizzle-pg.mjs +20 -0
  96. package/dist/entry-drizzle-pg.mjs.map +1 -0
  97. package/dist/entry-drizzle-sqlite.cjs +21 -0
  98. package/dist/entry-drizzle-sqlite.cjs.map +1 -0
  99. package/dist/entry-drizzle-sqlite.d.cts +23 -0
  100. package/dist/entry-drizzle-sqlite.d.mts +23 -0
  101. package/dist/entry-drizzle-sqlite.mjs +20 -0
  102. package/dist/entry-drizzle-sqlite.mjs.map +1 -0
  103. package/dist/entry-elysia.cjs +125 -0
  104. package/dist/entry-elysia.cjs.map +1 -0
  105. package/dist/entry-elysia.d.cts +1017 -0
  106. package/dist/entry-elysia.d.mts +1017 -0
  107. package/dist/entry-elysia.mjs +123 -0
  108. package/dist/entry-elysia.mjs.map +1 -0
  109. package/dist/entry-express.cjs +57 -0
  110. package/dist/entry-express.cjs.map +1 -0
  111. package/dist/entry-express.d.cts +15 -0
  112. package/dist/entry-express.d.mts +15 -0
  113. package/dist/entry-express.mjs +56 -0
  114. package/dist/entry-express.mjs.map +1 -0
  115. package/dist/entry-hono.cjs +35 -0
  116. package/dist/entry-hono.cjs.map +1 -0
  117. package/dist/entry-hono.d.cts +16 -0
  118. package/dist/entry-hono.d.mts +16 -0
  119. package/dist/entry-hono.mjs +34 -0
  120. package/dist/entry-hono.mjs.map +1 -0
  121. package/dist/entry-hooks-log.cjs +22 -0
  122. package/dist/entry-hooks-log.cjs.map +1 -0
  123. package/dist/entry-hooks-log.d.cts +23 -0
  124. package/dist/entry-hooks-log.d.mts +23 -0
  125. package/dist/entry-hooks-log.mjs +21 -0
  126. package/dist/entry-hooks-log.mjs.map +1 -0
  127. package/dist/entry-storage-cloudflare-kv.cjs +47 -0
  128. package/dist/entry-storage-cloudflare-kv.cjs.map +1 -0
  129. package/dist/entry-storage-cloudflare-kv.d.cts +42 -0
  130. package/dist/entry-storage-cloudflare-kv.d.mts +42 -0
  131. package/dist/entry-storage-cloudflare-kv.mjs +46 -0
  132. package/dist/entry-storage-cloudflare-kv.mjs.map +1 -0
  133. package/dist/entry-storage-drizzle.cjs +78 -0
  134. package/dist/entry-storage-drizzle.cjs.map +1 -0
  135. package/dist/entry-storage-drizzle.d.cts +30 -0
  136. package/dist/entry-storage-drizzle.d.mts +30 -0
  137. package/dist/entry-storage-drizzle.mjs +77 -0
  138. package/dist/entry-storage-drizzle.mjs.map +1 -0
  139. package/dist/entry-storage-file.cjs +4 -0
  140. package/dist/entry-storage-file.d.cts +30 -0
  141. package/dist/entry-storage-file.d.mts +30 -0
  142. package/dist/entry-storage-file.mjs +2 -0
  143. package/dist/entry-storage-libsql.cjs +3 -0
  144. package/dist/entry-storage-libsql.d.cts +48 -0
  145. package/dist/entry-storage-libsql.d.mts +48 -0
  146. package/dist/entry-storage-libsql.mjs +2 -0
  147. package/dist/entry-storage-memory.cjs +3 -0
  148. package/dist/entry-storage-memory.d.cts +2 -0
  149. package/dist/entry-storage-memory.d.mts +2 -0
  150. package/dist/entry-storage-memory.mjs +2 -0
  151. package/dist/entry-storage-mongodb.cjs +3 -0
  152. package/dist/entry-storage-mongodb.d.cts +55 -0
  153. package/dist/entry-storage-mongodb.d.mts +55 -0
  154. package/dist/entry-storage-mongodb.mjs +2 -0
  155. package/dist/entry-storage-postgres.cjs +3 -0
  156. package/dist/entry-storage-postgres.d.cts +62 -0
  157. package/dist/entry-storage-postgres.d.mts +62 -0
  158. package/dist/entry-storage-postgres.mjs +2 -0
  159. package/dist/entry-storage-redis.cjs +4 -0
  160. package/dist/entry-storage-redis.d.cts +77 -0
  161. package/dist/entry-storage-redis.d.mts +77 -0
  162. package/dist/entry-storage-redis.mjs +2 -0
  163. package/dist/entry-storage-sqlite.cjs +3 -0
  164. package/dist/entry-storage-sqlite.d.cts +36 -0
  165. package/dist/entry-storage-sqlite.d.mts +36 -0
  166. package/dist/entry-storage-sqlite.mjs +2 -0
  167. package/dist/entry-storage-unstorage.cjs +42 -0
  168. package/dist/entry-storage-unstorage.cjs.map +1 -0
  169. package/dist/entry-storage-unstorage.d.cts +29 -0
  170. package/dist/entry-storage-unstorage.d.mts +29 -0
  171. package/dist/entry-storage-unstorage.mjs +41 -0
  172. package/dist/entry-storage-unstorage.mjs.map +1 -0
  173. package/dist/file-COBYZA4Q.cjs +148 -0
  174. package/dist/file-COBYZA4Q.cjs.map +1 -0
  175. package/dist/file-fi02eFHk.mjs +131 -0
  176. package/dist/file-fi02eFHk.mjs.map +1 -0
  177. package/dist/index.cjs +123 -0
  178. package/dist/index.cjs.map +1 -0
  179. package/dist/index.d.cts +368 -0
  180. package/dist/index.d.mts +366 -0
  181. package/dist/index.mjs +61 -0
  182. package/dist/index.mjs.map +1 -0
  183. package/dist/keys-Byyj4quQ.mjs +111 -0
  184. package/dist/keys-Byyj4quQ.mjs.map +1 -0
  185. package/dist/keys-FiKpaVHX.cjs +302 -0
  186. package/dist/keys-FiKpaVHX.cjs.map +1 -0
  187. package/dist/libsql-bpVi0bXN.mjs +113 -0
  188. package/dist/libsql-bpVi0bXN.mjs.map +1 -0
  189. package/dist/libsql-pPJEo1e4.cjs +124 -0
  190. package/dist/libsql-pPJEo1e4.cjs.map +1 -0
  191. package/dist/memory-8Ef-PL5a.cjs +137 -0
  192. package/dist/memory-8Ef-PL5a.cjs.map +1 -0
  193. package/dist/memory-BMsSSwqn.mjs +127 -0
  194. package/dist/memory-BMsSSwqn.mjs.map +1 -0
  195. package/dist/memory-FnMJWCmB.d.cts +28 -0
  196. package/dist/memory-qIvANEs_.d.mts +28 -0
  197. package/dist/mongodb-Cy8yo0uk.cjs +108 -0
  198. package/dist/mongodb-Cy8yo0uk.cjs.map +1 -0
  199. package/dist/mongodb-Ddaq9mml.mjs +97 -0
  200. package/dist/mongodb-Ddaq9mml.mjs.map +1 -0
  201. package/dist/none-BnZtaGNJ.mjs +23 -0
  202. package/dist/none-BnZtaGNJ.mjs.map +1 -0
  203. package/dist/none-CAsxCOWN.cjs +49 -0
  204. package/dist/none-CAsxCOWN.cjs.map +1 -0
  205. package/dist/none-CZVrfnmF.cjs +33 -0
  206. package/dist/none-CZVrfnmF.cjs.map +1 -0
  207. package/dist/none-GhVIoh_s.mjs +33 -0
  208. package/dist/none-GhVIoh_s.mjs.map +1 -0
  209. package/dist/postgres-C8WbchFa.cjs +134 -0
  210. package/dist/postgres-C8WbchFa.cjs.map +1 -0
  211. package/dist/postgres-c3pAhmhr.mjs +123 -0
  212. package/dist/postgres-c3pAhmhr.mjs.map +1 -0
  213. package/dist/react.css +1 -0
  214. package/dist/react.js +31465 -0
  215. package/dist/receiver.cjs +43 -0
  216. package/dist/receiver.cjs.map +1 -0
  217. package/dist/receiver.d.cts +36 -0
  218. package/dist/receiver.d.mts +36 -0
  219. package/dist/receiver.mjs +40 -0
  220. package/dist/receiver.mjs.map +1 -0
  221. package/dist/redis-CFJkuSgB.cjs +270 -0
  222. package/dist/redis-CFJkuSgB.cjs.map +1 -0
  223. package/dist/redis-CvLi0KF7.mjs +254 -0
  224. package/dist/redis-CvLi0KF7.mjs.map +1 -0
  225. package/dist/roles-D0G9XqBq.cjs +128 -0
  226. package/dist/roles-D0G9XqBq.cjs.map +1 -0
  227. package/dist/roles-vp361lTk.mjs +99 -0
  228. package/dist/roles-vp361lTk.mjs.map +1 -0
  229. package/dist/schema-mo__wv4P.d.cts +233 -0
  230. package/dist/schema-mo__wv4P.d.mts +233 -0
  231. package/dist/schema.cjs +13 -0
  232. package/dist/schema.cjs.map +1 -0
  233. package/dist/schema.d.cts +2 -0
  234. package/dist/schema.d.mts +2 -0
  235. package/dist/schema.mjs +11 -0
  236. package/dist/schema.mjs.map +1 -0
  237. package/dist/signing.cjs +162 -0
  238. package/dist/signing.cjs.map +1 -0
  239. package/dist/signing.d.cts +73 -0
  240. package/dist/signing.d.mts +73 -0
  241. package/dist/signing.mjs +156 -0
  242. package/dist/signing.mjs.map +1 -0
  243. package/dist/sqlite-Cmqnrjes.mjs +67 -0
  244. package/dist/sqlite-Cmqnrjes.mjs.map +1 -0
  245. package/dist/sqlite-Dcufk0x3.cjs +78 -0
  246. package/dist/sqlite-Dcufk0x3.cjs.map +1 -0
  247. package/dist/table-Ce3Tzwqs.d.cts +11 -0
  248. package/dist/table-Ce3Tzwqs.d.mts +11 -0
  249. package/dist/testing.cjs +134 -0
  250. package/dist/testing.cjs.map +1 -0
  251. package/dist/testing.d.cts +80 -0
  252. package/dist/testing.d.mts +80 -0
  253. package/dist/testing.mjs +131 -0
  254. package/dist/testing.mjs.map +1 -0
  255. package/dist/types-react/react.d.ts +98 -0
  256. package/dist/types-react/schema.d.ts +229 -0
  257. package/dist/types-react/ui/App.d.ts +22 -0
  258. package/dist/types-react/ui/api.d.ts +97 -0
  259. package/dist/types-react/ui/components/JsonCodeEditor.d.ts +12 -0
  260. package/dist/types-react/ui/components/ThemeToggle.d.ts +2 -0
  261. package/dist/types-react/ui/components/Toast.d.ts +16 -0
  262. package/dist/types-react/ui/components/primitives.d.ts +50 -0
  263. package/dist/types-react/ui/components/ui-bits.d.ts +22 -0
  264. package/dist/types-react/ui/components/webhook-bits.d.ts +51 -0
  265. package/dist/types-react/ui/lib/format.d.ts +39 -0
  266. package/dist/types-react/ui/lib/nav-guard.d.ts +20 -0
  267. package/dist/types-react/ui/lib/utils.d.ts +3 -0
  268. package/dist/types-react/ui/theme.d.ts +12 -0
  269. package/dist/types-react/ui/types.d.ts +80 -0
  270. package/dist/types-react/ui/views/AuditView.d.ts +6 -0
  271. package/dist/types-react/ui/views/DeliveriesView.d.ts +12 -0
  272. package/dist/types-react/ui/views/EndpointsView.d.ts +11 -0
  273. package/dist/types-react/ui/views/EventTypesView.d.ts +11 -0
  274. package/dist/types-react/ui/views/MessagesView.d.ts +10 -0
  275. package/dist/types-react/ui/views/OverviewView.d.ts +12 -0
  276. package/dist/ui/assets/index-B0eoQX2U.css +1 -0
  277. package/dist/ui/assets/index-S5t_CLOe.js +209 -0
  278. package/dist/ui/index.html +14 -0
  279. package/package.json +487 -0
@@ -0,0 +1,98 @@
1
+ /**
2
+ * `@xtandard/webhooks/react` — embed the dashboard as a React component in your
3
+ * own app (advanced mode). Most users mount the bundled SPA via a framework
4
+ * adapter and never touch React; this is for teams that want the panel inside an
5
+ * existing React shell.
6
+ *
7
+ * Two components share the same SPA internals:
8
+ *
9
+ * ```tsx
10
+ * import { WebhooksDashboard, WebhooksPortal } from "@xtandard/webhooks/react";
11
+ * import "@xtandard/webhooks/react/styles.css";
12
+ *
13
+ * // Full admin — point it at wherever the panel API is mounted:
14
+ * <WebhooksDashboard apiBaseUrl="/webhooks" />
15
+ *
16
+ * // Customer-scoped portal — pass a portal token minted server-side:
17
+ * <WebhooksPortal apiBaseUrl="/webhooks" token={portalToken} />
18
+ * ```
19
+ *
20
+ * `react` and `react-dom` are peer dependencies in this mode. The components are
21
+ * self-contained otherwise (TanStack Query and styles are bundled).
22
+ *
23
+ * @module
24
+ */
25
+ import React from "react";
26
+ import type { BaseLocationHook } from "wouter";
27
+ import { QueryClient } from "@tanstack/react-query";
28
+ import { type FetchLike } from "./ui/api.ts";
29
+ import "./ui/styles.css";
30
+ /** Props for {@link WebhooksDashboard}. */
31
+ export interface WebhooksDashboardProps {
32
+ /**
33
+ * Base URL where the panel API + `/config` are mounted (e.g. `"/webhooks"` or
34
+ * `"https://admin.example.com/webhooks"`). Defaults to `""` (same origin, relative).
35
+ */
36
+ apiBaseUrl?: string;
37
+ /**
38
+ * `credentials` mode for API requests. Defaults to `"same-origin"`. For a
39
+ * **cross-origin** embed (panel served from a different origin than this app)
40
+ * with cookie-based auth, set `"include"` — and enable `cors` on the panel
41
+ * server so it returns `Access-Control-Allow-Credentials`.
42
+ */
43
+ credentials?: RequestCredentials;
44
+ /**
45
+ * Custom `fetch` for API requests — inject a bearer token / extra headers, or
46
+ * instrument calls. Defaults to the global `fetch`.
47
+ */
48
+ fetch?: FetchLike;
49
+ /** Bring your own QueryClient; one is created if omitted. */
50
+ queryClient?: QueryClient;
51
+ /** Control theme handling. `"auto"` (default) initializes the system/light/dark switcher. */
52
+ theme?: "auto" | "inherit";
53
+ /** Extra className on the dashboard root wrapper. */
54
+ className?: string;
55
+ /**
56
+ * How the dashboard routes between views/records.
57
+ * - `"hash"` (default) — routes in `location.hash`; never touches the host app's
58
+ * router or pathname. Safest when mounted inside another app.
59
+ * - `"browser"` — real history paths (clean URLs). Requires the host to serve the
60
+ * panel's `index.html` as a catch-all under `routerBase`, else refresh 404s.
61
+ * - `"memory"` — in-memory only; no URL coupling at all.
62
+ * - a custom wouter location hook for full control.
63
+ */
64
+ routing?: "hash" | "browser" | "memory" | BaseLocationHook;
65
+ /** Base path the panel is mounted at, used by `routing: "browser"` (e.g. `"/admin/webhooks"`). */
66
+ routerBase?: string;
67
+ /** Navbar logo image URL (replaces the title wordmark). Overrides server `/config`. */
68
+ logoUrl?: string;
69
+ /**
70
+ * Pre-select an application on first render, overriding the default (first
71
+ * application) — e.g. an admin panel embedding one dashboard per tenant. The
72
+ * URL still wins when it names one (`?app=`), so deep links and the in-app
73
+ * switcher behave normally afterwards.
74
+ */
75
+ initialApplicationKey?: string;
76
+ }
77
+ /** Props for {@link WebhooksPortal}. */
78
+ export interface WebhooksPortalProps extends Omit<WebhooksDashboardProps, "initialApplicationKey"> {
79
+ /**
80
+ * Portal token (`whpt_…`) minted server-side via `createPortalToken()`. Sent as
81
+ * an `Authorization: Bearer` credential on every API request; `/config` then
82
+ * reports `portal: true` and the shell renders the reduced, application-pinned
83
+ * portal chrome.
84
+ */
85
+ token: string;
86
+ }
87
+ /** The full webhooks admin dashboard as an embeddable React component. */
88
+ export declare function WebhooksDashboard({ apiBaseUrl, credentials, fetch: fetchImpl, queryClient, theme, className, routing, routerBase, logoUrl, initialApplicationKey, }: WebhooksDashboardProps): React.ReactElement;
89
+ /**
90
+ * The customer-scoped consumer portal as an embeddable React component. Renders
91
+ * the same shell as {@link WebhooksDashboard} with the portal token attached as
92
+ * a Bearer credential; the server scopes every request to the token's
93
+ * application and `/config` flips the shell into the reduced portal chrome.
94
+ */
95
+ export declare function WebhooksPortal({ token, ...rest }: WebhooksPortalProps): React.ReactElement;
96
+ export { setApiBase, setApiToken, type FetchLike } from "./ui/api.ts";
97
+ export { setThemePref, getThemePref, type ThemePref } from "./ui/theme.ts";
98
+ export default WebhooksDashboard;
@@ -0,0 +1,229 @@
1
+ /**
2
+ * Core type definitions for `@xtandard/webhooks`.
3
+ *
4
+ * This module is **types only** — no runtime code beyond tiny guards and the
5
+ * schema-version constant, no dependencies. It is safe to import from the
6
+ * publish hot path and the receiver. Runtime validation (which pulls in
7
+ * `valibot`) lives in {@link ./validation}.
8
+ *
9
+ * @module
10
+ */
11
+ /** JSON-serializable value, used for payloads, metadata, and event-type schemas. */
12
+ export type JsonValue = string | number | boolean | null | JsonValue[] | {
13
+ [key: string]: JsonValue;
14
+ };
15
+ /**
16
+ * A duration for configuration options: either milliseconds as a number, or a
17
+ * human-friendly string such as `"5s"`, `"30m"`, `"2h"`, `"5d"`. Parsed by
18
+ * {@link ./duration.durationToMs}.
19
+ */
20
+ export type WebhookDuration = number | `${number}${"ms" | "s" | "m" | "h" | "d"}`;
21
+ /** Identity captured on audit records and passed by callers of audited mutations. */
22
+ export interface Actor {
23
+ id: string;
24
+ email?: string;
25
+ name?: string;
26
+ }
27
+ /** Current schema version for stored records. */
28
+ export declare const SCHEMA_VERSION: 1;
29
+ /**
30
+ * An application is the unit of tenancy — typically one of *your* customers.
31
+ * Endpoints, messages, and deliveries are all scoped to an application, and a
32
+ * portal token grants access to exactly one application.
33
+ */
34
+ export interface Application {
35
+ /** Unique key. Allowed characters: `a-z A-Z 0-9 . _ -`. */
36
+ key: string;
37
+ /** Optional human-friendly name shown in the UI. */
38
+ name?: string;
39
+ /** Arbitrary host-defined metadata. */
40
+ metadata?: JsonValue;
41
+ /** ISO-8601 creation timestamp, server-stamped. */
42
+ createdAt?: string;
43
+ /** ISO-8601 last-update timestamp, server-stamped. */
44
+ updatedAt?: string;
45
+ }
46
+ /**
47
+ * A named kind of event in the **global** catalog (shared by all applications),
48
+ * e.g. `"invoice.paid"`. Endpoints subscribe to event types by name.
49
+ */
50
+ export interface EventType {
51
+ /** Unique dot-delimited name, e.g. `"invoice.paid"`. Allowed: `a-z A-Z 0-9 . _ -`. */
52
+ name: string;
53
+ /** Optional description shown in the UI and the public catalog. */
54
+ description?: string;
55
+ /** Optional UI grouping label (e.g. `"Billing"`). Purely presentational. */
56
+ groupName?: string;
57
+ /**
58
+ * Optional JSON Schema describing the payload. Documentation by default;
59
+ * enforced only when the core is configured with a `payloadValidator`.
60
+ */
61
+ schema?: JsonValue;
62
+ /** Deprecated event types are flagged in the UI but still deliverable. */
63
+ deprecated?: boolean;
64
+ /** ISO-8601 creation timestamp, server-stamped. */
65
+ createdAt?: string;
66
+ /** ISO-8601 last-update timestamp, server-stamped. */
67
+ updatedAt?: string;
68
+ }
69
+ /**
70
+ * A signing secret attached to an endpoint. Secrets are `whsec_` + base64 per
71
+ * the Standard Webhooks spec. Rotation keeps the previous secret verifiable
72
+ * until `expiresAt` (the grace window); expired secrets are pruned lazily.
73
+ */
74
+ export interface EndpointSecret {
75
+ /** `"whsec_"` + base64-encoded random key material. */
76
+ secret: string;
77
+ /** ISO-8601 creation timestamp. */
78
+ createdAt: string;
79
+ /** Set when superseded by rotation; the secret stops signing after this instant. */
80
+ expiresAt?: string;
81
+ }
82
+ /** Why an endpoint is disabled: by an operator, or by the auto-disable policy. */
83
+ export type EndpointDisabledReason = "manual" | "auto";
84
+ /**
85
+ * A customer-registered URL that receives deliveries for one application.
86
+ */
87
+ export interface Endpoint {
88
+ /** `"ep_"` + 22-char base62, server-generated. */
89
+ id: string;
90
+ /** Destination URL. `https` is enforced by default (`allowInsecureUrls` for dev). */
91
+ url: string;
92
+ /** Optional description shown in the UI. */
93
+ description?: string;
94
+ /**
95
+ * Event type names this endpoint subscribes to. Empty or absent = receives
96
+ * **all** event types.
97
+ */
98
+ eventTypes?: string[];
99
+ /** Disabled endpoints receive no deliveries (pending ones are held, not failed). */
100
+ disabled?: boolean;
101
+ /** Who/what disabled it — an operator (`"manual"`) or the failure policy (`"auto"`). */
102
+ disabledReason?: EndpointDisabledReason;
103
+ /** Static extra headers merged into every delivery request. */
104
+ headers?: Record<string, string>;
105
+ /** Signing secrets. `[0]` is current; extras are still-verifiable rotation grace. */
106
+ secrets: EndpointSecret[];
107
+ /** Arbitrary host-defined metadata. */
108
+ metadata?: JsonValue;
109
+ /** ISO-8601 creation timestamp, server-stamped. */
110
+ createdAt?: string;
111
+ /** ISO-8601 last-update timestamp, server-stamped. */
112
+ updatedAt?: string;
113
+ /**
114
+ * Start of the current unbroken failure streak (any successful delivery
115
+ * clears it). Drives the auto-disable policy.
116
+ */
117
+ firstFailingAt?: string | null;
118
+ }
119
+ /**
120
+ * A published event for one application. The message is the receiver-facing
121
+ * unit: its id is the `webhook-id` header (stable across retries — receivers
122
+ * dedupe on it), and its payload is the `data` of the wire envelope.
123
+ */
124
+ export interface Message {
125
+ /** `"msg_"` + 22-char base62, server-generated. Sent as `webhook-id`. */
126
+ id: string;
127
+ /** Event type name (usually from the global catalog). */
128
+ eventType: string;
129
+ /** The `data` of the wire envelope. */
130
+ payload: JsonValue;
131
+ /** ISO-8601 event-occurred time (defaults to publish time). Sent in the envelope. */
132
+ timestamp: string;
133
+ /** Optional caller-supplied dedupe key; same key within retention returns the same message. */
134
+ idempotencyKey?: string;
135
+ /**
136
+ * The wire envelope serialized **once at publish time** so the signed bytes
137
+ * are identical across every retry of every delivery.
138
+ */
139
+ envelope: string;
140
+ /** ISO-8601 creation timestamp, server-stamped. */
141
+ createdAt: string;
142
+ }
143
+ /**
144
+ * Delivery lifecycle. `"failed"` means the retry schedule is exhausted — the
145
+ * dead-letter state. A manual retry moves a failed delivery back to `"pending"`.
146
+ */
147
+ export type DeliveryStatus = "pending" | "delivering" | "succeeded" | "failed";
148
+ /** Guard: has this delivery reached a terminal state? */
149
+ export declare function isTerminalDeliveryStatus(status: DeliveryStatus): boolean;
150
+ /**
151
+ * One message × one endpoint. Created at publish time (fan-out) and driven to
152
+ * a terminal state by the dispatcher.
153
+ */
154
+ export interface Delivery {
155
+ /** `"dlv_"` + 22-char base62, server-generated. */
156
+ id: string;
157
+ /** The application this delivery belongs to (deliveries cross the dispatcher denormalized). */
158
+ applicationKey: string;
159
+ messageId: string;
160
+ endpointId: string;
161
+ status: DeliveryStatus;
162
+ /** Number of attempts made so far. */
163
+ attemptCount: number;
164
+ /** When the next attempt is due. `null` once terminal. */
165
+ nextAttemptAt?: string | null;
166
+ /** Dispatcher claim expiry; an expired lease makes the delivery reclaimable. */
167
+ leaseUntil?: string | null;
168
+ /**
169
+ * How the *next* attempt should be recorded: set to `"manual"` by
170
+ * retry/recover so the resulting attempt is attributed correctly, then
171
+ * cleared. Absent = `"schedule"`.
172
+ */
173
+ pendingTrigger?: AttemptTrigger;
174
+ /** ISO-8601 creation timestamp. */
175
+ createdAt: string;
176
+ /** ISO-8601 last-transition timestamp. */
177
+ updatedAt: string;
178
+ }
179
+ /** What initiated a delivery attempt. */
180
+ export type AttemptTrigger = "schedule" | "manual" | "test";
181
+ /** The recorded outcome of a single HTTP delivery attempt. */
182
+ export interface DeliveryAttempt {
183
+ /** `"atp_"` + 22-char base62, server-generated. */
184
+ id: string;
185
+ deliveryId: string;
186
+ /** 1-based attempt ordinal within the delivery. */
187
+ attemptNumber: number;
188
+ /** ISO-8601 timestamp of the attempt. */
189
+ at: string;
190
+ /** Wall-clock duration of the HTTP round trip. */
191
+ durationMs: number;
192
+ /** True when the receiver answered 2xx. */
193
+ ok: boolean;
194
+ /** HTTP status code; absent on network error or timeout. */
195
+ httpStatus?: number;
196
+ /** Truncated error message on network error/timeout. */
197
+ error?: string;
198
+ /** Truncated response body (default cap 4096 chars). */
199
+ responseBody?: string;
200
+ /** What initiated this attempt. */
201
+ trigger: AttemptTrigger;
202
+ }
203
+ /** Actions recorded in the control-plane audit log. */
204
+ export type AuditAction = "application.create" | "application.update" | "application.delete" | "event-type.create" | "event-type.update" | "event-type.delete" | "endpoint.create" | "endpoint.update" | "endpoint.delete" | "endpoint.rotate-secret" | "endpoint.disable" | "endpoint.enable" | "delivery.retry" | "endpoint.recover";
205
+ /**
206
+ * A single control-plane audit record. Publishes are deliberately *not*
207
+ * audited — they are data-plane traffic, and the message log is their record.
208
+ */
209
+ export interface AuditEntry {
210
+ action: AuditAction;
211
+ /** ISO-8601 timestamp. */
212
+ at: string;
213
+ by?: Actor | null;
214
+ /** The application the action was scoped to (absent for global event types). */
215
+ applicationKey?: string;
216
+ /** Endpoint id / event type name / delivery id the action targeted. */
217
+ subjectId?: string;
218
+ message?: string;
219
+ }
220
+ /**
221
+ * The wire envelope receivers get — the Standard Webhooks recommended shape.
222
+ * `type` is the event type name, `timestamp` the event-occurred time, and
223
+ * `data` the published payload.
224
+ */
225
+ export interface WebhookEnvelope {
226
+ type: string;
227
+ timestamp: string;
228
+ data: JsonValue;
229
+ }
@@ -0,0 +1,22 @@
1
+ import React from "react";
2
+ import { type BaseLocationHook } from "wouter";
3
+ import type { WebhooksConfig } from "./types.ts";
4
+ declare global {
5
+ interface Window {
6
+ __WEBHOOKS_CONFIG__?: WebhooksConfig;
7
+ }
8
+ }
9
+ /**
10
+ * The dashboard, wrapped in a wouter {@link Router}. `locationHook` + `base` make
11
+ * routing pluggable: the bundled SPA uses browser history (clean paths, served by
12
+ * the handler's SPA catch-all), while the embeddable defaults to hash routing so
13
+ * it never touches the host app's router. Pass a custom hook (e.g. memory) to override.
14
+ */
15
+ export declare function App({ locationHook, base, logoUrl, initialApplicationKey, }: {
16
+ locationHook?: BaseLocationHook;
17
+ base?: string;
18
+ /** Override the navbar logo image (otherwise taken from server `/config`). */
19
+ logoUrl?: string;
20
+ /** Pre-select an application when the URL doesn't name one (embed hosts). */
21
+ initialApplicationKey?: string;
22
+ }): React.ReactElement;
@@ -0,0 +1,97 @@
1
+ import type { Application, AuditEntry, Delivery, DeliveryDetail, DeliveryStatus, EndpointSecret, EndpointSummary, EventType, JsonValue, Message, MessageDetail, PublishResponse, RecoverResponse, SendExampleResponse, SignedRequest, WebhooksConfig } from "./types.ts";
2
+ /** The subset of `fetch` the client uses (the global `fetch` satisfies it). */
3
+ export type FetchLike = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
4
+ /** Options for {@link setApiBase}. */
5
+ export interface ApiClientOptions {
6
+ /** `credentials` mode for requests. Use `"include"` for cross-origin cookie auth. */
7
+ credentials?: RequestCredentials;
8
+ /** Custom fetch implementation (bearer tokens, extra headers, instrumentation). */
9
+ fetch?: FetchLike;
10
+ }
11
+ /** Point the API client at a base URL (used by the `@xtandard/webhooks/react` export). */
12
+ export declare function setApiBase(base: string, opts?: ApiClientOptions): void;
13
+ /** Attach a portal token (`whpt_…`) as a Bearer credential on every request. */
14
+ export declare function setApiToken(token: string | null): void;
15
+ export declare function getConfig(): Promise<WebhooksConfig>;
16
+ export declare function listApplications(): Promise<Application[]>;
17
+ export declare function createApplication(input: {
18
+ key: string;
19
+ name?: string;
20
+ metadata?: JsonValue;
21
+ }): Promise<Application>;
22
+ export declare function updateApplication(app: string, patch: {
23
+ name?: string;
24
+ metadata?: JsonValue;
25
+ }): Promise<Application>;
26
+ export declare function deleteApplication(app: string): Promise<{
27
+ ok: boolean;
28
+ }>;
29
+ export declare function listEventTypes(): Promise<EventType[]>;
30
+ export declare function upsertEventType(input: EventType): Promise<EventType>;
31
+ export declare function updateEventType(name: string, input: EventType): Promise<EventType>;
32
+ export declare function deleteEventType(name: string): Promise<{
33
+ ok: boolean;
34
+ }>;
35
+ export interface EndpointInput {
36
+ url: string;
37
+ description?: string;
38
+ eventTypes?: string[];
39
+ headers?: Record<string, string>;
40
+ metadata?: JsonValue;
41
+ disabled?: boolean;
42
+ }
43
+ export declare function listEndpoints(app: string): Promise<EndpointSummary[]>;
44
+ export declare function getEndpoint(app: string, id: string): Promise<EndpointSummary>;
45
+ /** The one response carrying `secrets` — capture the signing secret now. */
46
+ export declare function createEndpoint(app: string, input: EndpointInput): Promise<EndpointSummary & {
47
+ secrets: EndpointSecret[];
48
+ }>;
49
+ export declare function updateEndpoint(app: string, id: string, patch: Partial<Omit<EndpointInput, "disabled">>): Promise<EndpointSummary>;
50
+ export declare function deleteEndpoint(app: string, id: string): Promise<{
51
+ ok: boolean;
52
+ }>;
53
+ export declare function getEndpointSecrets(app: string, id: string): Promise<EndpointSecret[]>;
54
+ /** Rotation mints a secret the caller must capture — the response includes `secrets`. */
55
+ export declare function rotateEndpointSecret(app: string, id: string): Promise<EndpointSummary & {
56
+ secrets: EndpointSecret[];
57
+ }>;
58
+ export declare function enableEndpoint(app: string, id: string): Promise<EndpointSummary>;
59
+ export declare function disableEndpoint(app: string, id: string): Promise<EndpointSummary>;
60
+ /** Fire a one-off signed test delivery through the real wire path. */
61
+ export declare function sendExample(app: string, id: string, input: {
62
+ eventType: string;
63
+ payload?: JsonValue;
64
+ }): Promise<SendExampleResponse>;
65
+ /** Re-queue every failed delivery for the endpoint created at/after `since`. */
66
+ export declare function recoverEndpoint(app: string, id: string, input: {
67
+ since: string;
68
+ }): Promise<RecoverResponse>;
69
+ export interface ListMessagesFilters {
70
+ eventType?: string;
71
+ before?: string;
72
+ limit?: number;
73
+ }
74
+ export declare function listMessages(app: string, filters?: ListMessagesFilters): Promise<Message[]>;
75
+ export declare function getMessage(app: string, id: string): Promise<MessageDetail>;
76
+ export declare function publishMessage(app: string, input: {
77
+ eventType: string;
78
+ payload: JsonValue;
79
+ timestamp?: string;
80
+ idempotencyKey?: string;
81
+ }): Promise<PublishResponse>;
82
+ export interface ListDeliveriesFilters {
83
+ status?: DeliveryStatus;
84
+ endpoint?: string;
85
+ before?: string;
86
+ limit?: number;
87
+ }
88
+ export declare function listDeliveries(app: string, filters?: ListDeliveriesFilters): Promise<Delivery[]>;
89
+ export declare function getDelivery(app: string, id: string): Promise<DeliveryDetail>;
90
+ /**
91
+ * The exact signed HTTP request this delivery sends. The signature and
92
+ * `webhook-timestamp` are computed live (each attempt re-signs).
93
+ */
94
+ export declare function getDeliveryRequest(app: string, id: string): Promise<SignedRequest>;
95
+ /** Re-queue a dead-lettered delivery (`failed` → `pending`, due immediately). */
96
+ export declare function retryDelivery(app: string, id: string): Promise<Delivery>;
97
+ export declare function listAudit(app: string): Promise<AuditEntry[]>;
@@ -0,0 +1,12 @@
1
+ import React from "react";
2
+ /**
3
+ * A small controlled CodeMirror 6 editor for JSON. Syntax highlighting + line
4
+ * numbers + history; no autocomplete/lint (kept light). Reports raw text via
5
+ * `onChange`; the parent owns parsing/validation.
6
+ */
7
+ export declare function JsonCodeEditor({ value, onChange, readOnly, placeholderText, }: {
8
+ value: string;
9
+ onChange: (text: string) => void;
10
+ readOnly?: boolean;
11
+ placeholderText?: string;
12
+ }): React.JSX.Element;
@@ -0,0 +1,2 @@
1
+ import React from "react";
2
+ export declare function ThemeToggle(): React.JSX.Element;
@@ -0,0 +1,16 @@
1
+ import React from "react";
2
+ export type ToastKind = "success" | "error" | "info" | "warning";
3
+ export interface Toast {
4
+ id: string;
5
+ kind: ToastKind;
6
+ message: string;
7
+ detail?: string;
8
+ }
9
+ interface ToastContextValue {
10
+ add: (kind: ToastKind, message: string, detail?: string) => void;
11
+ }
12
+ export declare function useToast(): ToastContextValue;
13
+ export declare function ToastProvider({ children }: {
14
+ children: React.ReactNode;
15
+ }): React.JSX.Element;
16
+ export {};
@@ -0,0 +1,50 @@
1
+ import * as React from "react";
2
+ export declare function ToggleSwitch({ checked, onCheckedChange, disabled, "aria-label": ariaLabel, }: {
3
+ checked: boolean;
4
+ onCheckedChange: (checked: boolean) => void;
5
+ disabled?: boolean;
6
+ "aria-label"?: string;
7
+ }): React.JSX.Element;
8
+ export interface SegmentOption<T extends string> {
9
+ value: T;
10
+ label: React.ReactNode;
11
+ "aria-label"?: string;
12
+ }
13
+ export declare function Segmented<T extends string>({ value, onValueChange, options, size, }: {
14
+ value: T;
15
+ onValueChange: (value: T) => void;
16
+ options: SegmentOption<T>[];
17
+ size?: "sm" | "md";
18
+ }): React.JSX.Element;
19
+ export interface DropdownOption {
20
+ value: string;
21
+ label: string;
22
+ description?: string;
23
+ }
24
+ export declare function Dropdown({ value, onValueChange, options, placeholder, disabled, className, "aria-label": ariaLabel, }: {
25
+ value: string;
26
+ onValueChange: (value: string) => void;
27
+ options: DropdownOption[];
28
+ placeholder?: string;
29
+ disabled?: boolean;
30
+ className?: string;
31
+ "aria-label"?: string;
32
+ }): React.JSX.Element;
33
+ /**
34
+ * A single-select combobox that lets you pick an existing option **or type a new
35
+ * one** — when the typed text matches nothing, a "Create …" item appears and
36
+ * selecting it calls {@link onCreate}. Used for the project/environment switchers.
37
+ */
38
+ export declare function CreatableCombobox({ value, options, onSelect, onCreate, placeholder, createLabel, disabled, className, "aria-label": ariaLabel, }: {
39
+ value: string;
40
+ options: string[];
41
+ onSelect: (value: string) => void;
42
+ onCreate: (value: string) => void;
43
+ placeholder?: string;
44
+ createLabel?: (query: string) => string;
45
+ disabled?: boolean;
46
+ className?: string;
47
+ "aria-label"?: string;
48
+ }): React.JSX.Element;
49
+ export declare const TextInput: React.ForwardRefExoticComponent<React.InputHTMLAttributes<HTMLInputElement> & React.RefAttributes<HTMLInputElement>>;
50
+ export declare const TextArea: React.ForwardRefExoticComponent<React.TextareaHTMLAttributes<HTMLTextAreaElement> & React.RefAttributes<HTMLTextAreaElement>>;
@@ -0,0 +1,22 @@
1
+ import * as React from "react";
2
+ type ButtonVariant = "primary" | "secondary" | "ghost" | "danger";
3
+ type ButtonSize = "sm" | "md" | "icon";
4
+ interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
5
+ variant?: ButtonVariant;
6
+ size?: ButtonSize;
7
+ loading?: boolean;
8
+ icon?: React.ReactNode;
9
+ }
10
+ export declare const Button: React.ForwardRefExoticComponent<ButtonProps & React.RefAttributes<HTMLButtonElement>>;
11
+ export declare function Badge({ className, children }: {
12
+ className?: string;
13
+ children: React.ReactNode;
14
+ }): React.JSX.Element;
15
+ export declare function Avatar({ initials, className }: {
16
+ initials: string;
17
+ className?: string;
18
+ }): React.JSX.Element;
19
+ export declare function Kbd({ children }: {
20
+ children: React.ReactNode;
21
+ }): React.JSX.Element;
22
+ export {};
@@ -0,0 +1,51 @@
1
+ import * as React from "react";
2
+ import type { DeliveryStatus } from "../types.ts";
3
+ export declare function Spinner({ className }: {
4
+ className?: string;
5
+ }): React.JSX.Element;
6
+ export declare function LoadingRow({ label }: {
7
+ label: string;
8
+ }): React.JSX.Element;
9
+ export declare function DeliveryStatusBadge({ status }: {
10
+ status: DeliveryStatus;
11
+ }): React.JSX.Element;
12
+ /** Keys/ids render in JetBrains Mono per the design system. */
13
+ export declare function Mono({ className, children }: {
14
+ className?: string;
15
+ children: React.ReactNode;
16
+ }): React.JSX.Element;
17
+ export declare function PageHeader({ title, description, actions, }: {
18
+ title: string;
19
+ description?: React.ReactNode;
20
+ actions?: React.ReactNode;
21
+ }): React.JSX.Element;
22
+ export declare function SectionCard({ title, description, children, className, }: {
23
+ title: string;
24
+ description?: React.ReactNode;
25
+ children: React.ReactNode;
26
+ className?: string;
27
+ }): React.JSX.Element;
28
+ export declare function ModalDialog({ open, onClose, title, children, footer, wide, }: {
29
+ open: boolean;
30
+ onClose: () => void;
31
+ title: React.ReactNode;
32
+ children: React.ReactNode;
33
+ footer?: React.ReactNode;
34
+ wide?: boolean;
35
+ }): React.JSX.Element;
36
+ export declare function ConfirmDialog({ open, onClose, onConfirm, title, confirmLabel, danger, loading, children, confirmDisabled, }: {
37
+ open: boolean;
38
+ onClose: () => void;
39
+ onConfirm: () => void;
40
+ title: React.ReactNode;
41
+ confirmLabel: string;
42
+ danger?: boolean;
43
+ loading?: boolean;
44
+ children: React.ReactNode;
45
+ confirmDisabled?: boolean;
46
+ }): React.JSX.Element;
47
+ export declare function EmptyCard({ children }: {
48
+ children: React.ReactNode;
49
+ }): React.JSX.Element;
50
+ /** Shown by app-scoped views when no application exists/is selected yet. */
51
+ export declare function NoApplication(): React.JSX.Element;
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Pure UI helpers — no React, no DOM. Unit-tested in `test/ui-lib.test.ts`
3
+ * (visual behavior is covered by the Playwright e2e suite instead).
4
+ *
5
+ * @module
6
+ */
7
+ import type { DeliveryStatus, EventType } from "../types.ts";
8
+ /** Absolute date-time, locale-aware. Returns `"—"` for missing/invalid input. */
9
+ export declare function formatDateTime(iso: string | undefined | null): string;
10
+ /**
11
+ * Compact relative time: `"just now"`, `"5m ago"`, `"in 2h"`, `"3d ago"`.
12
+ * `now` is injectable for tests; defaults to the current time.
13
+ */
14
+ export declare function relativeTime(iso: string | undefined | null, now?: number): string;
15
+ /** Attempt duration: `"87 ms"` below a second, `"1.25 s"` above. */
16
+ export declare function formatDurationMs(ms: number | undefined | null): string;
17
+ /** Human label for a delivery status (`failed` reads as the dead-letter state). */
18
+ export declare function deliveryStatusLabel(status: DeliveryStatus): string;
19
+ /** Tailwind classes for a delivery-status badge (flags badge tone conventions). */
20
+ export declare function deliveryStatusTone(status: DeliveryStatus): string;
21
+ /** Truncate with an ellipsis; never longer than `max` (including the ellipsis). */
22
+ export declare function truncate(text: string, max: number): string;
23
+ /**
24
+ * Append the app-scoped query string to an in-app path, preserving existing
25
+ * params on the path. `""` app (portal mode) leaves the path untouched.
26
+ */
27
+ export declare function withAppQuery(path: string, app: string): string;
28
+ /**
29
+ * Group an event-type catalog by `groupName`, ungrouped last. Groups sorted by
30
+ * name; entries keep catalog (name) order within a group.
31
+ */
32
+ export declare function groupEventTypes(catalog: EventType[]): {
33
+ group: string;
34
+ types: EventType[];
35
+ }[];
36
+ /** Success rate in percent over terminal deliveries; `null` when there are none. */
37
+ export declare function successRate(succeeded: number, failed: number): number | null;
38
+ /** True when `iso` falls within the last `windowMs` before `now`. */
39
+ export declare function withinWindow(iso: string | undefined | null, windowMs: number, now?: number): boolean;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * A tiny process-wide navigation guard. A view with unsaved edits registers a
3
+ * blocker; the app's in-app navigation (tabs, project/env switch, opening another
4
+ * flag) calls {@link canLeave} first and aborts if the blocker says no.
5
+ *
6
+ * wouter has no built-in navigation blocker, so this bridges the dirty view to the
7
+ * navigation entry points without prop-drilling. Single active blocker (only one
8
+ * editor is open at a time).
9
+ *
10
+ * @module
11
+ */
12
+ /** Returns `true` if navigation is allowed, `false` to block it (e.g. user cancelled). */
13
+ type Blocker = () => boolean;
14
+ /** Register the current blocker (replaces any previous one). */
15
+ export declare function setNavBlocker(blocker: Blocker): void;
16
+ /** Clear `blocker` if it is still the active one (safe from stale cleanups). */
17
+ export declare function clearNavBlocker(blocker: Blocker): void;
18
+ /** Consult the active blocker. `true` when navigation may proceed. */
19
+ export declare function canLeave(): boolean;
20
+ export {};