bunderstack 0.17.0-beta.2 → 0.17.0-beta.4

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 +233 -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 +108 -0
  60. package/dist/config.d.ts.map +1 -0
  61. package/dist/config.js +90 -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 +480 -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
@@ -0,0 +1,178 @@
1
+ // ---------------------------------------------------------------------------
2
+ // parseSize
3
+ // ---------------------------------------------------------------------------
4
+ const SIZE_UNITS = {
5
+ b: 1,
6
+ kb: 1024,
7
+ mb: 1024 * 1024,
8
+ gb: 1024 * 1024 * 1024,
9
+ };
10
+ export function parseSize(value) {
11
+ if (typeof value === 'number') {
12
+ if (!Number.isFinite(value) || value < 0 || !Number.isInteger(value)) {
13
+ throw new Error(`[bunderstack] invalid size "${value}"`);
14
+ }
15
+ return value;
16
+ }
17
+ const trimmed = value.trim();
18
+ if (trimmed === '')
19
+ throw new Error(`[bunderstack] invalid size "${value}"`);
20
+ const match = /^(\d+(?:\.\d+)?)\s*([a-z]*)$/i.exec(trimmed);
21
+ if (!match)
22
+ throw new Error(`[bunderstack] invalid size "${value}"`);
23
+ const numStr = match[1];
24
+ if (numStr === undefined)
25
+ throw new Error(`[bunderstack] invalid size "${value}"`);
26
+ const num = parseFloat(numStr);
27
+ const unit = (match[2] ?? '').toLowerCase();
28
+ if (unit === '')
29
+ return Math.floor(num);
30
+ const multiplier = SIZE_UNITS[unit];
31
+ if (multiplier === undefined)
32
+ throw new Error(`[bunderstack] invalid size "${value}"`);
33
+ return Math.floor(num * multiplier);
34
+ }
35
+ // ---------------------------------------------------------------------------
36
+ // Platform override (Bunderhost & co.)
37
+ // ---------------------------------------------------------------------------
38
+ /**
39
+ * A deployment platform that injects BUNDERSTACK_S3_ENDPOINT forces every
40
+ * bucket onto that backend — code-level `local`/per-bucket `s3` blocks are
41
+ * ignored so apps deploy unchanged. Logical buckets already prefix object
42
+ * keys ("<bucket>/<uuid>"), so one physical bucket per environment suffices.
43
+ */
44
+ function platformS3Backend(env) {
45
+ const endpoint = env['BUNDERSTACK_S3_ENDPOINT'];
46
+ if (!endpoint)
47
+ return undefined;
48
+ return {
49
+ type: 's3',
50
+ bucket: env['BUNDERSTACK_S3_BUCKET'] ?? '',
51
+ region: env['BUNDERSTACK_S3_REGION'] ?? 'auto',
52
+ endpoint,
53
+ accessKeyId: env['BUNDERSTACK_S3_ACCESS_KEY_ID'] ?? '',
54
+ secretAccessKey: env['BUNDERSTACK_S3_SECRET_ACCESS_KEY'] ?? '',
55
+ publicUrl: env['BUNDERSTACK_S3_PUBLIC_URL'],
56
+ };
57
+ }
58
+ // ---------------------------------------------------------------------------
59
+ // Shared backend resolution
60
+ // ---------------------------------------------------------------------------
61
+ function resolveSharedBackend(input, env) {
62
+ if (!input)
63
+ return { type: 'local', path: './uploads' };
64
+ if ('local' in input && input.local !== undefined) {
65
+ return {
66
+ type: 'local',
67
+ path: input.local === true ? './uploads' : input.local,
68
+ };
69
+ }
70
+ if ('s3' in input && input.s3 !== undefined) {
71
+ const s3Cfg = typeof input.s3 === 'object' ? input.s3 : {};
72
+ return {
73
+ type: 's3',
74
+ bucket: env['S3_BUCKET'] ?? '',
75
+ region: env['S3_REGION'] ?? 'us-east-1',
76
+ endpoint: s3Cfg.endpoint ?? env['S3_ENDPOINT'],
77
+ accessKeyId: env['S3_ACCESS_KEY_ID'] ?? '',
78
+ secretAccessKey: env['S3_SECRET_ACCESS_KEY'] ?? '',
79
+ };
80
+ }
81
+ return { type: 'local', path: './uploads' };
82
+ }
83
+ // ---------------------------------------------------------------------------
84
+ // Per-bucket backend resolution
85
+ // ---------------------------------------------------------------------------
86
+ function resolveBucketBackend(bucketInput, sharedBackend, env) {
87
+ // Platform override active → sharedBackend IS the platform backend and
88
+ // code-level per-bucket backends are ignored.
89
+ if (env['BUNDERSTACK_S3_ENDPOINT'])
90
+ return sharedBackend;
91
+ if ('s3' in bucketInput && bucketInput.s3 !== undefined) {
92
+ const block = bucketInput.s3;
93
+ return {
94
+ type: 's3',
95
+ bucket: block.bucket,
96
+ region: block.region ?? env['S3_REGION'] ?? 'us-east-1',
97
+ endpoint: block.endpoint ?? env['S3_ENDPOINT'],
98
+ accessKeyId: env['S3_ACCESS_KEY_ID'] ?? '',
99
+ secretAccessKey: env['S3_SECRET_ACCESS_KEY'] ?? '',
100
+ publicUrl: block.publicUrl,
101
+ };
102
+ }
103
+ if ('local' in bucketInput && bucketInput.local !== undefined) {
104
+ return { type: 'local', path: bucketInput.local };
105
+ }
106
+ return sharedBackend;
107
+ }
108
+ // ---------------------------------------------------------------------------
109
+ // Bucket resolution
110
+ // ---------------------------------------------------------------------------
111
+ function resolveSingleBucket(name, input, sharedBackend, env) {
112
+ const visibility = input.visibility ?? 'private';
113
+ const backend = resolveBucketBackend(input, sharedBackend, env);
114
+ const defaultGet = visibility === 'public' ? 'public' : 'owner';
115
+ const access = {
116
+ create: input.access?.create ?? 'authenticated',
117
+ get: input.access?.get ?? defaultGet,
118
+ delete: input.access?.delete ?? 'owner',
119
+ };
120
+ let upload = undefined;
121
+ if (input.upload !== undefined) {
122
+ upload = {};
123
+ if (input.upload.maxSize !== undefined) {
124
+ upload.maxSizeBytes = parseSize(input.upload.maxSize);
125
+ }
126
+ if (input.upload.accept !== undefined) {
127
+ upload.accept = input.upload.accept;
128
+ }
129
+ }
130
+ let quota = undefined;
131
+ if (input.quota !== undefined) {
132
+ quota = {};
133
+ if (input.quota.perUser !== undefined) {
134
+ quota.perUserBytes = parseSize(input.quota.perUser);
135
+ }
136
+ if (input.quota.perScope !== undefined) {
137
+ quota.perScopeBytes = parseSize(input.quota.perScope);
138
+ }
139
+ }
140
+ return {
141
+ name,
142
+ backend,
143
+ visibility,
144
+ access,
145
+ upload,
146
+ transforms: input.transforms ?? false,
147
+ readScope: input.scope?.read,
148
+ writeScope: input.scope?.write,
149
+ quota,
150
+ };
151
+ }
152
+ // ---------------------------------------------------------------------------
153
+ // resolveBuckets
154
+ // ---------------------------------------------------------------------------
155
+ export function resolveBuckets(input, env = process.env) {
156
+ const sharedBackend = platformS3Backend(env) ?? resolveSharedBackend(input, env);
157
+ const bucketsInput = input?.buckets;
158
+ const declaredNames = bucketsInput ? Object.keys(bucketsInput) : [];
159
+ const defaultBucketName = input?.defaultBucket ??
160
+ // A single declared bucket is unambiguous — treat it as the default.
161
+ (declaredNames.length === 1 ? declaredNames[0] : 'default');
162
+ const buckets = new Map();
163
+ if (!bucketsInput || Object.keys(bucketsInput).length === 0) {
164
+ // Synthesize a single default bucket
165
+ const defaultBucket = resolveSingleBucket(defaultBucketName, {}, sharedBackend, env);
166
+ buckets.set(defaultBucketName, defaultBucket);
167
+ return { defaultBucket: defaultBucketName, buckets };
168
+ }
169
+ // Validate defaultBucket is declared
170
+ if (!(defaultBucketName in bucketsInput)) {
171
+ throw new Error(`[bunderstack] defaultBucket "${defaultBucketName}" is not a declared bucket`);
172
+ }
173
+ for (const [name, bucketInput] of Object.entries(bucketsInput)) {
174
+ buckets.set(name, resolveSingleBucket(name, bucketInput, sharedBackend, env));
175
+ }
176
+ return { defaultBucket: defaultBucketName, buckets };
177
+ }
178
+ //# sourceMappingURL=buckets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buckets.js","sourceRoot":"","sources":["../../src/storage/buckets.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { AnyDb } from '../dialect.js';
2
+ import type { StorageAdapter } from './index.js';
3
+ /**
4
+ * Delete a logical file: its transform-cache derivatives (under
5
+ * `${fileId}__transforms/`), the original object, and the file-meta row.
6
+ *
7
+ * All deletion goes through this helper so the model stays addable to a future
8
+ * blob/file split (see file-meta.ts header) — never inline `adapter.delete`.
9
+ */
10
+ export declare function deleteFileWithDerivatives(adapter: StorageAdapter, db: AnyDb, fileId: string): Promise<void>;
11
+ //# sourceMappingURL=delete.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delete.d.ts","sourceRoot":"","sources":["../../src/storage/delete.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AACvC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAI7C;;;;;;GAMG;AACH,wBAAsB,yBAAyB,CAC7C,OAAO,EAAE,cAAc,EACvB,EAAE,EAAE,KAAK,EACT,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,CAQf"}
@@ -0,0 +1,18 @@
1
+ import { deleteFileMetaRow } from './file-meta.js';
2
+ /**
3
+ * Delete a logical file: its transform-cache derivatives (under
4
+ * `${fileId}__transforms/`), the original object, and the file-meta row.
5
+ *
6
+ * All deletion goes through this helper so the model stays addable to a future
7
+ * blob/file split (see file-meta.ts header) — never inline `adapter.delete`.
8
+ */
9
+ export async function deleteFileWithDerivatives(adapter, db, fileId) {
10
+ if (adapter.list) {
11
+ const derivs = await adapter.list(`${fileId}__transforms/`);
12
+ // Ignore individual derivative-delete errors — orphan sweep is the backstop.
13
+ await Promise.all(derivs.map((k) => adapter.delete(k).catch(() => { })));
14
+ }
15
+ await adapter.delete(fileId);
16
+ await deleteFileMetaRow(db, fileId);
17
+ }
18
+ //# sourceMappingURL=delete.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delete.js","sourceRoot":"","sources":["../../src/storage/delete.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAE/C;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,OAAuB,EACvB,EAAS,EACT,MAAc;IAEd,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;QACjB,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,MAAM,eAAe,CAAC,CAAA;QAC3D,6EAA6E;QAC7E,MAAM,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC,CAAC,CAAA;IACzE,CAAC;IACD,MAAM,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;IAC5B,MAAM,iBAAiB,CAAC,EAAE,EAAE,MAAM,CAAC,CAAA;AACrC,CAAC"}
@@ -0,0 +1,37 @@
1
+ import type { ScopeMap } from '../access.js';
2
+ import type { AnyDb } from '../dialect.js';
3
+ import { bunderstackFiles } from '../internal-tables.js';
4
+ export type FileMetaRow = typeof bunderstackFiles.$inferSelect;
5
+ export declare function insertPendingFile(db: AnyDb, input: {
6
+ fileId: string;
7
+ bucket: string;
8
+ ownerId: string | null;
9
+ scopeJson: string | null;
10
+ filename: string | null;
11
+ contentType: string | null;
12
+ }): Promise<void>;
13
+ export declare function insertReadyFile(db: AnyDb, input: {
14
+ fileId: string;
15
+ bucket: string;
16
+ ownerId: string | null;
17
+ scopeJson: string | null;
18
+ filename: string | null;
19
+ contentType: string | null;
20
+ size: number | null;
21
+ }): Promise<void>;
22
+ export declare function markFileReady(db: AnyDb, fileId: string, patch: {
23
+ size: number | null;
24
+ contentType: string | null;
25
+ }): Promise<void>;
26
+ export declare function getFileMeta(db: AnyDb, fileId: string): Promise<FileMetaRow | null>;
27
+ export declare function deleteFileMetaRow(db: AnyDb, fileId: string): Promise<void>;
28
+ export declare function listStalePendingFiles(db: AnyDb, olderThanMs: number): Promise<FileMetaRow[]>;
29
+ export declare function sumReadySize(db: AnyDb, q: {
30
+ bucket: string;
31
+ ownerId?: string;
32
+ scopeJson?: string;
33
+ }): Promise<number>;
34
+ export declare function scopeToJson(scope: ScopeMap | undefined | null): string | null;
35
+ export declare function parseScopeJson(json: string | null): ScopeMap | null;
36
+ export declare function fileMatchesScope(row: FileMetaRow, requesterScope: ScopeMap | undefined): boolean;
37
+ //# sourceMappingURL=file-meta.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-meta.d.ts","sourceRoot":"","sources":["../../src/storage/file-meta.ts"],"names":[],"mappings":"AA+BA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAGvC,OAAO,EAAE,gBAAgB,EAAiB,MAAM,oBAAoB,CAAA;AAEpE,MAAM,MAAM,WAAW,GAAG,OAAO,gBAAgB,CAAC,YAAY,CAAA;AAI9D,wBAAsB,iBAAiB,CACrC,EAAE,EAAE,KAAK,EACT,KAAK,EAAE;IACL,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;CAC3B,GACA,OAAO,CAAC,IAAI,CAAC,CAcf;AAED,wBAAsB,eAAe,CACnC,EAAE,EAAE,KAAK,EACT,KAAK,EAAE;IACL,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CACpB,GACA,OAAO,CAAC,IAAI,CAAC,CAef;AAED,wBAAsB,aAAa,CACjC,EAAE,EAAE,KAAK,EACT,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GACzD,OAAO,CAAC,IAAI,CAAC,CAWf;AAED,wBAAsB,WAAW,CAC/B,EAAE,EAAE,KAAK,EACT,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAQ7B;AAED,wBAAsB,iBAAiB,CACrC,EAAE,EAAE,KAAK,EACT,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,CAGf;AAID,wBAAsB,qBAAqB,CACzC,EAAE,EAAE,KAAK,EACT,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,WAAW,EAAE,CAAC,CAMxB;AAID,wBAAsB,YAAY,CAChC,EAAE,EAAE,KAAK,EACT,CAAC,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GAC1D,OAAO,CAAC,MAAM,CAAC,CAmBjB;AAID,wBAAgB,WAAW,CAAC,KAAK,EAAE,QAAQ,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAW7E;AAeD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,QAAQ,GAAG,IAAI,CASnE;AAED,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,WAAW,EAChB,cAAc,EAAE,QAAQ,GAAG,SAAS,GACnC,OAAO,CAOT"}
@@ -0,0 +1,166 @@
1
+ /*
2
+ * DEFERRED — logical/physical blob/file split
3
+ *
4
+ * The current model is 1:1 logical-file = physical-object: one row in
5
+ * bunderstack_file_meta maps to exactly one object in storage. A future
6
+ * blob/file split would introduce:
7
+ *
8
+ * bunderstackBlob { key, bucket, size, contentType, refcount, createdAt }
9
+ * bunderstackFile { id, blobKey, ownerId, scopeJson, status, filename,
10
+ * createdAt, confirmedAt, deletedAt }
11
+ *
12
+ * This would enable:
13
+ * - Soft/shadow delete (file.deletedAt + per-bucket retention policy)
14
+ * - Transfer ownership/scope (mutate file.ownerId / scopeJson, no byte movement)
15
+ * - Duplicate/share to many users or orgs by reference (refcount++) so sharing
16
+ * to N users stores bytes once; or by-copy via S3 CopyObject.
17
+ *
18
+ * Deletion would decrement refcount; bytes removed only at 0; orphan sweep also
19
+ * reaps refcount=0 blobs (self-heals crash mid-decrement).
20
+ *
21
+ * Deferred because refcount correctness is a real cost vs. current need.
22
+ *
23
+ * Two forward-compat invariants keep this addable without breaking the public API:
24
+ * 1. fileId is opaque — clients never parse it.
25
+ * 2. All deletion goes through the storage delete helper, never inline.
26
+ *
27
+ * See docs/plans/2026-06-28-multi-bucket-storage-design.md §6 for full rationale.
28
+ */
29
+ import { eq, and, lt, sql } from 'drizzle-orm';
30
+ import { rowMatchesScope } from '../access.js';
31
+ import { bunderstackFiles, filesTableFor } from '../internal-tables.js';
32
+ // ─── CRUD ─────────────────────────────────────────────────────────────────────
33
+ export async function insertPendingFile(db, input) {
34
+ const files = filesTableFor(db);
35
+ await db.insert(files).values({
36
+ fileId: input.fileId,
37
+ bucket: input.bucket,
38
+ ownerId: input.ownerId,
39
+ scopeJson: input.scopeJson,
40
+ filename: input.filename,
41
+ contentType: input.contentType,
42
+ status: 'pending',
43
+ createdAt: Date.now(),
44
+ confirmedAt: null,
45
+ size: null,
46
+ });
47
+ }
48
+ export async function insertReadyFile(db, input) {
49
+ const files = filesTableFor(db);
50
+ const now = Date.now();
51
+ await db.insert(files).values({
52
+ fileId: input.fileId,
53
+ bucket: input.bucket,
54
+ ownerId: input.ownerId,
55
+ scopeJson: input.scopeJson,
56
+ filename: input.filename,
57
+ contentType: input.contentType,
58
+ size: input.size,
59
+ status: 'ready',
60
+ createdAt: now,
61
+ confirmedAt: now,
62
+ });
63
+ }
64
+ export async function markFileReady(db, fileId, patch) {
65
+ const files = filesTableFor(db);
66
+ await db
67
+ .update(files)
68
+ .set({
69
+ status: 'ready',
70
+ confirmedAt: Date.now(),
71
+ size: patch.size,
72
+ contentType: patch.contentType,
73
+ })
74
+ .where(eq(files.fileId, fileId));
75
+ }
76
+ export async function getFileMeta(db, fileId) {
77
+ const files = filesTableFor(db);
78
+ const rows = await db
79
+ .select()
80
+ .from(files)
81
+ .where(eq(files.fileId, fileId))
82
+ .limit(1);
83
+ return rows[0] ?? null;
84
+ }
85
+ export async function deleteFileMetaRow(db, fileId) {
86
+ const files = filesTableFor(db);
87
+ await db.delete(files).where(eq(files.fileId, fileId));
88
+ }
89
+ // ─── Sweep ────────────────────────────────────────────────────────────────────
90
+ export async function listStalePendingFiles(db, olderThanMs) {
91
+ const files = filesTableFor(db);
92
+ return db
93
+ .select()
94
+ .from(files)
95
+ .where(and(eq(files.status, 'pending'), lt(files.createdAt, olderThanMs)));
96
+ }
97
+ // ─── Quota ────────────────────────────────────────────────────────────────────
98
+ export async function sumReadySize(db, q) {
99
+ const files = filesTableFor(db);
100
+ const conditions = [eq(files.status, 'ready'), eq(files.bucket, q.bucket)];
101
+ if (q.ownerId !== undefined) {
102
+ conditions.push(eq(files.ownerId, q.ownerId));
103
+ }
104
+ if (q.scopeJson !== undefined) {
105
+ conditions.push(eq(files.scopeJson, q.scopeJson));
106
+ }
107
+ const rows = await db
108
+ .select({
109
+ total: sql `coalesce(sum(${files.size}), 0)`,
110
+ })
111
+ .from(files)
112
+ .where(and(...conditions));
113
+ const raw = rows[0]?.total ?? 0;
114
+ return Number(raw);
115
+ }
116
+ // ─── Scope helpers ────────────────────────────────────────────────────────────
117
+ export function scopeToJson(scope) {
118
+ if (scope == null)
119
+ return null;
120
+ const keys = Object.keys(scope);
121
+ if (keys.length === 0)
122
+ return null;
123
+ const sorted = keys.sort();
124
+ const ordered = {};
125
+ for (const k of sorted) {
126
+ const value = scope[k];
127
+ if (value !== undefined)
128
+ ordered[k] = value;
129
+ }
130
+ return JSON.stringify(ordered);
131
+ }
132
+ function isScopeMap(value) {
133
+ if (value === null || typeof value !== 'object' || Array.isArray(value)) {
134
+ return false;
135
+ }
136
+ for (const item of Object.values(value)) {
137
+ if (typeof item === 'string')
138
+ continue;
139
+ if (Array.isArray(item) && item.every((entry) => typeof entry === 'string'))
140
+ continue;
141
+ return false;
142
+ }
143
+ return true;
144
+ }
145
+ export function parseScopeJson(json) {
146
+ if (json == null)
147
+ return null;
148
+ try {
149
+ const parsed = JSON.parse(json);
150
+ return isScopeMap(parsed) ? parsed : null;
151
+ }
152
+ catch {
153
+ // Corrupt/legacy scope_json should not crash a read; treat as no scope.
154
+ return null;
155
+ }
156
+ }
157
+ export function fileMatchesScope(row, requesterScope) {
158
+ if (requesterScope == null || Object.keys(requesterScope).length === 0) {
159
+ return true;
160
+ }
161
+ if (row.scopeJson == null)
162
+ return false;
163
+ const parsed = parseScopeJson(row.scopeJson) ?? {};
164
+ return rowMatchesScope(parsed, requesterScope);
165
+ }
166
+ //# sourceMappingURL=file-meta.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-meta.js","sourceRoot":"","sources":["../../src/storage/file-meta.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,MAAM,aAAa,CAAA;AAK9C,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AAC3C,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAIpE,iFAAiF;AAEjF,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,EAAS,EACT,KAOC;IAED,MAAM,KAAK,GAAG,aAAa,CAAC,EAAE,CAAC,CAAA;IAC/B,MAAM,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC;QAC5B,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,MAAM,EAAE,SAAS;QACjB,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;QACrB,WAAW,EAAE,IAAI;QACjB,IAAI,EAAE,IAAI;KACX,CAAC,CAAA;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,EAAS,EACT,KAQC;IAED,MAAM,KAAK,GAAG,aAAa,CAAC,EAAE,CAAC,CAAA;IAC/B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;IACtB,MAAM,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC;QAC5B,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,MAAM,EAAE,OAAO;QACf,SAAS,EAAE,GAAG;QACd,WAAW,EAAE,GAAG;KACjB,CAAC,CAAA;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,EAAS,EACT,MAAc,EACd,KAA0D;IAE1D,MAAM,KAAK,GAAG,aAAa,CAAC,EAAE,CAAC,CAAA;IAC/B,MAAM,EAAE;SACL,MAAM,CAAC,KAAK,CAAC;SACb,GAAG,CAAC;QACH,MAAM,EAAE,OAAO;QACf,WAAW,EAAE,IAAI,CAAC,GAAG,EAAE;QACvB,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,WAAW,EAAE,KAAK,CAAC,WAAW;KAC/B,CAAC;SACD,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;AACpC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,EAAS,EACT,MAAc;IAEd,MAAM,KAAK,GAAG,aAAa,CAAC,EAAE,CAAC,CAAA;IAC/B,MAAM,IAAI,GAAG,MAAM,EAAE;SAClB,MAAM,EAAE;SACR,IAAI,CAAC,KAAK,CAAC;SACX,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;SAC/B,KAAK,CAAC,CAAC,CAAC,CAAA;IACX,OAAQ,IAAI,CAAC,CAAC,CAA6B,IAAI,IAAI,CAAA;AACrD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,EAAS,EACT,MAAc;IAEd,MAAM,KAAK,GAAG,aAAa,CAAC,EAAE,CAAC,CAAA;IAC/B,MAAM,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;AACxD,CAAC;AAED,iFAAiF;AAEjF,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,EAAS,EACT,WAAmB;IAEnB,MAAM,KAAK,GAAG,aAAa,CAAC,EAAE,CAAC,CAAA;IAC/B,OAAO,EAAE;SACN,MAAM,EAAE;SACR,IAAI,CAAC,KAAK,CAAC;SACX,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,SAAS,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC,CAAC,CAAA;AAC9E,CAAC;AAED,iFAAiF;AAEjF,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,EAAS,EACT,CAA2D;IAE3D,MAAM,KAAK,GAAG,aAAa,CAAC,EAAE,CAAC,CAAA;IAC/B,MAAM,UAAU,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAA;IAC1E,IAAI,CAAC,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;QAC5B,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAA;IAC/C,CAAC;IACD,IAAI,CAAC,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;QAC9B,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAA;IACnD,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,EAAE;SAClB,MAAM,CAAC;QACN,KAAK,EAAE,GAAG,CAAQ,gBAAgB,KAAK,CAAC,IAAI,OAAO;KACpD,CAAC;SACD,IAAI,CAAC,KAAK,CAAC;SACX,KAAK,CAAC,GAAG,CAAC,GAAG,UAAU,CAAC,CAAC,CAAA;IAE5B,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC,CAAA;IAC/B,OAAO,MAAM,CAAC,GAAG,CAAC,CAAA;AACpB,CAAC;AAED,iFAAiF;AAEjF,MAAM,UAAU,WAAW,CAAC,KAAkC;IAC5D,IAAI,KAAK,IAAI,IAAI;QAAE,OAAO,IAAI,CAAA;IAC9B,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IAC/B,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAA;IAClC,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,CAAA;IAC1B,MAAM,OAAO,GAAsC,EAAE,CAAA;IACrD,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAA;QACtB,IAAI,KAAK,KAAK,SAAS;YAAE,OAAO,CAAC,CAAC,CAAC,GAAG,KAAK,CAAA;IAC7C,CAAC;IACD,OAAO,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAA;AAChC,CAAC;AAED,SAAS,UAAU,CAAC,KAAc;IAChC,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACxE,OAAO,KAAK,CAAA;IACd,CAAC;IACD,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;QACxC,IAAI,OAAO,IAAI,KAAK,QAAQ;YAAE,SAAQ;QACtC,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,KAAK,KAAK,QAAQ,CAAC;YACzE,SAAQ;QACV,OAAO,KAAK,CAAA;IACd,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,IAAmB;IAChD,IAAI,IAAI,IAAI,IAAI;QAAE,OAAO,IAAI,CAAA;IAC7B,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;QAC/B,OAAO,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAA;IAC3C,CAAC;IAAC,MAAM,CAAC;QACP,wEAAwE;QACxE,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC;AAED,MAAM,UAAU,gBAAgB,CAC9B,GAAgB,EAChB,cAAoC;IAEpC,IAAI,cAAc,IAAI,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvE,OAAO,IAAI,CAAA;IACb,CAAC;IACD,IAAI,GAAG,CAAC,SAAS,IAAI,IAAI;QAAE,OAAO,KAAK,CAAA;IACvC,MAAM,MAAM,GAAG,cAAc,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,CAAA;IAClD,OAAO,eAAe,CAAC,MAAM,EAAE,cAAc,CAAC,CAAA;AAChD,CAAC"}
@@ -0,0 +1,28 @@
1
+ import { LocalStorageAdapter } from './local.js';
2
+ import { S3StorageAdapter } from './s3.js';
3
+ export type { LocalStorageAdapter, S3StorageAdapter };
4
+ export { createStorageOperations } from './operations.js';
5
+ export type { PrepareUploadResult, StorageDownload, StorageExecutionContext, StorageOperations, StorageOperationsOptions, } from './operations.js';
6
+ export interface PresignPutOptions {
7
+ contentType?: string;
8
+ expiresIn: number;
9
+ }
10
+ export interface PresignGetOptions {
11
+ expiresIn: number;
12
+ }
13
+ export interface StorageAdapter {
14
+ upload(fileId: string, data: Blob | ArrayBuffer, contentType: string): Promise<void>;
15
+ get(fileId: string): Promise<Response>;
16
+ delete(fileId: string): Promise<void>;
17
+ exists(fileId: string): Promise<boolean>;
18
+ presignPut?(key: string, opts: PresignPutOptions): Promise<string>;
19
+ presignGet?(key: string, opts: PresignGetOptions): Promise<string>;
20
+ stat?(key: string): Promise<{
21
+ size: number;
22
+ contentType: string;
23
+ } | null>;
24
+ publicUrlFor?(key: string): string | undefined;
25
+ /** List full object keys under a prefix (used to reap transform derivatives). */
26
+ list?(prefix: string): Promise<string[]>;
27
+ }
28
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/storage/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAA;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,MAAM,CAAA;AAEvC,YAAY,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,CAAA;AACrD,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAA;AACtD,YAAY,EACV,mBAAmB,EACnB,eAAe,EACf,uBAAuB,EACvB,iBAAiB,EACjB,wBAAwB,GACzB,MAAM,cAAc,CAAA;AAErB,MAAM,WAAW,iBAAiB;IAChC,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,SAAS,EAAE,MAAM,CAAA;CAClB;AACD,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,CACJ,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,IAAI,GAAG,WAAW,EACxB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC,CAAA;IAChB,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA;IACtC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACrC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAExC,UAAU,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;IAClE,UAAU,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;IAClE,IAAI,CAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC,CAAA;IACzE,YAAY,CAAC,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;IAC9C,iFAAiF;IACjF,IAAI,CAAC,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAA;CACzC"}
@@ -0,0 +1,5 @@
1
+ // src/storage/index.ts
2
+ import { LocalStorageAdapter } from './local.js';
3
+ import { S3StorageAdapter } from './s3.js';
4
+ export { createStorageOperations } from './operations.js';
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/storage/index.ts"],"names":[],"mappings":"AAAA,uBAAuB;AACvB,OAAO,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAA;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,MAAM,CAAA;AAGvC,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAA"}
@@ -0,0 +1,15 @@
1
+ export declare class LocalStorageAdapter {
2
+ private readonly basePath;
3
+ constructor(basePath: string);
4
+ private filePath;
5
+ upload(fileId: string, data: Blob | ArrayBuffer, contentType: string): Promise<void>;
6
+ get(fileId: string): Promise<Response>;
7
+ delete(fileId: string): Promise<void>;
8
+ exists(fileId: string): Promise<boolean>;
9
+ stat(key: string): Promise<{
10
+ size: number;
11
+ contentType: string;
12
+ } | null>;
13
+ list(prefix: string): Promise<string[]>;
14
+ }
15
+ //# sourceMappingURL=local.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local.d.ts","sourceRoot":"","sources":["../../src/storage/local.ts"],"names":[],"mappings":"AAIA,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAQ;gBACrB,QAAQ,EAAE,MAAM;IAI5B,OAAO,CAAC,QAAQ;IAIV,MAAM,CACV,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,IAAI,GAAG,WAAW,EACxB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC;IASV,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;IAYtC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKrC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAIxC,IAAI,CACR,GAAG,EAAE,MAAM,GACV,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAMlD,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;CAY9C"}
@@ -0,0 +1,56 @@
1
+ // src/storage/local.ts
2
+ import { mkdir, readdir } from 'node:fs/promises';
3
+ import { join } from 'node:path';
4
+ export class LocalStorageAdapter {
5
+ basePath;
6
+ constructor(basePath) {
7
+ this.basePath = basePath;
8
+ }
9
+ filePath(fileId) {
10
+ return join(this.basePath, fileId);
11
+ }
12
+ async upload(fileId, data, contentType) {
13
+ await mkdir(this.basePath, { recursive: true });
14
+ const bytes = data instanceof Blob ? await data.arrayBuffer() : data;
15
+ await Bun.write(Bun.file(this.filePath(fileId), { type: contentType }), bytes);
16
+ }
17
+ async get(fileId) {
18
+ const file = Bun.file(this.filePath(fileId));
19
+ if (!(await file.exists()))
20
+ return new Response('Not found', { status: 404 });
21
+ return new Response(file, {
22
+ headers: {
23
+ 'Content-Type': file.type,
24
+ 'Cache-Control': 'public, max-age=31536000',
25
+ },
26
+ });
27
+ }
28
+ async delete(fileId) {
29
+ const file = Bun.file(this.filePath(fileId));
30
+ if (await file.exists())
31
+ await file.unlink();
32
+ }
33
+ async exists(fileId) {
34
+ return Bun.file(this.filePath(fileId)).exists();
35
+ }
36
+ async stat(key) {
37
+ const file = Bun.file(this.filePath(key));
38
+ if (!(await file.exists()))
39
+ return null;
40
+ return { size: file.size, contentType: file.type };
41
+ }
42
+ async list(prefix) {
43
+ const cleanPrefix = prefix.replace(/\/$/, '');
44
+ const dir = join(this.basePath, cleanPrefix);
45
+ let entries;
46
+ try {
47
+ entries = await readdir(dir);
48
+ }
49
+ catch {
50
+ // Missing dir (or prefix isn't a directory) → no derivatives.
51
+ return [];
52
+ }
53
+ return entries.map((name) => `${cleanPrefix}/${name}`);
54
+ }
55
+ }
56
+ //# sourceMappingURL=local.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local.js","sourceRoot":"","sources":["../../src/storage/local.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,54 @@
1
+ import type { AccessContext } from '../access.js';
2
+ import type { AnyDb } from '../dialect.js';
3
+ import type { BucketStorageRegistry } from './registry.js';
4
+ export interface StorageExecutionContext {
5
+ request: Request;
6
+ user: AccessContext['user'];
7
+ session: {
8
+ activeOrganizationId: string | null;
9
+ };
10
+ }
11
+ export interface StorageOperationsOptions {
12
+ registry: BucketStorageRegistry;
13
+ db: AnyDb;
14
+ presignExpiresSec?: number;
15
+ }
16
+ export type PrepareUploadResult = {
17
+ mode: 'proxy';
18
+ uploadUrl: string;
19
+ } | {
20
+ mode: 'presign';
21
+ fileId: string;
22
+ uploadUrl: string;
23
+ method: 'PUT';
24
+ confirmUrl: string;
25
+ };
26
+ export type StorageDownload = {
27
+ kind: 'redirect';
28
+ status: 302;
29
+ url: string;
30
+ } | {
31
+ kind: 'body';
32
+ status: number;
33
+ body: ConstructorParameters<typeof Response>[0];
34
+ headers: Headers;
35
+ };
36
+ export declare function createStorageOperations(options: StorageOperationsOptions): {
37
+ prepareUpload(bucketName: string, body: {
38
+ filename?: string;
39
+ contentType?: string;
40
+ }, context: StorageExecutionContext): Promise<PrepareUploadResult>;
41
+ upload(bucketName: string, file: File, context: StorageExecutionContext): Promise<{
42
+ status: 201;
43
+ fileId: string;
44
+ url: string;
45
+ }>;
46
+ confirmUpload(bucketName: string, id: string, context: StorageExecutionContext): Promise<{
47
+ fileId: string;
48
+ url: string;
49
+ }>;
50
+ download(bucketName: string, id: string, query: Record<string, string>, context: StorageExecutionContext): Promise<StorageDownload>;
51
+ delete(bucketName: string, id: string, context: StorageExecutionContext): Promise<void>;
52
+ };
53
+ export type StorageOperations = ReturnType<typeof createStorageOperations>;
54
+ //# sourceMappingURL=operations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"operations.d.ts","sourceRoot":"","sources":["../../src/storage/operations.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAiB,MAAM,WAAW,CAAA;AAC7D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AACvC,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAkBvD,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,OAAO,CAAA;IAChB,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;IAC3B,OAAO,EAAE;QAAE,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAA;CACjD;AAED,MAAM,WAAW,wBAAwB;IACvC,QAAQ,EAAE,qBAAqB,CAAA;IAC/B,EAAE,EAAE,KAAK,CAAA;IACT,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAC3B;AAED,MAAM,MAAM,mBAAmB,GAC3B;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GACpC;IACE,IAAI,EAAE,SAAS,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,KAAK,CAAA;IACb,UAAU,EAAE,MAAM,CAAA;CACnB,CAAA;AAEL,MAAM,MAAM,eAAe,GACvB;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,GAAG,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAC9C;IACE,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,qBAAqB,CAAC,OAAO,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAA;IAC/C,OAAO,EAAE,OAAO,CAAA;CACjB,CAAA;AAgEL,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,wBAAwB;8BAYvD,MAAM,QACZ;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,WACxC,uBAAuB,GAC/B,OAAO,CAAC,mBAAmB,CAAC;uBAgDjB,MAAM,QACZ,IAAI,WACD,uBAAuB;;;;;8BAsDpB,MAAM,MACd,MAAM,WACD,uBAAuB;;;;yBAuDpB,MAAM,MACd,MAAM,SACH,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,WACpB,uBAAuB,GAC/B,OAAO,CAAC,eAAe,CAAC;uBAqFb,MAAM,MACd,MAAM,WACD,uBAAuB,GAC/B,OAAO,CAAC,IAAI,CAAC;EAWnB;AAED,MAAM,MAAM,iBAAiB,GAAG,UAAU,CAAC,OAAO,uBAAuB,CAAC,CAAA"}