@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,148 @@
1
+ const require_keys = require("./keys-FiKpaVHX.cjs");
2
+ let node_fs = require("node:fs");
3
+ let node_fs_promises = require("node:fs/promises");
4
+ let node_path = require("node:path");
5
+ //#region src/storage/file.ts
6
+ /**
7
+ * File-system storage adapter. Persists each key as a JSON file under a base
8
+ * directory, mirroring the slash-delimited key layout (`whk/{app}/…`) as a
9
+ * tree of nested directories with a `.json` extension on the leaf. Zero
10
+ * external dependencies — uses `node:fs/promises`, which works in both Bun and
11
+ * Node.
12
+ *
13
+ * @module
14
+ */
15
+ var file_exports = /* @__PURE__ */ require_keys.__exportAll({
16
+ clearFileStorage: () => clearFileStorage,
17
+ createFileStorage: () => createFileStorage
18
+ });
19
+ /** Suffix appended to the leaf file for every stored key. */
20
+ const JSON_SUFFIX = ".json";
21
+ /**
22
+ * Map a storage key (e.g. `whk/acme/endpoints/ep_1`) to an absolute file path
23
+ * inside `dir`. Each key segment becomes a directory; the last segment gains a
24
+ * `.json` extension.
25
+ */
26
+ function keyToPath(dir, key) {
27
+ return (0, node_path.join)(dir, ...key.split("/")) + JSON_SUFFIX;
28
+ }
29
+ /**
30
+ * Reverse {@link keyToPath}: turn an absolute (or `dir`-relative) file path back
31
+ * into the original slash-delimited storage key. Returns `null` for paths that
32
+ * are not `.json` leaves under `dir`.
33
+ */
34
+ function pathToKey(dir, filePath) {
35
+ const rel = (0, node_path.relative)(dir, filePath);
36
+ if (rel.startsWith("..") || !rel.endsWith(JSON_SUFFIX)) return null;
37
+ return rel.slice(0, -5).split(node_path.sep).join("/");
38
+ }
39
+ /** Recursively collect every `.json` leaf file path under `root`. */
40
+ async function walk(root) {
41
+ const out = [];
42
+ let entries;
43
+ try {
44
+ entries = await (0, node_fs_promises.readdir)(root, { withFileTypes: true });
45
+ } catch (err) {
46
+ if (err.code === "ENOENT") return out;
47
+ throw err;
48
+ }
49
+ for (const entry of entries) {
50
+ const name = entry.name.toString();
51
+ const full = (0, node_path.join)(root, name);
52
+ if (entry.isDirectory()) out.push(...await walk(full));
53
+ else if (entry.isFile() && name.endsWith(JSON_SUFFIX)) out.push(full);
54
+ }
55
+ return out;
56
+ }
57
+ /**
58
+ * Create a file-backed {@link WatchableWebhooksStorage}. Values are serialized
59
+ * as pretty-printed JSON. `watch` is implemented via `fs.watch` on the base
60
+ * directory (recursive); it is best-effort and may coalesce or miss events on
61
+ * platforms without recursive-watch support.
62
+ *
63
+ * @example
64
+ * ```ts
65
+ * import { createFileStorage } from "@xtandard/webhooks/storage/file";
66
+ *
67
+ * const storage = createFileStorage({ dir: "./data/webhooks" });
68
+ * ```
69
+ */
70
+ function createFileStorage(options) {
71
+ const { dir } = options;
72
+ return {
73
+ async getItem(key) {
74
+ try {
75
+ const raw = await (0, node_fs_promises.readFile)(keyToPath(dir, key), "utf8");
76
+ return JSON.parse(raw);
77
+ } catch (err) {
78
+ if (err.code === "ENOENT") return null;
79
+ throw err;
80
+ }
81
+ },
82
+ async setItem(key, value) {
83
+ const path = keyToPath(dir, key);
84
+ await (0, node_fs_promises.mkdir)((0, node_path.dirname)(path), { recursive: true });
85
+ await (0, node_fs_promises.writeFile)(path, JSON.stringify(value, null, 2), "utf8");
86
+ },
87
+ async removeItem(key) {
88
+ try {
89
+ await (0, node_fs_promises.unlink)(keyToPath(dir, key));
90
+ } catch (err) {
91
+ if (err.code !== "ENOENT") throw err;
92
+ }
93
+ },
94
+ async getKeys(prefix) {
95
+ const files = await walk(dir);
96
+ const out = [];
97
+ for (const file of files) {
98
+ const key = pathToKey(dir, file);
99
+ if (key !== null && key.startsWith(prefix)) out.push(key);
100
+ }
101
+ return out;
102
+ },
103
+ async watch(prefix, callback) {
104
+ await (0, node_fs_promises.mkdir)(dir, { recursive: true });
105
+ const watcher = (0, node_fs.watch)(dir, { recursive: true }, (eventType, filename) => {
106
+ if (!filename) return;
107
+ const key = pathToKey(dir, (0, node_path.join)(dir, filename.toString()));
108
+ if (key === null || !key.startsWith(prefix)) return;
109
+ callback({
110
+ type: eventType === "rename" ? "remove" : "update",
111
+ key
112
+ });
113
+ });
114
+ return () => watcher.close();
115
+ }
116
+ };
117
+ }
118
+ /**
119
+ * Delete every file written by a {@link createFileStorage} instance by removing
120
+ * its base directory. Exposed primarily for tests and cleanup tooling.
121
+ */
122
+ async function clearFileStorage(options) {
123
+ await (0, node_fs_promises.rm)(options.dir, {
124
+ recursive: true,
125
+ force: true
126
+ });
127
+ }
128
+ //#endregion
129
+ Object.defineProperty(exports, "clearFileStorage", {
130
+ enumerable: true,
131
+ get: function() {
132
+ return clearFileStorage;
133
+ }
134
+ });
135
+ Object.defineProperty(exports, "createFileStorage", {
136
+ enumerable: true,
137
+ get: function() {
138
+ return createFileStorage;
139
+ }
140
+ });
141
+ Object.defineProperty(exports, "file_exports", {
142
+ enumerable: true,
143
+ get: function() {
144
+ return file_exports;
145
+ }
146
+ });
147
+
148
+ //# sourceMappingURL=file-COBYZA4Q.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-COBYZA4Q.cjs","names":["sep"],"sources":["../src/storage/file.ts"],"sourcesContent":["/**\n * File-system storage adapter. Persists each key as a JSON file under a base\n * directory, mirroring the slash-delimited key layout (`whk/{app}/…`) as a\n * tree of nested directories with a `.json` extension on the leaf. Zero\n * external dependencies — uses `node:fs/promises`, which works in both Bun and\n * Node.\n *\n * @module\n */\n\nimport { watch as fsWatch } from \"node:fs\";\nimport type { Dirent } from \"node:fs\";\nimport { mkdir, readdir, readFile, rm, unlink, writeFile } from \"node:fs/promises\";\nimport { dirname, join, relative, sep } from \"node:path\";\nimport type { StorageChangeEvent, WatchableWebhooksStorage } from \"./contract.ts\";\n\n/** Options for {@link createFileStorage}. */\nexport interface FileStorageOptions {\n /** Base directory under which key files are written. Created on demand. */\n dir: string;\n}\n\n/** Suffix appended to the leaf file for every stored key. */\nconst JSON_SUFFIX = \".json\";\n\n/**\n * Map a storage key (e.g. `whk/acme/endpoints/ep_1`) to an absolute file path\n * inside `dir`. Each key segment becomes a directory; the last segment gains a\n * `.json` extension.\n */\nfunction keyToPath(dir: string, key: string): string {\n return join(dir, ...key.split(\"/\")) + JSON_SUFFIX;\n}\n\n/**\n * Reverse {@link keyToPath}: turn an absolute (or `dir`-relative) file path back\n * into the original slash-delimited storage key. Returns `null` for paths that\n * are not `.json` leaves under `dir`.\n */\nfunction pathToKey(dir: string, filePath: string): string | null {\n const rel = relative(dir, filePath);\n if (rel.startsWith(\"..\") || !rel.endsWith(JSON_SUFFIX)) return null;\n return rel.slice(0, -JSON_SUFFIX.length).split(sep).join(\"/\");\n}\n\n/** Recursively collect every `.json` leaf file path under `root`. */\nasync function walk(root: string): Promise<string[]> {\n const out: string[] = [];\n let entries: Dirent[];\n try {\n entries = (await readdir(root, { withFileTypes: true })) as Dirent[];\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return out;\n throw err;\n }\n for (const entry of entries) {\n const name = entry.name.toString();\n const full = join(root, name);\n if (entry.isDirectory()) {\n out.push(...(await walk(full)));\n } else if (entry.isFile() && name.endsWith(JSON_SUFFIX)) {\n out.push(full);\n }\n }\n return out;\n}\n\n/**\n * Create a file-backed {@link WatchableWebhooksStorage}. Values are serialized\n * as pretty-printed JSON. `watch` is implemented via `fs.watch` on the base\n * directory (recursive); it is best-effort and may coalesce or miss events on\n * platforms without recursive-watch support.\n *\n * @example\n * ```ts\n * import { createFileStorage } from \"@xtandard/webhooks/storage/file\";\n *\n * const storage = createFileStorage({ dir: \"./data/webhooks\" });\n * ```\n */\nexport function createFileStorage(options: FileStorageOptions): WatchableWebhooksStorage {\n const { dir } = options;\n\n return {\n async getItem<T>(key: string): Promise<T | null> {\n try {\n const raw = await readFile(keyToPath(dir, key), \"utf8\");\n return JSON.parse(raw) as T;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return null;\n throw err;\n }\n },\n\n async setItem<T>(key: string, value: T): Promise<void> {\n const path = keyToPath(dir, key);\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, JSON.stringify(value, null, 2), \"utf8\");\n },\n\n async removeItem(key: string): Promise<void> {\n try {\n await unlink(keyToPath(dir, key));\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"ENOENT\") throw err;\n }\n },\n\n async getKeys(prefix: string): Promise<string[]> {\n const files = await walk(dir);\n const out: string[] = [];\n for (const file of files) {\n const key = pathToKey(dir, file);\n if (key !== null && key.startsWith(prefix)) out.push(key);\n }\n return out;\n },\n\n async watch(\n prefix: string,\n callback: (event: StorageChangeEvent) => void,\n ): Promise<() => void> {\n // Ensure the watched root exists so fs.watch does not throw on a fresh dir.\n await mkdir(dir, { recursive: true });\n const watcher = fsWatch(dir, { recursive: true }, (eventType, filename) => {\n if (!filename) return;\n const key = pathToKey(dir, join(dir, filename.toString()));\n if (key === null || !key.startsWith(prefix)) return;\n // `rename` covers both creation and deletion; we cannot reliably tell\n // them apart from the event alone, so report \"update\" for changes and\n // \"remove\" only when the underlying file is gone.\n const type: StorageChangeEvent[\"type\"] = eventType === \"rename\" ? \"remove\" : \"update\";\n callback({ type, key });\n });\n return () => watcher.close();\n },\n } satisfies WatchableWebhooksStorage;\n}\n\n/**\n * Delete every file written by a {@link createFileStorage} instance by removing\n * its base directory. Exposed primarily for tests and cleanup tooling.\n */\nexport async function clearFileStorage(options: FileStorageOptions): Promise<void> {\n await rm(options.dir, { recursive: true, force: true });\n}\n"],"mappings":"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"}
@@ -0,0 +1,131 @@
1
+ import { t as __exportAll } from "./chunk-D7D4PA-g.mjs";
2
+ import { watch } from "node:fs";
3
+ import { mkdir, readFile, readdir, rm, unlink, writeFile } from "node:fs/promises";
4
+ import { dirname, join, relative, sep } from "node:path";
5
+ //#region src/storage/file.ts
6
+ /**
7
+ * File-system storage adapter. Persists each key as a JSON file under a base
8
+ * directory, mirroring the slash-delimited key layout (`whk/{app}/…`) as a
9
+ * tree of nested directories with a `.json` extension on the leaf. Zero
10
+ * external dependencies — uses `node:fs/promises`, which works in both Bun and
11
+ * Node.
12
+ *
13
+ * @module
14
+ */
15
+ var file_exports = /* @__PURE__ */ __exportAll({
16
+ clearFileStorage: () => clearFileStorage,
17
+ createFileStorage: () => createFileStorage
18
+ });
19
+ /** Suffix appended to the leaf file for every stored key. */
20
+ const JSON_SUFFIX = ".json";
21
+ /**
22
+ * Map a storage key (e.g. `whk/acme/endpoints/ep_1`) to an absolute file path
23
+ * inside `dir`. Each key segment becomes a directory; the last segment gains a
24
+ * `.json` extension.
25
+ */
26
+ function keyToPath(dir, key) {
27
+ return join(dir, ...key.split("/")) + JSON_SUFFIX;
28
+ }
29
+ /**
30
+ * Reverse {@link keyToPath}: turn an absolute (or `dir`-relative) file path back
31
+ * into the original slash-delimited storage key. Returns `null` for paths that
32
+ * are not `.json` leaves under `dir`.
33
+ */
34
+ function pathToKey(dir, filePath) {
35
+ const rel = relative(dir, filePath);
36
+ if (rel.startsWith("..") || !rel.endsWith(JSON_SUFFIX)) return null;
37
+ return rel.slice(0, -5).split(sep).join("/");
38
+ }
39
+ /** Recursively collect every `.json` leaf file path under `root`. */
40
+ async function walk(root) {
41
+ const out = [];
42
+ let entries;
43
+ try {
44
+ entries = await readdir(root, { withFileTypes: true });
45
+ } catch (err) {
46
+ if (err.code === "ENOENT") return out;
47
+ throw err;
48
+ }
49
+ for (const entry of entries) {
50
+ const name = entry.name.toString();
51
+ const full = join(root, name);
52
+ if (entry.isDirectory()) out.push(...await walk(full));
53
+ else if (entry.isFile() && name.endsWith(JSON_SUFFIX)) out.push(full);
54
+ }
55
+ return out;
56
+ }
57
+ /**
58
+ * Create a file-backed {@link WatchableWebhooksStorage}. Values are serialized
59
+ * as pretty-printed JSON. `watch` is implemented via `fs.watch` on the base
60
+ * directory (recursive); it is best-effort and may coalesce or miss events on
61
+ * platforms without recursive-watch support.
62
+ *
63
+ * @example
64
+ * ```ts
65
+ * import { createFileStorage } from "@xtandard/webhooks/storage/file";
66
+ *
67
+ * const storage = createFileStorage({ dir: "./data/webhooks" });
68
+ * ```
69
+ */
70
+ function createFileStorage(options) {
71
+ const { dir } = options;
72
+ return {
73
+ async getItem(key) {
74
+ try {
75
+ const raw = await readFile(keyToPath(dir, key), "utf8");
76
+ return JSON.parse(raw);
77
+ } catch (err) {
78
+ if (err.code === "ENOENT") return null;
79
+ throw err;
80
+ }
81
+ },
82
+ async setItem(key, value) {
83
+ const path = keyToPath(dir, key);
84
+ await mkdir(dirname(path), { recursive: true });
85
+ await writeFile(path, JSON.stringify(value, null, 2), "utf8");
86
+ },
87
+ async removeItem(key) {
88
+ try {
89
+ await unlink(keyToPath(dir, key));
90
+ } catch (err) {
91
+ if (err.code !== "ENOENT") throw err;
92
+ }
93
+ },
94
+ async getKeys(prefix) {
95
+ const files = await walk(dir);
96
+ const out = [];
97
+ for (const file of files) {
98
+ const key = pathToKey(dir, file);
99
+ if (key !== null && key.startsWith(prefix)) out.push(key);
100
+ }
101
+ return out;
102
+ },
103
+ async watch(prefix, callback) {
104
+ await mkdir(dir, { recursive: true });
105
+ const watcher = watch(dir, { recursive: true }, (eventType, filename) => {
106
+ if (!filename) return;
107
+ const key = pathToKey(dir, join(dir, filename.toString()));
108
+ if (key === null || !key.startsWith(prefix)) return;
109
+ callback({
110
+ type: eventType === "rename" ? "remove" : "update",
111
+ key
112
+ });
113
+ });
114
+ return () => watcher.close();
115
+ }
116
+ };
117
+ }
118
+ /**
119
+ * Delete every file written by a {@link createFileStorage} instance by removing
120
+ * its base directory. Exposed primarily for tests and cleanup tooling.
121
+ */
122
+ async function clearFileStorage(options) {
123
+ await rm(options.dir, {
124
+ recursive: true,
125
+ force: true
126
+ });
127
+ }
128
+ //#endregion
129
+ export { createFileStorage as n, file_exports as r, clearFileStorage as t };
130
+
131
+ //# sourceMappingURL=file-fi02eFHk.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-fi02eFHk.mjs","names":["fsWatch"],"sources":["../src/storage/file.ts"],"sourcesContent":["/**\n * File-system storage adapter. Persists each key as a JSON file under a base\n * directory, mirroring the slash-delimited key layout (`whk/{app}/…`) as a\n * tree of nested directories with a `.json` extension on the leaf. Zero\n * external dependencies — uses `node:fs/promises`, which works in both Bun and\n * Node.\n *\n * @module\n */\n\nimport { watch as fsWatch } from \"node:fs\";\nimport type { Dirent } from \"node:fs\";\nimport { mkdir, readdir, readFile, rm, unlink, writeFile } from \"node:fs/promises\";\nimport { dirname, join, relative, sep } from \"node:path\";\nimport type { StorageChangeEvent, WatchableWebhooksStorage } from \"./contract.ts\";\n\n/** Options for {@link createFileStorage}. */\nexport interface FileStorageOptions {\n /** Base directory under which key files are written. Created on demand. */\n dir: string;\n}\n\n/** Suffix appended to the leaf file for every stored key. */\nconst JSON_SUFFIX = \".json\";\n\n/**\n * Map a storage key (e.g. `whk/acme/endpoints/ep_1`) to an absolute file path\n * inside `dir`. Each key segment becomes a directory; the last segment gains a\n * `.json` extension.\n */\nfunction keyToPath(dir: string, key: string): string {\n return join(dir, ...key.split(\"/\")) + JSON_SUFFIX;\n}\n\n/**\n * Reverse {@link keyToPath}: turn an absolute (or `dir`-relative) file path back\n * into the original slash-delimited storage key. Returns `null` for paths that\n * are not `.json` leaves under `dir`.\n */\nfunction pathToKey(dir: string, filePath: string): string | null {\n const rel = relative(dir, filePath);\n if (rel.startsWith(\"..\") || !rel.endsWith(JSON_SUFFIX)) return null;\n return rel.slice(0, -JSON_SUFFIX.length).split(sep).join(\"/\");\n}\n\n/** Recursively collect every `.json` leaf file path under `root`. */\nasync function walk(root: string): Promise<string[]> {\n const out: string[] = [];\n let entries: Dirent[];\n try {\n entries = (await readdir(root, { withFileTypes: true })) as Dirent[];\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return out;\n throw err;\n }\n for (const entry of entries) {\n const name = entry.name.toString();\n const full = join(root, name);\n if (entry.isDirectory()) {\n out.push(...(await walk(full)));\n } else if (entry.isFile() && name.endsWith(JSON_SUFFIX)) {\n out.push(full);\n }\n }\n return out;\n}\n\n/**\n * Create a file-backed {@link WatchableWebhooksStorage}. Values are serialized\n * as pretty-printed JSON. `watch` is implemented via `fs.watch` on the base\n * directory (recursive); it is best-effort and may coalesce or miss events on\n * platforms without recursive-watch support.\n *\n * @example\n * ```ts\n * import { createFileStorage } from \"@xtandard/webhooks/storage/file\";\n *\n * const storage = createFileStorage({ dir: \"./data/webhooks\" });\n * ```\n */\nexport function createFileStorage(options: FileStorageOptions): WatchableWebhooksStorage {\n const { dir } = options;\n\n return {\n async getItem<T>(key: string): Promise<T | null> {\n try {\n const raw = await readFile(keyToPath(dir, key), \"utf8\");\n return JSON.parse(raw) as T;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return null;\n throw err;\n }\n },\n\n async setItem<T>(key: string, value: T): Promise<void> {\n const path = keyToPath(dir, key);\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, JSON.stringify(value, null, 2), \"utf8\");\n },\n\n async removeItem(key: string): Promise<void> {\n try {\n await unlink(keyToPath(dir, key));\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"ENOENT\") throw err;\n }\n },\n\n async getKeys(prefix: string): Promise<string[]> {\n const files = await walk(dir);\n const out: string[] = [];\n for (const file of files) {\n const key = pathToKey(dir, file);\n if (key !== null && key.startsWith(prefix)) out.push(key);\n }\n return out;\n },\n\n async watch(\n prefix: string,\n callback: (event: StorageChangeEvent) => void,\n ): Promise<() => void> {\n // Ensure the watched root exists so fs.watch does not throw on a fresh dir.\n await mkdir(dir, { recursive: true });\n const watcher = fsWatch(dir, { recursive: true }, (eventType, filename) => {\n if (!filename) return;\n const key = pathToKey(dir, join(dir, filename.toString()));\n if (key === null || !key.startsWith(prefix)) return;\n // `rename` covers both creation and deletion; we cannot reliably tell\n // them apart from the event alone, so report \"update\" for changes and\n // \"remove\" only when the underlying file is gone.\n const type: StorageChangeEvent[\"type\"] = eventType === \"rename\" ? \"remove\" : \"update\";\n callback({ type, key });\n });\n return () => watcher.close();\n },\n } satisfies WatchableWebhooksStorage;\n}\n\n/**\n * Delete every file written by a {@link createFileStorage} instance by removing\n * its base directory. Exposed primarily for tests and cleanup tooling.\n */\nexport async function clearFileStorage(options: FileStorageOptions): Promise<void> {\n await rm(options.dir, { recursive: true, force: true });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAuBA,MAAM,cAAc;;;;;;AAOpB,SAAS,UAAU,KAAa,KAAqB;CACnD,OAAO,KAAK,KAAK,GAAG,IAAI,MAAM,GAAG,CAAC,IAAI;AACxC;;;;;;AAOA,SAAS,UAAU,KAAa,UAAiC;CAC/D,MAAM,MAAM,SAAS,KAAK,QAAQ;CAClC,IAAI,IAAI,WAAW,IAAI,KAAK,CAAC,IAAI,SAAS,WAAW,GAAG,OAAO;CAC/D,OAAO,IAAI,MAAM,GAAG,EAAmB,EAAE,MAAM,GAAG,EAAE,KAAK,GAAG;AAC9D;;AAGA,eAAe,KAAK,MAAiC;CACnD,MAAM,MAAgB,CAAC;CACvB,IAAI;CACJ,IAAI;EACF,UAAW,MAAM,QAAQ,MAAM,EAAE,eAAe,KAAK,CAAC;CACxD,SAAS,KAAK;EACZ,IAAK,IAA8B,SAAS,UAAU,OAAO;EAC7D,MAAM;CACR;CACA,KAAK,MAAM,SAAS,SAAS;EAC3B,MAAM,OAAO,MAAM,KAAK,SAAS;EACjC,MAAM,OAAO,KAAK,MAAM,IAAI;EAC5B,IAAI,MAAM,YAAY,GACpB,IAAI,KAAK,GAAI,MAAM,KAAK,IAAI,CAAE;OACzB,IAAI,MAAM,OAAO,KAAK,KAAK,SAAS,WAAW,GACpD,IAAI,KAAK,IAAI;CAEjB;CACA,OAAO;AACT;;;;;;;;;;;;;;AAeA,SAAgB,kBAAkB,SAAuD;CACvF,MAAM,EAAE,QAAQ;CAEhB,OAAO;EACL,MAAM,QAAW,KAAgC;GAC/C,IAAI;IACF,MAAM,MAAM,MAAM,SAAS,UAAU,KAAK,GAAG,GAAG,MAAM;IACtD,OAAO,KAAK,MAAM,GAAG;GACvB,SAAS,KAAK;IACZ,IAAK,IAA8B,SAAS,UAAU,OAAO;IAC7D,MAAM;GACR;EACF;EAEA,MAAM,QAAW,KAAa,OAAyB;GACrD,MAAM,OAAO,UAAU,KAAK,GAAG;GAC/B,MAAM,MAAM,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;GAC9C,MAAM,UAAU,MAAM,KAAK,UAAU,OAAO,MAAM,CAAC,GAAG,MAAM;EAC9D;EAEA,MAAM,WAAW,KAA4B;GAC3C,IAAI;IACF,MAAM,OAAO,UAAU,KAAK,GAAG,CAAC;GAClC,SAAS,KAAK;IACZ,IAAK,IAA8B,SAAS,UAAU,MAAM;GAC9D;EACF;EAEA,MAAM,QAAQ,QAAmC;GAC/C,MAAM,QAAQ,MAAM,KAAK,GAAG;GAC5B,MAAM,MAAgB,CAAC;GACvB,KAAK,MAAM,QAAQ,OAAO;IACxB,MAAM,MAAM,UAAU,KAAK,IAAI;IAC/B,IAAI,QAAQ,QAAQ,IAAI,WAAW,MAAM,GAAG,IAAI,KAAK,GAAG;GAC1D;GACA,OAAO;EACT;EAEA,MAAM,MACJ,QACA,UACqB;GAErB,MAAM,MAAM,KAAK,EAAE,WAAW,KAAK,CAAC;GACpC,MAAM,UAAUA,MAAQ,KAAK,EAAE,WAAW,KAAK,IAAI,WAAW,aAAa;IACzE,IAAI,CAAC,UAAU;IACf,MAAM,MAAM,UAAU,KAAK,KAAK,KAAK,SAAS,SAAS,CAAC,CAAC;IACzD,IAAI,QAAQ,QAAQ,CAAC,IAAI,WAAW,MAAM,GAAG;IAK7C,SAAS;KAAE,MAD8B,cAAc,WAAW,WAAW;KAC5D;IAAI,CAAC;GACxB,CAAC;GACD,aAAa,QAAQ,MAAM;EAC7B;CACF;AACF;;;;;AAMA,eAAsB,iBAAiB,SAA4C;CACjF,MAAM,GAAG,QAAQ,KAAK;EAAE,WAAW;EAAM,OAAO;CAAK,CAAC;AACxD"}
package/dist/index.cjs ADDED
@@ -0,0 +1,123 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_keys = require("./keys-FiKpaVHX.cjs");
3
+ const require_schema = require("./schema.cjs");
4
+ const require_signing = require("./signing.cjs");
5
+ const require_core = require("./core-ZGhH6Vs2.cjs");
6
+ const require_contract = require("./contract-Bf1qguwt.cjs");
7
+ const require_dispatcher = require("./dispatcher-B0xTEHt1.cjs");
8
+ const require_contract$1 = require("./contract-B2d5dNU3.cjs");
9
+ const require_create_fetch_handler = require("./create-fetch-handler-CmooujQo.cjs");
10
+ //#region src/storage/watch.ts
11
+ /**
12
+ * Wrap a storage so it implements {@link WatchableWebhooksStorage} using
13
+ * `subscribe` as the change source. The wrapper delegates all reads/writes to
14
+ * `storage` and only adds `watch`; keys not under the watched `prefix` are
15
+ * filtered out. A notification without a key is delivered as a change to the
16
+ * prefix itself (enough to trigger a refresh).
17
+ */
18
+ function withWatch(storage, subscribe) {
19
+ const watchable = Object.create(storage);
20
+ watchable.watch = (prefix, callback) => {
21
+ const notify = (key) => {
22
+ if (key !== void 0 && key !== "" && !key.startsWith(prefix)) return;
23
+ callback({
24
+ type: "update",
25
+ key: key && key !== "" ? key : prefix
26
+ });
27
+ };
28
+ return Promise.resolve(subscribe(notify));
29
+ };
30
+ return watchable;
31
+ }
32
+ /**
33
+ * A {@link WatchSubscribe} backed by Postgres `LISTEN`/`NOTIFY`. Pair with
34
+ * {@link withWatch} for the postgres or drizzle-over-pg adapters. Your migration
35
+ * owns the trigger that `pg_notify`s `channel` with the changed key as payload;
36
+ * this only `LISTEN`s.
37
+ *
38
+ * @example
39
+ * ```ts
40
+ * import { Client } from "pg";
41
+ * const listener = new Client({ connectionString: process.env.DATABASE_URL });
42
+ * await listener.connect();
43
+ * const storage = withWatch(base, pgListenNotify(listener, "xtandard_webhooks"));
44
+ * ```
45
+ */
46
+ function pgListenNotify(client, channel = "xtandard_webhooks") {
47
+ return async (notify) => {
48
+ const listener = (msg) => {
49
+ if (msg.channel === channel) notify(msg.payload);
50
+ };
51
+ client.on("notification", listener);
52
+ await client.query(`LISTEN "${channel.replace(/"/g, "\"\"")}"`);
53
+ return async () => {
54
+ client.removeListener("notification", listener);
55
+ await client.query(`UNLISTEN "${channel.replace(/"/g, "\"\"")}"`);
56
+ };
57
+ };
58
+ }
59
+ //#endregion
60
+ exports.ConflictError = require_core.ConflictError;
61
+ exports.DEFAULT_ATTEMPT_TIMEOUT_MS = require_core.DEFAULT_ATTEMPT_TIMEOUT_MS;
62
+ exports.DEFAULT_PORTAL_ACTIONS = require_create_fetch_handler.DEFAULT_PORTAL_ACTIONS;
63
+ exports.DEFAULT_RESPONSE_BODY_LIMIT = require_core.DEFAULT_RESPONSE_BODY_LIMIT;
64
+ exports.DEFAULT_RETRY_SCHEDULE = require_dispatcher.DEFAULT_RETRY_SCHEDULE;
65
+ exports.HookDeniedError = require_core.HookDeniedError;
66
+ exports.IdempotencyConflictError = require_core.IdempotencyConflictError;
67
+ exports.KEY_REGEX = require_core.KEY_REGEX;
68
+ exports.MUTATING_ACTIONS = require_contract$1.MUTATING_ACTIONS;
69
+ exports.NotFoundError = require_core.NotFoundError;
70
+ exports.PORTAL_TOKEN_PREFIX = require_create_fetch_handler.PORTAL_TOKEN_PREFIX;
71
+ exports.PayloadTooLargeError = require_core.PayloadTooLargeError;
72
+ exports.PortalTokenError = require_create_fetch_handler.PortalTokenError;
73
+ exports.RESERVED_HEADERS = require_core.RESERVED_HEADERS;
74
+ exports.ReadonlyError = require_core.ReadonlyError;
75
+ exports.SCHEMA_VERSION = require_schema.SCHEMA_VERSION;
76
+ exports.SECRET_PREFIX = require_signing.SECRET_PREFIX;
77
+ exports.VERSION = require_core.VERSION;
78
+ exports.ValidationError = require_core.ValidationError;
79
+ exports.WebhookVerificationError = require_signing.WebhookVerificationError;
80
+ exports.activeSecrets = require_core.activeSecrets;
81
+ exports.assertValid = require_core.assertValid;
82
+ exports.attemptDelivery = require_core.attemptDelivery;
83
+ exports.buildOpenApiDocument = require_create_fetch_handler.buildOpenApiDocument;
84
+ exports.buildSignedRequest = require_core.buildSignedRequest;
85
+ exports.createDispatcher = require_dispatcher.createDispatcher;
86
+ exports.createFetchHandler = require_create_fetch_handler.createFetchHandler;
87
+ exports.createPortalToken = require_create_fetch_handler.createPortalToken;
88
+ exports.createWebhooksCore = require_core.createWebhooksCore;
89
+ exports.defaultHookErrorReporter = require_core.defaultHookErrorReporter;
90
+ exports.durationToMs = require_core.durationToMs;
91
+ exports.emitDelivery = require_core.emitDelivery;
92
+ exports.generateSecret = require_signing.generateSecret;
93
+ exports.hasDeliveryQueue = require_contract.hasDeliveryQueue;
94
+ exports.idPattern = require_core.idPattern;
95
+ exports.isCompareAndSwap = require_contract.isCompareAndSwap;
96
+ exports.isMutatingAction = require_contract$1.isMutatingAction;
97
+ exports.isTerminalDeliveryStatus = require_schema.isTerminalDeliveryStatus;
98
+ exports.isTransactional = require_contract.isTransactional;
99
+ exports.isWatchable = require_contract.isWatchable;
100
+ Object.defineProperty(exports, "keys", {
101
+ enumerable: true,
102
+ get: function() {
103
+ return require_keys.keys_exports;
104
+ }
105
+ });
106
+ exports.newId = require_core.newId;
107
+ exports.normalizeHooks = require_core.normalizeHooks;
108
+ exports.parseDurationList = require_core.parseDurationList;
109
+ exports.pgListenNotify = pgListenNotify;
110
+ exports.requirePeer = require_contract.requirePeer;
111
+ exports.runAfter = require_core.runAfter;
112
+ exports.runBefore = require_core.runBefore;
113
+ exports.sign = require_signing.sign;
114
+ exports.signatureHeader = require_signing.signatureHeader;
115
+ exports.validateApplication = require_core.validateApplication;
116
+ exports.validateEndpoint = require_core.validateEndpoint;
117
+ exports.validateEndpointUrl = require_core.validateEndpointUrl;
118
+ exports.validateEventType = require_core.validateEventType;
119
+ exports.verify = require_signing.verify;
120
+ exports.verifyPortalToken = require_create_fetch_handler.verifyPortalToken;
121
+ exports.withWatch = withWatch;
122
+
123
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","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"}