@aurabase/aurabase-js 0.3.0 → 0.4.0
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.
- package/README.md +26 -795
- package/{packages/aurabase/dist/AuraClient-DAtDbfPB.d.ts → dist/AuraClient-BcFE8u1v.d.cts} +1 -1
- package/{packages/aurabase/dist/AuraClient-DAtDbfPB.d.cts → dist/AuraClient-BcFE8u1v.d.ts} +1 -1
- package/{packages/aurabase/dist → dist}/index.d.cts +1 -1
- package/{packages/aurabase/dist → dist}/index.d.ts +1 -1
- package/{packages/aurabase/dist → dist}/ssr.d.cts +1 -1
- package/{packages/aurabase/dist → dist}/ssr.d.ts +1 -1
- package/package.json +72 -82
- package/.changeset/config.json +0 -11
- package/CHANGELOG.md +0 -30
- package/packages/ai/LICENSE +0 -21
- package/packages/ai/README.md +0 -106
- package/packages/ai/dist/index.cjs +0 -229
- package/packages/ai/dist/index.d.cts +0 -139
- package/packages/ai/dist/index.d.ts +0 -139
- package/packages/ai/dist/index.js +0 -226
- package/packages/ai/node_modules/.bin/esbuild +0 -19
- package/packages/ai/node_modules/.bin/tsc +0 -22
- package/packages/ai/node_modules/.bin/tsserver +0 -22
- package/packages/ai/node_modules/.bin/tsup +0 -22
- package/packages/ai/node_modules/.bin/tsup-node +0 -22
- package/packages/ai/node_modules/.bin/vite +0 -22
- package/packages/ai/node_modules/.bin/vitest +0 -22
- package/packages/ai/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
- package/packages/ai/package.json +0 -61
- package/packages/ai/src/AiService.ts +0 -226
- package/packages/ai/src/index.ts +0 -18
- package/packages/ai/src/sse.ts +0 -141
- package/packages/ai/src/types.ts +0 -30
- package/packages/ai/tests/chat-stream.test.ts +0 -151
- package/packages/ai/tests/rag-namespaces.test.ts +0 -54
- package/packages/ai/tests/sse.test.ts +0 -69
- package/packages/ai/tsconfig.json +0 -5
- package/packages/ai/tsup.config.ts +0 -11
- package/packages/aurabase/LICENSE +0 -21
- package/packages/aurabase/README.md +0 -64
- package/packages/aurabase/node_modules/.bin/esbuild +0 -19
- package/packages/aurabase/node_modules/.bin/tsc +0 -22
- package/packages/aurabase/node_modules/.bin/tsserver +0 -22
- package/packages/aurabase/node_modules/.bin/tsup +0 -22
- package/packages/aurabase/node_modules/.bin/tsup-node +0 -22
- package/packages/aurabase/node_modules/.bin/vite +0 -22
- package/packages/aurabase/node_modules/.bin/vitest +0 -22
- package/packages/aurabase/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
- package/packages/aurabase/package.json +0 -80
- package/packages/aurabase/src/AuraClient.ts +0 -237
- package/packages/aurabase/src/index.ts +0 -152
- package/packages/aurabase/src/ssr.ts +0 -156
- package/packages/aurabase/tests/ai-streaming-rag-e2e.test.ts +0 -82
- package/packages/aurabase/tests/ai.test.ts +0 -111
- package/packages/aurabase/tests/auth-admin.test.ts +0 -515
- package/packages/aurabase/tests/auth-e2e.test.ts +0 -135
- package/packages/aurabase/tests/auth-hardening.test.ts +0 -206
- package/packages/aurabase/tests/auth.test.ts +0 -947
- package/packages/aurabase/tests/database.test.ts +0 -230
- package/packages/aurabase/tests/db-types-e2e.test.ts +0 -217
- package/packages/aurabase/tests/dispose.test.ts +0 -23
- package/packages/aurabase/tests/foreign-token.e2e.test.ts +0 -129
- package/packages/aurabase/tests/functions.test.ts +0 -107
- package/packages/aurabase/tests/integration.test.ts +0 -413
- package/packages/aurabase/tests/live-production-validation.js +0 -241
- package/packages/aurabase/tests/live-sdk-production-validation.js +0 -293
- package/packages/aurabase/tests/mocks.ts +0 -139
- package/packages/aurabase/tests/notifications.test.ts +0 -314
- package/packages/aurabase/tests/observability-backup.test.ts +0 -186
- package/packages/aurabase/tests/observability-hook.test.ts +0 -130
- package/packages/aurabase/tests/production-robustness.test.ts +0 -268
- package/packages/aurabase/tests/public-api-surface.test.ts +0 -53
- package/packages/aurabase/tests/query-builder.test.ts +0 -329
- package/packages/aurabase/tests/readme-smoke-e2e.test.ts +0 -231
- package/packages/aurabase/tests/realtime-filter-e2e.test.ts +0 -344
- package/packages/aurabase/tests/refresh-locks.test.ts +0 -210
- package/packages/aurabase/tests/retry-option.test.ts +0 -17
- package/packages/aurabase/tests/rpc-e2e.test.ts +0 -254
- package/packages/aurabase/tests/rs256-local-e2e.test.ts +0 -114
- package/packages/aurabase/tests/rt-alias-public-e2e.test.ts +0 -235
- package/packages/aurabase/tests/rt-http-broadcast-e2e.test.ts +0 -124
- package/packages/aurabase/tests/service-role-guard.test.ts +0 -99
- package/packages/aurabase/tests/ssr.test.ts +0 -91
- package/packages/aurabase/tests/storage-backend-e2e.test.ts +0 -481
- package/packages/aurabase/tests/storage-sdk-parity-e2e.test.ts +0 -104
- package/packages/aurabase/tests/storage-transform-e2e.test.ts +0 -684
- package/packages/aurabase/tests/storage.test.ts +0 -55
- package/packages/aurabase/tests/types/client-generic.test-d.ts +0 -79
- package/packages/aurabase/tests/types/docs-snippets.test-d.ts +0 -571
- package/packages/aurabase/tests/types/error-code.test-d.ts +0 -53
- package/packages/aurabase/tsconfig.json +0 -5
- package/packages/aurabase/tsconfig.test-d.json +0 -5
- package/packages/aurabase/tsup.config.ts +0 -11
- package/packages/aurabase/vitest.config.ts +0 -16
- package/packages/aurabase/vitest.e2e.config.ts +0 -9
- package/packages/auth/LICENSE +0 -21
- package/packages/auth/README.md +0 -215
- package/packages/auth/dist/index.cjs +0 -1175
- package/packages/auth/dist/index.d.cts +0 -397
- package/packages/auth/dist/index.d.ts +0 -397
- package/packages/auth/dist/index.js +0 -1170
- package/packages/auth/node_modules/.bin/esbuild +0 -19
- package/packages/auth/node_modules/.bin/tsc +0 -22
- package/packages/auth/node_modules/.bin/tsserver +0 -22
- package/packages/auth/node_modules/.bin/tsup +0 -22
- package/packages/auth/node_modules/.bin/tsup-node +0 -22
- package/packages/auth/node_modules/.bin/vite +0 -22
- package/packages/auth/node_modules/.bin/vitest +0 -22
- package/packages/auth/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
- package/packages/auth/package.json +0 -61
- package/packages/auth/src/AuthAdminService.ts +0 -149
- package/packages/auth/src/AuthService.ts +0 -1166
- package/packages/auth/src/SessionManager.ts +0 -92
- package/packages/auth/src/index.ts +0 -17
- package/packages/auth/src/jwks.ts +0 -337
- package/packages/auth/src/pkce.ts +0 -27
- package/packages/auth/src/types.ts +0 -89
- package/packages/auth/tests/auth-lifecycle.test.ts +0 -149
- package/packages/auth/tests/detect-session.test.ts +0 -361
- package/packages/auth/tests/getclaims.test.ts +0 -780
- package/packages/auth/tests/is-mfa-challenge.test.ts +0 -27
- package/packages/auth/tests/password-recovery.test.ts +0 -124
- package/packages/auth/tests/pkce.test.ts +0 -192
- package/packages/auth/tests/session-manager.test.ts +0 -103
- package/packages/auth/tests/set-session.test.ts +0 -139
- package/packages/auth/tests/verify-otp.test.ts +0 -153
- package/packages/auth/tsconfig.json +0 -5
- package/packages/auth/tsup.config.ts +0 -11
- package/packages/core/LICENSE +0 -21
- package/packages/core/README.md +0 -132
- package/packages/core/dist/index.cjs +0 -695
- package/packages/core/dist/index.d.cts +0 -1137
- package/packages/core/dist/index.d.ts +0 -1137
- package/packages/core/dist/index.js +0 -673
- package/packages/core/node_modules/.bin/esbuild +0 -22
- package/packages/core/node_modules/.bin/tsc +0 -22
- package/packages/core/node_modules/.bin/tsserver +0 -22
- package/packages/core/node_modules/.bin/tsup +0 -22
- package/packages/core/node_modules/.bin/tsup-node +0 -22
- package/packages/core/node_modules/.bin/vite +0 -22
- package/packages/core/node_modules/.bin/vitest +0 -22
- package/packages/core/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
- package/packages/core/package.json +0 -57
- package/packages/core/src/AuraError.ts +0 -89
- package/packages/core/src/BaseService.ts +0 -44
- package/packages/core/src/client.ts +0 -34
- package/packages/core/src/cookie-utils.ts +0 -42
- package/packages/core/src/fetch.ts +0 -435
- package/packages/core/src/index.ts +0 -22
- package/packages/core/src/retry.ts +0 -63
- package/packages/core/src/serialize.ts +0 -6
- package/packages/core/src/stores/BaseAuthStore.ts +0 -164
- package/packages/core/src/stores/LocalAuthStore.ts +0 -199
- package/packages/core/src/types.ts +0 -1042
- package/packages/core/tests/auth-store.test.ts +0 -300
- package/packages/core/tests/error-code.test.ts +0 -61
- package/packages/core/tests/error.test.ts +0 -24
- package/packages/core/tests/fetch.test.ts +0 -286
- package/packages/core/tests/mocks.ts +0 -139
- package/packages/core/tests/observability.test.ts +0 -86
- package/packages/core/tests/refresh-hardening.test.ts +0 -164
- package/packages/core/tests/refresh-observability.test.ts +0 -111
- package/packages/core/tests/retry.test.ts +0 -106
- package/packages/core/tests/serialize.test.ts +0 -12
- package/packages/core/tests/timeout.test.ts +0 -79
- package/packages/core/tsconfig.json +0 -5
- package/packages/core/tsup.config.ts +0 -11
- package/packages/db/LICENSE +0 -21
- package/packages/db/README.md +0 -152
- package/packages/db/VENDORED.md +0 -93
- package/packages/db/dist/index.cjs +0 -544
- package/packages/db/dist/index.d.cts +0 -1111
- package/packages/db/dist/index.d.ts +0 -1111
- package/packages/db/dist/index.js +0 -541
- package/packages/db/node_modules/.bin/esbuild +0 -19
- package/packages/db/node_modules/.bin/tsc +0 -22
- package/packages/db/node_modules/.bin/tsserver +0 -22
- package/packages/db/node_modules/.bin/tsup +0 -22
- package/packages/db/node_modules/.bin/tsup-node +0 -22
- package/packages/db/node_modules/.bin/vite +0 -22
- package/packages/db/node_modules/.bin/vitest +0 -22
- package/packages/db/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
- package/packages/db/package.json +0 -62
- package/packages/db/src/DatabaseService.ts +0 -170
- package/packages/db/src/QueryBuilder.ts +0 -830
- package/packages/db/src/index.ts +0 -2
- package/packages/db/src/pgrst-types/common/common.ts +0 -86
- package/packages/db/src/pgrst-types/common/rpc.ts +0 -151
- package/packages/db/src/pgrst-types/feature-flags.ts +0 -20
- package/packages/db/src/pgrst-types/types.ts +0 -134
- package/packages/db/src/select-query-parser/parser.ts +0 -479
- package/packages/db/src/select-query-parser/result.ts +0 -553
- package/packages/db/src/select-query-parser/types.ts +0 -132
- package/packages/db/src/select-query-parser/utils.ts +0 -707
- package/packages/db/tests/filters.test.ts +0 -81
- package/packages/db/tests/mutations-filtered.e2e.test.ts +0 -132
- package/packages/db/tests/mutations-filtered.test.ts +0 -188
- package/packages/db/tests/order.test.ts +0 -45
- package/packages/db/tests/rpc.test.ts +0 -41
- package/packages/db/tests/scale/build-large-schema.ts +0 -94
- package/packages/db/tests/scale/scale-compile.test.ts +0 -167
- package/packages/db/tests/schema.test.ts +0 -77
- package/packages/db/tests/single-accept.e2e.test.ts +0 -123
- package/packages/db/tests/single.test.ts +0 -211
- package/packages/db/tests/types/filters.test-d.ts +0 -52
- package/packages/db/tests/types/fixtures.ts +0 -40
- package/packages/db/tests/types/from-views.test-d.ts +0 -18
- package/packages/db/tests/types/mutations.test-d.ts +0 -23
- package/packages/db/tests/types/rpc.test-d.ts +0 -60
- package/packages/db/tests/types/schema-shape.test-d.ts +0 -8
- package/packages/db/tests/types/select-inference.test-d.ts +0 -38
- package/packages/db/tests/vendored/fixtures.ts +0 -83
- package/packages/db/tests/vendored/get-result.test-d.ts +0 -75
- package/packages/db/tsconfig.json +0 -5
- package/packages/db/tsconfig.test-d.json +0 -5
- package/packages/db/tsup.config.ts +0 -11
- package/packages/db/vitest.config.ts +0 -8
- package/packages/functions/LICENSE +0 -21
- package/packages/functions/README.md +0 -96
- package/packages/functions/dist/index.cjs +0 -173
- package/packages/functions/dist/index.d.cts +0 -81
- package/packages/functions/dist/index.d.ts +0 -81
- package/packages/functions/dist/index.js +0 -168
- package/packages/functions/node_modules/.bin/esbuild +0 -19
- package/packages/functions/node_modules/.bin/tsc +0 -22
- package/packages/functions/node_modules/.bin/tsserver +0 -22
- package/packages/functions/node_modules/.bin/tsup +0 -22
- package/packages/functions/node_modules/.bin/tsup-node +0 -22
- package/packages/functions/node_modules/.bin/vite +0 -22
- package/packages/functions/node_modules/.bin/vitest +0 -22
- package/packages/functions/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
- package/packages/functions/package.json +0 -60
- package/packages/functions/src/FunctionsService.ts +0 -188
- package/packages/functions/src/errors.ts +0 -70
- package/packages/functions/src/index.ts +0 -6
- package/packages/functions/tests/invoke.test.ts +0 -97
- package/packages/functions/tests/response-type-errors.test.ts +0 -135
- package/packages/functions/tsconfig.json +0 -5
- package/packages/functions/tsup.config.ts +0 -11
- package/packages/gen/LICENSE +0 -21
- package/packages/gen/README.md +0 -84
- package/packages/gen/dist/cli.cjs +0 -334
- package/packages/gen/dist/cli.d.cts +0 -1
- package/packages/gen/dist/cli.d.ts +0 -1
- package/packages/gen/dist/cli.js +0 -332
- package/packages/gen/dist/index.cjs +0 -257
- package/packages/gen/dist/index.d.cts +0 -12
- package/packages/gen/dist/index.d.ts +0 -12
- package/packages/gen/dist/index.js +0 -255
- package/packages/gen/node_modules/.bin/esbuild +0 -19
- package/packages/gen/node_modules/.bin/tsc +0 -22
- package/packages/gen/node_modules/.bin/tsserver +0 -22
- package/packages/gen/node_modules/.bin/tsup +0 -22
- package/packages/gen/node_modules/.bin/tsup-node +0 -22
- package/packages/gen/node_modules/.bin/vite +0 -22
- package/packages/gen/node_modules/.bin/vitest +0 -22
- package/packages/gen/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
- package/packages/gen/package.json +0 -66
- package/packages/gen/src/cli.ts +0 -67
- package/packages/gen/src/generate.ts +0 -0
- package/packages/gen/src/index.ts +0 -2
- package/packages/gen/tests/compile.test.ts +0 -213
- package/packages/gen/tests/e2e.test.ts +0 -130
- package/packages/gen/tests/generate.test.ts +0 -396
- package/packages/gen/tsconfig.json +0 -5
- package/packages/gen/tsup.config.ts +0 -11
- package/packages/notifications/LICENSE +0 -21
- package/packages/notifications/README.md +0 -134
- package/packages/notifications/dist/index.cjs +0 -128
- package/packages/notifications/dist/index.d.cts +0 -81
- package/packages/notifications/dist/index.d.ts +0 -81
- package/packages/notifications/dist/index.js +0 -125
- package/packages/notifications/node_modules/.bin/esbuild +0 -19
- package/packages/notifications/node_modules/.bin/tsc +0 -22
- package/packages/notifications/node_modules/.bin/tsserver +0 -22
- package/packages/notifications/node_modules/.bin/tsup +0 -22
- package/packages/notifications/node_modules/.bin/tsup-node +0 -22
- package/packages/notifications/node_modules/.bin/vite +0 -22
- package/packages/notifications/node_modules/.bin/vitest +0 -22
- package/packages/notifications/package.json +0 -60
- package/packages/notifications/src/NotificationsService.ts +0 -230
- package/packages/notifications/src/index.ts +0 -19
- package/packages/notifications/tsconfig.json +0 -5
- package/packages/notifications/tsup.config.ts +0 -11
- package/packages/realtime/LICENSE +0 -21
- package/packages/realtime/README.md +0 -183
- package/packages/realtime/dist/index.cjs +0 -977
- package/packages/realtime/dist/index.d.cts +0 -259
- package/packages/realtime/dist/index.d.ts +0 -259
- package/packages/realtime/dist/index.js +0 -973
- package/packages/realtime/node_modules/.bin/esbuild +0 -19
- package/packages/realtime/node_modules/.bin/tsc +0 -22
- package/packages/realtime/node_modules/.bin/tsserver +0 -22
- package/packages/realtime/node_modules/.bin/tsup +0 -22
- package/packages/realtime/node_modules/.bin/tsup-node +0 -22
- package/packages/realtime/node_modules/.bin/vite +0 -22
- package/packages/realtime/node_modules/.bin/vitest +0 -22
- package/packages/realtime/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
- package/packages/realtime/package.json +0 -60
- package/packages/realtime/src/AuraChannel.ts +0 -485
- package/packages/realtime/src/HeartbeatManager.ts +0 -73
- package/packages/realtime/src/RealtimeService.ts +0 -505
- package/packages/realtime/src/columnFilter.ts +0 -301
- package/packages/realtime/src/index.ts +0 -4
- package/packages/realtime/tests/ack-routing.test.ts +0 -81
- package/packages/realtime/tests/column-filter.test.ts +0 -144
- package/packages/realtime/tests/dispatch.test.ts +0 -191
- package/packages/realtime/tests/heartbeat.test.ts +0 -67
- package/packages/realtime/tests/http-broadcast.test.ts +0 -68
- package/packages/realtime/tests/realtime-token.test.ts +0 -43
- package/packages/realtime/tests/reconnect-race.test.ts +0 -149
- package/packages/realtime/tests/ref-id.test.ts +0 -34
- package/packages/realtime/tests/set-auth.test.ts +0 -70
- package/packages/realtime/tsconfig.json +0 -5
- package/packages/realtime/tsup.config.ts +0 -11
- package/packages/storage/LICENSE +0 -21
- package/packages/storage/README.md +0 -141
- package/packages/storage/dist/index.cjs +0 -333
- package/packages/storage/dist/index.d.cts +0 -185
- package/packages/storage/dist/index.d.ts +0 -185
- package/packages/storage/dist/index.js +0 -329
- package/packages/storage/node_modules/.bin/esbuild +0 -19
- package/packages/storage/node_modules/.bin/tsc +0 -22
- package/packages/storage/node_modules/.bin/tsserver +0 -22
- package/packages/storage/node_modules/.bin/tsup +0 -22
- package/packages/storage/node_modules/.bin/tsup-node +0 -22
- package/packages/storage/node_modules/.bin/vite +0 -22
- package/packages/storage/node_modules/.bin/vitest +0 -22
- package/packages/storage/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
- package/packages/storage/package.json +0 -60
- package/packages/storage/src/DownloadBuilder.ts +0 -60
- package/packages/storage/src/StorageService.ts +0 -323
- package/packages/storage/src/index.ts +0 -2
- package/packages/storage/src/path.ts +0 -35
- package/packages/storage/src/transform.ts +0 -15
- package/packages/storage/tests/download.test.ts +0 -64
- package/packages/storage/tests/parity.test.ts +0 -154
- package/packages/storage/tests/path.test.ts +0 -87
- package/packages/storage/tests/transform.test.ts +0 -79
- package/packages/storage/tsconfig.json +0 -5
- package/packages/storage/tsup.config.ts +0 -11
- package/pnpm-workspace.yaml +0 -24
- package/tsconfig.base.json +0 -15
- /package/{packages/aurabase/dist → dist}/index.cjs +0 -0
- /package/{packages/aurabase/dist → dist}/index.js +0 -0
- /package/{packages/aurabase/dist → dist}/ssr.cjs +0 -0
- /package/{packages/aurabase/dist → dist}/ssr.js +0 -0
|
@@ -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
|
-
})
|