bunderstack 0.17.0-beta.0 → 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 (337) 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 +81 -29
  272. package/src/access.ts +0 -552
  273. package/src/api/api-types.types.ts +0 -106
  274. package/src/api/builder.ts +0 -52
  275. package/src/api/context.ts +0 -83
  276. package/src/api/crud-router.ts +0 -321
  277. package/src/api/openapi.ts +0 -184
  278. package/src/api/realtime-router.ts +0 -75
  279. package/src/api/registry.ts +0 -338
  280. package/src/api/router.ts +0 -34
  281. package/src/api/storage-router.ts +0 -224
  282. package/src/api/types.ts +0 -84
  283. package/src/auth.ts +0 -117
  284. package/src/blueprint-generator.ts +0 -203
  285. package/src/blueprint.ts +0 -264
  286. package/src/cli.ts +0 -90
  287. package/src/config.ts +0 -215
  288. package/src/cron.ts +0 -3
  289. package/src/crud-operations.ts +0 -488
  290. package/src/database/adapter.ts +0 -26
  291. package/src/database/bun-sql.ts +0 -23
  292. package/src/database/libsql.ts +0 -28
  293. package/src/database/pglite.ts +0 -33
  294. package/src/database/postgres-js.ts +0 -33
  295. package/src/db.ts +0 -61
  296. package/src/dialect.ts +0 -38
  297. package/src/email/smtp.ts +0 -45
  298. package/src/email.ts +0 -154
  299. package/src/env.ts +0 -180
  300. package/src/errors.ts +0 -118
  301. package/src/handler.ts +0 -30
  302. package/src/idempotency.ts +0 -97
  303. package/src/index.ts +0 -822
  304. package/src/internal-tables.ts +0 -149
  305. package/src/jobs/cron.ts +0 -87
  306. package/src/jobs/define.ts +0 -283
  307. package/src/jobs/index.ts +0 -37
  308. package/src/jobs/queue.ts +0 -67
  309. package/src/jobs/runtime.ts +0 -58
  310. package/src/jobs/slots.ts +0 -52
  311. package/src/jobs/worker.ts +0 -369
  312. package/src/lifecycle.ts +0 -49
  313. package/src/list-query.ts +0 -416
  314. package/src/manifest.ts +0 -297
  315. package/src/provision-internals.ts +0 -29
  316. package/src/provision.ts +0 -128
  317. package/src/rate-limit.ts +0 -71
  318. package/src/realtime/facade.ts +0 -55
  319. package/src/realtime/filter.ts +0 -77
  320. package/src/realtime/heartbeat.ts +0 -80
  321. package/src/realtime/publisher.ts +0 -46
  322. package/src/schema-export-pg.ts +0 -7
  323. package/src/schema-export.ts +0 -5
  324. package/src/scope.ts +0 -17
  325. package/src/standard-schema.ts +0 -59
  326. package/src/storage/buckets.ts +0 -308
  327. package/src/storage/delete.ts +0 -26
  328. package/src/storage/file-meta.ts +0 -222
  329. package/src/storage/index.ts +0 -39
  330. package/src/storage/local.ts +0 -69
  331. package/src/storage/operations.ts +0 -398
  332. package/src/storage/registry.ts +0 -40
  333. package/src/storage/s3.ts +0 -99
  334. package/src/storage/sweep.ts +0 -25
  335. package/src/storage/thumbnails.ts +0 -65
  336. package/src/testing.ts +0 -31
  337. package/src/typeid.ts +0 -116
