@digimuza/saas-core 0.0.2

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 (474) hide show
  1. package/CLAUDE.md +1165 -0
  2. package/LICENSE +21 -0
  3. package/README.md +234 -0
  4. package/dist/api-keys/index.d.ts +9 -0
  5. package/dist/api-keys/index.d.ts.map +1 -0
  6. package/dist/api-keys/index.js +37 -0
  7. package/dist/api-keys/index.js.map +1 -0
  8. package/dist/api-keys/keys.d.ts +55 -0
  9. package/dist/api-keys/keys.d.ts.map +1 -0
  10. package/dist/api-keys/keys.js +92 -0
  11. package/dist/api-keys/keys.js.map +1 -0
  12. package/dist/api-keys/service.d.ts +111 -0
  13. package/dist/api-keys/service.d.ts.map +1 -0
  14. package/dist/api-keys/service.js +210 -0
  15. package/dist/api-keys/service.js.map +1 -0
  16. package/dist/audit/index.d.ts +5 -0
  17. package/dist/audit/index.d.ts.map +1 -0
  18. package/dist/audit/index.js +44 -0
  19. package/dist/audit/index.js.map +1 -0
  20. package/dist/audit/service.d.ts +88 -0
  21. package/dist/audit/service.d.ts.map +1 -0
  22. package/dist/audit/service.js +128 -0
  23. package/dist/audit/service.js.map +1 -0
  24. package/dist/auth/client.d.ts +5 -0
  25. package/dist/auth/client.d.ts.map +1 -0
  26. package/dist/auth/client.js +26 -0
  27. package/dist/auth/client.js.map +1 -0
  28. package/dist/auth/cookies.d.ts +51 -0
  29. package/dist/auth/cookies.d.ts.map +1 -0
  30. package/dist/auth/cookies.js +58 -0
  31. package/dist/auth/cookies.js.map +1 -0
  32. package/dist/auth/index.d.ts +14 -0
  33. package/dist/auth/index.d.ts.map +1 -0
  34. package/dist/auth/index.js +46 -0
  35. package/dist/auth/index.js.map +1 -0
  36. package/dist/auth/service.d.ts +67 -0
  37. package/dist/auth/service.d.ts.map +1 -0
  38. package/dist/auth/service.js +275 -0
  39. package/dist/auth/service.js.map +1 -0
  40. package/dist/auth/types.d.ts +56 -0
  41. package/dist/auth/types.d.ts.map +1 -0
  42. package/dist/auth/types.js +2 -0
  43. package/dist/auth/types.js.map +1 -0
  44. package/dist/authorization/index.d.ts +4 -0
  45. package/dist/authorization/index.d.ts.map +1 -0
  46. package/dist/authorization/index.js +24 -0
  47. package/dist/authorization/index.js.map +1 -0
  48. package/dist/authorization/service.d.ts +92 -0
  49. package/dist/authorization/service.d.ts.map +1 -0
  50. package/dist/authorization/service.js +254 -0
  51. package/dist/authorization/service.js.map +1 -0
  52. package/dist/billing/index.d.ts +16 -0
  53. package/dist/billing/index.d.ts.map +1 -0
  54. package/dist/billing/index.js +47 -0
  55. package/dist/billing/index.js.map +1 -0
  56. package/dist/billing/provider.d.ts +114 -0
  57. package/dist/billing/provider.d.ts.map +1 -0
  58. package/dist/billing/provider.js +2 -0
  59. package/dist/billing/provider.js.map +1 -0
  60. package/dist/billing/revolut.d.ts +68 -0
  61. package/dist/billing/revolut.d.ts.map +1 -0
  62. package/dist/billing/revolut.js +181 -0
  63. package/dist/billing/revolut.js.map +1 -0
  64. package/dist/billing/service.d.ts +124 -0
  65. package/dist/billing/service.d.ts.map +1 -0
  66. package/dist/billing/service.js +470 -0
  67. package/dist/billing/service.js.map +1 -0
  68. package/dist/cli/commands/dev.d.ts +43 -0
  69. package/dist/cli/commands/dev.d.ts.map +1 -0
  70. package/dist/cli/commands/dev.js +212 -0
  71. package/dist/cli/commands/dev.js.map +1 -0
  72. package/dist/cli/commands/project.d.ts +12 -0
  73. package/dist/cli/commands/project.d.ts.map +1 -0
  74. package/dist/cli/commands/project.js +49 -0
  75. package/dist/cli/commands/project.js.map +1 -0
  76. package/dist/cli/docker.d.ts +50 -0
  77. package/dist/cli/docker.d.ts.map +1 -0
  78. package/dist/cli/docker.js +84 -0
  79. package/dist/cli/docker.js.map +1 -0
  80. package/dist/cli/index.d.ts +6 -0
  81. package/dist/cli/index.d.ts.map +1 -0
  82. package/dist/cli/index.js +198 -0
  83. package/dist/cli/index.js.map +1 -0
  84. package/dist/cli/output.d.ts +30 -0
  85. package/dist/cli/output.d.ts.map +1 -0
  86. package/dist/cli/output.js +66 -0
  87. package/dist/cli/output.js.map +1 -0
  88. package/dist/cli/templates.d.ts +52 -0
  89. package/dist/cli/templates.d.ts.map +1 -0
  90. package/dist/cli/templates.js +225 -0
  91. package/dist/cli/templates.js.map +1 -0
  92. package/dist/config/client.d.ts +60 -0
  93. package/dist/config/client.d.ts.map +1 -0
  94. package/dist/config/client.js +94 -0
  95. package/dist/config/client.js.map +1 -0
  96. package/dist/config/config-loader.d.ts +2 -0
  97. package/dist/config/config-loader.d.ts.map +1 -0
  98. package/dist/config/config-loader.js +2 -0
  99. package/dist/config/config-loader.js.map +1 -0
  100. package/dist/config/env.d.ts +24 -0
  101. package/dist/config/env.d.ts.map +1 -0
  102. package/dist/config/env.js +40 -0
  103. package/dist/config/env.js.map +1 -0
  104. package/dist/config/errors.d.ts +17 -0
  105. package/dist/config/errors.d.ts.map +1 -0
  106. package/dist/config/errors.js +26 -0
  107. package/dist/config/errors.js.map +1 -0
  108. package/dist/config/index.d.ts +72 -0
  109. package/dist/config/index.d.ts.map +1 -0
  110. package/dist/config/index.js +106 -0
  111. package/dist/config/index.js.map +1 -0
  112. package/dist/config/runtime.d.ts +15 -0
  113. package/dist/config/runtime.d.ts.map +1 -0
  114. package/dist/config/runtime.js +22 -0
  115. package/dist/config/runtime.js.map +1 -0
  116. package/dist/config/sections.d.ts +226 -0
  117. package/dist/config/sections.d.ts.map +1 -0
  118. package/dist/config/sections.js +141 -0
  119. package/dist/config/sections.js.map +1 -0
  120. package/dist/db/client.d.ts +43 -0
  121. package/dist/db/client.d.ts.map +1 -0
  122. package/dist/db/client.js +77 -0
  123. package/dist/db/client.js.map +1 -0
  124. package/dist/db/errors.d.ts +39 -0
  125. package/dist/db/errors.d.ts.map +1 -0
  126. package/dist/db/errors.js +67 -0
  127. package/dist/db/errors.js.map +1 -0
  128. package/dist/db/index.d.ts +10 -0
  129. package/dist/db/index.d.ts.map +1 -0
  130. package/dist/db/index.js +10 -0
  131. package/dist/db/index.js.map +1 -0
  132. package/dist/db/migrate.d.ts +21 -0
  133. package/dist/db/migrate.d.ts.map +1 -0
  134. package/dist/db/migrate.js +28 -0
  135. package/dist/db/migrate.js.map +1 -0
  136. package/dist/db/schema/_shared.d.ts +30 -0
  137. package/dist/db/schema/_shared.d.ts.map +1 -0
  138. package/dist/db/schema/_shared.js +36 -0
  139. package/dist/db/schema/_shared.js.map +1 -0
  140. package/dist/db/schema/apikeys.d.ts +263 -0
  141. package/dist/db/schema/apikeys.d.ts.map +1 -0
  142. package/dist/db/schema/apikeys.js +70 -0
  143. package/dist/db/schema/apikeys.js.map +1 -0
  144. package/dist/db/schema/authorization.d.ts +465 -0
  145. package/dist/db/schema/authorization.d.ts.map +1 -0
  146. package/dist/db/schema/authorization.js +134 -0
  147. package/dist/db/schema/authorization.js.map +1 -0
  148. package/dist/db/schema/billing.d.ts +1100 -0
  149. package/dist/db/schema/billing.d.ts.map +1 -0
  150. package/dist/db/schema/billing.js +223 -0
  151. package/dist/db/schema/billing.js.map +1 -0
  152. package/dist/db/schema/events.d.ts +161 -0
  153. package/dist/db/schema/events.d.ts.map +1 -0
  154. package/dist/db/schema/events.js +66 -0
  155. package/dist/db/schema/events.js.map +1 -0
  156. package/dist/db/schema/features.d.ts +243 -0
  157. package/dist/db/schema/features.d.ts.map +1 -0
  158. package/dist/db/schema/features.js +87 -0
  159. package/dist/db/schema/features.js.map +1 -0
  160. package/dist/db/schema/files.d.ts +253 -0
  161. package/dist/db/schema/files.d.ts.map +1 -0
  162. package/dist/db/schema/files.js +74 -0
  163. package/dist/db/schema/files.js.map +1 -0
  164. package/dist/db/schema/index.d.ts +38 -0
  165. package/dist/db/schema/index.d.ts.map +1 -0
  166. package/dist/db/schema/index.js +38 -0
  167. package/dist/db/schema/index.js.map +1 -0
  168. package/dist/db/schema/invitations.d.ts +214 -0
  169. package/dist/db/schema/invitations.d.ts.map +1 -0
  170. package/dist/db/schema/invitations.js +80 -0
  171. package/dist/db/schema/invitations.js.map +1 -0
  172. package/dist/db/schema/messaging.d.ts +442 -0
  173. package/dist/db/schema/messaging.d.ts.map +1 -0
  174. package/dist/db/schema/messaging.js +120 -0
  175. package/dist/db/schema/messaging.js.map +1 -0
  176. package/dist/db/schema/users.d.ts +246 -0
  177. package/dist/db/schema/users.d.ts.map +1 -0
  178. package/dist/db/schema/users.js +60 -0
  179. package/dist/db/schema/users.js.map +1 -0
  180. package/dist/db/schema/webhooks.d.ts +693 -0
  181. package/dist/db/schema/webhooks.d.ts.map +1 -0
  182. package/dist/db/schema/webhooks.js +147 -0
  183. package/dist/db/schema/webhooks.js.map +1 -0
  184. package/dist/db/schema/workspaces.d.ts +196 -0
  185. package/dist/db/schema/workspaces.d.ts.map +1 -0
  186. package/dist/db/schema/workspaces.js +57 -0
  187. package/dist/db/schema/workspaces.js.map +1 -0
  188. package/dist/db/types.d.ts +18 -0
  189. package/dist/db/types.d.ts.map +1 -0
  190. package/dist/db/types.js +2 -0
  191. package/dist/db/types.js.map +1 -0
  192. package/dist/email/adapters.d.ts +29 -0
  193. package/dist/email/adapters.d.ts.map +1 -0
  194. package/dist/email/adapters.js +60 -0
  195. package/dist/email/adapters.js.map +1 -0
  196. package/dist/email/index.d.ts +20 -0
  197. package/dist/email/index.d.ts.map +1 -0
  198. package/dist/email/index.js +79 -0
  199. package/dist/email/index.js.map +1 -0
  200. package/dist/email/provider.d.ts +47 -0
  201. package/dist/email/provider.d.ts.map +1 -0
  202. package/dist/email/provider.js +7 -0
  203. package/dist/email/provider.js.map +1 -0
  204. package/dist/email/resend.d.ts +22 -0
  205. package/dist/email/resend.d.ts.map +1 -0
  206. package/dist/email/resend.js +51 -0
  207. package/dist/email/resend.js.map +1 -0
  208. package/dist/email/service.d.ts +55 -0
  209. package/dist/email/service.d.ts.map +1 -0
  210. package/dist/email/service.js +87 -0
  211. package/dist/email/service.js.map +1 -0
  212. package/dist/email/templates.d.ts +81 -0
  213. package/dist/email/templates.d.ts.map +1 -0
  214. package/dist/email/templates.js +84 -0
  215. package/dist/email/templates.js.map +1 -0
  216. package/dist/entitlements/index.d.ts +10 -0
  217. package/dist/entitlements/index.d.ts.map +1 -0
  218. package/dist/entitlements/index.js +28 -0
  219. package/dist/entitlements/index.js.map +1 -0
  220. package/dist/entitlements/service.d.ts +83 -0
  221. package/dist/entitlements/service.d.ts.map +1 -0
  222. package/dist/entitlements/service.js +158 -0
  223. package/dist/entitlements/service.js.map +1 -0
  224. package/dist/errors.d.ts +85 -0
  225. package/dist/errors.d.ts.map +1 -0
  226. package/dist/errors.js +95 -0
  227. package/dist/errors.js.map +1 -0
  228. package/dist/events/core-events.d.ts +120 -0
  229. package/dist/events/core-events.d.ts.map +1 -0
  230. package/dist/events/core-events.js +109 -0
  231. package/dist/events/core-events.js.map +1 -0
  232. package/dist/events/index.d.ts +15 -0
  233. package/dist/events/index.d.ts.map +1 -0
  234. package/dist/events/index.js +58 -0
  235. package/dist/events/index.js.map +1 -0
  236. package/dist/events/publisher.d.ts +46 -0
  237. package/dist/events/publisher.d.ts.map +1 -0
  238. package/dist/events/publisher.js +54 -0
  239. package/dist/events/publisher.js.map +1 -0
  240. package/dist/events/registry.d.ts +87 -0
  241. package/dist/events/registry.d.ts.map +1 -0
  242. package/dist/events/registry.js +34 -0
  243. package/dist/events/registry.js.map +1 -0
  244. package/dist/events/service.d.ts +78 -0
  245. package/dist/events/service.d.ts.map +1 -0
  246. package/dist/events/service.js +182 -0
  247. package/dist/events/service.js.map +1 -0
  248. package/dist/features/bucket.d.ts +22 -0
  249. package/dist/features/bucket.d.ts.map +1 -0
  250. package/dist/features/bucket.js +42 -0
  251. package/dist/features/bucket.js.map +1 -0
  252. package/dist/features/index.d.ts +15 -0
  253. package/dist/features/index.d.ts.map +1 -0
  254. package/dist/features/index.js +78 -0
  255. package/dist/features/index.js.map +1 -0
  256. package/dist/features/registry.d.ts +51 -0
  257. package/dist/features/registry.d.ts.map +1 -0
  258. package/dist/features/registry.js +52 -0
  259. package/dist/features/registry.js.map +1 -0
  260. package/dist/features/service.d.ts +78 -0
  261. package/dist/features/service.d.ts.map +1 -0
  262. package/dist/features/service.js +207 -0
  263. package/dist/features/service.js.map +1 -0
  264. package/dist/files/index.d.ts +6 -0
  265. package/dist/files/index.d.ts.map +1 -0
  266. package/dist/files/index.js +64 -0
  267. package/dist/files/index.js.map +1 -0
  268. package/dist/files/service.d.ts +92 -0
  269. package/dist/files/service.d.ts.map +1 -0
  270. package/dist/files/service.js +303 -0
  271. package/dist/files/service.js.map +1 -0
  272. package/dist/index.d.ts +33 -0
  273. package/dist/index.d.ts.map +1 -0
  274. package/dist/index.js +33 -0
  275. package/dist/index.js.map +1 -0
  276. package/dist/jobs/backend.d.ts +23 -0
  277. package/dist/jobs/backend.d.ts.map +1 -0
  278. package/dist/jobs/backend.js +23 -0
  279. package/dist/jobs/backend.js.map +1 -0
  280. package/dist/jobs/index.d.ts +29 -0
  281. package/dist/jobs/index.d.ts.map +1 -0
  282. package/dist/jobs/index.js +114 -0
  283. package/dist/jobs/index.js.map +1 -0
  284. package/dist/jobs/service.d.ts +67 -0
  285. package/dist/jobs/service.d.ts.map +1 -0
  286. package/dist/jobs/service.js +139 -0
  287. package/dist/jobs/service.js.map +1 -0
  288. package/dist/jobs/types.d.ts +78 -0
  289. package/dist/jobs/types.d.ts.map +1 -0
  290. package/dist/jobs/types.js +4 -0
  291. package/dist/jobs/types.js.map +1 -0
  292. package/dist/logger/default.d.ts +7 -0
  293. package/dist/logger/default.d.ts.map +1 -0
  294. package/dist/logger/default.js +28 -0
  295. package/dist/logger/default.js.map +1 -0
  296. package/dist/logger/index.d.ts +24 -0
  297. package/dist/logger/index.d.ts.map +1 -0
  298. package/dist/logger/index.js +90 -0
  299. package/dist/logger/index.js.map +1 -0
  300. package/dist/logger/redact.d.ts +40 -0
  301. package/dist/logger/redact.d.ts.map +1 -0
  302. package/dist/logger/redact.js +178 -0
  303. package/dist/logger/redact.js.map +1 -0
  304. package/dist/logger/service.d.ts +14 -0
  305. package/dist/logger/service.d.ts.map +1 -0
  306. package/dist/logger/service.js +56 -0
  307. package/dist/logger/service.js.map +1 -0
  308. package/dist/logger/sinks.d.ts +37 -0
  309. package/dist/logger/sinks.d.ts.map +1 -0
  310. package/dist/logger/sinks.js +135 -0
  311. package/dist/logger/sinks.js.map +1 -0
  312. package/dist/logger/types.d.ts +47 -0
  313. package/dist/logger/types.d.ts.map +1 -0
  314. package/dist/logger/types.js +8 -0
  315. package/dist/logger/types.js.map +1 -0
  316. package/dist/notifications/index.d.ts +8 -0
  317. package/dist/notifications/index.d.ts.map +1 -0
  318. package/dist/notifications/index.js +72 -0
  319. package/dist/notifications/index.js.map +1 -0
  320. package/dist/notifications/service.d.ts +79 -0
  321. package/dist/notifications/service.d.ts.map +1 -0
  322. package/dist/notifications/service.js +164 -0
  323. package/dist/notifications/service.js.map +1 -0
  324. package/dist/realtime/channels.d.ts +37 -0
  325. package/dist/realtime/channels.d.ts.map +1 -0
  326. package/dist/realtime/channels.js +58 -0
  327. package/dist/realtime/channels.js.map +1 -0
  328. package/dist/realtime/index.d.ts +19 -0
  329. package/dist/realtime/index.d.ts.map +1 -0
  330. package/dist/realtime/index.js +118 -0
  331. package/dist/realtime/index.js.map +1 -0
  332. package/dist/realtime/postgres.d.ts +48 -0
  333. package/dist/realtime/postgres.d.ts.map +1 -0
  334. package/dist/realtime/postgres.js +151 -0
  335. package/dist/realtime/postgres.js.map +1 -0
  336. package/dist/realtime/service.d.ts +75 -0
  337. package/dist/realtime/service.d.ts.map +1 -0
  338. package/dist/realtime/service.js +103 -0
  339. package/dist/realtime/service.js.map +1 -0
  340. package/dist/realtime/sse.d.ts +55 -0
  341. package/dist/realtime/sse.d.ts.map +1 -0
  342. package/dist/realtime/sse.js +123 -0
  343. package/dist/realtime/sse.js.map +1 -0
  344. package/dist/realtime/transport.d.ts +52 -0
  345. package/dist/realtime/transport.d.ts.map +1 -0
  346. package/dist/realtime/transport.js +2 -0
  347. package/dist/realtime/transport.js.map +1 -0
  348. package/dist/storage/index.d.ts +18 -0
  349. package/dist/storage/index.d.ts.map +1 -0
  350. package/dist/storage/index.js +89 -0
  351. package/dist/storage/index.js.map +1 -0
  352. package/dist/storage/keys.d.ts +50 -0
  353. package/dist/storage/keys.d.ts.map +1 -0
  354. package/dist/storage/keys.js +120 -0
  355. package/dist/storage/keys.js.map +1 -0
  356. package/dist/storage/memory.d.ts +32 -0
  357. package/dist/storage/memory.d.ts.map +1 -0
  358. package/dist/storage/memory.js +84 -0
  359. package/dist/storage/memory.js.map +1 -0
  360. package/dist/storage/provider.d.ts +93 -0
  361. package/dist/storage/provider.d.ts.map +1 -0
  362. package/dist/storage/provider.js +2 -0
  363. package/dist/storage/provider.js.map +1 -0
  364. package/dist/storage/s3.d.ts +34 -0
  365. package/dist/storage/s3.d.ts.map +1 -0
  366. package/dist/storage/s3.js +175 -0
  367. package/dist/storage/s3.js.map +1 -0
  368. package/dist/storage/service.d.ts +86 -0
  369. package/dist/storage/service.d.ts.map +1 -0
  370. package/dist/storage/service.js +107 -0
  371. package/dist/storage/service.js.map +1 -0
  372. package/dist/storage/sigv4.d.ts +58 -0
  373. package/dist/storage/sigv4.d.ts.map +1 -0
  374. package/dist/storage/sigv4.js +114 -0
  375. package/dist/storage/sigv4.js.map +1 -0
  376. package/dist/streams/client.d.ts +91 -0
  377. package/dist/streams/client.d.ts.map +1 -0
  378. package/dist/streams/client.js +128 -0
  379. package/dist/streams/client.js.map +1 -0
  380. package/dist/streams/events.d.ts +68 -0
  381. package/dist/streams/events.d.ts.map +1 -0
  382. package/dist/streams/events.js +81 -0
  383. package/dist/streams/events.js.map +1 -0
  384. package/dist/streams/index.d.ts +47 -0
  385. package/dist/streams/index.d.ts.map +1 -0
  386. package/dist/streams/index.js +47 -0
  387. package/dist/streams/index.js.map +1 -0
  388. package/dist/streams/realtime.d.ts +27 -0
  389. package/dist/streams/realtime.d.ts.map +1 -0
  390. package/dist/streams/realtime.js +50 -0
  391. package/dist/streams/realtime.js.map +1 -0
  392. package/dist/streams/state.d.ts +26 -0
  393. package/dist/streams/state.d.ts.map +1 -0
  394. package/dist/streams/state.js +34 -0
  395. package/dist/streams/state.js.map +1 -0
  396. package/dist/users/index.d.ts +9 -0
  397. package/dist/users/index.d.ts.map +1 -0
  398. package/dist/users/index.js +23 -0
  399. package/dist/users/index.js.map +1 -0
  400. package/dist/users/service.d.ts +43 -0
  401. package/dist/users/service.d.ts.map +1 -0
  402. package/dist/users/service.js +113 -0
  403. package/dist/users/service.js.map +1 -0
  404. package/dist/webhooks/inbound.d.ts +70 -0
  405. package/dist/webhooks/inbound.d.ts.map +1 -0
  406. package/dist/webhooks/inbound.js +146 -0
  407. package/dist/webhooks/inbound.js.map +1 -0
  408. package/dist/webhooks/index.d.ts +12 -0
  409. package/dist/webhooks/index.d.ts.map +1 -0
  410. package/dist/webhooks/index.js +65 -0
  411. package/dist/webhooks/index.js.map +1 -0
  412. package/dist/webhooks/outgoing.d.ts +89 -0
  413. package/dist/webhooks/outgoing.d.ts.map +1 -0
  414. package/dist/webhooks/outgoing.js +375 -0
  415. package/dist/webhooks/outgoing.js.map +1 -0
  416. package/dist/webhooks/signature.d.ts +40 -0
  417. package/dist/webhooks/signature.d.ts.map +1 -0
  418. package/dist/webhooks/signature.js +77 -0
  419. package/dist/webhooks/signature.js.map +1 -0
  420. package/dist/workspaces/context.d.ts +30 -0
  421. package/dist/workspaces/context.d.ts.map +1 -0
  422. package/dist/workspaces/context.js +12 -0
  423. package/dist/workspaces/context.js.map +1 -0
  424. package/dist/workspaces/index.d.ts +5 -0
  425. package/dist/workspaces/index.d.ts.map +1 -0
  426. package/dist/workspaces/index.js +21 -0
  427. package/dist/workspaces/index.js.map +1 -0
  428. package/dist/workspaces/service.d.ts +126 -0
  429. package/dist/workspaces/service.d.ts.map +1 -0
  430. package/dist/workspaces/service.js +495 -0
  431. package/dist/workspaces/service.js.map +1 -0
  432. package/docs/README.md +71 -0
  433. package/docs/api-keys.md +184 -0
  434. package/docs/audit.md +198 -0
  435. package/docs/auth.md +196 -0
  436. package/docs/authorization.md +225 -0
  437. package/docs/billing.md +293 -0
  438. package/docs/cli.md +238 -0
  439. package/docs/config.md +163 -0
  440. package/docs/db.md +219 -0
  441. package/docs/email.md +222 -0
  442. package/docs/entitlements.md +194 -0
  443. package/docs/errors.md +158 -0
  444. package/docs/events.md +279 -0
  445. package/docs/feature-flags.md +237 -0
  446. package/docs/files.md +229 -0
  447. package/docs/jobs.md +288 -0
  448. package/docs/logger.md +269 -0
  449. package/docs/notifications.md +179 -0
  450. package/docs/publishing.md +128 -0
  451. package/docs/realtime.md +360 -0
  452. package/docs/storage.md +279 -0
  453. package/docs/streams.md +296 -0
  454. package/docs/users.md +117 -0
  455. package/docs/webhooks.md +272 -0
  456. package/docs/workspaces.md +269 -0
  457. package/drizzle/0000_init_users.sql +21 -0
  458. package/drizzle/0001_workspaces_roles_permissions.sql +86 -0
  459. package/drizzle/0002_billing_entitlements.sql +97 -0
  460. package/drizzle/0003_events.sql +17 -0
  461. package/drizzle/0004_notifications_audit.sql +64 -0
  462. package/drizzle/0005_api_keys_webhooks.sql +77 -0
  463. package/drizzle/0006_feature_flags.sql +22 -0
  464. package/drizzle/0007_files.sql +23 -0
  465. package/drizzle/meta/0000_snapshot.json +160 -0
  466. package/drizzle/meta/0001_snapshot.json +837 -0
  467. package/drizzle/meta/0002_snapshot.json +1580 -0
  468. package/drizzle/meta/0003_snapshot.json +1751 -0
  469. package/drizzle/meta/0004_snapshot.json +2109 -0
  470. package/drizzle/meta/0005_snapshot.json +2719 -0
  471. package/drizzle/meta/0006_snapshot.json +2909 -0
  472. package/drizzle/meta/0007_snapshot.json +3113 -0
  473. package/drizzle/meta/_journal.json +62 -0
  474. package/package.json +180 -0
