@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,375 @@
1
+ import { and, asc, eq, inArray, lte, or, sql } from "drizzle-orm";
2
+ import { webhookDeliveries, webhookEndpoints } from "../db/schema/index.js";
3
+ import { ConflictError, InvalidInputError, NotFoundError } from "../errors.js";
4
+ import { isUniqueViolation } from "../db/errors.js";
5
+ import { generateWebhookSecret, signWebhookPayload, SIGNATURE_HEADER, TIMESTAMP_HEADER, } from "./signature.js";
6
+ const DEFAULT_MAX_ATTEMPTS = 6;
7
+ const DEFAULT_INITIAL_RETRY_MS = 30_000;
8
+ const DEFAULT_MAX_RETRY_MS = 6 * 60 * 60 * 1000;
9
+ const DEFAULT_FAILURE_THRESHOLD = 20;
10
+ const DEFAULT_CLAIM_LIMIT = 50;
11
+ const MAX_CLAIM_LIMIT = 500;
12
+ const MAX_ERROR_LENGTH = 1000;
13
+ /** Reject URLs a tenant should not be able to make the server call. */
14
+ function assertDeliverableUrl(url) {
15
+ let parsed;
16
+ try {
17
+ parsed = new URL(url);
18
+ }
19
+ catch {
20
+ throw new InvalidInputError("A webhook URL must be absolute.");
21
+ }
22
+ if (parsed.protocol !== "https:") {
23
+ /**
24
+ * HTTPS only. A signed payload over plain HTTP still exposes its contents,
25
+ * and the signature does not stop an interceptor reading it.
26
+ */
27
+ throw new InvalidInputError("A webhook URL must use https.");
28
+ }
29
+ const host = parsed.hostname.toLowerCase();
30
+ /**
31
+ * First-pass SSRF guard. A tenant registering `http://169.254.169.254/` would
32
+ * otherwise have the server fetch its own cloud metadata and post the result
33
+ * back. DNS can still resolve a public name to a private address, so a
34
+ * production deployment should also route egress through a proxy that enforces
35
+ * this at connect time — which is why {@link WebhookSender} is injectable.
36
+ */
37
+ const blocked = host === "localhost" ||
38
+ host === "0.0.0.0" ||
39
+ host.endsWith(".localhost") ||
40
+ host.endsWith(".internal") ||
41
+ host.endsWith(".local") ||
42
+ /^127\./.test(host) ||
43
+ /^10\./.test(host) ||
44
+ /^192\.168\./.test(host) ||
45
+ /^169\.254\./.test(host) ||
46
+ /^172\.(1[6-9]|2\d|3[01])\./.test(host) ||
47
+ host === "[::1]" ||
48
+ host === "::1";
49
+ if (blocked) {
50
+ throw new InvalidInputError("A webhook URL may not point at a private or loopback address.");
51
+ }
52
+ }
53
+ function truncate(message) {
54
+ return message.length > MAX_ERROR_LENGTH
55
+ ? `${message.slice(0, MAX_ERROR_LENGTH - 1)}…`
56
+ : message;
57
+ }
58
+ export function createOutgoingWebhooksService(deps) {
59
+ const now = deps.now ?? (() => new Date());
60
+ const maxAttempts = deps.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;
61
+ const initialRetryMs = deps.initialRetryDelayMs ?? DEFAULT_INITIAL_RETRY_MS;
62
+ const maxRetryMs = deps.maxRetryDelayMs ?? DEFAULT_MAX_RETRY_MS;
63
+ const failureThreshold = deps.failureThreshold ?? DEFAULT_FAILURE_THRESHOLD;
64
+ /** Doubling backoff, capped. Attempt 1 has already happened when this runs. */
65
+ function retryDelayMs(attemptCount) {
66
+ return Math.min(initialRetryMs * 2 ** (attemptCount - 1), maxRetryMs);
67
+ }
68
+ async function requireOwnEndpoint(context, endpointId) {
69
+ const rows = await deps.db
70
+ .select()
71
+ .from(webhookEndpoints)
72
+ .where(and(eq(webhookEndpoints.id, endpointId),
73
+ // Scoped, so another tenant's endpoint id is simply not found.
74
+ eq(webhookEndpoints.workspaceId, context.workspaceId)))
75
+ .limit(1);
76
+ const endpoint = rows[0];
77
+ if (endpoint === undefined) {
78
+ throw new NotFoundError("Webhook endpoint not found.");
79
+ }
80
+ return endpoint;
81
+ }
82
+ const endpoints = {
83
+ async create(context, input) {
84
+ assertDeliverableUrl(input.url);
85
+ const secret = generateWebhookSecret();
86
+ try {
87
+ const [endpoint] = await deps.db
88
+ .insert(webhookEndpoints)
89
+ .values({
90
+ workspaceId: context.workspaceId,
91
+ url: input.url,
92
+ description: input.description ?? null,
93
+ secret,
94
+ eventTypes: [...new Set(input.eventTypes ?? [])],
95
+ })
96
+ .returning();
97
+ return { endpoint: endpoint, secret };
98
+ }
99
+ catch (error) {
100
+ if (isUniqueViolation(error, "webhook_endpoints_workspace_url_unique")) {
101
+ throw new ConflictError("That URL is already registered for this workspace.", {
102
+ cause: error,
103
+ });
104
+ }
105
+ throw error;
106
+ }
107
+ },
108
+ async list(context) {
109
+ return deps.db
110
+ .select()
111
+ .from(webhookEndpoints)
112
+ .where(eq(webhookEndpoints.workspaceId, context.workspaceId))
113
+ .orderBy(asc(webhookEndpoints.createdAt));
114
+ },
115
+ async get(context, endpointId) {
116
+ const endpoint = await requireOwnEndpoint(context, endpointId);
117
+ return { endpoint, secret: endpoint.secret };
118
+ },
119
+ async update(context, endpointId, patch) {
120
+ await requireOwnEndpoint(context, endpointId);
121
+ if (patch.url !== undefined) {
122
+ assertDeliverableUrl(patch.url);
123
+ }
124
+ const [updated] = await deps.db
125
+ .update(webhookEndpoints)
126
+ .set({
127
+ ...(patch.url !== undefined ? { url: patch.url } : {}),
128
+ ...(patch.description !== undefined ? { description: patch.description } : {}),
129
+ ...(patch.eventTypes !== undefined
130
+ ? { eventTypes: [...new Set(patch.eventTypes)] }
131
+ : {}),
132
+ ...(patch.isActive !== undefined
133
+ ? {
134
+ isActive: patch.isActive,
135
+ // Re-enabling clears the failure state, or it would disable again
136
+ // on the next failure regardless of what changed.
137
+ ...(patch.isActive
138
+ ? { consecutiveFailures: 0, disabledAt: null }
139
+ : { disabledAt: now() }),
140
+ }
141
+ : {}),
142
+ updatedAt: now(),
143
+ })
144
+ .where(eq(webhookEndpoints.id, endpointId))
145
+ .returning();
146
+ return updated;
147
+ },
148
+ async rotateSecret(context, endpointId) {
149
+ await requireOwnEndpoint(context, endpointId);
150
+ const secret = generateWebhookSecret();
151
+ const [updated] = await deps.db
152
+ .update(webhookEndpoints)
153
+ .set({ secret, updatedAt: now() })
154
+ .where(eq(webhookEndpoints.id, endpointId))
155
+ .returning();
156
+ return { endpoint: updated, secret };
157
+ },
158
+ async remove(context, endpointId) {
159
+ await requireOwnEndpoint(context, endpointId);
160
+ // Deliveries cascade; a removed endpoint has no history worth retrying.
161
+ await deps.db.delete(webhookEndpoints).where(eq(webhookEndpoints.id, endpointId));
162
+ },
163
+ };
164
+ return {
165
+ endpoints,
166
+ async dispatch(event, tx) {
167
+ if (event.workspaceId === null) {
168
+ /**
169
+ * Outgoing webhooks are a tenant feature. A platform-wide event has no
170
+ * workspace whose endpoints should receive it, and picking one would leak
171
+ * across tenants.
172
+ */
173
+ return [];
174
+ }
175
+ const db = tx ?? deps.db;
176
+ const subscribed = await db
177
+ .select()
178
+ .from(webhookEndpoints)
179
+ .where(and(eq(webhookEndpoints.workspaceId, event.workspaceId), eq(webhookEndpoints.isActive, true),
180
+ // Empty `event_types` means "everything".
181
+ or(sql `cardinality(${webhookEndpoints.eventTypes}) = 0`, sql `${event.type} = any(${webhookEndpoints.eventTypes})`)));
182
+ if (subscribed.length === 0) {
183
+ return [];
184
+ }
185
+ const payload = {
186
+ id: event.id,
187
+ type: event.type,
188
+ workspaceId: event.workspaceId,
189
+ occurredAt: event.occurredAt.toISOString(),
190
+ data: event.data,
191
+ };
192
+ /**
193
+ * `onConflictDoNothing` on (endpoint, event) is the idempotency guarantee:
194
+ * dispatching the same event twice — a retried job, a replayed event —
195
+ * cannot produce a second delivery.
196
+ */
197
+ return db
198
+ .insert(webhookDeliveries)
199
+ .values(subscribed.map((endpoint) => ({
200
+ endpointId: endpoint.id,
201
+ eventId: event.id,
202
+ eventType: event.type,
203
+ payload: payload,
204
+ status: "pending",
205
+ nextAttemptAt: now(),
206
+ })))
207
+ .onConflictDoNothing({
208
+ target: [webhookDeliveries.endpointId, webhookDeliveries.eventId],
209
+ })
210
+ .returning();
211
+ },
212
+ async claimDue(options = {}) {
213
+ const at = now();
214
+ return deps.db
215
+ .select()
216
+ .from(webhookDeliveries)
217
+ .where(and(inArray(webhookDeliveries.status, ["pending", "failed"]), lte(webhookDeliveries.nextAttemptAt, at)))
218
+ .orderBy(asc(webhookDeliveries.nextAttemptAt))
219
+ .limit(Math.min(options.limit ?? DEFAULT_CLAIM_LIMIT, MAX_CLAIM_LIMIT));
220
+ },
221
+ async attempt(delivery) {
222
+ const endpointRows = await deps.db
223
+ .select()
224
+ .from(webhookEndpoints)
225
+ .where(eq(webhookEndpoints.id, delivery.endpointId))
226
+ .limit(1);
227
+ const endpoint = endpointRows[0];
228
+ if (endpoint === undefined) {
229
+ throw new NotFoundError("Webhook endpoint not found.");
230
+ }
231
+ const at = now();
232
+ const body = JSON.stringify(delivery.payload);
233
+ const signature = signWebhookPayload({
234
+ rawBody: body,
235
+ secret: endpoint.secret,
236
+ timestampMs: at.getTime(),
237
+ });
238
+ let result;
239
+ try {
240
+ result = await deps.sender.send({
241
+ url: endpoint.url,
242
+ body,
243
+ headers: {
244
+ "Content-Type": "application/json",
245
+ [SIGNATURE_HEADER]: signature,
246
+ [TIMESTAMP_HEADER]: String(at.getTime()),
247
+ "X-Saas-Core-Event-Id": delivery.eventId,
248
+ "X-Saas-Core-Event-Type": delivery.eventType,
249
+ "X-Saas-Core-Delivery-Id": delivery.id,
250
+ "X-Saas-Core-Attempt": String(delivery.attemptCount + 1),
251
+ },
252
+ });
253
+ }
254
+ catch (error) {
255
+ // A sender that throws is a failed attempt, not a crashed worker.
256
+ result = {
257
+ delivered: false,
258
+ error: truncate(error instanceof Error ? error.message : "Unknown error"),
259
+ };
260
+ }
261
+ const attemptCount = delivery.attemptCount + 1;
262
+ if (result.delivered) {
263
+ await deps.db.transaction(async (tx) => {
264
+ await tx
265
+ .update(webhookDeliveries)
266
+ .set({
267
+ status: "succeeded",
268
+ attemptCount,
269
+ lastAttemptedAt: at,
270
+ nextAttemptAt: null,
271
+ responseStatus: result.responseStatus ?? null,
272
+ error: null,
273
+ updatedAt: at,
274
+ })
275
+ .where(eq(webhookDeliveries.id, delivery.id));
276
+ // Any success clears the failure streak.
277
+ await tx
278
+ .update(webhookEndpoints)
279
+ .set({ consecutiveFailures: 0, updatedAt: at })
280
+ .where(eq(webhookEndpoints.id, endpoint.id));
281
+ });
282
+ return result;
283
+ }
284
+ const exhausted = attemptCount >= maxAttempts;
285
+ await deps.db.transaction(async (tx) => {
286
+ await tx
287
+ .update(webhookDeliveries)
288
+ .set({
289
+ status: exhausted ? "exhausted" : "failed",
290
+ attemptCount,
291
+ lastAttemptedAt: at,
292
+ // Null once terminal, so an exhausted delivery is never re-claimed.
293
+ nextAttemptAt: exhausted ? null : new Date(at.getTime() + retryDelayMs(attemptCount)),
294
+ responseStatus: result.responseStatus ?? null,
295
+ error: result.error ?? "Delivery failed.",
296
+ updatedAt: at,
297
+ })
298
+ .where(eq(webhookDeliveries.id, delivery.id));
299
+ const failures = endpoint.consecutiveFailures + 1;
300
+ await tx
301
+ .update(webhookEndpoints)
302
+ .set({
303
+ consecutiveFailures: failures,
304
+ /**
305
+ * An endpoint broken for a long time should stop consuming retry
306
+ * budget. Disabling it is recoverable — an operator re-enables it,
307
+ * which clears the streak.
308
+ */
309
+ ...(failures >= failureThreshold
310
+ ? { isActive: false, disabledAt: at }
311
+ : {}),
312
+ updatedAt: at,
313
+ })
314
+ .where(eq(webhookEndpoints.id, endpoint.id));
315
+ });
316
+ return result;
317
+ },
318
+ async listDeliveries(context, options = {}) {
319
+ const own = await deps.db
320
+ .select({ id: webhookEndpoints.id })
321
+ .from(webhookEndpoints)
322
+ .where(eq(webhookEndpoints.workspaceId, context.workspaceId));
323
+ const endpointIds = own.map((row) => row.id);
324
+ if (endpointIds.length === 0) {
325
+ return [];
326
+ }
327
+ if (options.endpointId !== undefined && !endpointIds.includes(options.endpointId)) {
328
+ // Another tenant's endpoint id: return nothing rather than confirming it.
329
+ return [];
330
+ }
331
+ return deps.db
332
+ .select()
333
+ .from(webhookDeliveries)
334
+ .where(options.endpointId !== undefined
335
+ ? eq(webhookDeliveries.endpointId, options.endpointId)
336
+ : inArray(webhookDeliveries.endpointId, endpointIds))
337
+ .orderBy(sql `${webhookDeliveries.createdAt} desc`)
338
+ .limit(Math.min(options.limit ?? DEFAULT_CLAIM_LIMIT, MAX_CLAIM_LIMIT));
339
+ },
340
+ };
341
+ }
342
+ /** A {@link WebhookSender} over `fetch`, with a timeout. */
343
+ export function createFetchWebhookSender(options = {}) {
344
+ const timeoutMs = options.timeoutMs ?? 10_000;
345
+ const fetchImpl = options.fetchImpl ?? globalThis.fetch;
346
+ return {
347
+ async send({ url, body, headers }) {
348
+ // Without a timeout, one slow receiver holds a worker slot indefinitely.
349
+ const controller = new AbortController();
350
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
351
+ try {
352
+ const response = await fetchImpl(url, {
353
+ method: "POST",
354
+ headers,
355
+ body,
356
+ signal: controller.signal,
357
+ // Never follow a redirect: it could land somewhere the URL check
358
+ // rejected.
359
+ redirect: "manual",
360
+ });
361
+ return {
362
+ delivered: response.status >= 200 && response.status < 300,
363
+ responseStatus: response.status,
364
+ ...(response.status >= 300
365
+ ? { error: `Receiver responded with ${response.status}.` }
366
+ : {}),
367
+ };
368
+ }
369
+ finally {
370
+ clearTimeout(timer);
371
+ }
372
+ },
373
+ };
374
+ }
375
+ //# sourceMappingURL=outgoing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outgoing.js","sourceRoot":"","sources":["../../src/webhooks/outgoing.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Webhook signing, shared by both directions.
3
+ *
4
+ * The same scheme the core already *verifies* from Revolut: HMAC-SHA256 over
5
+ * `v1.{timestamp}.{body}`, presented as `v1=<hex>`. Using one scheme for outgoing
6
+ * signatures too means there is a single implementation to audit, and receivers
7
+ * can follow the same instructions Stripe and Revolut users already know.
8
+ */
9
+ export declare const SIGNATURE_VERSION = "v1";
10
+ export declare const SIGNATURE_HEADER = "x-saas-core-signature";
11
+ export declare const TIMESTAMP_HEADER = "x-saas-core-timestamp";
12
+ /** 256 bits, base64url. Long enough that brute force is not a concern. */
13
+ export declare function generateWebhookSecret(): string;
14
+ /** The exact string that gets signed. Both sides must agree on it byte for byte. */
15
+ export declare function signingPayload(timestampMs: number, rawBody: string): string;
16
+ export declare function signWebhookPayload(input: {
17
+ rawBody: string;
18
+ secret: string;
19
+ timestampMs: number;
20
+ }): string;
21
+ /**
22
+ * Verify a signature over a raw body.
23
+ *
24
+ * Must be given the body **exactly as received**. Parsing and re-serialising JSON
25
+ * changes key order and whitespace, which invalidates the signature — so a route
26
+ * handler has to read it as text before touching it.
27
+ *
28
+ * Accepts several comma-separated signatures, because that is how a sender
29
+ * presents both the old and new secret while rotating.
30
+ */
31
+ export declare function verifyWebhookSignature(input: {
32
+ rawBody: string;
33
+ signature: string;
34
+ timestamp: string | number;
35
+ /** One secret, or several during rotation. */
36
+ secret: string | readonly string[];
37
+ toleranceSeconds?: number;
38
+ now?: () => number;
39
+ }): boolean;
40
+ //# sourceMappingURL=signature.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signature.d.ts","sourceRoot":"","sources":["../../src/webhooks/signature.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,OAAO,CAAC;AACtC,eAAO,MAAM,gBAAgB,0BAA0B,CAAC;AACxD,eAAO,MAAM,gBAAgB,0BAA0B,CAAC;AAKxD,0EAA0E;AAC1E,wBAAgB,qBAAqB,IAAI,MAAM,CAE9C;AAED,oFAAoF;AACpF,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAE3E;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;CACrB,GAAG,MAAM,CAMT;AAED;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE;IAC5C,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3B,8CAA8C;IAC9C,MAAM,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAC;IACnC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB,GAAG,OAAO,CA+CV"}
@@ -0,0 +1,77 @@
1
+ import { createHmac, randomBytes, timingSafeEqual } from "node:crypto";
2
+ /**
3
+ * Webhook signing, shared by both directions.
4
+ *
5
+ * The same scheme the core already *verifies* from Revolut: HMAC-SHA256 over
6
+ * `v1.{timestamp}.{body}`, presented as `v1=<hex>`. Using one scheme for outgoing
7
+ * signatures too means there is a single implementation to audit, and receivers
8
+ * can follow the same instructions Stripe and Revolut users already know.
9
+ */
10
+ export const SIGNATURE_VERSION = "v1";
11
+ export const SIGNATURE_HEADER = "x-saas-core-signature";
12
+ export const TIMESTAMP_HEADER = "x-saas-core-timestamp";
13
+ /** Default freshness window. A signature is valid forever without one. */
14
+ const DEFAULT_TOLERANCE_SECONDS = 300;
15
+ /** 256 bits, base64url. Long enough that brute force is not a concern. */
16
+ export function generateWebhookSecret() {
17
+ return `whsec_${randomBytes(32).toString("base64url")}`;
18
+ }
19
+ /** The exact string that gets signed. Both sides must agree on it byte for byte. */
20
+ export function signingPayload(timestampMs, rawBody) {
21
+ return `${SIGNATURE_VERSION}.${timestampMs}.${rawBody}`;
22
+ }
23
+ export function signWebhookPayload(input) {
24
+ const digest = createHmac("sha256", input.secret)
25
+ .update(signingPayload(input.timestampMs, input.rawBody))
26
+ .digest("hex");
27
+ return `${SIGNATURE_VERSION}=${digest}`;
28
+ }
29
+ /**
30
+ * Verify a signature over a raw body.
31
+ *
32
+ * Must be given the body **exactly as received**. Parsing and re-serialising JSON
33
+ * changes key order and whitespace, which invalidates the signature — so a route
34
+ * handler has to read it as text before touching it.
35
+ *
36
+ * Accepts several comma-separated signatures, because that is how a sender
37
+ * presents both the old and new secret while rotating.
38
+ */
39
+ export function verifyWebhookSignature(input) {
40
+ const timestampMs = typeof input.timestamp === "number" ? input.timestamp : Number(input.timestamp);
41
+ if (!Number.isFinite(timestampMs)) {
42
+ return false;
43
+ }
44
+ const tolerance = (input.toleranceSeconds ?? DEFAULT_TOLERANCE_SECONDS) * 1000;
45
+ const at = (input.now ?? Date.now)();
46
+ /**
47
+ * Stale *and* future timestamps are rejected. A signature stays valid forever,
48
+ * so without a window a captured request could be replayed indefinitely — and a
49
+ * clock skewed forward would widen that window rather than narrow it.
50
+ */
51
+ if (Math.abs(at - timestampMs) > tolerance) {
52
+ return false;
53
+ }
54
+ const secrets = typeof input.secret === "string" ? [input.secret] : input.secret;
55
+ const presented = input.signature
56
+ .split(",")
57
+ .map((part) => part.trim())
58
+ .map((part) => (part.startsWith(`${SIGNATURE_VERSION}=`) ? part.slice(3) : part))
59
+ .filter((part) => part.length > 0);
60
+ if (presented.length === 0) {
61
+ return false;
62
+ }
63
+ return secrets.some((secret) => {
64
+ const expected = Buffer.from(createHmac("sha256", secret)
65
+ .update(signingPayload(timestampMs, input.rawBody))
66
+ .digest("hex"), "utf8");
67
+ return presented.some((candidate) => {
68
+ const candidateBuffer = Buffer.from(candidate, "utf8");
69
+ if (candidateBuffer.length !== expected.length) {
70
+ return false;
71
+ }
72
+ // Constant-time: a hex comparison leaks how many leading digits matched.
73
+ return timingSafeEqual(candidateBuffer, expected);
74
+ });
75
+ });
76
+ }
77
+ //# sourceMappingURL=signature.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signature.js","sourceRoot":"","sources":["../../src/webhooks/signature.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * A caller's identity within one workspace, proven to correspond to a real
3
+ * membership.
4
+ *
5
+ * Every tenant-scoped operation takes one of these. The `membershipVerified`
6
+ * marker exists so that a `workspaceId` taken straight from a request body,
7
+ * query string or path parameter cannot be passed where a verified context is
8
+ * required — the type will not accept it. Obtain one from
9
+ * `workspaces.resolveContext()` or `workspaces.requireContext()`, which check
10
+ * `workspace_members` first.
11
+ *
12
+ * The marker is a convention, not a cryptographic guarantee: it is greppable and
13
+ * makes bypassing the check a deliberate, visible act rather than an oversight.
14
+ */
15
+ export interface WorkspaceContext {
16
+ readonly userId: string;
17
+ readonly workspaceId: string;
18
+ /** @internal Set only by a verified membership lookup. Do not construct by hand. */
19
+ readonly membershipVerified: true;
20
+ }
21
+ /**
22
+ * Construct a verified context.
23
+ *
24
+ * Internal to the core. Callers outside it must go through
25
+ * `workspaces.resolveContext()`, which performs the membership check.
26
+ *
27
+ * @internal
28
+ */
29
+ export declare function unsafeWorkspaceContext(userId: string, workspaceId: string): WorkspaceContext;
30
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/workspaces/context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,oFAAoF;IACpF,QAAQ,CAAC,kBAAkB,EAAE,IAAI,CAAC;CACnC;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,gBAAgB,CAE5F"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Construct a verified context.
3
+ *
4
+ * Internal to the core. Callers outside it must go through
5
+ * `workspaces.resolveContext()`, which performs the membership check.
6
+ *
7
+ * @internal
8
+ */
9
+ export function unsafeWorkspaceContext(userId, workspaceId) {
10
+ return { userId, workspaceId, membershipVerified: true };
11
+ }
12
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/workspaces/context.ts"],"names":[],"mappings":"AAqBA;;;;;;;GAOG;AACH,MAAM,UAAU,sBAAsB,CAAC,MAAc,EAAE,WAAmB;IACxE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,kBAAkB,EAAE,IAAI,EAAE,CAAC;AAC3D,CAAC"}
@@ -0,0 +1,5 @@
1
+ export { type WorkspaceContext } from "./context.ts";
2
+ export { createWorkspacesService, DEFAULT_SYSTEM_ROLES, slugify, type AcceptInvitationInput, type CreateWorkspaceInput, type InvitationWithToken, type InviteMemberInput, type SystemRoleDefinition, type WorkspaceMemberSummary, type WorkspacesService, type WorkspacesServiceDeps, } from "./service.ts";
3
+ /** Default service, bound to the configuration-driven database handle. */
4
+ export declare const workspaces: import("./service.ts").WorkspacesService;
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/workspaces/index.ts"],"names":[],"mappings":"AAYA,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,EACL,uBAAuB,EACvB,oBAAoB,EACpB,OAAO,EACP,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,EACzB,KAAK,sBAAsB,EAC3B,KAAK,iBAAiB,EACtB,KAAK,qBAAqB,GAC3B,MAAM,cAAc,CAAC;AAEtB,0EAA0E;AAC1E,eAAO,MAAM,UAAU,0CAKrB,CAAC"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Public workspaces surface: `@digimuza/saas-core/workspaces`. Server-only.
3
+ *
4
+ * A workspace is the tenancy boundary. Every operation here takes a
5
+ * {@link WorkspaceContext}, which can only be produced by a verified membership
6
+ * lookup — so a `workspaceId` supplied by a client cannot reach a query without
7
+ * passing through `resolveContext()` first.
8
+ */
9
+ import { getDatabase } from "../db/client.js";
10
+ import { events as coreEventsService } from "../events/index.js";
11
+ import { createWorkspacesService } from "./service.js";
12
+ export {} from "./context.js";
13
+ export { createWorkspacesService, DEFAULT_SYSTEM_ROLES, slugify, } from "./service.js";
14
+ /** Default service, bound to the configuration-driven database handle. */
15
+ export const workspaces = createWorkspacesService({
16
+ get db() {
17
+ return getDatabase();
18
+ },
19
+ events: coreEventsService,
20
+ });
21
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/workspaces/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAE,MAAM,IAAI,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AAEvD,OAAO,EAAyB,MAAM,cAAc,CAAC;AACrD,OAAO,EACL,uBAAuB,EACvB,oBAAoB,EACpB,OAAO,GASR,MAAM,cAAc,CAAC;AAEtB,0EAA0E;AAC1E,MAAM,CAAC,MAAM,UAAU,GAAG,uBAAuB,CAAC;IAChD,IAAI,EAAE;QACJ,OAAO,WAAW,EAAE,CAAC;IACvB,CAAC;IACD,MAAM,EAAE,iBAAiB;CAC1B,CAAC,CAAC"}