package/src/errors.ts DELETED
@@ -1,118 +0,0 @@
1
- import { ORPCError, os, type ErrorMap } from '@orpc/server'
2
- import * as v from 'valibot'
3
-
4
- import { StandardSchemaValidationError } from './standard-schema'
5
-
6
- export const BUNDERSTACK_ERROR_CODES = [
7
- 'VALIDATION_ERROR',
8
- 'UNAUTHORIZED',
9
- 'FORBIDDEN',
10
- 'NOT_FOUND',
11
- 'CONFLICT',
12
- 'PAYLOAD_TOO_LARGE',
13
- 'RATE_LIMITED',
14
- ] as const
15
-
16
- export type BunderstackErrorCode = (typeof BUNDERSTACK_ERROR_CODES)[number]
17
-
18
- export const BUNDERSTACK_ERROR_STATUS_MAP = {
19
- VALIDATION_ERROR: 400,
20
- UNAUTHORIZED: 401,
21
- FORBIDDEN: 403,
22
- NOT_FOUND: 404,
23
- CONFLICT: 409,
24
- PAYLOAD_TOO_LARGE: 413,
25
- RATE_LIMITED: 429,
26
- } as const satisfies Record<BunderstackErrorCode, number>
27
-
28
- function errorDataSchema<const TCode extends BunderstackErrorCode>(
29
- code: TCode,
30
- ) {
31
- return v.strictObject({
32
- code: v.literal(code),
33
- details: v.optional(v.unknown()),
34
- })
35
- }
36
-
37
- export const BUNDERSTACK_ERRORS = {
38
- VALIDATION_ERROR: { data: errorDataSchema('VALIDATION_ERROR') },
39
- UNAUTHORIZED: { data: errorDataSchema('UNAUTHORIZED') },
40
- FORBIDDEN: { data: errorDataSchema('FORBIDDEN') },
41
- NOT_FOUND: { data: errorDataSchema('NOT_FOUND') },
42
- CONFLICT: { data: errorDataSchema('CONFLICT') },
43
- PAYLOAD_TOO_LARGE: { data: errorDataSchema('PAYLOAD_TOO_LARGE') },
44
- RATE_LIMITED: { data: errorDataSchema('RATE_LIMITED') },
45
- } as const satisfies ErrorMap
46
-
47
- export class BunderstackError extends Error {
48
- readonly status: number
49
-
50
- constructor(
51
- readonly code: BunderstackErrorCode,
52
- message: string,
53
- readonly details?: unknown,
54
- options?: ErrorOptions,
55
- ) {
56
- super(message, options)
57
- this.name = 'BunderstackError'
58
- this.status = BUNDERSTACK_ERROR_STATUS_MAP[code]
59
- }
60
- }
61
-
62
- export const mapBunderstackErrors = os
63
- .errors(BUNDERSTACK_ERRORS)
64
- .middleware(async ({ next }) => {
65
- try {
66
- return await next()
67
- } catch (error) {
68
- const mapped =
69
- error instanceof StandardSchemaValidationError
70
- ? new BunderstackError(
71
- 'VALIDATION_ERROR',
72
- error.message,
73
- error.issues,
74
- { cause: error },
75
- )
76
- : error
77
- if (!(mapped instanceof BunderstackError)) throw error
78
-
79
- throw new ORPCError(mapped.code, {
80
- message: mapped.message,
81
- data:
82
- mapped.details === undefined
83
- ? { code: mapped.code }
84
- : { code: mapped.code, details: mapped.details },
85
- cause: mapped,
86
- })
87
- }
88
- })
89
-
90
- /** @deprecated Legacy internal error codes retained for list-query compatibility. */
91
- export const ErrorCode = {
92
- VALIDATION_ERROR: 'VALIDATION_ERROR',
93
- FORBIDDEN: 'FORBIDDEN',
94
- NOT_FOUND: 'NOT_FOUND',
95
- CONFLICT: 'CONFLICT',
96
- INVALID_CURSOR: 'INVALID_CURSOR',
97
- RATE_LIMITED: 'RATE_LIMITED',
98
- IDEMPOTENCY_REPLAY: 'IDEMPOTENCY_REPLAY',
99
- IDEMPOTENCY_CONFLICT: 'IDEMPOTENCY_CONFLICT',
100
- } as const
101
-
102
- export type ErrorCodeValue = (typeof ErrorCode)[keyof typeof ErrorCode]
103
-
104
- export class ListQueryError extends Error {
105
- readonly code: ErrorCodeValue
106
- readonly details?: unknown
107
-
108
- constructor(
109
- message: string,
110
- code: ErrorCodeValue = ErrorCode.VALIDATION_ERROR,
111
- details?: unknown,
112
- ) {
113
- super(message)
114
- this.name = 'ListQueryError'
115
- this.code = code
116
- this.details = details
117
- }
118
- }
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
- }