bunderstack 0.17.0-beta.2 → 0.17.0-beta.3

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 (336) hide show
  1. package/CHANGELOG.md +227 -0
  2. package/README.md +11 -1
  3. package/dist/access.d.ts +116 -0
  4. package/dist/access.d.ts.map +1 -0
  5. package/dist/access.js +325 -0
  6. package/dist/access.js.map +1 -0
  7. package/dist/api/builder.d.ts +145 -0
  8. package/dist/api/builder.d.ts.map +1 -0
  9. package/dist/api/builder.js +28 -0
  10. package/dist/api/builder.js.map +1 -0
  11. package/dist/api/context.d.ts +34 -0
  12. package/dist/api/context.d.ts.map +1 -0
  13. package/dist/api/context.js +32 -0
  14. package/dist/api/context.js.map +1 -0
  15. package/dist/api/crud-router.d.ts +290 -0
  16. package/dist/api/crud-router.d.ts.map +1 -0
  17. package/dist/api/crud-router.js +249 -0
  18. package/dist/api/crud-router.js.map +1 -0
  19. package/dist/api/openapi.d.ts +6 -0
  20. package/dist/api/openapi.d.ts.map +1 -0
  21. package/dist/api/openapi.js +146 -0
  22. package/dist/api/openapi.js.map +1 -0
  23. package/dist/api/realtime-router.d.ts +76 -0
  24. package/dist/api/realtime-router.d.ts.map +1 -0
  25. package/dist/api/realtime-router.js +41 -0
  26. package/dist/api/realtime-router.js.map +1 -0
  27. package/dist/api/registry.d.ts +30 -0
  28. package/dist/api/registry.d.ts.map +1 -0
  29. package/dist/api/registry.js +239 -0
  30. package/dist/api/registry.js.map +1 -0
  31. package/dist/api/router.d.ts +9 -0
  32. package/dist/api/router.d.ts.map +1 -0
  33. package/dist/api/router.js +19 -0
  34. package/dist/api/router.js.map +1 -0
  35. package/dist/api/storage-router.d.ts +279 -0
  36. package/dist/api/storage-router.d.ts.map +1 -0
  37. package/dist/api/storage-router.js +155 -0
  38. package/dist/api/storage-router.js.map +1 -0
  39. package/dist/api/types.d.ts +55 -0
  40. package/dist/api/types.d.ts.map +1 -0
  41. package/dist/api/types.js +2 -0
  42. package/dist/api/types.js.map +1 -0
  43. package/dist/auth.d.ts +31 -0
  44. package/dist/auth.d.ts.map +1 -0
  45. package/dist/auth.js +102 -0
  46. package/dist/auth.js.map +1 -0
  47. package/dist/blueprint-generator.d.ts +18 -0
  48. package/dist/blueprint-generator.d.ts.map +1 -0
  49. package/dist/blueprint-generator.js +125 -0
  50. package/dist/blueprint-generator.js.map +1 -0
  51. package/dist/blueprint.d.ts +46 -0
  52. package/dist/blueprint.d.ts.map +1 -0
  53. package/dist/blueprint.js +162 -0
  54. package/dist/blueprint.js.map +1 -0
  55. package/dist/cli.d.ts +8 -0
  56. package/dist/cli.d.ts.map +1 -0
  57. package/dist/cli.js +68 -0
  58. package/dist/cli.js.map +1 -0
  59. package/dist/config.d.ts +76 -0
  60. package/dist/config.d.ts.map +1 -0
  61. package/dist/config.js +81 -0
  62. package/dist/config.js.map +1 -0
  63. package/dist/cron.d.ts +4 -0
  64. package/dist/cron.d.ts.map +1 -0
  65. package/dist/cron.js +3 -0
  66. package/dist/cron.js.map +1 -0
  67. package/dist/crud-operations.d.ts +43 -0
  68. package/dist/crud-operations.d.ts.map +1 -0
  69. package/dist/crud-operations.js +238 -0
  70. package/dist/crud-operations.js.map +1 -0
  71. package/dist/database/adapter.d.ts +20 -0
  72. package/dist/database/adapter.d.ts.map +1 -0
  73. package/dist/database/adapter.js +2 -0
  74. package/dist/database/adapter.js.map +1 -0
  75. package/dist/database/bun-sql.d.ts +3 -0
  76. package/dist/database/bun-sql.d.ts.map +1 -0
  77. package/dist/database/bun-sql.js +21 -0
  78. package/dist/database/bun-sql.js.map +1 -0
  79. package/dist/database/libsql.d.ts +3 -0
  80. package/dist/database/libsql.d.ts.map +1 -0
  81. package/dist/database/libsql.js +22 -0
  82. package/dist/database/libsql.js.map +1 -0
  83. package/dist/database/pglite.d.ts +3 -0
  84. package/dist/database/pglite.d.ts.map +1 -0
  85. package/dist/database/pglite.js +27 -0
  86. package/dist/database/pglite.js.map +1 -0
  87. package/dist/database/postgres-js.d.ts +3 -0
  88. package/dist/database/postgres-js.d.ts.map +1 -0
  89. package/dist/database/postgres-js.js +21 -0
  90. package/dist/database/postgres-js.js.map +1 -0
  91. package/dist/db.d.ts +18 -0
  92. package/dist/db.d.ts.map +1 -0
  93. package/dist/db.js +26 -0
  94. package/dist/db.js.map +1 -0
  95. package/dist/dialect.d.ts +17 -0
  96. package/dist/dialect.d.ts.map +1 -0
  97. package/dist/dialect.js +22 -0
  98. package/dist/dialect.js.map +1 -0
  99. package/dist/email/smtp.d.ts +14 -0
  100. package/dist/email/smtp.d.ts.map +1 -0
  101. package/dist/email/smtp.js +27 -0
  102. package/dist/email/smtp.js.map +1 -0
  103. package/dist/email.d.ts +39 -0
  104. package/dist/email.d.ts.map +1 -0
  105. package/dist/email.js +97 -0
  106. package/dist/email.js.map +1 -0
  107. package/dist/env.d.ts +47 -0
  108. package/dist/env.d.ts.map +1 -0
  109. package/dist/env.js +99 -0
  110. package/dist/env.js.map +1 -0
  111. package/dist/errors.d.ts +125 -0
  112. package/dist/errors.d.ts.map +1 -0
  113. package/dist/errors.js +92 -0
  114. package/dist/errors.js.map +1 -0
  115. package/dist/handler.d.ts +9 -0
  116. package/dist/handler.d.ts.map +1 -0
  117. package/dist/handler.js +22 -0
  118. package/dist/handler.js.map +1 -0
  119. package/dist/idempotency.d.ts +17 -0
  120. package/dist/idempotency.d.ts.map +1 -0
  121. package/dist/idempotency.js +65 -0
  122. package/dist/idempotency.js.map +1 -0
  123. package/dist/index.d.ts +134 -0
  124. package/dist/index.d.ts.map +1 -0
  125. package/dist/index.js +477 -0
  126. package/dist/index.js.map +1 -0
  127. package/dist/internal-tables-pg.d.ts +481 -0
  128. package/dist/internal-tables-pg.d.ts.map +1 -0
  129. package/{src/internal-tables-pg.ts → dist/internal-tables-pg.js} +10 -34
  130. package/dist/internal-tables-pg.js.map +1 -0
  131. package/dist/internal-tables.d.ts +2509 -0
  132. package/dist/internal-tables.d.ts.map +1 -0
  133. package/dist/internal-tables.js +108 -0
  134. package/dist/internal-tables.js.map +1 -0
  135. package/dist/jobs/cron.d.ts +15 -0
  136. package/dist/jobs/cron.d.ts.map +1 -0
  137. package/dist/jobs/cron.js +74 -0
  138. package/dist/jobs/cron.js.map +1 -0
  139. package/dist/jobs/define.d.ts +133 -0
  140. package/dist/jobs/define.d.ts.map +1 -0
  141. package/dist/jobs/define.js +74 -0
  142. package/dist/jobs/define.js.map +1 -0
  143. package/dist/jobs/index.d.ts +11 -0
  144. package/dist/jobs/index.d.ts.map +1 -0
  145. package/dist/jobs/index.js +8 -0
  146. package/dist/jobs/index.js.map +1 -0
  147. package/dist/jobs/queue.d.ts +6 -0
  148. package/dist/jobs/queue.d.ts.map +1 -0
  149. package/dist/jobs/queue.js +55 -0
  150. package/dist/jobs/queue.js.map +1 -0
  151. package/dist/jobs/runtime.d.ts +13 -0
  152. package/dist/jobs/runtime.d.ts.map +1 -0
  153. package/dist/jobs/runtime.js +46 -0
  154. package/dist/jobs/runtime.js.map +1 -0
  155. package/dist/jobs/slots.d.ts +25 -0
  156. package/dist/jobs/slots.d.ts.map +1 -0
  157. package/dist/jobs/slots.js +41 -0
  158. package/dist/jobs/slots.js.map +1 -0
  159. package/dist/jobs/worker.d.ts +12 -0
  160. package/dist/jobs/worker.d.ts.map +1 -0
  161. package/dist/jobs/worker.js +311 -0
  162. package/dist/jobs/worker.js.map +1 -0
  163. package/dist/lifecycle.d.ts +10 -0
  164. package/dist/lifecycle.d.ts.map +1 -0
  165. package/dist/lifecycle.js +37 -0
  166. package/dist/lifecycle.js.map +1 -0
  167. package/dist/list-query.d.ts +62 -0
  168. package/dist/list-query.d.ts.map +1 -0
  169. package/dist/list-query.js +194 -0
  170. package/dist/list-query.js.map +1 -0
  171. package/dist/manifest.d.ts +59 -0
  172. package/dist/manifest.d.ts.map +1 -0
  173. package/dist/manifest.js +167 -0
  174. package/dist/manifest.js.map +1 -0
  175. package/dist/provision-internals.d.ts +24 -0
  176. package/dist/provision-internals.d.ts.map +1 -0
  177. package/dist/provision-internals.js +7 -0
  178. package/dist/provision-internals.js.map +1 -0
  179. package/dist/provision.d.ts +23 -0
  180. package/dist/provision.d.ts.map +1 -0
  181. package/dist/provision.js +96 -0
  182. package/dist/provision.js.map +1 -0
  183. package/dist/rate-limit.d.ts +7 -0
  184. package/dist/rate-limit.d.ts.map +1 -0
  185. package/dist/rate-limit.js +49 -0
  186. package/dist/rate-limit.js.map +1 -0
  187. package/dist/realtime/facade.d.ts +18 -0
  188. package/dist/realtime/facade.d.ts.map +1 -0
  189. package/dist/realtime/facade.js +36 -0
  190. package/dist/realtime/facade.js.map +1 -0
  191. package/dist/realtime/filter.d.ts +13 -0
  192. package/dist/realtime/filter.d.ts.map +1 -0
  193. package/dist/realtime/filter.js +48 -0
  194. package/dist/realtime/filter.js.map +1 -0
  195. package/dist/realtime/heartbeat.d.ts +14 -0
  196. package/dist/realtime/heartbeat.d.ts.map +1 -0
  197. package/dist/realtime/heartbeat.js +64 -0
  198. package/dist/realtime/heartbeat.js.map +1 -0
  199. package/dist/realtime/publisher.d.ts +21 -0
  200. package/dist/realtime/publisher.d.ts.map +1 -0
  201. package/dist/realtime/publisher.js +17 -0
  202. package/dist/realtime/publisher.js.map +1 -0
  203. package/dist/schema-export-pg.d.ts +2 -0
  204. package/dist/schema-export-pg.d.ts.map +1 -0
  205. package/dist/schema-export-pg.js +4 -0
  206. package/dist/schema-export-pg.js.map +1 -0
  207. package/dist/schema-export.d.ts +2 -0
  208. package/dist/schema-export.d.ts.map +1 -0
  209. package/dist/schema-export.js +2 -0
  210. package/dist/schema-export.js.map +1 -0
  211. package/dist/scope.d.ts +4 -0
  212. package/dist/scope.d.ts.map +1 -0
  213. package/dist/scope.js +13 -0
  214. package/dist/scope.js.map +1 -0
  215. package/dist/standard-schema.d.ts +13 -0
  216. package/dist/standard-schema.d.ts.map +1 -0
  217. package/dist/standard-schema.js +34 -0
  218. package/dist/standard-schema.js.map +1 -0
  219. package/dist/storage/buckets.d.ts +80 -0
  220. package/dist/storage/buckets.d.ts.map +1 -0
  221. package/dist/storage/buckets.js +178 -0
  222. package/dist/storage/buckets.js.map +1 -0
  223. package/dist/storage/delete.d.ts +11 -0
  224. package/dist/storage/delete.d.ts.map +1 -0
  225. package/dist/storage/delete.js +18 -0
  226. package/dist/storage/delete.js.map +1 -0
  227. package/dist/storage/file-meta.d.ts +37 -0
  228. package/dist/storage/file-meta.d.ts.map +1 -0
  229. package/dist/storage/file-meta.js +166 -0
  230. package/dist/storage/file-meta.js.map +1 -0
  231. package/dist/storage/index.d.ts +28 -0
  232. package/dist/storage/index.d.ts.map +1 -0
  233. package/dist/storage/index.js +5 -0
  234. package/dist/storage/index.js.map +1 -0
  235. package/dist/storage/local.d.ts +15 -0
  236. package/dist/storage/local.d.ts.map +1 -0
  237. package/dist/storage/local.js +56 -0
  238. package/dist/storage/local.js.map +1 -0
  239. package/dist/storage/operations.d.ts +54 -0
  240. package/dist/storage/operations.d.ts.map +1 -0
  241. package/dist/storage/operations.js +250 -0
  242. package/dist/storage/operations.js.map +1 -0
  243. package/dist/storage/registry.d.ts +10 -0
  244. package/dist/storage/registry.d.ts.map +1 -0
  245. package/dist/storage/registry.js +23 -0
  246. package/dist/storage/registry.js.map +1 -0
  247. package/dist/storage/s3.d.ts +28 -0
  248. package/dist/storage/s3.d.ts.map +1 -0
  249. package/dist/storage/s3.js +71 -0
  250. package/dist/storage/s3.js.map +1 -0
  251. package/dist/storage/sweep.d.ts +9 -0
  252. package/dist/storage/sweep.d.ts.map +1 -0
  253. package/dist/storage/sweep.js +17 -0
  254. package/dist/storage/sweep.js.map +1 -0
  255. package/dist/storage/thumbnails.d.ts +11 -0
  256. package/dist/storage/thumbnails.d.ts.map +1 -0
  257. package/dist/storage/thumbnails.js +60 -0
  258. package/dist/storage/thumbnails.js.map +1 -0
  259. package/dist/testing.d.ts +27 -0
  260. package/dist/testing.d.ts.map +1 -0
  261. package/dist/testing.js +11 -0
  262. package/dist/testing.js.map +1 -0
  263. package/dist/typeid-pg.d.ts +19 -0
  264. package/dist/typeid-pg.d.ts.map +1 -0
  265. package/{src/typeid-pg.ts → dist/typeid-pg.js} +10 -13
  266. package/dist/typeid-pg.js.map +1 -0
  267. package/dist/typeid.d.ts +45 -0
  268. package/dist/typeid.d.ts.map +1 -0
  269. package/dist/typeid.js +100 -0
  270. package/dist/typeid.js.map +1 -0
  271. package/package.json +77 -28
  272. package/src/access.ts +0 -532
  273. package/src/api/builder.ts +0 -52
  274. package/src/api/context.ts +0 -83
  275. package/src/api/crud-router.ts +0 -386
  276. package/src/api/openapi.ts +0 -184
  277. package/src/api/realtime-router.ts +0 -75
  278. package/src/api/registry.ts +0 -338
  279. package/src/api/router.ts +0 -34
  280. package/src/api/storage-router.ts +0 -224
  281. package/src/api/types.ts +0 -105
  282. package/src/auth.ts +0 -117
  283. package/src/blueprint-generator.ts +0 -203
  284. package/src/blueprint.ts +0 -264
  285. package/src/cli.ts +0 -90
  286. package/src/config.ts +0 -215
  287. package/src/cron.ts +0 -3
  288. package/src/crud-operations.ts +0 -478
  289. package/src/database/adapter.ts +0 -26
  290. package/src/database/bun-sql.ts +0 -23
  291. package/src/database/libsql.ts +0 -28
  292. package/src/database/pglite.ts +0 -33
  293. package/src/database/postgres-js.ts +0 -33
  294. package/src/db.ts +0 -61
  295. package/src/dialect.ts +0 -38
  296. package/src/email/smtp.ts +0 -45
  297. package/src/email.ts +0 -154
  298. package/src/env.ts +0 -180
  299. package/src/errors.ts +0 -123
  300. package/src/handler.ts +0 -30
  301. package/src/idempotency.ts +0 -97
  302. package/src/index.ts +0 -834
  303. package/src/internal-tables.ts +0 -149
  304. package/src/jobs/cron.ts +0 -87
  305. package/src/jobs/define.ts +0 -283
  306. package/src/jobs/index.ts +0 -37
  307. package/src/jobs/queue.ts +0 -67
  308. package/src/jobs/runtime.ts +0 -58
  309. package/src/jobs/slots.ts +0 -52
  310. package/src/jobs/worker.ts +0 -369
  311. package/src/lifecycle.ts +0 -49
  312. package/src/list-query.ts +0 -333
  313. package/src/manifest.ts +0 -297
  314. package/src/provision-internals.ts +0 -29
  315. package/src/provision.ts +0 -128
  316. package/src/rate-limit.ts +0 -71
  317. package/src/realtime/facade.ts +0 -74
  318. package/src/realtime/filter.ts +0 -81
  319. package/src/realtime/heartbeat.ts +0 -80
  320. package/src/realtime/publisher.ts +0 -46
  321. package/src/schema-export-pg.ts +0 -7
  322. package/src/schema-export.ts +0 -5
  323. package/src/scope.ts +0 -17
  324. package/src/standard-schema.ts +0 -59
  325. package/src/storage/buckets.ts +0 -308
  326. package/src/storage/delete.ts +0 -26
  327. package/src/storage/file-meta.ts +0 -222
  328. package/src/storage/index.ts +0 -39
  329. package/src/storage/local.ts +0 -69
  330. package/src/storage/operations.ts +0 -398
  331. package/src/storage/registry.ts +0 -40
  332. package/src/storage/s3.ts +0 -99
  333. package/src/storage/sweep.ts +0 -25
  334. package/src/storage/thumbnails.ts +0 -65
  335. package/src/testing.ts +0 -31
  336. package/src/typeid.ts +0 -116
