@aurabase/aurabase-js 0.3.0 → 0.3.1

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 (258) hide show
  1. package/package.json +4 -4
  2. package/packages/ai/node_modules/.bin/esbuild +0 -19
  3. package/packages/ai/node_modules/.bin/tsc +0 -22
  4. package/packages/ai/node_modules/.bin/tsserver +0 -22
  5. package/packages/ai/node_modules/.bin/tsup +0 -22
  6. package/packages/ai/node_modules/.bin/tsup-node +0 -22
  7. package/packages/ai/node_modules/.bin/vite +0 -22
  8. package/packages/ai/node_modules/.bin/vitest +0 -22
  9. package/packages/ai/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
  10. package/packages/ai/src/AiService.ts +0 -226
  11. package/packages/ai/src/index.ts +0 -18
  12. package/packages/ai/src/sse.ts +0 -141
  13. package/packages/ai/src/types.ts +0 -30
  14. package/packages/ai/tests/chat-stream.test.ts +0 -151
  15. package/packages/ai/tests/rag-namespaces.test.ts +0 -54
  16. package/packages/ai/tests/sse.test.ts +0 -69
  17. package/packages/ai/tsconfig.json +0 -5
  18. package/packages/ai/tsup.config.ts +0 -11
  19. package/packages/aurabase/node_modules/.bin/esbuild +0 -19
  20. package/packages/aurabase/node_modules/.bin/tsc +0 -22
  21. package/packages/aurabase/node_modules/.bin/tsserver +0 -22
  22. package/packages/aurabase/node_modules/.bin/tsup +0 -22
  23. package/packages/aurabase/node_modules/.bin/tsup-node +0 -22
  24. package/packages/aurabase/node_modules/.bin/vite +0 -22
  25. package/packages/aurabase/node_modules/.bin/vitest +0 -22
  26. package/packages/aurabase/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
  27. package/packages/aurabase/src/AuraClient.ts +0 -237
  28. package/packages/aurabase/src/index.ts +0 -152
  29. package/packages/aurabase/src/ssr.ts +0 -156
  30. package/packages/aurabase/tests/ai-streaming-rag-e2e.test.ts +0 -82
  31. package/packages/aurabase/tests/ai.test.ts +0 -111
  32. package/packages/aurabase/tests/auth-admin.test.ts +0 -515
  33. package/packages/aurabase/tests/auth-e2e.test.ts +0 -135
  34. package/packages/aurabase/tests/auth-hardening.test.ts +0 -206
  35. package/packages/aurabase/tests/auth.test.ts +0 -947
  36. package/packages/aurabase/tests/database.test.ts +0 -230
  37. package/packages/aurabase/tests/db-types-e2e.test.ts +0 -217
  38. package/packages/aurabase/tests/dispose.test.ts +0 -23
  39. package/packages/aurabase/tests/foreign-token.e2e.test.ts +0 -129
  40. package/packages/aurabase/tests/functions.test.ts +0 -107
  41. package/packages/aurabase/tests/integration.test.ts +0 -413
  42. package/packages/aurabase/tests/live-production-validation.js +0 -241
  43. package/packages/aurabase/tests/live-sdk-production-validation.js +0 -293
  44. package/packages/aurabase/tests/mocks.ts +0 -139
  45. package/packages/aurabase/tests/notifications.test.ts +0 -314
  46. package/packages/aurabase/tests/observability-backup.test.ts +0 -186
  47. package/packages/aurabase/tests/observability-hook.test.ts +0 -130
  48. package/packages/aurabase/tests/production-robustness.test.ts +0 -268
  49. package/packages/aurabase/tests/public-api-surface.test.ts +0 -53
  50. package/packages/aurabase/tests/query-builder.test.ts +0 -329
  51. package/packages/aurabase/tests/readme-smoke-e2e.test.ts +0 -231
  52. package/packages/aurabase/tests/realtime-filter-e2e.test.ts +0 -344
  53. package/packages/aurabase/tests/refresh-locks.test.ts +0 -210
  54. package/packages/aurabase/tests/retry-option.test.ts +0 -17
  55. package/packages/aurabase/tests/rpc-e2e.test.ts +0 -254
  56. package/packages/aurabase/tests/rs256-local-e2e.test.ts +0 -114
  57. package/packages/aurabase/tests/rt-alias-public-e2e.test.ts +0 -235
  58. package/packages/aurabase/tests/rt-http-broadcast-e2e.test.ts +0 -124
  59. package/packages/aurabase/tests/service-role-guard.test.ts +0 -99
  60. package/packages/aurabase/tests/ssr.test.ts +0 -91
  61. package/packages/aurabase/tests/storage-backend-e2e.test.ts +0 -481
  62. package/packages/aurabase/tests/storage-sdk-parity-e2e.test.ts +0 -104
  63. package/packages/aurabase/tests/storage-transform-e2e.test.ts +0 -684
  64. package/packages/aurabase/tests/storage.test.ts +0 -55
  65. package/packages/aurabase/tests/types/client-generic.test-d.ts +0 -79
  66. package/packages/aurabase/tests/types/docs-snippets.test-d.ts +0 -571
  67. package/packages/aurabase/tests/types/error-code.test-d.ts +0 -53
  68. package/packages/aurabase/tsconfig.json +0 -5
  69. package/packages/aurabase/tsconfig.test-d.json +0 -5
  70. package/packages/aurabase/tsup.config.ts +0 -11
  71. package/packages/aurabase/vitest.config.ts +0 -16
  72. package/packages/auth/node_modules/.bin/esbuild +0 -19
  73. package/packages/auth/node_modules/.bin/tsc +0 -22
  74. package/packages/auth/node_modules/.bin/tsserver +0 -22
  75. package/packages/auth/node_modules/.bin/tsup +0 -22
  76. package/packages/auth/node_modules/.bin/tsup-node +0 -22
  77. package/packages/auth/node_modules/.bin/vite +0 -22
  78. package/packages/auth/node_modules/.bin/vitest +0 -22
  79. package/packages/auth/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
  80. package/packages/auth/src/AuthAdminService.ts +0 -149
  81. package/packages/auth/src/AuthService.ts +0 -1166
  82. package/packages/auth/src/SessionManager.ts +0 -92
  83. package/packages/auth/src/index.ts +0 -17
  84. package/packages/auth/src/jwks.ts +0 -337
  85. package/packages/auth/src/pkce.ts +0 -27
  86. package/packages/auth/src/types.ts +0 -89
  87. package/packages/auth/tests/auth-lifecycle.test.ts +0 -149
  88. package/packages/auth/tests/detect-session.test.ts +0 -361
  89. package/packages/auth/tests/getclaims.test.ts +0 -780
  90. package/packages/auth/tests/is-mfa-challenge.test.ts +0 -27
  91. package/packages/auth/tests/password-recovery.test.ts +0 -124
  92. package/packages/auth/tests/pkce.test.ts +0 -192
  93. package/packages/auth/tests/session-manager.test.ts +0 -103
  94. package/packages/auth/tests/set-session.test.ts +0 -139
  95. package/packages/auth/tests/verify-otp.test.ts +0 -153
  96. package/packages/auth/tsconfig.json +0 -5
  97. package/packages/auth/tsup.config.ts +0 -11
  98. package/packages/core/node_modules/.bin/esbuild +0 -22
  99. package/packages/core/node_modules/.bin/tsc +0 -22
  100. package/packages/core/node_modules/.bin/tsserver +0 -22
  101. package/packages/core/node_modules/.bin/tsup +0 -22
  102. package/packages/core/node_modules/.bin/tsup-node +0 -22
  103. package/packages/core/node_modules/.bin/vite +0 -22
  104. package/packages/core/node_modules/.bin/vitest +0 -22
  105. package/packages/core/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
  106. package/packages/core/src/AuraError.ts +0 -89
  107. package/packages/core/src/BaseService.ts +0 -44
  108. package/packages/core/src/client.ts +0 -34
  109. package/packages/core/src/cookie-utils.ts +0 -42
  110. package/packages/core/src/fetch.ts +0 -435
  111. package/packages/core/src/index.ts +0 -22
  112. package/packages/core/src/retry.ts +0 -63
  113. package/packages/core/src/serialize.ts +0 -6
  114. package/packages/core/src/stores/BaseAuthStore.ts +0 -164
  115. package/packages/core/src/stores/LocalAuthStore.ts +0 -199
  116. package/packages/core/src/types.ts +0 -1042
  117. package/packages/core/tests/auth-store.test.ts +0 -300
  118. package/packages/core/tests/error-code.test.ts +0 -61
  119. package/packages/core/tests/error.test.ts +0 -24
  120. package/packages/core/tests/fetch.test.ts +0 -286
  121. package/packages/core/tests/mocks.ts +0 -139
  122. package/packages/core/tests/observability.test.ts +0 -86
  123. package/packages/core/tests/refresh-hardening.test.ts +0 -164
  124. package/packages/core/tests/refresh-observability.test.ts +0 -111
  125. package/packages/core/tests/retry.test.ts +0 -106
  126. package/packages/core/tests/serialize.test.ts +0 -12
  127. package/packages/core/tests/timeout.test.ts +0 -79
  128. package/packages/core/tsconfig.json +0 -5
  129. package/packages/core/tsup.config.ts +0 -11
  130. package/packages/db/node_modules/.bin/esbuild +0 -19
  131. package/packages/db/node_modules/.bin/tsc +0 -22
  132. package/packages/db/node_modules/.bin/tsserver +0 -22
  133. package/packages/db/node_modules/.bin/tsup +0 -22
  134. package/packages/db/node_modules/.bin/tsup-node +0 -22
  135. package/packages/db/node_modules/.bin/vite +0 -22
  136. package/packages/db/node_modules/.bin/vitest +0 -22
  137. package/packages/db/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
  138. package/packages/db/src/DatabaseService.ts +0 -170
  139. package/packages/db/src/QueryBuilder.ts +0 -830
  140. package/packages/db/src/index.ts +0 -2
  141. package/packages/db/src/pgrst-types/common/common.ts +0 -86
  142. package/packages/db/src/pgrst-types/common/rpc.ts +0 -151
  143. package/packages/db/src/pgrst-types/feature-flags.ts +0 -20
  144. package/packages/db/src/pgrst-types/types.ts +0 -134
  145. package/packages/db/src/select-query-parser/parser.ts +0 -479
  146. package/packages/db/src/select-query-parser/result.ts +0 -553
  147. package/packages/db/src/select-query-parser/types.ts +0 -132
  148. package/packages/db/src/select-query-parser/utils.ts +0 -707
  149. package/packages/db/tests/filters.test.ts +0 -81
  150. package/packages/db/tests/mutations-filtered.e2e.test.ts +0 -132
  151. package/packages/db/tests/mutations-filtered.test.ts +0 -188
  152. package/packages/db/tests/order.test.ts +0 -45
  153. package/packages/db/tests/rpc.test.ts +0 -41
  154. package/packages/db/tests/scale/build-large-schema.ts +0 -94
  155. package/packages/db/tests/scale/scale-compile.test.ts +0 -167
  156. package/packages/db/tests/schema.test.ts +0 -77
  157. package/packages/db/tests/single-accept.e2e.test.ts +0 -123
  158. package/packages/db/tests/single.test.ts +0 -211
  159. package/packages/db/tests/types/filters.test-d.ts +0 -52
  160. package/packages/db/tests/types/fixtures.ts +0 -40
  161. package/packages/db/tests/types/from-views.test-d.ts +0 -18
  162. package/packages/db/tests/types/mutations.test-d.ts +0 -23
  163. package/packages/db/tests/types/rpc.test-d.ts +0 -60
  164. package/packages/db/tests/types/schema-shape.test-d.ts +0 -8
  165. package/packages/db/tests/types/select-inference.test-d.ts +0 -38
  166. package/packages/db/tests/vendored/fixtures.ts +0 -83
  167. package/packages/db/tests/vendored/get-result.test-d.ts +0 -75
  168. package/packages/db/tsconfig.json +0 -5
  169. package/packages/db/tsconfig.test-d.json +0 -5
  170. package/packages/db/tsup.config.ts +0 -11
  171. package/packages/db/vitest.config.ts +0 -8
  172. package/packages/functions/node_modules/.bin/esbuild +0 -19
  173. package/packages/functions/node_modules/.bin/tsc +0 -22
  174. package/packages/functions/node_modules/.bin/tsserver +0 -22
  175. package/packages/functions/node_modules/.bin/tsup +0 -22
  176. package/packages/functions/node_modules/.bin/tsup-node +0 -22
  177. package/packages/functions/node_modules/.bin/vite +0 -22
  178. package/packages/functions/node_modules/.bin/vitest +0 -22
  179. package/packages/functions/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
  180. package/packages/functions/src/FunctionsService.ts +0 -188
  181. package/packages/functions/src/errors.ts +0 -70
  182. package/packages/functions/src/index.ts +0 -6
  183. package/packages/functions/tests/invoke.test.ts +0 -97
  184. package/packages/functions/tests/response-type-errors.test.ts +0 -135
  185. package/packages/functions/tsconfig.json +0 -5
  186. package/packages/functions/tsup.config.ts +0 -11
  187. package/packages/gen/node_modules/.bin/esbuild +0 -19
  188. package/packages/gen/node_modules/.bin/tsc +0 -22
  189. package/packages/gen/node_modules/.bin/tsserver +0 -22
  190. package/packages/gen/node_modules/.bin/tsup +0 -22
  191. package/packages/gen/node_modules/.bin/tsup-node +0 -22
  192. package/packages/gen/node_modules/.bin/vite +0 -22
  193. package/packages/gen/node_modules/.bin/vitest +0 -22
  194. package/packages/gen/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
  195. package/packages/gen/src/cli.ts +0 -67
  196. package/packages/gen/src/generate.ts +0 -0
  197. package/packages/gen/src/index.ts +0 -2
  198. package/packages/gen/tests/compile.test.ts +0 -213
  199. package/packages/gen/tests/e2e.test.ts +0 -130
  200. package/packages/gen/tests/generate.test.ts +0 -396
  201. package/packages/gen/tsconfig.json +0 -5
  202. package/packages/gen/tsup.config.ts +0 -11
  203. package/packages/notifications/node_modules/.bin/esbuild +0 -19
  204. package/packages/notifications/node_modules/.bin/tsc +0 -22
  205. package/packages/notifications/node_modules/.bin/tsserver +0 -22
  206. package/packages/notifications/node_modules/.bin/tsup +0 -22
  207. package/packages/notifications/node_modules/.bin/tsup-node +0 -22
  208. package/packages/notifications/node_modules/.bin/vite +0 -22
  209. package/packages/notifications/node_modules/.bin/vitest +0 -22
  210. package/packages/notifications/src/NotificationsService.ts +0 -230
  211. package/packages/notifications/src/index.ts +0 -19
  212. package/packages/notifications/tsconfig.json +0 -5
  213. package/packages/notifications/tsup.config.ts +0 -11
  214. package/packages/realtime/node_modules/.bin/esbuild +0 -19
  215. package/packages/realtime/node_modules/.bin/tsc +0 -22
  216. package/packages/realtime/node_modules/.bin/tsserver +0 -22
  217. package/packages/realtime/node_modules/.bin/tsup +0 -22
  218. package/packages/realtime/node_modules/.bin/tsup-node +0 -22
  219. package/packages/realtime/node_modules/.bin/vite +0 -22
  220. package/packages/realtime/node_modules/.bin/vitest +0 -22
  221. package/packages/realtime/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
  222. package/packages/realtime/src/AuraChannel.ts +0 -485
  223. package/packages/realtime/src/HeartbeatManager.ts +0 -73
  224. package/packages/realtime/src/RealtimeService.ts +0 -505
  225. package/packages/realtime/src/columnFilter.ts +0 -301
  226. package/packages/realtime/src/index.ts +0 -4
  227. package/packages/realtime/tests/ack-routing.test.ts +0 -81
  228. package/packages/realtime/tests/column-filter.test.ts +0 -144
  229. package/packages/realtime/tests/dispatch.test.ts +0 -191
  230. package/packages/realtime/tests/heartbeat.test.ts +0 -67
  231. package/packages/realtime/tests/http-broadcast.test.ts +0 -68
  232. package/packages/realtime/tests/realtime-token.test.ts +0 -43
  233. package/packages/realtime/tests/reconnect-race.test.ts +0 -149
  234. package/packages/realtime/tests/ref-id.test.ts +0 -34
  235. package/packages/realtime/tests/set-auth.test.ts +0 -70
  236. package/packages/realtime/tsconfig.json +0 -5
  237. package/packages/realtime/tsup.config.ts +0 -11
  238. package/packages/storage/node_modules/.bin/esbuild +0 -19
  239. package/packages/storage/node_modules/.bin/tsc +0 -22
  240. package/packages/storage/node_modules/.bin/tsserver +0 -22
  241. package/packages/storage/node_modules/.bin/tsup +0 -22
  242. package/packages/storage/node_modules/.bin/tsup-node +0 -22
  243. package/packages/storage/node_modules/.bin/vite +0 -22
  244. package/packages/storage/node_modules/.bin/vitest +0 -22
  245. package/packages/storage/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
  246. package/packages/storage/src/DownloadBuilder.ts +0 -60
  247. package/packages/storage/src/StorageService.ts +0 -323
  248. package/packages/storage/src/index.ts +0 -2
  249. package/packages/storage/src/path.ts +0 -35
  250. package/packages/storage/src/transform.ts +0 -15
  251. package/packages/storage/tests/download.test.ts +0 -64
  252. package/packages/storage/tests/parity.test.ts +0 -154
  253. package/packages/storage/tests/path.test.ts +0 -87
  254. package/packages/storage/tests/transform.test.ts +0 -79
  255. package/packages/storage/tsconfig.json +0 -5
  256. package/packages/storage/tsup.config.ts +0 -11
  257. package/pnpm-workspace.yaml +0 -24
  258. package/tsconfig.base.json +0 -15
