@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
package/docs/email.md ADDED
@@ -0,0 +1,222 @@
1
+ # Email
2
+
3
+ ```ts
4
+ import {
5
+ createEmailService, createResendProvider, coreEmailTemplates,
6
+ defineEmailTemplates, emailTemplate, mergeEmailTemplates,
7
+ } from "@digimuza/saas-core/email";
8
+ ```
9
+
10
+ > Orientation and recipes: [`src/email/README.md`](../src/email/README.md).
11
+
12
+ Provider-independent sending with typed templates. Everything above
13
+ `EmailProvider` is generic, so adopting another provider means writing a ~20-line
14
+ adapter.
15
+
16
+ ---
17
+
18
+ ## Setup
19
+
20
+ ```ts
21
+ // lib/email.ts
22
+ import { z } from "zod";
23
+ import { config } from "@digimuza/saas-core/config";
24
+
25
+ const templates = mergeEmailTemplates(
26
+ coreEmailTemplates,
27
+ defineEmailTemplates({
28
+ "project.shared": emailTemplate({
29
+ schema: z.object({ projectName: z.string().min(1), sharedBy: z.string().min(1) }),
30
+ subject: (data) => `${data.sharedBy} shared "${data.projectName}" with you`,
31
+ text: (data) => `${data.sharedBy} shared ${data.projectName}.`,
32
+ html: (data) => `<p>${data.sharedBy} shared ${data.projectName}.</p>`,
33
+ }),
34
+ }),
35
+ );
36
+
37
+ export const email = createEmailService({
38
+ provider: createResendProvider({ apiKey: config.email.apiKey }),
39
+ templates,
40
+ from: { address: config.email.fromAddress, name: config.email.fromName },
41
+ replyTo: { address: "support@acme.com" },
42
+ });
43
+ ```
44
+
45
+ `createConfiguredEmailProvider()` and `configuredSender()` build both from the
46
+ `email` config section, choosing the console logger in development.
47
+
48
+ ### `emailTemplate()` is load-bearing
49
+
50
+ TypeScript cannot infer a callback's parameter type from a **sibling** property of
51
+ the same object literal. Writing a template inline leaves `data` as `unknown` in
52
+ `subject` and `text`; binding the schema generic inside `emailTemplate()` is what
53
+ types them.
54
+
55
+ ---
56
+
57
+ ## `email.sendTemplate(input)`
58
+
59
+ ```ts
60
+ function sendTemplate(input: {
61
+ to: string | EmailAddress | readonly (string | EmailAddress)[];
62
+ template: K; // a key of the registry
63
+ data: z.input<T[K]["schema"]>; // narrowed to that template
64
+ from?: EmailAddress;
65
+ replyTo?: string | EmailAddress;
66
+ idempotencyKey?: string;
67
+ headers?: Record<string, string>;
68
+ }): Promise<SentEmail>
69
+ ```
70
+
71
+ `data` is validated **before** a message is built, so a missing field throws
72
+ `InvalidInputError` naming it rather than emailing a customer the word
73
+ "undefined".
74
+
75
+ ## `email.send(input)`
76
+
77
+ ```ts
78
+ function send(input: {
79
+ to: string | EmailAddress | readonly (string | EmailAddress)[];
80
+ subject: string;
81
+ text: string; // required
82
+ html?: string; // optional
83
+ from?: EmailAddress;
84
+ replyTo?: string | EmailAddress;
85
+ cc?: readonly (string | EmailAddress)[];
86
+ bcc?: readonly (string | EmailAddress)[];
87
+ idempotencyKey?: string;
88
+ headers?: Record<string, string>;
89
+ }): Promise<SentEmail>
90
+ ```
91
+
92
+ **A text part is mandatory; HTML is not.** Some clients render only text, and a
93
+ text-less message scores worse with spam filters.
94
+
95
+ Recipients accept a string, an object, or a list of either.
96
+
97
+ ## `email.render(template, data)`
98
+
99
+ ```ts
100
+ function render<K>(template: K, data: z.input<T[K]["schema"]>):
101
+ { subject: string; text: string; html?: string }
102
+ ```
103
+
104
+ Renders without sending — for previews, and for asserting copy in tests.
105
+
106
+ ---
107
+
108
+ ## Idempotency
109
+
110
+ **Sending is not transactional with the database.** A retried request can send
111
+ twice, and `idempotencyKey` is the only defence. Pass one for anything a customer
112
+ would notice receiving twice.
113
+
114
+ Resend honours it for 24 hours, via an `Idempotency-Key` header. A provider that
115
+ ignores it will simply deliver twice — the interface cannot fix that.
116
+
117
+ ---
118
+
119
+ ## Templates
120
+
121
+ ### `defineEmailTemplates` / `mergeEmailTemplates` / `overrideEmailTemplates`
122
+
123
+ `mergeEmailTemplates` **throws** on a duplicate key. `overrideEmailTemplates`
124
+ replaces. Two functions, so shadowing a core template is a visible decision rather
125
+ than silent:
126
+
127
+ ```ts
128
+ const templates = overrideEmailTemplates(
129
+ coreEmailTemplates,
130
+ defineEmailTemplates({
131
+ "workspace.invitation": emailTemplate({ /* your branding */ }),
132
+ }),
133
+ );
134
+ ```
135
+
136
+ ### What the core ships
137
+
138
+ **One template: `workspace.invitation`.** It has to exist — the core creates
139
+ invitations, and the single-use token cannot be delivered without an email.
140
+
141
+ ```ts
142
+ {
143
+ workspaceName: string;
144
+ inviterName?: string;
145
+ acceptUrl: string; // absolute, carries the token
146
+ expiresAt: Date;
147
+ }
148
+ ```
149
+
150
+ Its copy is intentionally plain. Override it for anything customer-facing.
151
+
152
+ Welcome, security and billing emails are **not** shipped. They are product copy;
153
+ a default would mean shipping branding decisions every application overrides
154
+ anyway.
155
+
156
+ ---
157
+
158
+ ## Providers
159
+
160
+ ```ts
161
+ interface EmailProvider {
162
+ readonly name: string;
163
+ send(message: OutgoingEmail): Promise<SentEmail>;
164
+ }
165
+ ```
166
+
167
+ One method, because sending is all the core needs. Bounce handling, suppression
168
+ lists and analytics live in the provider's dashboard — wrapping them would mean
169
+ maintaining a lowest common denominator of every provider's feature set.
170
+
171
+ | Provider | Use |
172
+ | --- | --- |
173
+ | `createResendProvider({ apiKey })` | Production. **HTTP call unverified** against live credentials. |
174
+ | `createConsoleEmailProvider()` | Development. Refuses to run in production. |
175
+ | `createMemoryEmailProvider()` | Tests. Collects messages, dedupes on `idempotencyKey`. |
176
+
177
+ ### Writing your own
178
+
179
+ ```ts
180
+ const provider: EmailProvider = {
181
+ name: "postmark",
182
+ async send(message) {
183
+ const response = await fetch("https://api.postmarkapp.com/email", {
184
+ method: "POST",
185
+ headers: { "X-Postmark-Server-Token": token, "Content-Type": "application/json" },
186
+ body: JSON.stringify({
187
+ From: formatAddress(message.from),
188
+ To: message.to.map(formatAddress).join(","),
189
+ Subject: message.subject,
190
+ TextBody: message.text,
191
+ HtmlBody: message.html,
192
+ }),
193
+ });
194
+
195
+ // Do not include the response body: provider errors echo request contents,
196
+ // and a bounced address is personal data.
197
+ if (!response.ok) throw new Error(`Postmark rejected: ${response.status}`);
198
+
199
+ const body = (await response.json()) as { MessageID: string };
200
+ return { id: body.MessageID, provider: "postmark" };
201
+ },
202
+ };
203
+ ```
204
+
205
+ ### `formatAddress(address)`
206
+
207
+ Quotes the display name and strips embedded quotes, so a name containing a comma
208
+ stays in one field and one containing a quote cannot split the header.
209
+
210
+ ---
211
+
212
+ ## Testing
213
+
214
+ ```ts
215
+ const provider = createMemoryEmailProvider();
216
+ const email = createEmailService({ provider, templates, from: { address: "a@b.test" } });
217
+
218
+ await email.sendTemplate({ to: "user@example.test", template: "project.shared", data });
219
+
220
+ expect(provider.to("user@example.test")).toHaveLength(1);
221
+ expect(provider.sent[0]?.subject).toContain("shared");
222
+ ```
@@ -0,0 +1,194 @@
1
+ # Entitlements
2
+
3
+ ```ts
4
+ import { entitlements, createEntitlementsService } from "@digimuza/saas-core/entitlements";
5
+ ```
6
+
7
+ > Orientation and recipes: [`src/entitlements/README.md`](../src/entitlements/README.md).
8
+
9
+
10
+ What a workspace has *paid for*, as opposed to what a user is *allowed* to do.
11
+
12
+ ```
13
+ Subscription → Plan → Entitlements → features and limits
14
+ ```
15
+
16
+ **Product code must never inspect a plan name.** Ask about the entitlement.
17
+
18
+ ---
19
+
20
+ ## Two kinds of entitlement
21
+
22
+ - **`feature`** — on or off. `ai_generation`, `api_access`, `custom_domains`.
23
+ - **`limit`** — a quota. `ai_generations`, `team_members`, `storage_bytes`.
24
+
25
+ Defined per plan; see [billing.md](./billing.md#plans).
26
+
27
+ ---
28
+
29
+ ## `entitlements.has(context, key)`
30
+
31
+ ```ts
32
+ function has(context: WorkspaceContext, key: string): Promise<boolean>
33
+ ```
34
+
35
+ Whether the workspace's plan includes this key at all.
36
+
37
+ ## `entitlements.require(context, key)`
38
+
39
+ ```ts
40
+ function require(context: WorkspaceContext, key: string): Promise<void>
41
+ ```
42
+
43
+ Throws `EntitlementRequiredError` (code `payment_required`, HTTP 402) with
44
+ `.entitlement` set. Deliberately distinct from `ForbiddenError`: a client shows an
45
+ upsell for this and an access error for that.
46
+
47
+ ## `entitlements.getLimit(context, key)`
48
+
49
+ ```ts
50
+ function getLimit(context: WorkspaceContext, key: string): Promise<number | null>
51
+ ```
52
+
53
+ Three states, all distinct:
54
+
55
+ | Return | Meaning |
56
+ | --- | --- |
57
+ | `null` | Unlimited |
58
+ | `0` | Explicitly none |
59
+ | `0` | Key absent from the plan — **fails closed** |
60
+
61
+ A limit the plan does not mention is zero, not unlimited. A typo in a plan
62
+ definition must not hand out infinite quota. Asking for the limit of a *feature*
63
+ also returns `0`, because it is a meaningless question.
64
+
65
+ ## `entitlements.checkLimit(context, key, quantity?)`
66
+
67
+ ```ts
68
+ function checkLimit(context: WorkspaceContext, key: string, quantity = 1): Promise<LimitCheck>
69
+
70
+ interface LimitCheck {
71
+ allowed: boolean;
72
+ limit: number | null; // null = unlimited
73
+ used: number;
74
+ remaining: number | null; // null = unlimited
75
+ window: { start: Date; end: Date };
76
+ }
77
+ ```
78
+
79
+ `remaining` never goes negative. `requireLimit` is the throwing variant
80
+ (`LimitExceededError`, carrying `.limit` and `.used`).
81
+
82
+ ## `entitlements.getUsage(context, key)` / `recordUsage(...)`
83
+
84
+ ```ts
85
+ function getUsage(context: WorkspaceContext, key: string): Promise<number>
86
+
87
+ function recordUsage(context: WorkspaceContext, key: string, options?: {
88
+ quantity?: number; // 1
89
+ idempotencyKey?: string;
90
+ occurredAt?: Date;
91
+ }): Promise<void>
92
+ ```
93
+
94
+ `recordUsage` does not itself enforce the limit. A reused `idempotencyKey` is
95
+ counted **once**, so a network retry does not consume a customer's quota twice —
96
+ scoped per workspace, since two tenants may legitimately use the same request id.
97
+
98
+ ## `entitlements.consume(context, key, options?)`
99
+
100
+ ```ts
101
+ function consume(context: WorkspaceContext, key: string, options?: RecordUsageOptions): Promise<LimitCheck>
102
+ ```
103
+
104
+ Check and record together. Throws `LimitExceededError` **without recording
105
+ anything**.
106
+
107
+ ```ts
108
+ await entitlements.consume(context, "ai_generations", {
109
+ quantity: 1,
110
+ idempotencyKey: requestId,
111
+ });
112
+ ```
113
+
114
+ ### It is not transactional, on purpose
115
+
116
+ The check and the insert are separate statements, so concurrent consumers can
117
+ together overshoot the quota by their in-flight quantity. That is a deliberate
118
+ trade: serialising every metered call behind a lock would make the hot path a
119
+ contention point, and a small overshoot on a usage quota is cheaper than that.
120
+
121
+ If you need a hard cap, wrap `consume` in your own transaction with
122
+ `SELECT ... FOR UPDATE` on a per-workspace row.
123
+
124
+ ## `entitlements.resolve(context)` / `resolvePlan(context)`
125
+
126
+ ```ts
127
+ function resolve(context: WorkspaceContext): Promise<{
128
+ plan: Plan | undefined;
129
+ byKey: ReadonlyMap<string, Entitlement>;
130
+ }>
131
+ function resolvePlan(context: WorkspaceContext): Promise<Plan | undefined>
132
+ ```
133
+
134
+ Everything at once — for rendering a plan-comparison or usage page without a query
135
+ per key.
136
+
137
+ ---
138
+
139
+ ## Which subscriptions entitle
140
+
141
+ `trialing`, `active` and `past_due` all entitle. **`past_due` is a dunning state,
142
+ not a revocation** — cutting access the moment a card fails is usually the wrong
143
+ product decision, and the provider is still retrying.
144
+
145
+ `paused`, `canceled` and `incomplete` do not entitle.
146
+
147
+ Cancelling at period end keeps entitlements until the paid period ends; cancelling
148
+ immediately ends them now. See [billing.md](./billing.md).
149
+
150
+ ---
151
+
152
+ ## The usage window
153
+
154
+ Consumption is summed over a window, never all-time:
155
+
156
+ 1. The subscription's billing period, when it has one — that is what the customer
157
+ is paying for.
158
+ 2. Otherwise the current **UTC calendar month**.
159
+
160
+ The fallback matters: a workspace on a free plan has no billing period, and an
161
+ all-time total would leave a monthly quota permanently exhausted.
162
+
163
+ Usage is append-only. A running counter would lose the ability to attribute usage
164
+ to a period, and would make concurrent increments a contention point.
165
+
166
+ ---
167
+
168
+ ## Free tiers
169
+
170
+ The default `entitlements` export configures **no fallback plan**, so a workspace
171
+ without a live subscription has no entitlements at all. That is the safe default
172
+ but leaves no room for a free tier. Build your own:
173
+
174
+ ```ts
175
+ import { createEntitlementsService } from "@digimuza/saas-core/entitlements";
176
+ import { getDatabase } from "@digimuza/saas-core/db";
177
+
178
+ export const entitlements = createEntitlementsService({
179
+ db: getDatabase(),
180
+ fallbackPlanKey: "free",
181
+ });
182
+ ```
183
+
184
+ A live subscription always wins over the fallback.
185
+
186
+ ---
187
+
188
+ ## Testing
189
+
190
+ ```ts
191
+ createEntitlementsService({ db, now: () => new Date("2026-06-15T12:00:00Z") });
192
+ ```
193
+
194
+ Inject `now` to make calendar-month windows deterministic.
package/docs/errors.md ADDED
@@ -0,0 +1,158 @@
1
+ # Errors
2
+
3
+ ```ts
4
+ import {
5
+ SaasCoreError, UnauthorizedError, ForbiddenError, NotFoundError,
6
+ ConflictError, InvalidInputError, EntitlementRequiredError, LimitExceededError,
7
+ } from "@digimuza/saas-core";
8
+
9
+ // Identical, if you prefer the explicit subpath:
10
+ import { NotFoundError } from "@digimuza/saas-core/errors";
11
+ ```
12
+
13
+ Both entry points export the same classes. The root re-exports them because route
14
+ handlers map errors to status codes and would otherwise need a second import just
15
+ for the error types.
16
+
17
+ Every core error carries a stable machine-readable `code`, so handlers map to
18
+ status codes without matching on message text.
19
+
20
+ | Error | `code` | Status | Meaning |
21
+ | --- | --- | --- | --- |
22
+ | `UnauthorizedError` | `unauthorized` | 401 | No authenticated user |
23
+ | `ForbiddenError` | `forbidden` | 403 | Authenticated, lacks the permission |
24
+ | `NotFoundError` | `not_found` | 404 | Absent, or the caller may not know it exists |
25
+ | `ConflictError` | `conflict` | 409 | Conflicts with existing state |
26
+ | `InvalidInputError` | `invalid_input` | 400 | Structurally invalid input |
27
+ | `EntitlementRequiredError` | `payment_required` | 402 | Plan does not include the feature |
28
+ | `LimitExceededError` | `limit_exceeded` | 429 | Metered quota exhausted |
29
+
30
+ All extend `SaasCoreError`.
31
+
32
+ ---
33
+
34
+ ## Mapping to HTTP
35
+
36
+ ```ts
37
+ import { SaasCoreError } from "@digimuza/saas-core";
38
+
39
+ const STATUS: Record<string, number> = {
40
+ unauthorized: 401,
41
+ forbidden: 403,
42
+ not_found: 404,
43
+ conflict: 409,
44
+ invalid_input: 400,
45
+ payment_required: 402,
46
+ limit_exceeded: 429,
47
+ };
48
+
49
+ export function toResponse(error: unknown): Response {
50
+ if (error instanceof SaasCoreError) {
51
+ return Response.json(
52
+ { error: error.code, message: error.message },
53
+ { status: STATUS[error.code] ?? 500 },
54
+ );
55
+ }
56
+ throw error; // let genuine faults become a 500 and reach your tracker
57
+ }
58
+ ```
59
+
60
+ ---
61
+
62
+ ## Errors that carry detail
63
+
64
+ ```ts
65
+ class ForbiddenError extends SaasCoreError {
66
+ readonly permission?: string; // the permission that was required
67
+ }
68
+
69
+ class EntitlementRequiredError extends SaasCoreError {
70
+ readonly entitlement: string;
71
+ }
72
+
73
+ class LimitExceededError extends SaasCoreError {
74
+ readonly entitlement: string;
75
+ readonly limit: number;
76
+ readonly used: number;
77
+ }
78
+ ```
79
+
80
+ Enough to drive UI without parsing a message:
81
+
82
+ ```ts
83
+ try {
84
+ await entitlements.consume(context, "ai_generations");
85
+ } catch (error) {
86
+ if (error instanceof LimitExceededError) {
87
+ return upsell({ used: error.used, limit: error.limit });
88
+ }
89
+ throw error;
90
+ }
91
+ ```
92
+
93
+ ---
94
+
95
+ ## Not found vs forbidden
96
+
97
+ Tenant isolation reports **`NotFoundError`** for a workspace the caller is not a
98
+ member of, not `ForbiddenError`.
99
+
100
+ A 403 confirms that a given workspace id or slug *exists*, which lets an attacker
101
+ enumerate other tenants by iterating ids. A 404 reveals nothing.
102
+
103
+ `ForbiddenError` is for callers already known to belong to the workspace who lack a
104
+ specific permission — inside the tenant boundary, where existence is not a secret.
105
+
106
+ The same reasoning makes missing, expired, revoked and already-accepted invitation
107
+ tokens all raise one indistinguishable `NotFoundError`: telling them apart would
108
+ tell a token-guessing attacker which guesses hit.
109
+
110
+ ---
111
+
112
+ ## Forbidden vs payment required
113
+
114
+ `ForbiddenError` means "you may not". `EntitlementRequiredError` means "your plan
115
+ does not include this". Clients render an access error for the first and an upsell
116
+ for the second, so conflating them produces the wrong UI.
117
+
118
+ Both checks usually apply:
119
+
120
+ ```ts
121
+ await authorization.require(context, "projects.create"); // 403 if not permitted
122
+ await entitlements.require(context, "ai_generation"); // 402 if not paid for
123
+ ```
124
+
125
+ ---
126
+
127
+ ## Errors outside this hierarchy
128
+
129
+ Some deliberately do not extend `SaasCoreError`:
130
+
131
+ - **`ConfigError`** (`@digimuza/saas-core/config`) — a startup fault, not a request
132
+ outcome. Names the offending variables and never interpolates values, so a
133
+ malformed secret cannot leak into a log or error tracker.
134
+ - **`InvalidWebhookSignatureError`** (`@digimuza/saas-core/billing`) — map it to
135
+ 401 yourself. Its message is deliberately uninformative: a caller who cannot
136
+ produce a valid signature gets no hint about which part failed.
137
+
138
+ ---
139
+
140
+ ## Database constraint failures
141
+
142
+ Services translate the constraint violations that represent domain outcomes:
143
+
144
+ - duplicate workspace slug → `ConflictError`
145
+ - outstanding invitation for that email → `ConflictError`
146
+ - cross-workspace role assignment → `NotFoundError`
147
+
148
+ Anything else propagates. That is intentional — swallowing an unrecognised
149
+ constraint failure hides a real bug. If you catch these yourself, always match on
150
+ the **constraint name**, not just the SQLSTATE code:
151
+
152
+ ```ts
153
+ import { isUniqueViolation } from "@digimuza/saas-core/db";
154
+
155
+ if (isUniqueViolation(error, "projects_workspace_id_name_unique")) { /* ... */ }
156
+ ```
157
+
158
+ See [db.md](./db.md#constraint-error-introspection).