package/src/errors.ts DELETED
@@ -1,123 +0,0 @@
1
- import {
2
- COMMON_ERROR_STATUS_MAP,
3
- ORPCError,
4
- os,
5
- type ErrorMap,
6
- } from '@orpc/server'
7
- import * as v from 'valibot'
8
-
9
- import { StandardSchemaValidationError } from './standard-schema'
10
-
11
- /**
12
- * Every code we raise is one oRPC already knows, so handlers derive the HTTP
13
- * status from `COMMON_ERROR_STATUS_MAP` on their own and clients can use the
14
- * standard `isDefinedError` helpers. Do not add a code oRPC has no status for —
15
- * it would silently answer 500.
16
- */
17
- export const BUNDERSTACK_ERROR_CODES = [
18
- 'BAD_REQUEST',
19
- 'UNAUTHORIZED',
20
- 'FORBIDDEN',
21
- 'NOT_FOUND',
22
- 'CONFLICT',
23
- 'PAYLOAD_TOO_LARGE',
24
- 'TOO_MANY_REQUESTS',
25
- ] as const satisfies readonly (keyof typeof COMMON_ERROR_STATUS_MAP)[]
26
-
27
- export type BunderstackErrorCode = (typeof BUNDERSTACK_ERROR_CODES)[number]
28
-
29
- function errorDataSchema<const TCode extends BunderstackErrorCode>(
30
- code: TCode,
31
- ) {
32
- return v.strictObject({
33
- code: v.literal(code),
34
- details: v.optional(v.unknown()),
35
- })
36
- }
37
-
38
- export const BUNDERSTACK_ERRORS = {
39
- BAD_REQUEST: { data: errorDataSchema('BAD_REQUEST') },
40
- UNAUTHORIZED: { data: errorDataSchema('UNAUTHORIZED') },
41
- FORBIDDEN: { data: errorDataSchema('FORBIDDEN') },
42
- NOT_FOUND: { data: errorDataSchema('NOT_FOUND') },
43
- CONFLICT: { data: errorDataSchema('CONFLICT') },
44
- PAYLOAD_TOO_LARGE: { data: errorDataSchema('PAYLOAD_TOO_LARGE') },
45
- TOO_MANY_REQUESTS: { data: errorDataSchema('TOO_MANY_REQUESTS') },
46
- } as const satisfies ErrorMap
47
-
48
- export class BunderstackError extends Error {
49
- readonly status: number
50
-
51
- constructor(
52
- readonly code: BunderstackErrorCode,
53
- message: string,
54
- readonly details?: unknown,
55
- options?: ErrorOptions,
56
- ) {
57
- super(message, options)
58
- this.name = 'BunderstackError'
59
- this.status = COMMON_ERROR_STATUS_MAP[code]
60
- }
61
- }
62
-
63
- export const mapBunderstackErrors = os
64
- .errors(BUNDERSTACK_ERRORS)
65
- .middleware(async ({ next }) => {
66
- try {
67
- return await next()
68
- } catch (error) {
69
- const mapped =
70
- error instanceof StandardSchemaValidationError
71
- ? new BunderstackError(
72
- 'BAD_REQUEST',
73
- error.message,
74
- error.issues,
75
- { cause: error },
76
- )
77
- : error
78
- if (!(mapped instanceof BunderstackError)) throw error
79
-
80
- throw new ORPCError(mapped.code, {
81
- message: mapped.message,
82
- data:
83
- mapped.details === undefined
84
- ? { code: mapped.code }
85
- : { code: mapped.code, details: mapped.details },
86
- cause: mapped,
87
- })
88
- }
89
- })
90
-
91
- /**
92
- * Sub-codes carried in `data.details.code` when the oRPC code alone loses
93
- * information the client may want to branch on. Codes that duplicate an oRPC
94
- * code are suppressed as redundant, see {@link CrudOperationError}.
95
- */
96
- export const ErrorCode = {
97
- BAD_REQUEST: 'BAD_REQUEST',
98
- FORBIDDEN: 'FORBIDDEN',
99
- NOT_FOUND: 'NOT_FOUND',
100
- CONFLICT: 'CONFLICT',
101
- INVALID_CURSOR: 'INVALID_CURSOR',
102
- TOO_MANY_REQUESTS: 'TOO_MANY_REQUESTS',
103
- IDEMPOTENCY_REPLAY: 'IDEMPOTENCY_REPLAY',
104
- IDEMPOTENCY_CONFLICT: 'IDEMPOTENCY_CONFLICT',
105
- } as const
106
-
107
- export type ErrorCodeValue = (typeof ErrorCode)[keyof typeof ErrorCode]
108
-
109
- export class ListQueryError extends Error {
110
- readonly code: ErrorCodeValue
111
- readonly details?: unknown
112
-
113
- constructor(
114
- message: string,
115
- code: ErrorCodeValue = ErrorCode.BAD_REQUEST,
116
- details?: unknown,
117
- ) {
118
- super(message)
119
- this.name = 'ListQueryError'
120
- this.code = code
121
- this.details = details
122
- }
123
- }
package/src/handler.ts DELETED
@@ -1,30 +0,0 @@
1
- // src/handler.ts
2
- import { createRateLimiter, type RateLimitConfig } from './rate-limit'
3
-
4
- interface HandlerParts {
5
- authHandler?: (req: Request) => Promise<Response>
6
- apiHandler?: (req: Request) => Promise<Response | null>
7
- rateLimit?: boolean | RateLimitConfig
8
- }
9
-
10
- export function buildHandler(parts: HandlerParts): (req: Request) => Promise<Response> {
11
- const checkRateLimit = createRateLimiter(parts.rateLimit)
12
-
13
- return async (req: Request): Promise<Response> => {
14
- const limited = await checkRateLimit(req)
15
- if (limited) return limited
16
-
17
- const pathname = new URL(req.url).pathname
18
- if (
19
- parts.authHandler &&
20
- (pathname === '/api/auth' || pathname.startsWith('/api/auth/'))
21
- ) {
22
- return parts.authHandler(req)
23
- }
24
- if (parts.apiHandler) {
25
- const apiRes = await parts.apiHandler(req)
26
- if (apiRes) return apiRes
27
- }
28
- return new Response('Not Found', { status: 404 })
29
- }
30
- }
@@ -1,97 +0,0 @@
1
- import { and, eq, lt } from 'drizzle-orm'
2
- import { createHash } from 'node:crypto'
3
-
4
- import type { AnyDb } from './dialect'
5
-
6
- import { idempotencyTableFor } from './internal-tables'
7
-
8
- export type IdempotencyConfig = {
9
- ttlMs?: number
10
- }
11
-
12
- const DEFAULT_TTL_MS = 24 * 60 * 60 * 1000
13
-
14
- function hashBody(body: string): string {
15
- return createHash('sha256').update(body).digest('hex')
16
- }
17
-
18
- export type IdempotencyLookup =
19
- | { type: 'replay'; status: number; response: string }
20
- | { type: 'conflict' }
21
- | { type: 'proceed' }
22
-
23
- export async function lookupIdempotency(
24
- db: AnyDb,
25
- tableName: string,
26
- key: string,
27
- body: string,
28
- config: IdempotencyConfig,
29
- ): Promise<IdempotencyLookup> {
30
- const t = idempotencyTableFor(db)
31
- const now = Date.now()
32
-
33
- // TTL sweep: drop expired rows before reading.
34
- await db.delete(t).where(lt(t.expiresAt, now))
35
-
36
- const bodyHash = hashBody(body)
37
- const rows = await db
38
- .select({
39
- bodyHash: t.bodyHash,
40
- status: t.status,
41
- response: t.response,
42
- expiresAt: t.expiresAt,
43
- })
44
- .from(t)
45
- .where(and(eq(t.key, key), eq(t.tableName, tableName)))
46
- .limit(1)
47
-
48
- const row = rows[0]
49
-
50
- if (!row) return { type: 'proceed' }
51
- if (Number(row.expiresAt) < now) return { type: 'proceed' }
52
- if (row.bodyHash !== bodyHash) return { type: 'conflict' }
53
- return {
54
- type: 'replay',
55
- status: Number(row.status),
56
- response: String(row.response),
57
- }
58
- }
59
-
60
- export async function storeIdempotency(
61
- db: AnyDb,
62
- tableName: string,
63
- key: string,
64
- body: string,
65
- status: number,
66
- response: unknown,
67
- config: IdempotencyConfig,
68
- ): Promise<void> {
69
- const t = idempotencyTableFor(db)
70
- const ttlMs = config.ttlMs ?? DEFAULT_TTL_MS
71
- const expiresAt = Date.now() + ttlMs
72
- const bodyHash = hashBody(body)
73
- const responseText = JSON.stringify(response)
74
-
75
- await db
76
- .insert(t)
77
- .values({
78
- key,
79
- tableName,
80
- bodyHash,
81
- status,
82
- response: responseText,
83
- expiresAt,
84
- })
85
- .onConflictDoUpdate({
86
- target: [t.key, t.tableName],
87
- set: { bodyHash, status, response: responseText, expiresAt },
88
- })
89
- }
90
-
91
- export function resolveIdempotencyConfig(
92
- config: boolean | IdempotencyConfig | undefined,
93
- ): IdempotencyConfig | null {
94
- if (!config) return null
95
- if (config === true) return {}
96
- return config
97
- }