@@ -0,0 +1,9 @@
1
+ export { createUsersService, type ProfileUpdate, type UsersService, type UsersServiceDeps, type UserWithProfile, type WorkosUserAttributes, } from "./service.ts";
2
+ /**
3
+ * Default service, bound to the configuration-driven database handle.
4
+ *
5
+ * `db` is a getter so the handle — and therefore the `database` configuration
6
+ * section — is resolved on first use rather than at import.
7
+ */
8
+ export declare const users: import("./service.ts").UsersService;
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/users/index.ts"],"names":[],"mappings":"AAUA,OAAO,EACL,kBAAkB,EAClB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,oBAAoB,GAC1B,MAAM,cAAc,CAAC;AAEtB;;;;;GAKG;AACH,eAAO,MAAM,KAAK,qCAKhB,CAAC"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Public users surface: `@digimuza/saas-core/users`. Server-only.
3
+ *
4
+ * Identity (`users`) and profile (`user_profiles`) are separate tables but a
5
+ * single concern here, because callers almost always want both.
6
+ */
7
+ import { getDatabase } from "../db/client.js";
8
+ import { events as coreEventsService } from "../events/index.js";
9
+ import { createUsersService } from "./service.js";
10
+ export { createUsersService, } from "./service.js";
11
+ /**
12
+ * Default service, bound to the configuration-driven database handle.
13
+ *
14
+ * `db` is a getter so the handle — and therefore the `database` configuration
15
+ * section — is resolved on first use rather than at import.
16
+ */
17
+ export const users = createUsersService({
18
+ get db() {
19
+ return getDatabase();
20
+ },
21
+ events: coreEventsService,
22
+ });
23
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/users/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAE,MAAM,IAAI,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAElD,OAAO,EACL,kBAAkB,GAMnB,MAAM,cAAc,CAAC;AAEtB;;;;;GAKG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG,kBAAkB,CAAC;IACtC,IAAI,EAAE;QACJ,OAAO,WAAW,EAAE,CAAC;IACvB,CAAC;IACD,MAAM,EAAE,iBAAiB;CAC1B,CAAC,CAAC"}
@@ -0,0 +1,43 @@
1
+ import type { CoreDatabase } from "../db/types.ts";
2
+ import { type CoreEventPublisher } from "../events/publisher.ts";
3
+ import type { User, UserProfile } from "../db/schema/index.ts";
4
+ /** A user together with their profile, which is how callers almost always want them. */
5
+ export interface UserWithProfile extends User {
6
+ readonly profile: UserProfile | null;
7
+ }
8
+ /** Identity attributes as supplied by WorkOS. */
9
+ export interface WorkosUserAttributes {
10
+ readonly workosUserId: string;
11
+ readonly email: string;
12
+ readonly firstName?: string | null;
13
+ readonly lastName?: string | null;
14
+ readonly profilePictureUrl?: string | null;
15
+ }
16
+ export interface ProfileUpdate {
17
+ readonly firstName?: string | null;
18
+ readonly lastName?: string | null;
19
+ readonly displayName?: string | null;
20
+ readonly avatarUrl?: string | null;
21
+ }
22
+ export interface UsersService {
23
+ getById(userId: string): Promise<UserWithProfile | undefined>;
24
+ /** As {@link getById}, but throws {@link NotFoundError}. */
25
+ requireById(userId: string): Promise<UserWithProfile>;
26
+ getByWorkosId(workosUserId: string): Promise<UserWithProfile | undefined>;
27
+ /**
28
+ * Create or update the local record for a WorkOS identity.
29
+ *
30
+ * Called on every successful authentication so the local copy tracks the
31
+ * identity provider. Idempotent, and safe to call concurrently.
32
+ */
33
+ syncFromWorkos(attributes: WorkosUserAttributes): Promise<UserWithProfile>;
34
+ updateProfile(userId: string, update: ProfileUpdate): Promise<UserProfile>;
35
+ }
36
+ export interface UsersServiceDeps {
37
+ /** Read per call, so the caller can supply a lazily resolved handle. */
38
+ readonly db: CoreDatabase;
39
+ /** Optional event publisher. Omit it and the service emits nothing. */
40
+ readonly events?: CoreEventPublisher;
41
+ }
42
+ export declare function createUsersService(deps: UsersServiceDeps): UsersService;
43
+ //# sourceMappingURL=service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/users/service.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAe,KAAK,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC9E,OAAO,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAE/D,wFAAwF;AACxF,MAAM,WAAW,eAAgB,SAAQ,IAAI;IAC3C,QAAQ,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,CAAC;CACtC;AAED,iDAAiD;AACjD,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5C;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACpC;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC,CAAC;IAC9D,4DAA4D;IAC5D,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;IACtD,aAAa,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC,CAAC;IAC1E;;;;;OAKG;IACH,cAAc,CAAC,UAAU,EAAE,oBAAoB,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;IAC3E,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;CAC5E;AAED,MAAM,WAAW,gBAAgB;IAC/B,wEAAwE;IACxE,QAAQ,CAAC,EAAE,EAAE,YAAY,CAAC;IAC1B,uEAAuE;IACvE,QAAQ,CAAC,MAAM,CAAC,EAAE,kBAAkB,CAAC;CACtC;AASD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,gBAAgB,GAAG,YAAY,CA2HvE"}
@@ -0,0 +1,113 @@
1
+ import { eq } from "drizzle-orm";
2
+ import { NotFoundError } from "../errors.js";
3
+ import { userProfiles, users } from "../db/schema/index.js";
4
+ import { EventOutbox } from "../events/publisher.js";
5
+ function displayNameFrom(attributes) {
6
+ const parts = [attributes.firstName, attributes.lastName].filter((part) => typeof part === "string" && part.trim().length > 0);
7
+ return parts.length > 0 ? parts.join(" ") : null;
8
+ }
9
+ export function createUsersService(deps) {
10
+ async function loadBy(column, value) {
11
+ const rows = await deps.db
12
+ .select({ user: users, profile: userProfiles })
13
+ .from(users)
14
+ .leftJoin(userProfiles, eq(userProfiles.userId, users.id))
15
+ .where(eq(column, value))
16
+ .limit(1);
17
+ const row = rows[0];
18
+ return row === undefined ? undefined : { ...row.user, profile: row.profile };
19
+ }
20
+ return {
21
+ getById(userId) {
22
+ return loadBy(users.id, userId);
23
+ },
24
+ async requireById(userId) {
25
+ const user = await loadBy(users.id, userId);
26
+ if (user === undefined) {
27
+ throw new NotFoundError("User not found.");
28
+ }
29
+ return user;
30
+ },
31
+ getByWorkosId(workosUserId) {
32
+ return loadBy(users.workosUserId, workosUserId);
33
+ },
34
+ async syncFromWorkos(attributes) {
35
+ const events = new EventOutbox(deps.events);
36
+ const userId = await deps.db.transaction(async (tx) => {
37
+ /**
38
+ * `onConflictDoUpdate` rather than select-then-insert: two concurrent
39
+ * sign-ins for the same WorkOS identity would otherwise race and one
40
+ * would fail on the unique index. `updated_at` is touched so the upsert
41
+ * always returns a row.
42
+ */
43
+ const [user] = await tx
44
+ .insert(users)
45
+ .values({ workosUserId: attributes.workosUserId })
46
+ .onConflictDoUpdate({
47
+ target: users.workosUserId,
48
+ set: { updatedAt: new Date() },
49
+ })
50
+ .returning({ id: users.id });
51
+ const id = user.id;
52
+ await tx
53
+ .insert(userProfiles)
54
+ .values({
55
+ userId: id,
56
+ email: attributes.email,
57
+ firstName: attributes.firstName ?? null,
58
+ lastName: attributes.lastName ?? null,
59
+ displayName: displayNameFrom(attributes),
60
+ avatarUrl: attributes.profilePictureUrl ?? null,
61
+ })
62
+ .onConflictDoUpdate({
63
+ target: userProfiles.userId,
64
+ set: {
65
+ email: attributes.email,
66
+ firstName: attributes.firstName ?? null,
67
+ lastName: attributes.lastName ?? null,
68
+ avatarUrl: attributes.profilePictureUrl ?? null,
69
+ updatedAt: new Date(),
70
+ },
71
+ });
72
+ /**
73
+ * `user.created` fires once per identity, keyed on the WorkOS id.
74
+ *
75
+ * The upsert cannot report whether it inserted, and a select-then-decide
76
+ * would race. The idempotency key makes the unique index settle it: the
77
+ * first sync records the event, every later one silently finds it
78
+ * already there.
79
+ */
80
+ await events.record({
81
+ type: "user.created",
82
+ workspaceId: null,
83
+ actorId: id,
84
+ idempotencyKey: attributes.workosUserId,
85
+ data: { userId: id, workosUserId: attributes.workosUserId },
86
+ }, tx);
87
+ return id;
88
+ });
89
+ await events.flush();
90
+ /**
91
+ * `display_name` is intentionally not overwritten on update: a user may
92
+ * have set it themselves, and the identity provider must not clobber it.
93
+ */
94
+ const synced = await loadBy(users.id, userId);
95
+ if (synced === undefined) {
96
+ throw new NotFoundError("User disappeared during sync.");
97
+ }
98
+ return synced;
99
+ },
100
+ async updateProfile(userId, update) {
101
+ const [profile] = await deps.db
102
+ .update(userProfiles)
103
+ .set({ ...update, updatedAt: new Date() })
104
+ .where(eq(userProfiles.userId, userId))
105
+ .returning();
106
+ if (profile === undefined) {
107
+ throw new NotFoundError("User profile not found.");
108
+ }
109
+ return profile;
110
+ },
111
+ };
112
+ }
113
+ //# sourceMappingURL=service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/users/service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,70 @@
1
+ import type { InboundWebhook } from "../db/schema/index.ts";
2
+ import type { CoreDatabase } from "../db/types.ts";
3
+ /** Raised when an inbound signature does not verify. */
4
+ export declare class InvalidInboundSignatureError extends Error {
5
+ readonly name = "InvalidInboundSignatureError";
6
+ constructor();
7
+ }
8
+ export interface ReceiveWebhookInput {
9
+ /** Which system sent it, e.g. `"github"`. */
10
+ readonly source: string;
11
+ /** The sender's own event id. The idempotency key. */
12
+ readonly externalId: string;
13
+ readonly type: string;
14
+ readonly payload: unknown;
15
+ /** Set when the sender identifies a tenant you can resolve. */
16
+ readonly workspaceId?: string | null;
17
+ }
18
+ export type ReceiveOutcome =
19
+ /** Claimed for processing. Run the handler, then mark the result. */
20
+ {
21
+ readonly status: "claimed";
22
+ readonly id: string;
23
+ }
24
+ /** Already handled, or being handled now. Nothing was claimed. */
25
+ | {
26
+ readonly status: "duplicate";
27
+ readonly id: string | undefined;
28
+ };
29
+ export interface InboundWebhooksService {
30
+ /**
31
+ * Record and claim an inbound webhook.
32
+ *
33
+ * The insert *is* the claim, so at-least-once delivery and concurrent
34
+ * redeliveries are handled by PostgreSQL rather than by a check-then-act two
35
+ * requests could both pass. A retry re-claims an event whose previous attempt
36
+ * **failed**, but never one already `processed` or currently `pending`.
37
+ */
38
+ receive(input: ReceiveWebhookInput, tx?: CoreDatabase): Promise<ReceiveOutcome>;
39
+ /** Mark a claimed webhook processed. */
40
+ markProcessed(id: string): Promise<void>;
41
+ /** Mark a claimed webhook failed, so a redelivery can re-claim it. */
42
+ markFailed(id: string, error: unknown): Promise<void>;
43
+ /**
44
+ * Mark a claimed webhook as not actionable — an unknown tenant, an event type
45
+ * you do not handle. Distinct from failed, which needs attention.
46
+ */
47
+ markIgnored(id: string): Promise<void>;
48
+ /** Look one up, for replay or diagnostics. */
49
+ get(source: string, externalId: string): Promise<InboundWebhook | undefined>;
50
+ /** Everything awaiting replay. */
51
+ listFailed(options?: {
52
+ source?: string;
53
+ limit?: number;
54
+ }): Promise<InboundWebhook[]>;
55
+ /**
56
+ * Run a handler inside the claim, marking the outcome automatically.
57
+ *
58
+ * The shape most callers want: claim, handle, record. Rethrows so the sender
59
+ * sees a non-2xx and retries.
60
+ */
61
+ process(input: ReceiveWebhookInput, handler: (webhook: InboundWebhook) => Promise<"processed" | "ignored" | void>): Promise<ReceiveOutcome & {
62
+ readonly handled?: boolean;
63
+ }>;
64
+ }
65
+ export interface InboundWebhooksServiceDeps {
66
+ readonly db: CoreDatabase;
67
+ readonly now?: () => Date;
68
+ }
69
+ export declare function createInboundWebhooksService(deps: InboundWebhooksServiceDeps): InboundWebhooksService;
70
+ //# sourceMappingURL=inbound.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inbound.d.ts","sourceRoot":"","sources":["../../src/webhooks/inbound.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAGnD,wDAAwD;AACxD,qBAAa,4BAA6B,SAAQ,KAAK;IACrD,SAAkB,IAAI,kCAAkC;IAExD,cAMC;CACF;AAED,MAAM,WAAW,mBAAmB;IAClC,6CAA6C;IAC7C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,sDAAsD;IACtD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,+DAA+D;IAC/D,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACtC;AAED,MAAM,MAAM,cAAc;AACxB,qEAAqE;AACnE;IAAE,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE;AACrD,kEAAkE;GAChE;IAAE,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAAC;AAEtE,MAAM,WAAW,sBAAsB;IACrC;;;;;;;OAOG;IACH,OAAO,CAAC,KAAK,EAAE,mBAAmB,EAAE,EAAE,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;IAEhF,wCAAwC;IACxC,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEzC,sEAAsE;IACtE,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEtD;;;OAGG;IACH,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvC,8CAA8C;IAC9C,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAAC;IAE7E,kCAAkC;IAClC,UAAU,CAAC,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC;IAErF;;;;;OAKG;IACH,OAAO,CACL,KAAK,EAAE,mBAAmB,EAC1B,OAAO,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,OAAO,CAAC,WAAW,GAAG,SAAS,GAAG,IAAI,CAAC,GAC5E,OAAO,CAAC,cAAc,GAAG;QAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CAC7D;AAED,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,EAAE,EAAE,YAAY,CAAC;IAC1B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;CAC3B;AAKD,wBAAgB,4BAA4B,CAC1C,IAAI,EAAE,0BAA0B,GAC/B,sBAAsB,CA6JxB"}
@@ -0,0 +1,146 @@
1
+ import { and, eq } from "drizzle-orm";
2
+ import { inboundWebhooks } from "../db/schema/index.js";
3
+ import { InvalidInputError } from "../errors.js";
4
+ /** Raised when an inbound signature does not verify. */
5
+ export class InvalidInboundSignatureError extends Error {
6
+ name = "InvalidInboundSignatureError";
7
+ constructor() {
8
+ /**
9
+ * Deliberately uninformative. A caller who cannot produce a valid signature
10
+ * gets no hint about which part failed.
11
+ */
12
+ super("Webhook signature verification failed.");
13
+ }
14
+ }
15
+ const MAX_ERROR_LENGTH = 1000;
16
+ const DEFAULT_LIMIT = 50;
17
+ export function createInboundWebhooksService(deps) {
18
+ const now = deps.now ?? (() => new Date());
19
+ async function load(id) {
20
+ const rows = await deps.db
21
+ .select()
22
+ .from(inboundWebhooks)
23
+ .where(eq(inboundWebhooks.id, id))
24
+ .limit(1);
25
+ return rows[0];
26
+ }
27
+ const service = {
28
+ async receive(input, tx) {
29
+ if (input.source.trim().length === 0 || input.externalId.trim().length === 0) {
30
+ /**
31
+ * Without a stable source and external id there is nothing to deduplicate
32
+ * on, and accepting the event would risk applying it repeatedly.
33
+ */
34
+ throw new InvalidInputError("An inbound webhook needs a source and an external id.");
35
+ }
36
+ const db = tx ?? deps.db;
37
+ const claimed = await db
38
+ .insert(inboundWebhooks)
39
+ .values({
40
+ source: input.source,
41
+ externalId: input.externalId,
42
+ type: input.type,
43
+ payload: input.payload,
44
+ workspaceId: input.workspaceId ?? null,
45
+ status: "pending",
46
+ })
47
+ .onConflictDoUpdate({
48
+ target: [inboundWebhooks.source, inboundWebhooks.externalId],
49
+ set: { status: "pending", error: null, updatedAt: now() },
50
+ /**
51
+ * Only re-claim a *failed* event. One already `processed` must not be
52
+ * applied twice, and one currently `pending` is being handled by
53
+ * another request right now.
54
+ */
55
+ setWhere: eq(inboundWebhooks.status, "failed"),
56
+ })
57
+ .returning({ id: inboundWebhooks.id });
58
+ const claim = claimed[0];
59
+ if (claim !== undefined) {
60
+ return { status: "claimed", id: claim.id };
61
+ }
62
+ const existing = await db
63
+ .select({ id: inboundWebhooks.id })
64
+ .from(inboundWebhooks)
65
+ .where(and(eq(inboundWebhooks.source, input.source), eq(inboundWebhooks.externalId, input.externalId)))
66
+ .limit(1);
67
+ return { status: "duplicate", id: existing[0]?.id };
68
+ },
69
+ async markProcessed(id) {
70
+ const at = now();
71
+ await deps.db
72
+ .update(inboundWebhooks)
73
+ .set({ status: "processed", processedAt: at, error: null, updatedAt: at })
74
+ .where(eq(inboundWebhooks.id, id));
75
+ },
76
+ async markFailed(id, error) {
77
+ const message = error instanceof Error ? error.message : "Unknown error";
78
+ const at = now();
79
+ await deps.db
80
+ .update(inboundWebhooks)
81
+ .set({
82
+ status: "failed",
83
+ error: message.length > MAX_ERROR_LENGTH
84
+ ? `${message.slice(0, MAX_ERROR_LENGTH - 1)}…`
85
+ : message,
86
+ updatedAt: at,
87
+ })
88
+ .where(eq(inboundWebhooks.id, id));
89
+ },
90
+ async markIgnored(id) {
91
+ const at = now();
92
+ await deps.db
93
+ .update(inboundWebhooks)
94
+ .set({ status: "ignored", processedAt: at, updatedAt: at })
95
+ .where(eq(inboundWebhooks.id, id));
96
+ },
97
+ async get(source, externalId) {
98
+ const rows = await deps.db
99
+ .select()
100
+ .from(inboundWebhooks)
101
+ .where(and(eq(inboundWebhooks.source, source), eq(inboundWebhooks.externalId, externalId)))
102
+ .limit(1);
103
+ return rows[0];
104
+ },
105
+ async listFailed(options = {}) {
106
+ const clauses = [eq(inboundWebhooks.status, "failed")];
107
+ if (options.source !== undefined) {
108
+ clauses.push(eq(inboundWebhooks.source, options.source));
109
+ }
110
+ return deps.db
111
+ .select()
112
+ .from(inboundWebhooks)
113
+ .where(and(...clauses))
114
+ .limit(options.limit ?? DEFAULT_LIMIT);
115
+ },
116
+ async process(input, handler) {
117
+ const outcome = await service.receive(input);
118
+ if (outcome.status === "duplicate") {
119
+ return outcome;
120
+ }
121
+ const webhook = await load(outcome.id);
122
+ if (webhook === undefined) {
123
+ throw new Error("Claimed inbound webhook disappeared.");
124
+ }
125
+ try {
126
+ const result = await handler(webhook);
127
+ if (result === "ignored") {
128
+ await service.markIgnored(outcome.id);
129
+ return { ...outcome, handled: false };
130
+ }
131
+ await service.markProcessed(outcome.id);
132
+ return { ...outcome, handled: true };
133
+ }
134
+ catch (error) {
135
+ /**
136
+ * Record the failure so the event can be replayed from the stored
137
+ * payload, then rethrow so the sender sees a non-2xx and retries.
138
+ */
139
+ await service.markFailed(outcome.id, error);
140
+ throw error;
141
+ }
142
+ },
143
+ };
144
+ return service;
145
+ }
146
+ //# sourceMappingURL=inbound.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inbound.js","sourceRoot":"","sources":["../../src/webhooks/inbound.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import { type InboundWebhooksService } from "./inbound.ts";
2
+ import { type OutgoingWebhooksService } from "./outgoing.ts";
3
+ export { createInboundWebhooksService, InvalidInboundSignatureError, type InboundWebhooksService, type InboundWebhooksServiceDeps, type ReceiveOutcome, type ReceiveWebhookInput, } from "./inbound.ts";
4
+ export { createFetchWebhookSender, createOutgoingWebhooksService, type CreateEndpointInput, type DeliveryAttemptResult, type EndpointWithSecret, type OutgoingWebhooksService, type OutgoingWebhooksServiceDeps, type WebhookSender, } from "./outgoing.ts";
5
+ export { generateWebhookSecret, SIGNATURE_HEADER, SIGNATURE_VERSION, signingPayload, signWebhookPayload, TIMESTAMP_HEADER, verifyWebhookSignature, } from "./signature.ts";
6
+ export interface WebhooksService {
7
+ readonly outgoing: OutgoingWebhooksService;
8
+ readonly inbound: InboundWebhooksService;
9
+ }
10
+ /** Default service, bound to the database and a `fetch`-based sender. */
11
+ export declare const webhooks: WebhooksService;
12
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/webhooks/index.ts"],"names":[],"mappings":"AA6CA,OAAO,EAEL,KAAK,sBAAsB,EAC5B,MAAM,cAAc,CAAC;AACtB,OAAO,EAGL,KAAK,uBAAuB,EAC7B,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,4BAA4B,EAC5B,4BAA4B,EAC5B,KAAK,sBAAsB,EAC3B,KAAK,0BAA0B,EAC/B,KAAK,cAAc,EACnB,KAAK,mBAAmB,GACzB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,wBAAwB,EACxB,6BAA6B,EAC7B,KAAK,mBAAmB,EACxB,KAAK,qBAAqB,EAC1B,KAAK,kBAAkB,EACvB,KAAK,uBAAuB,EAC5B,KAAK,2BAA2B,EAChC,KAAK,aAAa,GACnB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,qBAAqB,EACrB,gBAAgB,EAChB,iBAAiB,EACjB,cAAc,EACd,kBAAkB,EAClB,gBAAgB,EAChB,sBAAsB,GACvB,MAAM,gBAAgB,CAAC;AAExB,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,QAAQ,EAAE,uBAAuB,CAAC;IAC3C,QAAQ,CAAC,OAAO,EAAE,sBAAsB,CAAC;CAC1C;AAED,yEAAyE;AACzE,eAAO,MAAM,QAAQ,EAAE,eAYtB,CAAC"}
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Public webhooks surface: `@digimuza/saas-core/webhooks`. Server-only.
3
+ *
4
+ * Both directions, sharing one signature scheme so there is a single
5
+ * implementation to audit:
6
+ *
7
+ * **Outgoing.** A workspace registers endpoints; events are queued as delivery
8
+ * *records* and sent by a durable worker with signing, retries and backoff. A
9
+ * process dying mid-attempt loses nothing.
10
+ *
11
+ * **Inbound.** Receive from an external system with signature verification and
12
+ * database-enforced idempotency — the same mechanism billing already uses for
13
+ * Revolut, generalised.
14
+ *
15
+ * ```ts
16
+ * import { webhooks } from "@digimuza/saas-core/webhooks";
17
+ *
18
+ * // Register an endpoint. The secret is shown so the receiver can be configured.
19
+ * const { endpoint, secret } = await webhooks.outgoing.endpoints.create(context, {
20
+ * url: "https://customer.example.com/hooks/acme",
21
+ * eventTypes: ["project.created"],
22
+ * });
23
+ *
24
+ * // Queue deliveries, in the same transaction as the change if you like.
25
+ * await webhooks.outgoing.dispatch(event);
26
+ * ```
27
+ *
28
+ * ## Driving delivery
29
+ *
30
+ * The core records deliveries; it does not run a loop. Wire a Trigger.dev
31
+ * scheduled task, so retries are durable rather than tied to a process:
32
+ *
33
+ * ```ts
34
+ * export const drainWebhooks = schedules.task({
35
+ * id: "drain-webhooks",
36
+ * cron: "* * * * *",
37
+ * run: async () => {
38
+ * for (const delivery of await webhooks.outgoing.claimDue({ limit: 100 })) {
39
+ * await webhooks.outgoing.attempt(delivery);
40
+ * }
41
+ * },
42
+ * });
43
+ * ```
44
+ */
45
+ import { getDatabase } from "../db/client.js";
46
+ import { createInboundWebhooksService, } from "./inbound.js";
47
+ import { createFetchWebhookSender, createOutgoingWebhooksService, } from "./outgoing.js";
48
+ export { createInboundWebhooksService, InvalidInboundSignatureError, } from "./inbound.js";
49
+ export { createFetchWebhookSender, createOutgoingWebhooksService, } from "./outgoing.js";
50
+ export { generateWebhookSecret, SIGNATURE_HEADER, SIGNATURE_VERSION, signingPayload, signWebhookPayload, TIMESTAMP_HEADER, verifyWebhookSignature, } from "./signature.js";
51
+ /** Default service, bound to the database and a `fetch`-based sender. */
52
+ export const webhooks = {
53
+ outgoing: createOutgoingWebhooksService({
54
+ get db() {
55
+ return getDatabase();
56
+ },
57
+ sender: createFetchWebhookSender(),
58
+ }),
59
+ inbound: createInboundWebhooksService({
60
+ get db() {
61
+ return getDatabase();
62
+ },
63
+ }),
64
+ };
65
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/webhooks/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EACL,4BAA4B,GAE7B,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,wBAAwB,EACxB,6BAA6B,GAE9B,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,4BAA4B,EAC5B,4BAA4B,GAK7B,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,wBAAwB,EACxB,6BAA6B,GAO9B,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,qBAAqB,EACrB,gBAAgB,EAChB,iBAAiB,EACjB,cAAc,EACd,kBAAkB,EAClB,gBAAgB,EAChB,sBAAsB,GACvB,MAAM,gBAAgB,CAAC;AAOxB,yEAAyE;AACzE,MAAM,CAAC,MAAM,QAAQ,GAAoB;IACvC,QAAQ,EAAE,6BAA6B,CAAC;QACtC,IAAI,EAAE;YACJ,OAAO,WAAW,EAAE,CAAC;QACvB,CAAC;QACD,MAAM,EAAE,wBAAwB,EAAE;KACnC,CAAC;IACF,OAAO,EAAE,4BAA4B,CAAC;QACpC,IAAI,EAAE;YACJ,OAAO,WAAW,EAAE,CAAC;QACvB,CAAC;KACF,CAAC;CACH,CAAC"}
@@ -0,0 +1,89 @@
1
+ import type { WebhookDelivery, WebhookEndpoint } from "../db/schema/index.ts";
2
+ import type { CoreDatabase } from "../db/types.ts";
3
+ import type { SaasEvent } from "../events/registry.ts";
4
+ import type { WorkspaceContext } from "../workspaces/context.ts";
5
+ export interface CreateEndpointInput {
6
+ readonly url: string;
7
+ readonly description?: string;
8
+ /** Event types to receive. Empty means every type. */
9
+ readonly eventTypes?: readonly string[];
10
+ }
11
+ /** An endpoint as returned to a caller, with the secret shown. */
12
+ export interface EndpointWithSecret {
13
+ readonly endpoint: WebhookEndpoint;
14
+ readonly secret: string;
15
+ }
16
+ export interface DeliveryAttemptResult {
17
+ readonly delivered: boolean;
18
+ readonly responseStatus?: number;
19
+ readonly error?: string;
20
+ }
21
+ /**
22
+ * Performs the actual HTTP request.
23
+ *
24
+ * Injectable so retry and backoff logic is testable without a network, and so a
25
+ * deployment can route webhook traffic through its own egress proxy — which is
26
+ * how you stop a tenant pointing an endpoint at an internal address.
27
+ */
28
+ export interface WebhookSender {
29
+ send(input: {
30
+ url: string;
31
+ body: string;
32
+ headers: Record<string, string>;
33
+ }): Promise<DeliveryAttemptResult>;
34
+ }
35
+ export interface OutgoingWebhooksService {
36
+ readonly endpoints: {
37
+ create(context: WorkspaceContext, input: CreateEndpointInput): Promise<EndpointWithSecret>;
38
+ list(context: WorkspaceContext): Promise<WebhookEndpoint[]>;
39
+ /** Includes the signing secret, so the receiver can be configured. */
40
+ get(context: WorkspaceContext, endpointId: string): Promise<EndpointWithSecret>;
41
+ update(context: WorkspaceContext, endpointId: string, patch: {
42
+ url?: string;
43
+ description?: string | null;
44
+ eventTypes?: readonly string[];
45
+ isActive?: boolean;
46
+ }): Promise<WebhookEndpoint>;
47
+ /** Issue a new secret. The old one stops working immediately. */
48
+ rotateSecret(context: WorkspaceContext, endpointId: string): Promise<EndpointWithSecret>;
49
+ remove(context: WorkspaceContext, endpointId: string): Promise<void>;
50
+ };
51
+ /**
52
+ * Queue deliveries for every active endpoint subscribed to this event.
53
+ *
54
+ * Records rows; sends nothing. The HTTP calls are driven by a durable worker
55
+ * reading the table, so a process dying between dispatch and delivery loses
56
+ * nothing.
57
+ */
58
+ dispatch(event: SaasEvent, tx?: CoreDatabase): Promise<WebhookDelivery[]>;
59
+ /** Deliveries whose next attempt is due. For a worker to pick up. */
60
+ claimDue(options?: {
61
+ limit?: number;
62
+ }): Promise<WebhookDelivery[]>;
63
+ /** Attempt one delivery and record the outcome. */
64
+ attempt(delivery: WebhookDelivery): Promise<DeliveryAttemptResult>;
65
+ /** A workspace's delivery history, newest first. */
66
+ listDeliveries(context: WorkspaceContext, options?: {
67
+ endpointId?: string;
68
+ limit?: number;
69
+ }): Promise<WebhookDelivery[]>;
70
+ }
71
+ export interface OutgoingWebhooksServiceDeps {
72
+ readonly db: CoreDatabase;
73
+ readonly sender: WebhookSender;
74
+ readonly now?: () => Date;
75
+ /** Attempts before a delivery is exhausted. */
76
+ readonly maxAttempts?: number;
77
+ /** First retry delay; doubles per attempt. */
78
+ readonly initialRetryDelayMs?: number;
79
+ readonly maxRetryDelayMs?: number;
80
+ /** Consecutive failures before an endpoint is disabled. */
81
+ readonly failureThreshold?: number;
82
+ }
83
+ export declare function createOutgoingWebhooksService(deps: OutgoingWebhooksServiceDeps): OutgoingWebhooksService;
84
+ /** A {@link WebhookSender} over `fetch`, with a timeout. */
85
+ export declare function createFetchWebhookSender(options?: {
86
+ timeoutMs?: number;
87
+ fetchImpl?: typeof fetch;
88
+ }): WebhookSender;
89
+ //# sourceMappingURL=outgoing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outgoing.d.ts","sourceRoot":"","sources":["../../src/webhooks/outgoing.ts"],"names":[],"mappings":"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"}