@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/logger.md ADDED
@@ -0,0 +1,269 @@
1
+ # Logger
2
+
3
+ ```ts
4
+ import { logger, createLogger, createConfiguredLogger, setDefaultLogger } from "@digimuza/saas-core/logger";
5
+ ```
6
+
7
+ > Orientation and recipes: [`src/logger/README.md`](../src/logger/README.md).
8
+
9
+ Structured logging with bound context and redaction. No logging dependency — a
10
+ level check, a field merge and a JSON line do not justify one, and the sink seam
11
+ makes adding pino or a hosted collector an adapter rather than a migration.
12
+
13
+ ---
14
+
15
+ ## Setup
16
+
17
+ ```ts
18
+ import { createConfiguredLogger, setDefaultLogger } from "@digimuza/saas-core/logger";
19
+
20
+ setDefaultLogger(createConfiguredLogger());
21
+ ```
22
+
23
+ `createConfiguredLogger()` reads the `app` config section: `LOG_LEVEL` for the
24
+ level, `APP_ENV` to choose JSON (production) or human-readable (everything else),
25
+ and binds `app` and `env` so several services shipping to one aggregator stay
26
+ distinguishable.
27
+
28
+ Until you call it, the core logs at `info` to stdout as JSON. Deliberate:
29
+ importing a core module must never require configuration, or a migration script
30
+ would need `LOG_LEVEL` set to run. That is also why `src/logger/default.ts`
31
+ imports nothing but the logger itself — if it pulled in `config`, every module
32
+ that logs would transitively depend on configuration.
33
+
34
+ ---
35
+
36
+ ## `Logger`
37
+
38
+ ```ts
39
+ interface Logger {
40
+ readonly level: LogLevel; // "debug" | "info" | "warn" | "error"
41
+
42
+ debug(message: string, fields?: LogFields): void;
43
+ info(message: string, fields?: LogFields): void;
44
+ warn(message: string, fields?: LogFields): void;
45
+ error(message: string, fields?: LogFields & { error?: unknown }): void;
46
+
47
+ child(fields: LogFields): Logger;
48
+ isEnabled(level: LogLevel): boolean;
49
+ }
50
+ ```
51
+
52
+ ### `child(fields)`
53
+
54
+ The reason the module exists:
55
+
56
+ ```ts
57
+ const log = logger.child({ requestId, workspaceId: context.workspaceId, userId: user.id });
58
+
59
+ log.info("project created", { projectId });
60
+ log.warn("slow query", { durationMs });
61
+ ```
62
+
63
+ Bind at the edge and every line beneath is attributable, without threading
64
+ identifiers through call signatures. Children nest, inherit the level, share the
65
+ sink, and never affect their parent.
66
+
67
+ Per-call fields override bound ones.
68
+
69
+ ### Level checks happen first
70
+
71
+ A suppressed record is never merged or serialised, and its field object is never
72
+ even evaluated:
73
+
74
+ ```ts
75
+ log.debug("plan", { get expensive() { return explain(); } }); // getter not called
76
+ ```
77
+
78
+ So debug logging is not something you strip out for performance. `isEnabled` is
79
+ there for work you would not want to do regardless — an extra query, a large
80
+ serialisation.
81
+
82
+ ---
83
+
84
+ ## Errors
85
+
86
+ ```ts
87
+ log.error("indexing failed", { projectId, error });
88
+ ```
89
+
90
+ `error` accepts any throwable and is serialised to `{ name, message, stack, code?,
91
+ cause?, errors? }`:
92
+
93
+ - **`stack`** is capped at 20 lines.
94
+ - **`cause`** is followed up to 4 levels. Dropping it would leave "query failed"
95
+ with no indication that the connection was refused.
96
+ - **`AggregateError.errors`** is kept, or "Invalid configuration in 3 sections"
97
+ would say nothing about which.
98
+ - **`code`** is kept when present, so [domain errors](./errors.md) stay filterable.
99
+ - **The message is value-scanned**, because it is the likeliest place for a token
100
+ to end up by accident.
101
+
102
+ ---
103
+
104
+ ## Redaction
105
+
106
+ On by default. The core handles API keys, session cookies, invitation tokens and
107
+ webhook secrets; a logger that faithfully serialises whatever it is handed is the
108
+ easiest way to move all of them into an aggregator a wider audience can read.
109
+
110
+ Two independent passes, because either alone has a hole.
111
+
112
+ ### By key
113
+
114
+ Case-insensitive **substring** match, so `workosCookiePassword` and
115
+ `cookie_password` are both caught without enumerating spellings.
116
+
117
+ `DEFAULT_REDACT_KEYS`: `password`, `passwd`, `secret`, `token`, `apikey`,
118
+ `api_key`, `authorization`, `cookie`, `credential`, `privatekey`, `private_key`,
119
+ `signature`, `keyhash`, `key_hash`, `sessiondata`, `bearer`, `otp`, `mfa`.
120
+
121
+ ### By value
122
+
123
+ Catches a credential under an innocent key — `{ input: "dgk_…" }`:
124
+
125
+ | Pattern | Source |
126
+ | --- | --- |
127
+ | `<prefix>_<32 hex>_<64 hex>` | this package's [API keys](./api-keys.md) |
128
+ | `whsec_…` | this package's [webhook secrets](./webhooks.md) |
129
+ | `wsk_…` | Revolut webhook secrets |
130
+ | `sk_`/`rk_`/`pk_` + `live`/`test` | Stripe-shaped provider keys |
131
+ | `re_…` | Resend keys |
132
+ | `eyJ….….…` | JWTs, including the sealed session cookie |
133
+ | `Bearer …` | an authorization header value |
134
+
135
+ Anchored on **prefixes this system and its providers issue**, not on entropy.
136
+ "Looks random" would strip ids, hashes and slugs, and a log with every identifier
137
+ removed is not useful. There is a test asserting UUIDs, slugs, digests and realtime
138
+ channel names all survive.
139
+
140
+ ### Configuring it
141
+
142
+ ```ts
143
+ createLogger({
144
+ redact: {
145
+ additionalKeys: ["ssn", "dateOfBirth"], // added to the defaults
146
+ // keys: [...], // replaces them entirely
147
+ // disableValueScanning: true, // key redaction stays on
148
+ },
149
+ });
150
+ ```
151
+
152
+ **Over-redaction is the intended failure mode.** `tokenCount` is redacted because
153
+ the key contains `token`. Replace `keys` if that matters more than the default
154
+ safety.
155
+
156
+ ### Bounds
157
+
158
+ A pathological object cannot stall the process or fill a disk: depth 6, 100 array
159
+ items, 8192-character strings, cycles replaced with `[circular]`, functions never
160
+ serialised (their source can embed a literal secret).
161
+
162
+ ### Redacting outside the logger
163
+
164
+ `createRedactor(options)` returns the same function the logger uses, for anywhere
165
+ else a value crosses a boundary you do not fully trust — an error-tracker
166
+ integration, a support-tool response, a debug endpoint:
167
+
168
+ ```ts
169
+ import { createRedactor } from "@digimuza/saas-core/logger";
170
+
171
+ const redact = createRedactor({ additionalKeys: ["ssn"] });
172
+ Sentry.setContext("request", redact(payload) as Record<string, unknown>);
173
+ ```
174
+
175
+ It returns a plain, JSON-safe structure with the same bounds and both passes
176
+ applied.
177
+
178
+ ---
179
+
180
+ ## Sinks
181
+
182
+ ```ts
183
+ interface LogSink {
184
+ readonly name: string;
185
+ write(record: LogRecord): void;
186
+ }
187
+ ```
188
+
189
+ One method, because that is the whole job. Sampling, buffering and shipping belong
190
+ to whatever you plug in.
191
+
192
+ | Sink | Use |
193
+ | --- | --- |
194
+ | `createJsonSink({ write? })` | Production. One JSON object per line. |
195
+ | `createPrettySink({ write? })` | Development. Lossy by design. |
196
+ | `createMemorySink()` | Tests. `records`, `at(level)`, `clear()`. |
197
+ | `createNoopSink()` | Silence. |
198
+ | `createMultiSink([...])` | stdout plus an aggregator. |
199
+
200
+ **Everything goes to stdout**, including errors. Many collectors treat anything on
201
+ stderr as an error regardless of the record's own level, which would double-count
202
+ warnings and mislabel info. Pass `write` to change that.
203
+
204
+ **Fields cannot shadow `level`, `time` or `message`** — they are spread under the
205
+ reserved keys, so a record cannot lie about itself.
206
+
207
+ ### Plugging in pino
208
+
209
+ ```ts
210
+ import pino from "pino";
211
+
212
+ const instance = pino();
213
+ const sink: LogSink = {
214
+ name: "pino",
215
+ write: (record) => instance[record.level]({ ...record.fields, time: record.time }, record.message),
216
+ };
217
+
218
+ setDefaultLogger(createLogger({ level: "info", sink }));
219
+ ```
220
+
221
+ ---
222
+
223
+ ## Logging never fails the caller
224
+
225
+ A throwing sink, an unserialisable field, a cyclic object — all swallowed.
226
+ Logging is diagnostic; it is never a reason for the logged operation to fail.
227
+ `createMultiSink` also isolates one broken sink from the rest.
228
+
229
+ There is deliberately no fallback reporting when a sink throws: there is nowhere
230
+ left to report it.
231
+
232
+ ---
233
+
234
+ ## What the core logs
235
+
236
+ Very little, on purpose — a library that logs chattily is noise in someone else's
237
+ application. Currently only failures it would otherwise swallow:
238
+
239
+ | Where | Record |
240
+ | --- | --- |
241
+ | [events](./events.md) | `event handler failed` — a subscriber threw |
242
+ | [realtime](./realtime.md) | `realtime transport error`, `realtime delivery error` |
243
+
244
+ Both are cases where the operation deliberately succeeds and the failure would
245
+ otherwise be invisible. Pass `onHandlerError` / `onError` to route them yourself.
246
+
247
+ ---
248
+
249
+ ## Testing
250
+
251
+ ```ts
252
+ const sink = createMemorySink();
253
+ const log = createLogger({ sink, level: "debug", now: () => fixedClock });
254
+
255
+ log.error("failed", { error });
256
+
257
+ expect(sink.at("error")[0]?.fields.error).toMatchObject({ name: "Error" });
258
+ ```
259
+
260
+ `silenceDefaultLogger()` quiets the core in a test run, and
261
+ `getDefaultLogger()` returns whatever is currently installed — useful for asserting
262
+ that setup ran, or for a module that wants the core's logger without importing
263
+ `config` through the `/logger` entry point.
264
+
265
+ ```ts
266
+ import { getDefaultLogger, setDefaultLogger, silenceDefaultLogger } from "@digimuza/saas-core/logger";
267
+
268
+ beforeEach(() => silenceDefaultLogger());
269
+ ```
@@ -0,0 +1,179 @@
1
+ # Notifications
2
+
3
+ ```ts
4
+ import { notifications, createNotificationsService } from "@digimuza/saas-core/notifications";
5
+ ```
6
+
7
+ > Orientation and recipes: [`src/notifications/README.md`](../src/notifications/README.md).
8
+
9
+ In-app notifications addressed to a person.
10
+
11
+ ---
12
+
13
+ ## The ownership model
14
+
15
+ A notification **belongs to its recipient**. `workspace_id` only records which
16
+ tenant it concerns.
17
+
18
+ Every read filters on `user_id`, so one user's notifications can never surface in
19
+ another's list regardless of the workspace filter. That is why `list`, `markRead`
20
+ and `remove` all take a `userId` rather than a `WorkspaceContext`.
21
+
22
+ ---
23
+
24
+ ## `notifications.create(input, tx?)`
25
+
26
+ ```ts
27
+ function create(input: {
28
+ userId: string; // the recipient
29
+ workspaceId?: string | null; // null for account-level notices
30
+ type: string;
31
+ title: string; // max 200 characters
32
+ body?: string;
33
+ url?: string; // relative, so it survives a domain change
34
+ data?: Record<string, unknown>;
35
+ dedupeKey?: string;
36
+ }, tx?: CoreDatabase): Promise<Notification>
37
+ ```
38
+
39
+ ```ts
40
+ await notifications.create({
41
+ userId: member.id,
42
+ workspaceId: context.workspaceId,
43
+ type: "project.shared",
44
+ title: "Apollo was shared with you",
45
+ url: `/w/${slug}/projects/${projectId}`,
46
+ dedupeKey: `project.shared:${projectId}:${member.id}`,
47
+ });
48
+ ```
49
+
50
+ **Notifying a non-member throws `NotFoundError`.** The title alone would tell them
51
+ the workspace exists, and something about what happens in it. Verified whenever a
52
+ `workspaces` service is wired — the default export wires it.
53
+
54
+ ## `notifications.createMany(inputs)`
55
+
56
+ Skips any that duplicate an existing dedupe key, and returns only what was created.
57
+
58
+ ## `notifications.list(userId, options?)`
59
+
60
+ ```ts
61
+ function list(userId: string, options?: {
62
+ unreadOnly?: boolean;
63
+ workspaceId?: string | null; // `null` selects account-level notices
64
+ types?: readonly string[];
65
+ limit?: number; // default 50, capped at 200
66
+ before?: Date; // keyset pagination
67
+ }): Promise<Notification[]>
68
+ ```
69
+
70
+ `userId` must come from the session.
71
+
72
+ Note the three-way `workspaceId`: omitted means every workspace, a value means one
73
+ workspace, and explicit `null` means only account-level notices.
74
+
75
+ ## `notifications.countUnread(userId, options?)`
76
+
77
+ ```ts
78
+ function countUnread(userId: string, options?: { workspaceId?: string | null }): Promise<number>
79
+ ```
80
+
81
+ The bell-icon badge. Backed by an index on `(user_id, read_at, created_at desc)`,
82
+ so unread rows are found without scanning a long history.
83
+
84
+ ## `notifications.markRead(userId, ids)` / `markAllRead(userId, options?)`
85
+
86
+ ```ts
87
+ function markRead(userId: string, notificationIds: readonly string[]): Promise<number>
88
+ function markAllRead(userId: string, options?: { workspaceId?: string | null }): Promise<number>
89
+ ```
90
+
91
+ Both return how many rows actually changed, so marking an already-read notification
92
+ returns `0` rather than erroring.
93
+
94
+ Caller-scoped: another user's id matches nothing rather than raising, because
95
+ raising would confirm it exists.
96
+
97
+ ## `notifications.remove(userId, id)`
98
+
99
+ Throws `NotFoundError` for an id the caller does not own — same reasoning.
100
+
101
+ ---
102
+
103
+ ## Deduplication
104
+
105
+ `(user_id, type, dedupe_key)` is a **partial** unique index. `create` returns the
106
+ existing row for a reused key, so the call is safe to retry.
107
+
108
+ Scoped per recipient *and* per type, deliberately: one underlying fact legitimately
109
+ notifies several people, and two event types may derive a key from the same
110
+ request.
111
+
112
+ > Because the index is partial (`where dedupe_key is not null`), the `ON CONFLICT`
113
+ > clause carries a matching predicate. PostgreSQL refuses to infer a partial index
114
+ > as the arbiter otherwise, and every insert fails with `42P10`.
115
+
116
+ ---
117
+
118
+ ## Consuming events
119
+
120
+ ```ts
121
+ events.on("workspace.member.created", async (event) => {
122
+ await notifications.create({
123
+ userId: event.data.userId,
124
+ workspaceId: event.workspaceId,
125
+ type: "workspace.joined",
126
+ title: "Welcome to the workspace",
127
+ dedupeKey: event.id, // handlers can run twice
128
+ });
129
+ });
130
+ ```
131
+
132
+ **A handler is not durable.** A crash between the commit and the handler loses the
133
+ notification. For anything that must not be missed, create it in the same
134
+ transaction as the change (`create(input, tx)`), or from a
135
+ [Trigger.dev task](./jobs.md) reading the persisted event.
136
+
137
+ ---
138
+
139
+ ## Email notifications are not automatic
140
+
141
+ Which notifications also warrant an email is a product decision, and per-user
142
+ preferences, digests and quiet hours are product features rather than core ones.
143
+ Compose the two explicitly:
144
+
145
+ ```ts
146
+ const notification = await notifications.create({ /* ... */ });
147
+
148
+ if (await wantsEmail(member.id, notification.type)) {
149
+ await email.sendTemplate({
150
+ to: profile.email,
151
+ template: "project.shared",
152
+ data: { projectName },
153
+ idempotencyKey: notification.id, // sending is not transactional
154
+ });
155
+ }
156
+ ```
157
+
158
+ ---
159
+
160
+ ## Lifecycle
161
+
162
+ Notifications **cascade away** when the recipient or the workspace is deleted —
163
+ unlike [audit entries](./audit.md), which must outlive their subject. An inbox is
164
+ disposable; a trail is not.
165
+
166
+ ---
167
+
168
+ ## Custom construction
169
+
170
+ ```ts
171
+ createNotificationsService({
172
+ db,
173
+ workspaces, // omit to skip the membership check
174
+ now: () => fixedClock,
175
+ });
176
+ ```
177
+
178
+ Omitting `workspaces` disables membership verification, which is only safe when
179
+ every caller already holds a verified context.
@@ -0,0 +1,128 @@
1
+ # Publishing
2
+
3
+ `@digimuza/saas-core` publishes to **npmjs.com with restricted access** — only your
4
+ npm organisation can install it, matching `license: UNLICENSED`.
5
+
6
+ The workflow is [`.github/workflows/publish.yml`](../.github/workflows/publish.yml).
7
+
8
+ ---
9
+
10
+ ## One-time setup
11
+
12
+ 1. **Create an npm automation token.** npmjs.com → Access Tokens → Generate →
13
+ *Automation*. Granular tokens work too; the token needs publish rights on the
14
+ `@digimuza` scope.
15
+
16
+ 2. **Add it as a repository secret** named `NPM_TOKEN`:
17
+ Settings → Secrets and variables → Actions → New repository secret.
18
+
19
+ 3. **Confirm the org can host restricted packages.** Restricted scoped packages
20
+ need a paid npm organisation. Publishing fails with a payment error otherwise —
21
+ at which point the options are a paid plan, going public, or GitHub Packages.
22
+
23
+ 4. **The `repository` field** points at `github.com/digimuza/sass-core` — note the
24
+ single `a`. The npm package is `@digimuza/saas-core`; only the repository is
25
+ spelled `sass-core`. npm links a package to its source from this field, so the
26
+ mismatch is intentional rather than a typo to "fix".
27
+
28
+ ---
29
+
30
+ ## Releasing
31
+
32
+ ```bash
33
+ # 1. Bump the version. This is the only thing that decides what gets published.
34
+ npm version minor # or patch / major
35
+
36
+ # 2. Push the commit and the tag it created.
37
+ git push --follow-tags
38
+
39
+ # 3. Create a GitHub Release for that tag.
40
+ # Publishing the release triggers the workflow.
41
+ gh release create v0.15.0 --generate-notes
42
+ ```
43
+
44
+ The tag must match `package.json` — the workflow checks, because tagging `v0.15.0`
45
+ while the manifest still says `0.14.0` publishes the wrong version under the right
46
+ tag, and the mistake is invisible afterwards.
47
+
48
+ ### Rehearsing it
49
+
50
+ `workflow_dispatch` runs every check and packs the tarball without publishing.
51
+ `dry-run` defaults to **true**, so an accidental manual run cannot publish:
52
+
53
+ Actions → Publish → Run workflow → leave *dry-run* ticked.
54
+
55
+ ---
56
+
57
+ ## What the workflow checks before publishing
58
+
59
+ Each of these has failed for real at some point, which is why it is a step rather
60
+ than a comment.
61
+
62
+ | Gate | Guards against |
63
+ | --- | --- |
64
+ | `npm ci` | a lockfile that disagrees with `package.json` |
65
+ | Typecheck | — |
66
+ | Test suite | — |
67
+ | Clean build | a `dist` that only works incrementally |
68
+ | **Migration drift** | shipping types that describe tables no migration creates |
69
+ | **Version not already published** | a confusing mid-publish failure |
70
+ | **Tag matches manifest** | publishing 0.14.0 as v0.15.0 |
71
+ | **Tarball contents** | a missing `drizzle/`, or shipping `src/` and `tests/` |
72
+ | **Loads under raw Node ESM** | a broken `.js` specifier in the built output |
73
+
74
+ The tarball check asserts both directions. A missing `drizzle/meta/_journal.json`
75
+ would leave consumers unable to migrate and would look fine until someone tried;
76
+ a stray `src/` would double the package size and ship the tests.
77
+
78
+ The raw-Node check matters because the package ships real ESM with `.js`
79
+ specifiers and is explicitly meant to load without a bundler — see
80
+ [db.md](./db.md) and `src/README.md`. A bad specifier would otherwise surface in
81
+ someone's migration script.
82
+
83
+ ### Scripts are skipped at the publish step
84
+
85
+ `prepublishOnly` runs `clean && build && test`. The gates already did all three, so
86
+ the publish itself uses `--ignore-scripts` to stay a single fast auditable step
87
+ rather than repeating a two-minute suite.
88
+
89
+ ---
90
+
91
+ ## Notes
92
+
93
+ **Concurrency is serialised, not cancelled.** Two publishes racing would both try
94
+ to claim the same version and the loser fails halfway through; cancelling one
95
+ mid-flight is worse than queueing it.
96
+
97
+ **No provenance attestation.** npm provenance requires a public package and a
98
+ public source repository. Going public later means adding `--provenance` and
99
+ `id-token: write`, and replacing `UNLICENSED` with a real licence.
100
+
101
+ **Publishing is never automatic on merge.** A release is a decision, so it takes
102
+ either a GitHub Release or an explicit manual run.
103
+
104
+ ---
105
+
106
+ ## Switching to GitHub Packages
107
+
108
+ If a paid npm organisation is not worth it, GitHub Packages hosts private packages
109
+ free on any plan and authenticates with the built-in `GITHUB_TOKEN` — no secret to
110
+ manage:
111
+
112
+ ```yaml
113
+ permissions:
114
+ contents: read
115
+ packages: write
116
+
117
+ - uses: actions/setup-node@v4
118
+ with:
119
+ node-version: 22
120
+ registry-url: https://npm.pkg.github.com
121
+
122
+ - run: npm publish --ignore-scripts
123
+ env:
124
+ NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
125
+ ```
126
+
127
+ Consumers then need `@digimuza:registry=https://npm.pkg.github.com` in their
128
+ `.npmrc`, which is the real cost of the switch.