@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,175 @@
1
+ import { createHash } from "node:crypto";
2
+ import { encodeObjectPath, presign } from "./sigv4.js";
3
+ function toBytes(body) {
4
+ return typeof body === "string" ? new TextEncoder().encode(body) : body;
5
+ }
6
+ /**
7
+ * S3 and S3-compatible object storage.
8
+ *
9
+ * Uses SigV4 presigned URLs for reads and browser uploads, and a signed `PUT`
10
+ * over `fetch` for server-side writes. No AWS SDK: see `sigv4.ts` for why, and
11
+ * note that the signing itself is verified against AWS's published test vector.
12
+ */
13
+ export function createS3Provider(options) {
14
+ const fetchImpl = options.fetchImpl ?? globalThis.fetch;
15
+ const now = options.now ?? (() => new Date());
16
+ const pathStyle = options.forcePathStyle ?? options.endpoint !== undefined;
17
+ const host = (() => {
18
+ if (options.endpoint === undefined) {
19
+ return pathStyle
20
+ ? `s3.${options.region}.amazonaws.com`
21
+ : `${options.bucket}.s3.${options.region}.amazonaws.com`;
22
+ }
23
+ const parsed = new URL(options.endpoint);
24
+ return pathStyle ? parsed.host : `${options.bucket}.${parsed.host}`;
25
+ })();
26
+ function pathFor(key) {
27
+ const encoded = encodeObjectPath(key);
28
+ return pathStyle ? `/${options.bucket}/${encoded}` : `/${encoded}`;
29
+ }
30
+ function sign(input) {
31
+ return presign({
32
+ method: input.method,
33
+ host,
34
+ path: pathFor(input.key),
35
+ region: options.region,
36
+ service: "s3",
37
+ credentials: options.credentials,
38
+ expiresInSeconds: input.expiresInSeconds,
39
+ now: now(),
40
+ ...(input.query !== undefined ? { query: input.query } : {}),
41
+ ...(input.headers !== undefined ? { headers: input.headers } : {}),
42
+ }).url;
43
+ }
44
+ return {
45
+ name: "s3",
46
+ async put(input) {
47
+ const bytes = toBytes(input.body);
48
+ /**
49
+ * A short-lived signed PUT rather than a signed request header set: the same
50
+ * code path the browser upload uses, so there is one signing path to get
51
+ * right instead of two.
52
+ */
53
+ const url = sign({
54
+ method: "PUT",
55
+ key: input.key,
56
+ expiresInSeconds: 300,
57
+ ...(input.contentType !== undefined
58
+ ? { headers: { "content-type": input.contentType } }
59
+ : {}),
60
+ });
61
+ const response = await fetchImpl(url, {
62
+ method: "PUT",
63
+ headers: {
64
+ ...(input.contentType !== undefined ? { "Content-Type": input.contentType } : {}),
65
+ ...(input.cacheControl !== undefined ? { "Cache-Control": input.cacheControl } : {}),
66
+ ...Object.fromEntries(Object.entries(input.metadata ?? {}).map(([name, value]) => [
67
+ `x-amz-meta-${name}`,
68
+ value,
69
+ ])),
70
+ },
71
+ /**
72
+ * Cast at the boundary: `BodyInit` comes from the DOM lib, which this
73
+ * package does not include — a `Uint8Array` is a valid body regardless.
74
+ */
75
+ body: bytes,
76
+ });
77
+ if (!response.ok) {
78
+ /**
79
+ * The body is not included. S3 error documents echo the key and can echo
80
+ * request parameters, and this message may reach a log.
81
+ */
82
+ throw new Error(`Object store rejected the upload with status ${response.status}.`);
83
+ }
84
+ const etag = response.headers?.get?.("etag") ?? undefined;
85
+ return {
86
+ key: input.key,
87
+ size: bytes.byteLength,
88
+ ...(etag !== null && etag !== undefined ? { etag: etag.replaceAll('"', "") } : {}),
89
+ };
90
+ },
91
+ async delete(key) {
92
+ const response = await fetchImpl(sign({ method: "DELETE", key, expiresInSeconds: 300 }), {
93
+ method: "DELETE",
94
+ });
95
+ // S3 returns 204 for a delete, and also for a key that was never there.
96
+ if (!response.ok && response.status !== 404) {
97
+ throw new Error(`Object store rejected the delete with status ${response.status}.`);
98
+ }
99
+ },
100
+ async exists(key) {
101
+ const response = await fetchImpl(sign({ method: "HEAD", key, expiresInSeconds: 300 }), {
102
+ method: "HEAD",
103
+ });
104
+ if (response.status === 404 || response.status === 403) {
105
+ // 403 on a HEAD usually means "no such key" on a bucket without ListBucket.
106
+ return false;
107
+ }
108
+ if (!response.ok) {
109
+ throw new Error(`Object store returned status ${response.status} for a HEAD.`);
110
+ }
111
+ return true;
112
+ },
113
+ async stat(key) {
114
+ const response = await fetchImpl(sign({ method: "HEAD", key, expiresInSeconds: 300 }), {
115
+ method: "HEAD",
116
+ });
117
+ if (response.status === 404 || response.status === 403) {
118
+ return undefined;
119
+ }
120
+ if (!response.ok) {
121
+ throw new Error(`Object store returned status ${response.status} for a HEAD.`);
122
+ }
123
+ const length = response.headers?.get?.("content-length");
124
+ const contentType = response.headers?.get?.("content-type") ?? undefined;
125
+ const etag = response.headers?.get?.("etag") ?? undefined;
126
+ return {
127
+ // A HEAD without content-length would be unusual; treat it as unknown.
128
+ size: length == null ? 0 : Number(length),
129
+ ...(contentType != null ? { contentType } : {}),
130
+ ...(etag != null ? { etag: etag.replaceAll('"', "") } : {}),
131
+ };
132
+ },
133
+ publicUrl(key) {
134
+ if (options.publicBaseUrl === undefined) {
135
+ return undefined;
136
+ }
137
+ const base = options.publicBaseUrl.replace(/\/+$/, "");
138
+ return `${base}/${encodeObjectPath(key)}`;
139
+ },
140
+ async signedDownloadUrl(input) {
141
+ const query = {};
142
+ if (input.downloadFilename !== undefined) {
143
+ /**
144
+ * `attachment` rather than `inline`. Serving user-uploaded HTML or SVG
145
+ * inline from a bucket origin is stored XSS; forcing a download is what
146
+ * makes arbitrary uploads safe to hand back.
147
+ */
148
+ query["response-content-disposition"] =
149
+ `attachment; filename="${input.downloadFilename.replace(/["\\]/g, "")}"`;
150
+ }
151
+ return sign({
152
+ method: "GET",
153
+ key: input.key,
154
+ expiresInSeconds: input.expiresInSeconds,
155
+ ...(Object.keys(query).length > 0 ? { query } : {}),
156
+ });
157
+ },
158
+ async signedUploadUrl(input) {
159
+ return sign({
160
+ method: "PUT",
161
+ key: input.key,
162
+ expiresInSeconds: input.expiresInSeconds,
163
+ // Signed, so the client must send exactly this type or be rejected.
164
+ ...(input.contentType !== undefined
165
+ ? { headers: { "content-type": input.contentType } }
166
+ : {}),
167
+ });
168
+ },
169
+ };
170
+ }
171
+ /** Content hash, for deduplicating or naming objects by content. */
172
+ export function objectDigest(body) {
173
+ return createHash("sha256").update(toBytes(body)).digest("hex");
174
+ }
175
+ //# sourceMappingURL=s3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"s3.js","sourceRoot":"","sources":["../../src/storage/s3.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,86 @@
1
+ import type { WorkspaceContext } from "../workspaces/context.ts";
2
+ import type { ObjectBody, StorageProvider, StoredObject } from "./provider.ts";
3
+ export interface UploadInput {
4
+ /**
5
+ * Path within the workspace, e.g. `avatars/user-1.png`.
6
+ *
7
+ * Validated, not sanitised — see `keys.ts`. The workspace prefix is added for
8
+ * you and cannot be bypassed.
9
+ */
10
+ readonly path: string;
11
+ readonly body: ObjectBody;
12
+ readonly contentType?: string;
13
+ readonly cacheControl?: string;
14
+ readonly metadata?: Readonly<Record<string, string>>;
15
+ /** Rejects a body larger than this, before it reaches the provider. */
16
+ readonly maxBytes?: number;
17
+ }
18
+ export interface UrlOptions {
19
+ /** Default 900 (15 minutes). Capped at {@link MAX_SIGNED_URL_SECONDS}. */
20
+ readonly expiresInSeconds?: number;
21
+ /**
22
+ * Force a download with this filename.
23
+ *
24
+ * The safe default for anything a user uploaded — see `getUrl`.
25
+ */
26
+ readonly downloadFilename?: string;
27
+ }
28
+ export interface UploadUrlOptions {
29
+ readonly expiresInSeconds?: number;
30
+ /** Signed in, so the client must send exactly this type. */
31
+ readonly contentType?: string;
32
+ }
33
+ /**
34
+ * Storage bound to one workspace.
35
+ *
36
+ * Every method takes a *path*, never a key, so a caller cannot address an object
37
+ * outside its own tenant. The prefix is applied here and nowhere else.
38
+ */
39
+ export interface ScopedStorage {
40
+ readonly workspaceId: string;
41
+ /** The fully-qualified key a path maps to. For storing in your own tables. */
42
+ keyFor(path: string): string;
43
+ upload(input: UploadInput): Promise<StoredObject>;
44
+ delete(path: string): Promise<void>;
45
+ exists(path: string): Promise<boolean>;
46
+ /**
47
+ * A URL for reading the object.
48
+ *
49
+ * Returns the public URL when the store has one, otherwise a time-limited
50
+ * signed URL.
51
+ */
52
+ getUrl(path: string, options?: UrlOptions): Promise<string>;
53
+ /** A URL the browser can `PUT` to directly. */
54
+ createUploadUrl(path: string, options?: UploadUrlOptions): Promise<string>;
55
+ /**
56
+ * Act on a key that was stored earlier, asserting it belongs to this workspace.
57
+ *
58
+ * The check that matters when a key comes back out of your own database: it may
59
+ * have been written before a bug was fixed, or by a different tenant.
60
+ */
61
+ urlForKey(key: string, options?: UrlOptions): Promise<string>;
62
+ deleteKey(key: string): Promise<void>;
63
+ }
64
+ export interface StorageService {
65
+ /** Bind to a verified workspace. The only way to address an object. */
66
+ forWorkspace(context: WorkspaceContext): ScopedStorage;
67
+ /** The underlying provider, for a capability the service does not wrap. */
68
+ readonly provider: StorageProvider;
69
+ }
70
+ export interface StorageServiceDeps {
71
+ readonly provider: StorageProvider;
72
+ /** Default signed-URL lifetime. */
73
+ readonly defaultExpiresInSeconds?: number;
74
+ /** Rejects any upload larger than this. */
75
+ readonly maxUploadBytes?: number;
76
+ }
77
+ /**
78
+ * Seven days. S3's own hard limit for a SigV4 presigned URL — beyond it the
79
+ * signature is simply invalid, so accepting a larger number would hand back a URL
80
+ * that never works.
81
+ */
82
+ export declare const MAX_SIGNED_URL_SECONDS: number;
83
+ export declare function createStorageService(deps: StorageServiceDeps): StorageService;
84
+ /** Thrown by callers that need a missing object to be an error. */
85
+ export declare function assertObjectExists(exists: boolean, path: string): void;
86
+ //# sourceMappingURL=service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/storage/service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAEjE,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE/E,MAAM,WAAW,WAAW;IAC1B;;;;;OAKG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACrD,uEAAuE;IACvE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,UAAU;IACzB,0EAA0E;IAC1E,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC;;;;OAIG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;CACpC;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC,4DAA4D;IAC5D,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAE7B,8EAA8E;IAC9E,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAE7B,MAAM,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IAClD,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAEvC;;;;;OAKG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAE5D,+CAA+C;IAC/C,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAE3E;;;;;OAKG;IACH,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9D,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACvC;AAED,MAAM,WAAW,cAAc;IAC7B,uEAAuE;IACvE,YAAY,CAAC,OAAO,EAAE,gBAAgB,GAAG,aAAa,CAAC;IACvD,2EAA2E;IAC3E,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC;CACpC;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC;IACnC,mCAAmC;IACnC,QAAQ,CAAC,uBAAuB,CAAC,EAAE,MAAM,CAAC;IAC1C,2CAA2C;IAC3C,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;CAClC;AAGD;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,QAAmB,CAAC;AAIvD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,kBAAkB,GAAG,cAAc,CA+G7E;AAED,mEAAmE;AACnE,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAItE"}
@@ -0,0 +1,107 @@
1
+ import { InvalidInputError, NotFoundError } from "../errors.js";
2
+ import { assertKeyBelongsTo, workspaceObjectKey } from "./keys.js";
3
+ const DEFAULT_EXPIRES_SECONDS = 900;
4
+ /**
5
+ * Seven days. S3's own hard limit for a SigV4 presigned URL — beyond it the
6
+ * signature is simply invalid, so accepting a larger number would hand back a URL
7
+ * that never works.
8
+ */
9
+ export const MAX_SIGNED_URL_SECONDS = 7 * 24 * 60 * 60;
10
+ const DEFAULT_MAX_UPLOAD_BYTES = 25 * 1024 * 1024;
11
+ export function createStorageService(deps) {
12
+ const defaultExpires = deps.defaultExpiresInSeconds ?? DEFAULT_EXPIRES_SECONDS;
13
+ const maxUploadBytes = deps.maxUploadBytes ?? DEFAULT_MAX_UPLOAD_BYTES;
14
+ function expiry(requested) {
15
+ const seconds = requested ?? defaultExpires;
16
+ if (!Number.isInteger(seconds) || seconds <= 0) {
17
+ throw new InvalidInputError("expiresInSeconds must be a positive integer.");
18
+ }
19
+ if (seconds > MAX_SIGNED_URL_SECONDS) {
20
+ // Clamping silently would produce a URL that expires sooner than promised.
21
+ throw new InvalidInputError(`expiresInSeconds may not exceed ${MAX_SIGNED_URL_SECONDS} (7 days).`);
22
+ }
23
+ return seconds;
24
+ }
25
+ return {
26
+ provider: deps.provider,
27
+ forWorkspace(context) {
28
+ const workspaceId = context.workspaceId;
29
+ function keyFor(path) {
30
+ return workspaceObjectKey(workspaceId, path);
31
+ }
32
+ async function urlFor(key, options = {}) {
33
+ /**
34
+ * A public URL is only used when no download filename was requested —
35
+ * asking for `attachment` and getting a bare CDN link back would silently
36
+ * drop the protection the caller asked for.
37
+ */
38
+ if (options.downloadFilename === undefined) {
39
+ const publicUrl = deps.provider.publicUrl(key);
40
+ if (publicUrl !== undefined) {
41
+ return publicUrl;
42
+ }
43
+ }
44
+ return deps.provider.signedDownloadUrl({
45
+ key,
46
+ expiresInSeconds: expiry(options.expiresInSeconds),
47
+ ...(options.downloadFilename !== undefined
48
+ ? { downloadFilename: options.downloadFilename }
49
+ : {}),
50
+ });
51
+ }
52
+ return {
53
+ workspaceId,
54
+ keyFor,
55
+ async upload(input) {
56
+ const key = keyFor(input.path);
57
+ const size = typeof input.body === "string"
58
+ ? Buffer.byteLength(input.body, "utf8")
59
+ : input.body.byteLength;
60
+ const limit = input.maxBytes ?? maxUploadBytes;
61
+ if (size > limit) {
62
+ // Checked before the provider call, so an oversized body is never sent.
63
+ throw new InvalidInputError(`Upload exceeds the ${limit} byte limit.`);
64
+ }
65
+ return deps.provider.put({
66
+ key,
67
+ body: input.body,
68
+ ...(input.contentType !== undefined ? { contentType: input.contentType } : {}),
69
+ ...(input.cacheControl !== undefined ? { cacheControl: input.cacheControl } : {}),
70
+ ...(input.metadata !== undefined ? { metadata: input.metadata } : {}),
71
+ });
72
+ },
73
+ async delete(path) {
74
+ await deps.provider.delete(keyFor(path));
75
+ },
76
+ async exists(path) {
77
+ return deps.provider.exists(keyFor(path));
78
+ },
79
+ async getUrl(path, options = {}) {
80
+ return urlFor(keyFor(path), options);
81
+ },
82
+ async createUploadUrl(path, options = {}) {
83
+ return deps.provider.signedUploadUrl({
84
+ key: keyFor(path),
85
+ expiresInSeconds: expiry(options.expiresInSeconds),
86
+ ...(options.contentType !== undefined ? { contentType: options.contentType } : {}),
87
+ });
88
+ },
89
+ async urlForKey(key, options = {}) {
90
+ assertKeyBelongsTo(workspaceId, key);
91
+ return urlFor(key, options);
92
+ },
93
+ async deleteKey(key) {
94
+ assertKeyBelongsTo(workspaceId, key);
95
+ await deps.provider.delete(key);
96
+ },
97
+ };
98
+ },
99
+ };
100
+ }
101
+ /** Thrown by callers that need a missing object to be an error. */
102
+ export function assertObjectExists(exists, path) {
103
+ if (!exists) {
104
+ throw new NotFoundError(`Object "${path}" not found.`);
105
+ }
106
+ }
107
+ //# sourceMappingURL=service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/storage/service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,58 @@
1
+ export interface SigV4Credentials {
2
+ readonly accessKeyId: string;
3
+ readonly secretAccessKey: string;
4
+ /** For temporary credentials (STS, instance roles). */
5
+ readonly sessionToken?: string;
6
+ }
7
+ export interface PresignInput {
8
+ readonly method: "GET" | "PUT" | "HEAD" | "DELETE";
9
+ /** Host only, no scheme — `bucket.s3.eu-west-1.amazonaws.com`. */
10
+ readonly host: string;
11
+ /** Already-encoded path, beginning with `/`. */
12
+ readonly path: string;
13
+ readonly region: string;
14
+ readonly service: string;
15
+ readonly credentials: SigV4Credentials;
16
+ readonly expiresInSeconds: number;
17
+ /** Extra query parameters to sign, e.g. `response-content-disposition`. */
18
+ readonly query?: Readonly<Record<string, string>>;
19
+ /**
20
+ * Additional headers to sign beyond `host`.
21
+ *
22
+ * A signed header must be sent by the client *exactly* as signed, or the
23
+ * request is rejected — so only include what the caller controls.
24
+ */
25
+ readonly headers?: Readonly<Record<string, string>>;
26
+ readonly now?: Date;
27
+ }
28
+ /** RFC 3986, which is stricter than `encodeURIComponent` about these four. */
29
+ export declare function uriEncode(value: string): string;
30
+ /**
31
+ * Encode an object key for use in a URL path.
32
+ *
33
+ * Slashes are preserved — they are path separators in a key, not data — while
34
+ * every other reserved character is escaped.
35
+ */
36
+ export declare function encodeObjectPath(key: string): string;
37
+ /**
38
+ * Derive the signing key.
39
+ *
40
+ * Four chained HMACs, each scoping the key more narrowly — so a leaked signing
41
+ * key is only good for one date, region and service rather than for the account.
42
+ */
43
+ export declare function signingKey(input: {
44
+ secretAccessKey: string;
45
+ dateStamp: string;
46
+ region: string;
47
+ service: string;
48
+ }): Buffer;
49
+ export interface PresignResult {
50
+ readonly url: string;
51
+ /** Exposed so tests can assert against AWS's published vectors. */
52
+ readonly signature: string;
53
+ readonly canonicalRequest: string;
54
+ readonly stringToSign: string;
55
+ readonly expiresAt: Date;
56
+ }
57
+ export declare function presign(input: PresignInput): PresignResult;
58
+ //# sourceMappingURL=sigv4.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sigv4.d.ts","sourceRoot":"","sources":["../../src/storage/sigv4.ts"],"names":[],"mappings":"AAmBA,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,uDAAuD;IACvD,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,MAAM,EAAE,KAAK,GAAG,KAAK,GAAG,MAAM,GAAG,QAAQ,CAAC;IACnD,kEAAkE;IAClE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,gDAAgD;IAChD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,WAAW,EAAE,gBAAgB,CAAC;IACvC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,2EAA2E;IAC3E,QAAQ,CAAC,KAAK,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAClD;;;;;OAKG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACpD,QAAQ,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC;CACrB;AAED,8EAA8E;AAC9E,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAK/C;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEpD;AAgBD;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE;IAChC,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB,GAAG,MAAM,CAKT;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,mEAAmE;IACnE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAC;CAC1B;AAED,wBAAgB,OAAO,CAAC,KAAK,EAAE,YAAY,GAAG,aAAa,CAyE1D"}
@@ -0,0 +1,114 @@
1
+ import { createHash, createHmac } from "node:crypto";
2
+ /**
3
+ * AWS Signature Version 4, query-string ("presigned URL") flavour.
4
+ *
5
+ * Implemented rather than pulled in, deliberately. `@aws-sdk/client-s3` plus the
6
+ * presigner is a large dependency tree for a package many applications install,
7
+ * and a deployment on R2, MinIO or Backblaze has no reason to carry the AWS SDK
8
+ * at all. SigV4 is a fully specified algorithm, so unlike an undocumented vendor
9
+ * API it can be *verified*: the tests check this against AWS's own published
10
+ * worked example.
11
+ *
12
+ * If you would rather use the SDK, `StorageProvider` is a six-method interface.
13
+ */
14
+ const ALGORITHM = "AWS4-HMAC-SHA256";
15
+ /** Presigned S3 URLs never sign the body. */
16
+ const UNSIGNED_PAYLOAD = "UNSIGNED-PAYLOAD";
17
+ /** RFC 3986, which is stricter than `encodeURIComponent` about these four. */
18
+ export function uriEncode(value) {
19
+ return encodeURIComponent(value).replace(/[!'()*]/g, (char) => `%${char.charCodeAt(0).toString(16).toUpperCase()}`);
20
+ }
21
+ /**
22
+ * Encode an object key for use in a URL path.
23
+ *
24
+ * Slashes are preserved — they are path separators in a key, not data — while
25
+ * every other reserved character is escaped.
26
+ */
27
+ export function encodeObjectPath(key) {
28
+ return key.split("/").map(uriEncode).join("/");
29
+ }
30
+ function hmac(key, data) {
31
+ return createHmac("sha256", key).update(data, "utf8").digest();
32
+ }
33
+ function sha256Hex(value) {
34
+ return createHash("sha256").update(value, "utf8").digest("hex");
35
+ }
36
+ /** `20130524T000000Z` and `20130524`. */
37
+ function timestamps(now) {
38
+ const amzDate = now.toISOString().replace(/[:-]|\.\d{3}/g, "");
39
+ return { amzDate, dateStamp: amzDate.slice(0, 8) };
40
+ }
41
+ /**
42
+ * Derive the signing key.
43
+ *
44
+ * Four chained HMACs, each scoping the key more narrowly — so a leaked signing
45
+ * key is only good for one date, region and service rather than for the account.
46
+ */
47
+ export function signingKey(input) {
48
+ const kDate = hmac(`AWS4${input.secretAccessKey}`, input.dateStamp);
49
+ const kRegion = hmac(kDate, input.region);
50
+ const kService = hmac(kRegion, input.service);
51
+ return hmac(kService, "aws4_request");
52
+ }
53
+ export function presign(input) {
54
+ const now = input.now ?? new Date();
55
+ const { amzDate, dateStamp } = timestamps(now);
56
+ const scope = `${dateStamp}/${input.region}/${input.service}/aws4_request`;
57
+ const signedHeaderEntries = Object.entries({
58
+ // `host` is always signed: it is what binds a signature to one bucket.
59
+ host: input.host,
60
+ ...(input.headers ?? {}),
61
+ }).map(([name, value]) => [name.toLowerCase(), value.trim()]);
62
+ // Header names sort lexicographically, and the sorted list is itself signed.
63
+ signedHeaderEntries.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));
64
+ const signedHeaders = signedHeaderEntries.map(([name]) => name).join(";");
65
+ const queryParameters = {
66
+ ...(input.query ?? {}),
67
+ "X-Amz-Algorithm": ALGORITHM,
68
+ "X-Amz-Credential": `${input.credentials.accessKeyId}/${scope}`,
69
+ "X-Amz-Date": amzDate,
70
+ "X-Amz-Expires": String(input.expiresInSeconds),
71
+ "X-Amz-SignedHeaders": signedHeaders,
72
+ };
73
+ if (input.credentials.sessionToken !== undefined) {
74
+ queryParameters["X-Amz-Security-Token"] = input.credentials.sessionToken;
75
+ }
76
+ /**
77
+ * Canonical query string: encoded, then sorted by encoded key. Sorting before
78
+ * encoding gives a different order for some inputs, and the signature would
79
+ * not match.
80
+ */
81
+ const canonicalQuery = Object.entries(queryParameters)
82
+ .map(([key, value]) => [uriEncode(key), uriEncode(value)])
83
+ .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
84
+ .map(([key, value]) => `${key}=${value}`)
85
+ .join("&");
86
+ const canonicalHeaders = signedHeaderEntries
87
+ .map(([name, value]) => `${name}:${value}\n`)
88
+ .join("");
89
+ const canonicalRequest = [
90
+ input.method,
91
+ input.path,
92
+ canonicalQuery,
93
+ canonicalHeaders,
94
+ signedHeaders,
95
+ UNSIGNED_PAYLOAD,
96
+ ].join("\n");
97
+ const stringToSign = [ALGORITHM, amzDate, scope, sha256Hex(canonicalRequest)].join("\n");
98
+ const signature = createHmac("sha256", signingKey({
99
+ secretAccessKey: input.credentials.secretAccessKey,
100
+ dateStamp,
101
+ region: input.region,
102
+ service: input.service,
103
+ }))
104
+ .update(stringToSign, "utf8")
105
+ .digest("hex");
106
+ return {
107
+ url: `https://${input.host}${input.path}?${canonicalQuery}&X-Amz-Signature=${signature}`,
108
+ signature,
109
+ canonicalRequest,
110
+ stringToSign,
111
+ expiresAt: new Date(now.getTime() + input.expiresInSeconds * 1000),
112
+ };
113
+ }
114
+ //# sourceMappingURL=sigv4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sigv4.js","sourceRoot":"","sources":["../../src/storage/sigv4.ts"],"names":[],"mappings":"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"}