@@ -1,684 +0,0 @@
1
- // ---------------------------------------------------------------------------
2
- // E2E réel des transforms image Storage (stack live) — Lot A / Task A3 du plan
3
- // docs/superpowers/plans/2026-07-13-sdk-storage-transforms.md.
4
- //
5
- // Ce test ne mocke rien : il upload un VRAI PNG (généré en pur JS, aucun
6
- // outil système, aucune lib image tierce — voir makePng ci-dessous) contre le
7
- // backend aura-storage réel, puis vérifie que le rendu transformé (resize)
8
- // produit effectivement une image de la largeur demandée, via les deux
9
- // chemins qui exposent `options.transform` côté SDK :
10
- // 1. download(bucket, key, { transform }) — requête directe authentifiée.
11
- // 2. createSignedUrl(bucket, key, ttl, { transform }) puis fetch(url) —
12
- // dérisquage de StorageService.createSignedUrl : les query params de
13
- // transform sont appendés APRÈS réception de l'URL signée (ils sont
14
- // HORS HMAC, cf. commentaire StorageService.ts). Si le backend couvrait
15
- // la signature sur l'URL complète (ou sur un jeu de query params figé),
16
- // un fetch de cette URL renverrait 403 — ce test le détecte et le
17
- // signale explicitement (message dédié) plutôt qu'un échec opaque
18
- // d'assertion sur la largeur.
19
- //
20
- // Générateur PNG (makePng) : implémentation minimale mais VALIDE du format —
21
- // signature PNG (8 octets) + chunk IHDR (width/height big-endian, bit depth
22
- // 8, color type 2 = RGB truecolor, pas de palette) + chunk IDAT (une seule
23
- // bande de scanlines, chacune préfixée d'un octet de filtre 0 = "None",
24
- // compressées avec zlib natif Node `deflateSync` — la crate `image` Rust
25
- // utilisée côté backend décode du zlib/DEFLATE standard, donc ce choix est
26
- // interopérable) + chunk IEND. Chaque chunk porte son CRC32 (implémentation
27
- // table-based standard, incluse ci-dessous — Node n'expose pas de crc32
28
- // public dans `node:zlib`). readPngWidth relit la largeur directement depuis
29
- // l'IHDR (octets 16-19, big-endian) de l'image reçue en retour, sans
30
- // dépendre d'un décodeur PNG complet.
31
- //
32
- // Gaté par RUN_E2E + credentials (identique aux autres *-e2e.test.ts) :
33
- // describe.runIf(RUN) SKIPPE proprement la suite entière (pas d'échec)
34
- // quand les variables d'env manquent — vérifié sans RUN_E2E via
35
- // `pnpm --filter aurabase test`.
36
- // ---------------------------------------------------------------------------
37
-
38
- import { describe, it, expect } from 'vitest'
39
- import { deflateSync } from 'node:zlib'
40
- import { createAdminClient } from '../src/index'
41
- import type { AuraResponse } from '../src/index'
42
-
43
- const URL = process.env.AURA_E2E_URL
44
- const PROJECT = process.env.AURA_E2E_PROJECT
45
- const ANON = process.env.AURA_E2E_ANON_KEY
46
- const SERVICE = process.env.AURA_E2E_SERVICE_KEY
47
- const RUN = Boolean(process.env.RUN_E2E && URL && PROJECT && ANON && SERVICE)
48
-
49
- /** Statuts transitoires observés sur la stack locale (timeout gateway→service sous charge). */
50
- const TRANSIENT_STATUS = new Set([408, 429, 502, 503, 504])
51
-
52
- /** Rejoue une requête (ou un builder paresseux comme BlobDownloadBuilder, tous deux
53
- * PromiseLike) tant qu'elle renvoie un statut transitoire, borné (cf. auth-e2e.test.ts /
54
- * readme-smoke-e2e.test.ts). */
55
- async function retryTransient<T>(fn: () => PromiseLike<AuraResponse<T>>): Promise<AuraResponse<T>> {
56
- let res = await fn()
57
- for (let i = 0; i < 10 && res.error && TRANSIENT_STATUS.has(res.error.status); i++) {
58
- await new Promise((r) => setTimeout(r, 1000))
59
- res = await fn()
60
- }
61
- return res
62
- }
63
-
64
- // ---------------------------------------------------------------------------
65
- // Générateur PNG minimal (pur JS, aucun outil système / lib image tierce).
66
- // ---------------------------------------------------------------------------
67
-
68
- /** Table CRC32 standard (polynôme 0xEDB88320 — identique zlib/gzip/PNG). */
69
- const CRC_TABLE: Uint32Array = (() => {
70
- const table = new Uint32Array(256)
71
- for (let n = 0; n < 256; n++) {
72
- let c = n
73
- for (let k = 0; k < 8; k++) {
74
- c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1
75
- }
76
- table[n] = c >>> 0
77
- }
78
- return table
79
- })()
80
-
81
- function crc32(bytes: Uint8Array): number {
82
- let c = 0xffffffff
83
- for (let i = 0; i < bytes.length; i++) {
84
- c = CRC_TABLE[(c ^ bytes[i]) & 0xff] ^ (c >>> 8)
85
- }
86
- return (c ^ 0xffffffff) >>> 0
87
- }
88
-
89
- function u32be(n: number): Uint8Array {
90
- return new Uint8Array([(n >>> 24) & 0xff, (n >>> 16) & 0xff, (n >>> 8) & 0xff, n & 0xff])
91
- }
92
-
93
- function concatBytes(...parts: Uint8Array[]): Uint8Array {
94
- const total = parts.reduce((sum, p) => sum + p.length, 0)
95
- const out = new Uint8Array(total)
96
- let offset = 0
97
- for (const p of parts) {
98
- out.set(p, offset)
99
- offset += p.length
100
- }
101
- return out
102
- }
103
-
104
- function pngChunk(type: string, data: Uint8Array): Uint8Array {
105
- const typeBytes = new Uint8Array(type.split('').map((c) => c.charCodeAt(0)))
106
- const crcInput = concatBytes(typeBytes, data)
107
- return concatBytes(u32be(data.length), typeBytes, data, u32be(crc32(crcInput)))
108
- }
109
-
110
- /**
111
- * Génère un PNG VALIDE d'un carré uni `width`x`height`, en pur JS.
112
- * RGB 8 bits par canal (color type 2, pas de palette, pas d'alpha) — décodable
113
- * par n'importe quel décodeur PNG standard, y compris la crate `image` (Rust)
114
- * utilisée par le backend aura-storage pour le rendu des transforms.
115
- */
116
- function makePng(width: number, height: number): Uint8Array {
117
- const SIGNATURE = new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10])
118
-
119
- const ihdrData = concatBytes(
120
- u32be(width),
121
- u32be(height),
122
- // bit depth 8, color type 2 (RGB truecolor), compression 0, filter 0, interlace 0
123
- new Uint8Array([8, 2, 0, 0, 0]),
124
- )
125
- const ihdr = pngChunk('IHDR', ihdrData)
126
-
127
- // Une bande de scanlines : 1 octet de filtre (0 = "None") + width*3 octets RGB par ligne.
128
- const stride = 1 + width * 3
129
- const raw = new Uint8Array(stride * height)
130
- // Bruit RGB déterministe (LCG) : une image UNIE compresse identiquement en JPEG
131
- // quelle que soit la qualité (rien à dégrader) → le test quality:20 vs :90 serait
132
- // non-discriminant. Un contenu haute-fréquence rend l'effet de `quality` mesurable.
133
- let seed = 0x12345678
134
- const nextByte = () => {
135
- seed = (seed * 1103515245 + 12345) & 0x7fffffff
136
- return (seed >>> 16) & 0xff
137
- }
138
- for (let y = 0; y < height; y++) {
139
- const rowStart = y * stride
140
- raw[rowStart] = 0 // filtre None
141
- for (let x = 0; x < width; x++) {
142
- const px = rowStart + 1 + x * 3
143
- raw[px] = nextByte()
144
- raw[px + 1] = nextByte()
145
- raw[px + 2] = nextByte()
146
- }
147
- }
148
- const compressed = deflateSync(raw)
149
- const idat = pngChunk('IDAT', new Uint8Array(compressed.buffer, compressed.byteOffset, compressed.byteLength))
150
-
151
- const iend = pngChunk('IEND', new Uint8Array(0))
152
-
153
- return concatBytes(SIGNATURE, ihdr, idat, iend)
154
- }
155
-
156
- /** Lit la largeur d'un PNG directement depuis son IHDR (octets 16-19, big-endian). */
157
- function readPngWidth(bytes: Uint8Array): number {
158
- return ((bytes[16] << 24) | (bytes[17] << 16) | (bytes[18] << 8) | bytes[19]) >>> 0
159
- }
160
-
161
- describe.runIf(RUN)('storage transforms E2E réel (stack live) — resize via download / createSignedUrl', () => {
162
- it(
163
- 'upload PNG 200x200 → download(transform width:50) === 50 → createSignedUrl(transform width:60) → fetch === 60',
164
- async () => {
165
- const admin = createAdminClient(URL!, PROJECT!, SERVICE!)
166
- const suffix = process.env.AURA_E2E_SUFFIX ?? Math.random().toString(36).slice(2, 8)
167
- const bucket = `e2e-storage-transform-${suffix}`
168
- const path = 'img.png'
169
-
170
- let created = false
171
- // upload() génère une clé serveur `<uuid>.<ext>` (renvoyée dans data.key) ;
172
- // le `path` fourni n'est PAS la clé finale. On download/signe/supprime via cette clé.
173
- let uploadedKey = path
174
- try {
175
- const createRes = await retryTransient(() =>
176
- admin.storage.createBucket(bucket, {
177
- public: false,
178
- allowedMimeTypes: ['image/png', 'image/webp', 'image/jpeg'],
179
- }),
180
- )
181
- if (createRes.error && TRANSIENT_STATUS.has(createRes.error.status)) {
182
- // eslint-disable-next-line no-console
183
- console.warn(
184
- '[storage-transform-e2e] SKIP (infra: createBucket indisponible après retries — statut transitoire ' +
185
- `${createRes.error.status} persistant) — NON un succès : assertions transform ignorées.`,
186
- )
187
- return
188
- }
189
- expect(createRes.error).toBeNull()
190
- created = true
191
-
192
- const png = makePng(200, 200)
193
- const uploadRes = await admin.storage.upload(bucket, path, new Blob([png], { type: 'image/png' }), {
194
- contentType: 'image/png',
195
- })
196
- expect(uploadRes.error).toBeNull()
197
- uploadedKey = uploadRes.data?.key ?? path
198
-
199
- // 1. download(bucket, key, { transform: { width: 50, resize: 'cover' } })
200
- const downloadRes = await retryTransient(() =>
201
- admin.storage.download(bucket, uploadedKey, { transform: { width: 50, resize: 'cover' } }),
202
- )
203
- if (downloadRes.error && TRANSIENT_STATUS.has(downloadRes.error.status)) {
204
- // eslint-disable-next-line no-console
205
- console.warn(
206
- '[storage-transform-e2e] SKIP download transform (infra: statut transitoire ' +
207
- `${downloadRes.error.status} persistant après retries).`,
208
- )
209
- return
210
- }
211
- expect(downloadRes.error).toBeNull()
212
- expect(downloadRes.data).not.toBeNull()
213
- const downloadedBuf = new Uint8Array(await downloadRes.data!.arrayBuffer())
214
- expect(readPngWidth(downloadedBuf)).toBe(50)
215
-
216
- // 2. createSignedUrl(bucket, key, ttl, { transform: { width: 60 } }) puis fetch réel —
217
- // dérisquage de la couverture HMAC sur les params transform.
218
- const signedRes = await retryTransient(() =>
219
- admin.storage.createSignedUrl(bucket, uploadedKey, 600, { transform: { width: 60 } }),
220
- )
221
- if (signedRes.error && TRANSIENT_STATUS.has(signedRes.error.status)) {
222
- // eslint-disable-next-line no-console
223
- console.warn(
224
- '[storage-transform-e2e] SKIP createSignedUrl transform (infra: statut transitoire ' +
225
- `${signedRes.error.status} persistant après retries).`,
226
- )
227
- return
228
- }
229
- expect(signedRes.error).toBeNull()
230
- expect(signedRes.data?.url).toBeTruthy()
231
-
232
- const signedResponse = await fetch(signedRes.data!.url)
233
- if (signedResponse.status === 403) {
234
- throw new Error(
235
- '[storage-transform-e2e] fetch(signedUrl) → 403 : la signature HMAC ne couvre PAS les query params ' +
236
- "de transform (width/height/fit/...) appendus après réception de l'URL signée (cf. StorageService." +
237
- 'createSignedUrl). Dérisquage NÉGATIF : la signature doit être élargie côté backend (ou générée ' +
238
- 'après ajout des params) avant de considérer ce flux transform+signedUrl comme utilisable.',
239
- )
240
- }
241
- if (TRANSIENT_STATUS.has(signedResponse.status)) {
242
- // eslint-disable-next-line no-console
243
- console.warn(
244
- `[storage-transform-e2e] SKIP fetch(signedUrl) (infra: statut transitoire ${signedResponse.status} sur le fetch).`,
245
- )
246
- return
247
- }
248
- expect(signedResponse.ok).toBe(true)
249
- const signedBuf = new Uint8Array(await signedResponse.arrayBuffer())
250
- expect(readPngWidth(signedBuf)).toBe(60)
251
- } finally {
252
- if (created) {
253
- try {
254
- await admin.storage.remove(bucket, [uploadedKey])
255
- } catch {
256
- // best-effort cleanup
257
- }
258
- try {
259
- await admin.storage.deleteBucket(bucket)
260
- } catch {
261
- // best-effort cleanup
262
- }
263
- }
264
- }
265
- },
266
- 120_000,
267
- )
268
-
269
- // -------------------------------------------------------------------------
270
- // Task B2 (Lot B) — durcissement backend : quality effectif, borne dimensions,
271
- // flag img_transform_enabled. Étend la couverture de l'E2E réel ci-dessus
272
- // après le rebuild aura-storage qui applique effectivement `quality`.
273
- // -------------------------------------------------------------------------
274
-
275
- it(
276
- 'download(transform quality:20 vs quality:90, format jpeg) → quality:20 nettement plus léger (quality appliqué)',
277
- async () => {
278
- const admin = createAdminClient(URL!, PROJECT!, SERVICE!)
279
- const suffix = process.env.AURA_E2E_SUFFIX ?? Math.random().toString(36).slice(2, 8)
280
- const bucket = `e2e-storage-transform-quality-${suffix}`
281
- const path = 'img.png'
282
-
283
- let created = false
284
- let uploadedKey = path
285
- try {
286
- const createRes = await retryTransient(() =>
287
- admin.storage.createBucket(bucket, {
288
- public: false,
289
- allowedMimeTypes: ['image/png', 'image/webp', 'image/jpeg'],
290
- }),
291
- )
292
- if (createRes.error && TRANSIENT_STATUS.has(createRes.error.status)) {
293
- // eslint-disable-next-line no-console
294
- console.warn(
295
- '[storage-transform-e2e] SKIP quality (infra: createBucket indisponible après retries — statut ' +
296
- `transitoire ${createRes.error.status} persistant) — NON un succès : assertion quality ignorée.`,
297
- )
298
- return
299
- }
300
- expect(createRes.error).toBeNull()
301
- created = true
302
-
303
- const png = makePng(200, 200)
304
- const uploadRes = await admin.storage.upload(bucket, path, new Blob([png], { type: 'image/png' }), {
305
- contentType: 'image/png',
306
- })
307
- expect(uploadRes.error).toBeNull()
308
- uploadedKey = uploadRes.data?.key ?? path
309
-
310
- const res20 = await retryTransient(() =>
311
- admin.storage.download(bucket, uploadedKey, { transform: { width: 150, format: 'jpeg', quality: 20 } }),
312
- )
313
- if (res20.error && TRANSIENT_STATUS.has(res20.error.status)) {
314
- // eslint-disable-next-line no-console
315
- console.warn(
316
- '[storage-transform-e2e] SKIP quality (infra: statut transitoire ' +
317
- `${res20.error.status} persistant sur quality:20).`,
318
- )
319
- return
320
- }
321
- expect(res20.error).toBeNull()
322
- expect(res20.data).not.toBeNull()
323
-
324
- const res90 = await retryTransient(() =>
325
- admin.storage.download(bucket, uploadedKey, { transform: { width: 150, format: 'jpeg', quality: 90 } }),
326
- )
327
- if (res90.error && TRANSIENT_STATUS.has(res90.error.status)) {
328
- // eslint-disable-next-line no-console
329
- console.warn(
330
- '[storage-transform-e2e] SKIP quality (infra: statut transitoire ' +
331
- `${res90.error.status} persistant sur quality:90).`,
332
- )
333
- return
334
- }
335
- expect(res90.error).toBeNull()
336
- expect(res90.data).not.toBeNull()
337
-
338
- const size20 = res20.data!.size
339
- const size90 = res90.data!.size
340
- // "Nettement" plus léger : la quantification JPEG à quality:20 doit produire un fichier
341
- // sensiblement plus petit qu'à quality:90 (même dimensions, même format de sortie).
342
- // Preuve que `quality` est effectivement propagé à l'encodeur côté backend (Lot B).
343
- expect(size20).toBeLessThan(size90)
344
- expect(size20).toBeLessThan(size90 * 0.8)
345
- } finally {
346
- if (created) {
347
- try {
348
- await admin.storage.remove(bucket, [uploadedKey])
349
- } catch {
350
- // best-effort cleanup
351
- }
352
- try {
353
- await admin.storage.deleteBucket(bucket)
354
- } catch {
355
- // best-effort cleanup
356
- }
357
- }
358
- }
359
- },
360
- 120_000,
361
- )
362
-
363
- it(
364
- 'download(transform width:999999) → 400 (borne MAX_TRANSFORM_DIMENSION dépassée)',
365
- async () => {
366
- const admin = createAdminClient(URL!, PROJECT!, SERVICE!)
367
- const suffix = process.env.AURA_E2E_SUFFIX ?? Math.random().toString(36).slice(2, 8)
368
- const bucket = `e2e-storage-transform-dims-${suffix}`
369
- const path = 'img.png'
370
-
371
- let created = false
372
- let uploadedKey = path
373
- try {
374
- const createRes = await retryTransient(() =>
375
- admin.storage.createBucket(bucket, { public: false, allowedMimeTypes: ['image/png'] }),
376
- )
377
- if (createRes.error && TRANSIENT_STATUS.has(createRes.error.status)) {
378
- // eslint-disable-next-line no-console
379
- console.warn(
380
- '[storage-transform-e2e] SKIP dims (infra: createBucket indisponible après retries — statut ' +
381
- `transitoire ${createRes.error.status} persistant) — NON un succès : assertion 400 ignorée.`,
382
- )
383
- return
384
- }
385
- expect(createRes.error).toBeNull()
386
- created = true
387
-
388
- const png = makePng(200, 200)
389
- const uploadRes = await admin.storage.upload(bucket, path, new Blob([png], { type: 'image/png' }), {
390
- contentType: 'image/png',
391
- })
392
- expect(uploadRes.error).toBeNull()
393
- uploadedKey = uploadRes.data?.key ?? path
394
-
395
- // width au-delà de MAX_TRANSFORM_DIMENSION (2500px, services/aura-storage/src/transform/mod.rs)
396
- // doit être rejeté par `validate_dimensions` AVANT tout décodage/rescale — HTTP 400.
397
- const res = await retryTransient(() =>
398
- admin.storage.download(bucket, uploadedKey, { transform: { width: 999_999 } }),
399
- )
400
- if (res.error && TRANSIENT_STATUS.has(res.error.status)) {
401
- // eslint-disable-next-line no-console
402
- console.warn(
403
- '[storage-transform-e2e] SKIP dims (infra: statut transitoire ' +
404
- `${res.error.status} persistant sur width:999999).`,
405
- )
406
- return
407
- }
408
- expect(res.data).toBeNull()
409
- expect(res.error).not.toBeNull()
410
- expect(res.error!.status).toBe(400)
411
- } finally {
412
- if (created) {
413
- try {
414
- await admin.storage.remove(bucket, [uploadedKey])
415
- } catch {
416
- // best-effort cleanup
417
- }
418
- try {
419
- await admin.storage.deleteBucket(bucket)
420
- } catch {
421
- // best-effort cleanup
422
- }
423
- }
424
- }
425
- },
426
- 60_000,
427
- )
428
-
429
- it(
430
- 'flag projet img_transform_enabled=false (PUT /v1/storage/{project}/settings, service_role) → transform 403, réactivation (best-effort)',
431
- async () => {
432
- const admin = createAdminClient(URL!, PROJECT!, SERVICE!)
433
- const suffix = process.env.AURA_E2E_SUFFIX ?? Math.random().toString(36).slice(2, 8)
434
- const bucket = `e2e-storage-transform-flag-${suffix}`
435
- const path = 'img.png'
436
- // Même préfixe que StorageService (`/v1/storage/${projectId}`) : pas de méthode SDK dédiée
437
- // pour les settings storage, donc fetch direct authentifié par la service key (comme le
438
- // ferait un appel admin serveur-à-serveur — cf. StorageService.ts prefix getter).
439
- const settingsUrl = `${admin.baseUrl}/v1/storage/${admin.projectId}/settings`
440
-
441
- const putImgTransformEnabled = (enabled: boolean) =>
442
- fetch(settingsUrl, {
443
- method: 'PUT',
444
- headers: { apikey: SERVICE!, 'content-type': 'application/json' },
445
- body: JSON.stringify({ img_transform_enabled: enabled }),
446
- })
447
-
448
- let created = false
449
- let uploadedKey = path
450
- let disabled = false
451
- try {
452
- const createRes = await retryTransient(() =>
453
- admin.storage.createBucket(bucket, { public: false, allowedMimeTypes: ['image/png'] }),
454
- )
455
- if (createRes.error && TRANSIENT_STATUS.has(createRes.error.status)) {
456
- // eslint-disable-next-line no-console
457
- console.warn(
458
- '[storage-transform-e2e] SKIP flag 403 (infra: createBucket indisponible après retries — statut ' +
459
- `transitoire ${createRes.error.status} persistant).`,
460
- )
461
- return
462
- }
463
- expect(createRes.error).toBeNull()
464
- created = true
465
-
466
- const png = makePng(200, 200)
467
- const uploadRes = await admin.storage.upload(bucket, path, new Blob([png], { type: 'image/png' }), {
468
- contentType: 'image/png',
469
- })
470
- expect(uploadRes.error).toBeNull()
471
- uploadedKey = uploadRes.data?.key ?? path
472
-
473
- let disableRes: Response
474
- try {
475
- disableRes = await putImgTransformEnabled(false)
476
- } catch (err) {
477
- // eslint-disable-next-line no-console
478
- console.warn(
479
- '[storage-transform-e2e] flag 403 non testé via SDK (couvert par cargo test backend) — ' +
480
- `fetch PUT ${settingsUrl} a levé : ${err instanceof Error ? err.message : String(err)}.`,
481
- )
482
- return
483
- }
484
- if (!disableRes.ok) {
485
- // eslint-disable-next-line no-console
486
- console.warn(
487
- '[storage-transform-e2e] flag 403 non testé via SDK (couvert par cargo test backend) — ' +
488
- `PUT ${settingsUrl} → ${disableRes.status} (endpoint settings non accessible proprement ` +
489
- 'depuis ce contexte E2E, ex. clé service_role sans les droits attendus).',
490
- )
491
- return
492
- }
493
- disabled = true
494
-
495
- const res = await retryTransient(() =>
496
- admin.storage.download(bucket, uploadedKey, { transform: { width: 100 } }),
497
- )
498
- if (res.error && TRANSIENT_STATUS.has(res.error.status)) {
499
- // eslint-disable-next-line no-console
500
- console.warn(
501
- '[storage-transform-e2e] SKIP flag 403 (infra: statut transitoire ' +
502
- `${res.error.status} persistant après désactivation du flag).`,
503
- )
504
- return
505
- }
506
- expect(res.data).toBeNull()
507
- expect(res.error).not.toBeNull()
508
- expect(res.error!.status).toBe(403)
509
- } finally {
510
- if (disabled) {
511
- try {
512
- const reenableRes = await putImgTransformEnabled(true)
513
- if (!reenableRes.ok) {
514
- // eslint-disable-next-line no-console
515
- console.warn(
516
- `[storage-transform-e2e] réactivation img_transform_enabled a échoué (${reenableRes.status}) — ` +
517
- 'vérifier manuellement les settings storage du projet E2E.',
518
- )
519
- }
520
- } catch (err) {
521
- // eslint-disable-next-line no-console
522
- console.warn(
523
- '[storage-transform-e2e] réactivation img_transform_enabled a levé — vérifier manuellement les ' +
524
- `settings storage du projet E2E : ${err instanceof Error ? err.message : String(err)}.`,
525
- )
526
- }
527
- }
528
- if (created) {
529
- try {
530
- await admin.storage.remove(bucket, [uploadedKey])
531
- } catch {
532
- // best-effort cleanup
533
- }
534
- try {
535
- await admin.storage.deleteBucket(bucket)
536
- } catch {
537
- // best-effort cleanup
538
- }
539
- }
540
- }
541
- },
542
- 60_000,
543
- )
544
-
545
- // -------------------------------------------------------------------------
546
- // Lot C — cache des dérivés (C1/C2) : le backend cache désormais les images
547
- // transformées et expose `x-aura-cache: HIT|MISS` sur la branche transform.
548
- // Ce test fetch RÉELLEMENT l'URL signée (le SDK download() ne donne pas
549
- // accès aux headers de réponse) : 1er fetch MISS (dérivé créé), 2e fetch de
550
- // la MÊME URL signée HIT (servi du cache). Si le header est absent (backend
551
- // pas rebuildé), on le signale explicitement plutôt qu'un échec opaque —
552
- // seul le HIT/MISS est asserté quand le header est présent.
553
- // -------------------------------------------------------------------------
554
-
555
- it(
556
- "createSignedUrl(transform) → fetch 1: x-aura-cache=MISS, fetch 2 (même URL): x-aura-cache=HIT",
557
- async () => {
558
- const admin = createAdminClient(URL!, PROJECT!, SERVICE!)
559
- const suffix = process.env.AURA_E2E_SUFFIX ?? Math.random().toString(36).slice(2, 8)
560
- const bucket = `e2e-storage-transform-cache-${suffix}`
561
- const path = 'img.png'
562
-
563
- let created = false
564
- let uploadedKey = path
565
- try {
566
- const createRes = await retryTransient(() =>
567
- admin.storage.createBucket(bucket, {
568
- public: false,
569
- allowedMimeTypes: ['image/png', 'image/webp', 'image/jpeg'],
570
- }),
571
- )
572
- if (createRes.error && TRANSIENT_STATUS.has(createRes.error.status)) {
573
- // eslint-disable-next-line no-console
574
- console.warn(
575
- '[storage-transform-e2e] SKIP cache (infra: createBucket indisponible après retries — statut ' +
576
- `transitoire ${createRes.error.status} persistant) — NON un succès : assertions cache ignorées.`,
577
- )
578
- return
579
- }
580
- expect(createRes.error).toBeNull()
581
- created = true
582
-
583
- const png = makePng(200, 200)
584
- const uploadRes = await admin.storage.upload(bucket, path, new Blob([png], { type: 'image/png' }), {
585
- contentType: 'image/png',
586
- })
587
- expect(uploadRes.error).toBeNull()
588
- uploadedKey = uploadRes.data?.key ?? path
589
-
590
- const signedRes = await retryTransient(() =>
591
- admin.storage.createSignedUrl(bucket, uploadedKey, 600, {
592
- // On garde le format PNG d'origine (pas de `format`) pour pouvoir relire
593
- // la largeur via readPngWidth ; le cache (x-aura-cache) est indépendant du format.
594
- transform: { width: 40 },
595
- }),
596
- )
597
- if (signedRes.error && TRANSIENT_STATUS.has(signedRes.error.status)) {
598
- // eslint-disable-next-line no-console
599
- console.warn(
600
- '[storage-transform-e2e] SKIP cache (infra: statut transitoire ' +
601
- `${signedRes.error.status} persistant sur createSignedUrl).`,
602
- )
603
- return
604
- }
605
- expect(signedRes.error).toBeNull()
606
- expect(signedRes.data?.url).toBeTruthy()
607
- const signedUrl = signedRes.data!.url
608
-
609
- // Fetch 1 — dérivé pas encore en cache → attendu MISS.
610
- const first = await fetch(signedUrl)
611
- if (TRANSIENT_STATUS.has(first.status)) {
612
- // eslint-disable-next-line no-console
613
- console.warn(
614
- `[storage-transform-e2e] SKIP cache (infra: statut transitoire ${first.status} sur le 1er fetch).`,
615
- )
616
- return
617
- }
618
- expect(first.ok).toBe(true)
619
- const firstCacheHeader = first.headers.get('x-aura-cache')
620
- const firstBuf = new Uint8Array(await first.arrayBuffer())
621
-
622
- if (firstCacheHeader === null) {
623
- // eslint-disable-next-line no-console
624
- console.warn(
625
- '[storage-transform-e2e] header x-aura-cache absent (backend cache non déployé ?) — ' +
626
- "vérification du contenu (largeur) faite, mais l'assertion HIT/MISS est ignorée.",
627
- )
628
- expect(readPngWidth(firstBuf)).toBe(40)
629
- } else {
630
- expect(firstCacheHeader).toBe('MISS')
631
- expect(readPngWidth(firstBuf)).toBe(40)
632
-
633
- // Fetch 2 — même URL signée, dérivé désormais en cache → attendu HIT.
634
- const second = await fetch(signedUrl)
635
- if (TRANSIENT_STATUS.has(second.status)) {
636
- // eslint-disable-next-line no-console
637
- console.warn(
638
- `[storage-transform-e2e] SKIP cache (infra: statut transitoire ${second.status} sur le 2e fetch).`,
639
- )
640
- return
641
- }
642
- expect(second.ok).toBe(true)
643
- const secondCacheHeader = second.headers.get('x-aura-cache')
644
- const secondBuf = new Uint8Array(await second.arrayBuffer())
645
-
646
- expect(secondCacheHeader).toBe('HIT')
647
- // Même contenu servi depuis le cache : largeur identique et même taille en octets.
648
- expect(readPngWidth(secondBuf)).toBe(40)
649
- expect(secondBuf.byteLength).toBe(firstBuf.byteLength)
650
- }
651
-
652
- // Invalidation sur delete (purge_derivatives) : couverte côté backend par cargo test —
653
- // best-effort ici, on ne fait que documenter/tenter sans faire échouer le test E2E si le
654
- // header est absent ou le comportement diffère.
655
- const deleteRes = await admin.storage.remove(bucket, [uploadedKey])
656
- if (!deleteRes.error) {
657
- const afterDelete = await fetch(signedUrl)
658
- if (!TRANSIENT_STATUS.has(afterDelete.status)) {
659
- // eslint-disable-next-line no-console
660
- console.warn(
661
- '[storage-transform-e2e] best-effort post-delete : fetch(signedUrl) après remove() → ' +
662
- `statut ${afterDelete.status} (invalidation cache prouvée par cargo test backend purge_derivatives, ` +
663
- 'non re-assertée ici).',
664
- )
665
- }
666
- }
667
- } finally {
668
- if (created) {
669
- try {
670
- await admin.storage.remove(bucket, [uploadedKey])
671
- } catch {
672
- // best-effort cleanup (déjà supprimé ci-dessus dans le cas nominal)
673
- }
674
- try {
675
- await admin.storage.deleteBucket(bucket)
676
- } catch {
677
- // best-effort cleanup
678
- }
679
- }
680
- }
681
- },
682
- 120_000,
683
- )
684
- })