@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,210 +0,0 @@
1
- import { describe, it, expect, afterEach, vi } from 'vitest'
2
- import { refreshOnce, BaseAuthStore } from '@aurabase/core'
3
- import type { AuraClientLike, User } from '@aurabase/core'
4
-
5
- function fakeUser(id = 'u1'): User {
6
- return {
7
- id,
8
- email: 't@e.com',
9
- role: 'authenticated',
10
- email_verified: true,
11
- is_banned: false,
12
- is_anonymous: false,
13
- mfa_enabled: false,
14
- user_metadata: {},
15
- app_metadata: {},
16
- created_at: '2025-01-01T00:00:00Z',
17
- }
18
- }
19
- function b64url(obj: unknown): string {
20
- return btoa(JSON.stringify(obj)).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '')
21
- }
22
- function jwt(sub = 'u1', ttlSec = 3600): string {
23
- const exp = Math.floor(Date.now() / 1000) + ttlSec
24
- return `${b64url({ alg: 'HS256', typ: 'JWT' })}.${b64url({ sub, exp })}.sig`
25
- }
26
-
27
- // -- navigator.locks install/uninstall (Node peut définir navigator en lecture seule) --
28
- // Les Web Locks ne s'activent qu'en contexte NAVIGATEUR (gate `window` dans
29
- // withRefreshLock) : on simule donc aussi `globalThis.window` le temps du test.
30
- let hadNavigator = false
31
- let originalNavigator: PropertyDescriptor | undefined
32
- let hadWindow = false
33
- function installLocks(request: unknown): void {
34
- hadNavigator = 'navigator' in globalThis
35
- originalNavigator = Object.getOwnPropertyDescriptor(globalThis, 'navigator')
36
- Object.defineProperty(globalThis, 'navigator', {
37
- value: { locks: { request } },
38
- configurable: true,
39
- writable: true,
40
- })
41
- hadWindow = 'window' in globalThis
42
- if (!hadWindow) {
43
- ;(globalThis as unknown as { window?: unknown }).window = globalThis
44
- }
45
- }
46
- function uninstallLocks(): void {
47
- if (originalNavigator) {
48
- Object.defineProperty(globalThis, 'navigator', originalNavigator)
49
- } else if (hadNavigator) {
50
- // rien
51
- } else {
52
- delete (globalThis as unknown as { navigator?: unknown }).navigator
53
- }
54
- originalNavigator = undefined
55
- if (!hadWindow) {
56
- delete (globalThis as unknown as { window?: unknown }).window
57
- }
58
- }
59
-
60
- afterEach(() => {
61
- uninstallLocks()
62
- vi.restoreAllMocks()
63
- })
64
-
65
- function makeClient(
66
- fetchFn: typeof globalThis.fetch,
67
- store: BaseAuthStore,
68
- projectId = 'proj-X',
69
- ): AuraClientLike {
70
- return {
71
- baseUrl: 'http://locks.local',
72
- projectId,
73
- apiKey: 'k',
74
- fetchFn,
75
- authStore: store,
76
- auth: null as unknown as AuraClientLike['auth'],
77
- }
78
- }
79
-
80
- function refreshFetch(tokenPair: unknown): typeof globalThis.fetch {
81
- return (async (url: string | URL | Request) => {
82
- if (String(url).endsWith('/refresh')) {
83
- return new Response(JSON.stringify({ data: tokenPair }), {
84
- status: 200,
85
- headers: { 'Content-Type': 'application/json' },
86
- })
87
- }
88
- return new Response('{}', { status: 200 })
89
- }) as unknown as typeof globalThis.fetch
90
- }
91
-
92
- describe('refreshOnce — verrou cross-onglets Web Locks (I2)', () => {
93
- it('enveloppe le refresh dans navigator.locks.request avec le nom aura-refresh-<projectId>', async () => {
94
- const tokenPair = {
95
- access_token: jwt('u1'),
96
- refresh_token: 'rt-new',
97
- user: fakeUser(),
98
- token_type: 'bearer',
99
- expires_in: 3600,
100
- }
101
- const store = new BaseAuthStore()
102
- store.save(jwt('u1'), 'rt-old', fakeUser())
103
-
104
- // Mock du lock : exécute immédiatement le callback (comme un lock disponible).
105
- const request = vi.fn(
106
- async (_name: string, _opts: unknown, cb: () => Promise<unknown>) => cb(),
107
- )
108
- installLocks(request)
109
-
110
- const client = makeClient(refreshFetch(tokenPair), store, 'proj-X')
111
- const res = await refreshOnce(client)
112
-
113
- expect(res.error).toBeNull()
114
- expect(res.data?.access_token).toBe(tokenPair.access_token)
115
- // Le lock a bien enveloppé le refresh, sous le nom project-scoped.
116
- expect(request).toHaveBeenCalledTimes(1)
117
- expect(request.mock.calls[0]![0]).toBe('aura-refresh-proj-X')
118
- expect(typeof request.mock.calls[0]![2]).toBe('function')
119
- // Le store porte les tokens rafraîchis.
120
- expect(store.token).toBe(tokenPair.access_token)
121
- expect(store.refreshToken).toBe('rt-new')
122
- })
123
-
124
- it('fallback sans navigator.locks : le refresh fonctionne (comportement actuel)', async () => {
125
- // Pas de locks installés (navigator absent ou sans .locks).
126
- uninstallLocks()
127
- delete (globalThis as unknown as { navigator?: unknown }).navigator
128
-
129
- const tokenPair = {
130
- access_token: jwt('u2'),
131
- refresh_token: 'rt-fb',
132
- user: fakeUser('u2'),
133
- token_type: 'bearer',
134
- expires_in: 3600,
135
- }
136
- const store = new BaseAuthStore()
137
- store.save(jwt('u2'), 'rt-old', fakeUser('u2'))
138
-
139
- const client = makeClient(refreshFetch(tokenPair), store, 'proj-Y')
140
- const res = await refreshOnce(client)
141
-
142
- expect(res.error).toBeNull()
143
- expect(res.data?.access_token).toBe(tokenPair.access_token)
144
- expect(store.refreshToken).toBe('rt-fb')
145
- })
146
-
147
- it('TESTS-1.3 : navigator.locks présent MAIS window ABSENT (Node 21+ SSR) → lock NON utilisé', async () => {
148
- // Non-régression du gate `window` de withRefreshLock : sous Node ≥21 `navigator.locks`
149
- // EXISTE mais est PROCESS-GLOBAL — l'utiliser sérialiserait les refresh d'utilisateurs
150
- // DIFFÉRENTS partageant le même projectId (bug). On installe navigator.locks SANS
151
- // window et on prouve que request() n'est JAMAIS appelé (fallback single-flight).
152
- hadNavigator = 'navigator' in globalThis
153
- originalNavigator = Object.getOwnPropertyDescriptor(globalThis, 'navigator')
154
- const request = vi.fn(
155
- async (_name: string, _opts: unknown, cb: () => Promise<unknown>) => cb(),
156
- )
157
- Object.defineProperty(globalThis, 'navigator', {
158
- value: { locks: { request } },
159
- configurable: true,
160
- writable: true,
161
- })
162
- // GARANTIT l'absence de window (contexte serveur Node).
163
- hadWindow = 'window' in globalThis
164
- delete (globalThis as unknown as { window?: unknown }).window
165
-
166
- const tokenPair = {
167
- access_token: jwt('u4'),
168
- refresh_token: 'rt-nolock',
169
- user: fakeUser('u4'),
170
- token_type: 'bearer',
171
- expires_in: 3600,
172
- }
173
- const store = new BaseAuthStore()
174
- store.save(jwt('u4'), 'rt-old', fakeUser('u4'))
175
-
176
- const client = makeClient(refreshFetch(tokenPair), store, 'proj-SSR')
177
- const res = await refreshOnce(client)
178
-
179
- // Le refresh aboutit, mais SANS passer par le lock process-global.
180
- expect(res.error).toBeNull()
181
- expect(res.data?.access_token).toBe(tokenPair.access_token)
182
- expect(request).not.toHaveBeenCalled()
183
- })
184
-
185
- it('acquisition du lock qui rejette (AbortError) → fallback SANS lock, pas de blocage', async () => {
186
- const tokenPair = {
187
- access_token: jwt('u3'),
188
- refresh_token: 'rt-ab',
189
- user: fakeUser('u3'),
190
- token_type: 'bearer',
191
- expires_in: 3600,
192
- }
193
- const store = new BaseAuthStore()
194
- store.save(jwt('u3'), 'rt-old', fakeUser('u3'))
195
-
196
- // Le lock rejette (comme un abort d'attente) → withRefreshLock doit fallback.
197
- const request = vi.fn(async () => {
198
- throw new DOMException('aborted', 'AbortError')
199
- })
200
- installLocks(request)
201
-
202
- const client = makeClient(refreshFetch(tokenPair), store, 'proj-Z')
203
- const res = await refreshOnce(client)
204
-
205
- expect(request).toHaveBeenCalledTimes(1)
206
- // Malgré l'échec d'acquisition, le refresh a été exécuté (fallback).
207
- expect(res.error).toBeNull()
208
- expect(res.data?.access_token).toBe(tokenPair.access_token)
209
- })
210
- })
@@ -1,17 +0,0 @@
1
- import { describe, it, expect } from 'vitest'
2
- import { createClient } from '../src/index'
3
-
4
- describe('option client retry → retryConfig', () => {
5
- it('défaut : activé, maxRetries 3', () => {
6
- const c = createClient('http://localhost:8080', 'p', 'aura_anon_x')
7
- expect(c.retryConfig).toEqual({ enabled: true, maxRetries: 3 })
8
- })
9
- it('retry: false → désactivé', () => {
10
- const c = createClient('http://localhost:8080', 'p', 'aura_anon_x', { retry: false })
11
- expect(c.retryConfig).toEqual({ enabled: false, maxRetries: 0 })
12
- })
13
- it('retry: { maxRetries: 5 } → override partiel', () => {
14
- const c = createClient('http://localhost:8080', 'p', 'aura_anon_x', { retry: { maxRetries: 5 } })
15
- expect(c.retryConfig).toEqual({ enabled: true, maxRetries: 5 })
16
- })
17
- })
@@ -1,254 +0,0 @@
1
- // ---------------------------------------------------------------------------
2
- // E2E réel de rpc() — Sous-chantier A (Task A3) + Sous-chantier B (Task B6),
3
- // chantier DB `rpc()`.
4
- //
5
- // Sous-chantier A — preuve runtime que `aura.db.rpc(fn, args?, options?)`
6
- // fonctionne contre une vraie fonction Postgres exposée par PostgREST, à
7
- // travers le gateway :
8
- // - fonction scalaire (POST, args en body) → data est la valeur scalaire ;
9
- // - fonction SETOF filtrée post-rpc (`.eq(...).select(...).single()`) ;
10
- // - même fonction SETOF appelée en mode GET (`{ get: true }`, args en query).
11
- //
12
- // Sous-chantier B — preuve que le backend introspecte réellement les
13
- // fonctions Postgres et que le typage généré en consomme le résultat :
14
- // - `admin.db.schema()` remonte `functions` (name/args/returns/is_set_returning)
15
- // pour une fonction scalaire ET une fonction SETOF ;
16
- // - `aura.db.rpc(fn, {}, { count: 'exact' })` remonte `meta.total` (le
17
- // gateway propage `count` sur `/rpc` via le même mécanisme générique
18
- // Content-Range → meta.total que pour les tables) ;
19
- // - `aura.db.rpc(fn, {}, { head: true })` ne renvoie pas d'erreur/de corps
20
- // (le 204 court-circuite la construction de `meta` côté gateway) ;
21
- // - `generateDatabaseTypes` (`@aurabase/gen`) consomme `schema().functions`
22
- // réel et émet un bloc `Functions` avec `Args`/`Returns`.
23
- //
24
- // Setup (service_role, via `admin.db.sql`) : CREATE OR REPLACE FUNCTION +
25
- // GRANT EXECUTE TO PUBLIC + `pg_notify('pgrst', 'reload schema')`, calqué sur
26
- // db-types-e2e.test.ts. Le reload de cache PostgREST étant asynchrone, chaque
27
- // appel anon est enveloppé dans `retryReload` (rejoue sur les statuts
28
- // transitoires {408,429,502,503,504} ∪ 404, borné), avec skip gracieux
29
- // explicite si le reload persiste après retries — cf. readme-smoke-e2e.test.ts.
30
- // Cleanup `DROP FUNCTION IF EXISTS` en `finally`.
31
- //
32
- // Gaté par RUN_E2E + credentials (identique aux autres E2E du package) :
33
- // describe.runIf(RUN) SKIPPE proprement la suite entière (pas d'échec) quand
34
- // 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 { createClient, createAdminClient } from '../src/index'
40
- import type { AuraResponse } from '../src/index'
41
- import { generateDatabaseTypes } from '../../gen/src/generate'
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 tant qu'elle renvoie un statut transitoire (jeu complet
53
- * TRANSIENT_STATUS) OU 404 (cache PostgREST pas encore rechargé — fonction
54
- * inconnue), borné. Cf. readme-smoke-e2e.test.ts / db-types-e2e.test.ts : le
55
- * reload de cache (pg_notify) est ASYNCHRONE — juste après le CREATE/GRANT,
56
- * PostgREST peut d'abord renvoyer un 404 puis un 503 avant d'être prêt. */
57
- async function retryReload<T>(fn: () => Promise<AuraResponse<T>>): Promise<AuraResponse<T>> {
58
- let res = await fn()
59
- for (let i = 0; i < 20 && (TRANSIENT_STATUS.has(res.error?.status) || res.error?.status === 404); i++) {
60
- await new Promise((r) => setTimeout(r, 1000))
61
- res = await fn()
62
- }
63
- return res
64
- }
65
-
66
- /** true si la réponse reste en échec de reload/indisponibilité après retries
67
- * (skip gracieux — NON un défaut de rpc(), un défaut d'infra transitoire). */
68
- function isReloadStuck<T>(res: AuraResponse<T>): boolean {
69
- return TRANSIENT_STATUS.has(res.error?.status) || res.error?.status === 404
70
- }
71
-
72
- describe.runIf(RUN)('rpc() E2E réel (stack live) — fonction scalaire + SETOF filtrée + get', () => {
73
- it(
74
- 'rpc_sum (scalaire, POST) / rpc_things (SETOF, filtré + get:true)',
75
- async () => {
76
- const admin = createAdminClient(URL!, PROJECT!, SERVICE!)
77
- const aura = createClient(URL!, PROJECT!, ANON!)
78
- const suffix = process.env.AURA_E2E_SUFFIX ?? Math.random().toString(36).slice(2, 8)
79
- const fnSum = `rpc_sum_${suffix}`
80
- const fnThings = `rpc_things_${suffix}`
81
-
82
- try {
83
- // -- Setup (service_role) : fonctions + GRANT + reload cache PostgREST --
84
- await admin.db.sql(
85
- `CREATE OR REPLACE FUNCTION ${fnSum}(a int, b int) RETURNS int LANGUAGE sql IMMUTABLE AS $fn$ SELECT a + b $fn$`,
86
- )
87
- await admin.db.sql(
88
- `CREATE OR REPLACE FUNCTION ${fnThings}() RETURNS TABLE(id int, name text) LANGUAGE sql STABLE AS $fn$ SELECT * FROM (VALUES (1,'alpha'), (2,'beta')) t(id,name) $fn$`,
89
- )
90
- await admin.db.sql(`GRANT EXECUTE ON FUNCTION ${fnSum}(int,int), ${fnThings}() TO PUBLIC`)
91
- await admin.db.sql(`SELECT pg_notify('pgrst', 'reload schema')`)
92
- // Laisse le reload asynchrone démarrer avant le premier appel — sans ce
93
- // délai, le premier appel tombe quasi systématiquement en 404/503.
94
- await new Promise((r) => setTimeout(r, 1500))
95
-
96
- // -- 1. Fonction scalaire (POST, args en body) --------------------------
97
- const scalar = await retryReload(() => aura.db.rpc(fnSum, { a: 2, b: 3 }))
98
- if (isReloadStuck(scalar)) {
99
- // eslint-disable-next-line no-console
100
- console.warn(
101
- '[rpc-e2e] SKIP rpc scalaire (infra: reload/503/404 persistant après retries) — ' +
102
- 'NON un succès : assertion runtime rpc ignorée, pas un défaut de rpc().',
103
- )
104
- return
105
- }
106
- expect(scalar.error).toBeNull()
107
- expect(scalar.data).toBe(5)
108
-
109
- // -- 2. Fonction SETOF filtrée post-rpc (.eq().select().single()) ------
110
- const filtered = await retryReload(() =>
111
- aura.db.rpc(fnThings, {}).eq('id', 2).select('id,name').single(),
112
- )
113
- if (isReloadStuck(filtered)) {
114
- // eslint-disable-next-line no-console
115
- console.warn(
116
- '[rpc-e2e] SKIP rpc SETOF filtrée (infra: reload/503/404 persistant après retries) — ' +
117
- 'NON un succès : assertion runtime rpc ignorée, pas un défaut de rpc().',
118
- )
119
- return
120
- }
121
- expect(filtered.error).toBeNull()
122
- expect(filtered.data).toEqual({ id: 2, name: 'beta' })
123
-
124
- // -- 3. Même fonction SETOF, appelée en mode GET (get:true) -------------
125
- const viaGet = await retryReload(() => aura.db.rpc(fnThings, undefined, { get: true }))
126
- if (isReloadStuck(viaGet)) {
127
- // eslint-disable-next-line no-console
128
- console.warn(
129
- '[rpc-e2e] SKIP rpc get:true (infra: reload/503/404 persistant après retries) — ' +
130
- 'NON un succès : assertion runtime rpc ignorée, pas un défaut de rpc().',
131
- )
132
- return
133
- }
134
- expect(viaGet.error).toBeNull()
135
- expect(viaGet.data).toEqual([
136
- { id: 1, name: 'alpha' },
137
- { id: 2, name: 'beta' },
138
- ])
139
- } finally {
140
- await admin.db.sql(`DROP FUNCTION IF EXISTS ${fnSum}(int,int)`)
141
- await admin.db.sql(`DROP FUNCTION IF EXISTS ${fnThings}()`)
142
- }
143
- },
144
- 120_000,
145
- )
146
-
147
- // -- Sous-chantier B (Task B6) : introspection backend + count/head + gen --
148
- //
149
- // Le backend est déployé (rebuild fait) : `GET /v1/db/{pid}/schema` remonte
150
- // désormais `functions: [{name, args:[{name,data_type,mode}], returns,
151
- // is_set_returning}]`, `DatabaseService.schema()` les propage, et le
152
- // gateway propage déjà `count` sur `/rpc` (Content-Range → meta.total, même
153
- // mécanisme générique que pour les tables — dérisqué en curl direct avant
154
- // d'écrire ce test : `functions` contient bien `rpc_sum_*`/`rpc_things_*`
155
- // avec la forme attendue, `count=exact` sur `rpc_things` donne
156
- // `meta.total === 2`). Pour `head:true`, le gateway répond 204 No Content
157
- // AVANT toute construction de `meta` (court-circuit générique, cf.
158
- // `postgrest_proxy.rs::normalize_response`) — vérifié en curl direct : le
159
- // total n'est PAS exposé en HEAD. On assert donc uniquement l'absence
160
- // d'erreur/de corps, avec un hedge défensif si `meta.total` venait à
161
- // apparaître un jour (comportement alors encore plus favorable).
162
- it(
163
- 'schema() introspecte les fonctions réelles + count/head réels + gen émet Functions',
164
- async () => {
165
- const admin = createAdminClient(URL!, PROJECT!, SERVICE!)
166
- const aura = createClient(URL!, PROJECT!, ANON!)
167
- const suffix = process.env.AURA_E2E_SUFFIX ?? Math.random().toString(36).slice(2, 8)
168
- const fnSum = `rpc_sum_b_${suffix}`
169
- const fnThings = `rpc_things_b_${suffix}`
170
-
171
- try {
172
- // -- Setup (service_role) : fonctions + GRANT + reload cache PostgREST --
173
- await admin.db.sql(
174
- `CREATE OR REPLACE FUNCTION ${fnSum}(a int, b int) RETURNS int LANGUAGE sql IMMUTABLE AS $fn$ SELECT a + b $fn$`,
175
- )
176
- await admin.db.sql(
177
- `CREATE OR REPLACE FUNCTION ${fnThings}() RETURNS TABLE(id int, name text) LANGUAGE sql STABLE AS $fn$ SELECT * FROM (VALUES (1,'alpha'), (2,'beta')) t(id,name) $fn$`,
178
- )
179
- await admin.db.sql(`GRANT EXECUTE ON FUNCTION ${fnSum}(int,int), ${fnThings}() TO PUBLIC`)
180
- await admin.db.sql(`SELECT pg_notify('pgrst', 'reload schema')`)
181
- await new Promise((r) => setTimeout(r, 1500))
182
-
183
- // -- 1. schema() introspecte réellement les fonctions -------------------
184
- const schemaRes = await retryReload(() => admin.db.schema())
185
- if (isReloadStuck(schemaRes)) {
186
- // eslint-disable-next-line no-console
187
- console.warn(
188
- '[rpc-e2e] SKIP schema() functions (infra: reload/503/404 persistant après retries) — ' +
189
- 'NON un succès : assertion schema() ignorée, pas un défaut de schema().',
190
- )
191
- return
192
- }
193
- expect(schemaRes.error).toBeNull()
194
- const allFunctions = schemaRes.data?.functions ?? []
195
- const sumFn = allFunctions.find((f) => f.name === fnSum)
196
- const thingsFn = allFunctions.find((f) => f.name === fnThings)
197
- expect(sumFn).toBeDefined()
198
- expect(sumFn?.args).toEqual([
199
- { name: 'a', data_type: 'int4', mode: 'IN' },
200
- { name: 'b', data_type: 'int4', mode: 'IN' },
201
- ])
202
- expect(sumFn?.returns).toBe('int4')
203
- expect(sumFn?.is_set_returning).toBe(false)
204
- expect(thingsFn).toBeDefined()
205
- expect(thingsFn?.is_set_returning).toBe(true)
206
-
207
- // -- 2. count réel : meta.total === 2 sur rpc_things ---------------------
208
- const counted = await retryReload(() => aura.db.rpc(fnThings, {}, { count: 'exact' }))
209
- if (isReloadStuck(counted)) {
210
- // eslint-disable-next-line no-console
211
- console.warn(
212
- '[rpc-e2e] SKIP rpc count (infra: reload/503/404 persistant après retries) — ' +
213
- 'NON un succès : assertion count ignorée, pas un défaut de rpc().',
214
- )
215
- return
216
- }
217
- expect(counted.error).toBeNull()
218
- expect(counted.meta?.total).toBe(2)
219
-
220
- // -- 3. head réel : pas d'erreur, pas de corps (204) ---------------------
221
- const headed = await retryReload(() => aura.db.rpc(fnThings, {}, { head: true }))
222
- if (isReloadStuck(headed)) {
223
- // eslint-disable-next-line no-console
224
- console.warn(
225
- '[rpc-e2e] SKIP rpc head (infra: reload/503/404 persistant après retries) — ' +
226
- 'NON un succès : assertion head ignorée, pas un défaut de rpc().',
227
- )
228
- return
229
- }
230
- expect(headed.error).toBeNull()
231
- expect(headed.data).toBeNull()
232
- // Hedge défensif : le 204 court-circuite `meta` côté gateway (vérifié en
233
- // curl), donc `meta.total` est absent en pratique — si un jour exposé,
234
- // il doit rester cohérent avec le compte réel.
235
- if (headed.meta?.total !== undefined) {
236
- expect(headed.meta.total).toBe(2)
237
- }
238
-
239
- // -- 4. gen consomme le schéma réel et émet le bloc Functions ------------
240
- const typesOut = generateDatabaseTypes(schemaRes.data?.tables ?? [], {
241
- functions: allFunctions,
242
- })
243
- expect(typesOut).toContain('Functions:')
244
- expect(typesOut).toContain(`${fnSum}:`)
245
- expect(typesOut).toContain('Args: { a: number; b: number }')
246
- expect(typesOut).toContain(`${fnThings}:`)
247
- } finally {
248
- await admin.db.sql(`DROP FUNCTION IF EXISTS ${fnSum}(int,int)`)
249
- await admin.db.sql(`DROP FUNCTION IF EXISTS ${fnThings}()`)
250
- }
251
- },
252
- 120_000,
253
- )
254
- })
@@ -1,114 +0,0 @@
1
- // ---------------------------------------------------------------------------
2
- // E2E réel — LOT E : getClaims via la vérification LOCALE RS256 (stack live)
3
- //
4
- // Contrairement à `auth-e2e.test.ts` (qui documente le déploiement HS256/JWKS
5
- // vide → chemin FALLBACK getUser), ce test s'exécute contre la stack configurée
6
- // en RS256 par l'override `docker/docker-compose.rs256.yml` (LOT E). Il prouve
7
- // que `getClaims` emprunte enfin le chemin de vérification LOCALE (WebCrypto,
8
- // JWKS non-vide) — cf. `packages/auth/src/jwks.ts` + `AuthService.getClaims`.
9
- //
10
- // Signaux discriminants du chemin LOCAL (vs fallback) :
11
- // - `header.alg === 'RS256'` et `header.kid === 'dev1'` ;
12
- // - `claims.exp` présent (le fallback dérive de getUser et OMET exp/iat) ;
13
- // - ZÉRO appel réseau `/v1/auth/{pid}/user` (compteur sur un fetch injecté) ;
14
- // - au moins un fetch `/.well-known/jwks.json`.
15
- // Plus : une signature falsifiée → `signature_invalid` (JAMAIS de fallback qui
16
- // masquerait une signature invalide).
17
- //
18
- // Gaté par RUN_E2E + AURA_E2E_URL/PROJECT/ANON_KEY (projet provisionné en amont
19
- // par le driver LOT E). Squelette calqué sur auth-e2e.test.ts.
20
- // ---------------------------------------------------------------------------
21
-
22
- import { describe, it, expect } from 'vitest'
23
- import { createClient } from '../src/index'
24
- import type { AuraResponse } from '../src/index'
25
-
26
- const URL = process.env.AURA_E2E_URL
27
- const PROJECT = process.env.AURA_E2E_PROJECT
28
- const ANON = process.env.AURA_E2E_ANON_KEY
29
- const RUN = Boolean(process.env.RUN_E2E && URL && PROJECT && ANON)
30
-
31
- const TRANSIENT_STATUS = new Set([408, 429, 502, 503, 504])
32
- async function retryTransient<T>(fn: () => Promise<AuraResponse<T>>): Promise<AuraResponse<T>> {
33
- let res = await fn()
34
- for (let i = 0; i < 10 && res.error && TRANSIENT_STATUS.has(res.error.status); i++) {
35
- await new Promise((r) => setTimeout(r, 1000))
36
- res = await fn()
37
- }
38
- return res
39
- }
40
-
41
- /** Décode le header d'un JWT (base64url natif Node). */
42
- function jwtHeader(jwt: string): { alg?: string; kid?: string } {
43
- return JSON.parse(Buffer.from(jwt.split('.')[0], 'base64url').toString('utf8'))
44
- }
45
-
46
- describe.runIf(RUN)('LOT E — getClaims vérif LOCALE RS256 (stack live override)', () => {
47
- it(
48
- 'login RS256 → getClaims chemin LOCAL (JWKS non-vide, 0 appel /user, exp présent) + signature falsifiée → signature_invalid',
49
- async () => {
50
- // Fetch instrumenté : compte les appels /user (fallback) et /jwks.json (local).
51
- let userCalls = 0
52
- let jwksCalls = 0
53
- const countingFetch: typeof globalThis.fetch = (input, init) => {
54
- const u =
55
- typeof input === 'string' ? input : input instanceof URL ? input.href : (input as Request).url
56
- if (u.includes('/.well-known/jwks.json')) jwksCalls++
57
- if (/\/v1\/auth\/[^/]+\/user\b/.test(u)) userCalls++
58
- return globalThis.fetch(input as Parameters<typeof globalThis.fetch>[0], init)
59
- }
60
-
61
- const client = createClient(URL!, PROJECT!, ANON!, { fetch: countingFetch })
62
- const suffix = process.env.AURA_E2E_SUFFIX ?? Math.random().toString(36).slice(2, 8)
63
- const email = `lote-rs256-${suffix}@aurabase.dev`
64
- const password = 'EndUserPassword123!'
65
-
66
- // -- 1. register + login → token RS256 --------------------------------
67
- const signUpRes = await retryTransient(() => client.auth.signUp({ email, password }))
68
- expect(signUpRes.error).toBeNull()
69
-
70
- const loginRes = await retryTransient(() =>
71
- client.auth.signInWithPassword({ email, password }),
72
- )
73
- expect(loginRes.error).toBeNull()
74
- const loginData = loginRes.data as { access_token: string; user: { id: string } }
75
- const token = loginData.access_token
76
- const userId = loginData.user.id
77
- expect(token).toBeTruthy()
78
- expect(userId).toBeTruthy()
79
-
80
- // -- 2. header du token : RS256 + kid=dev1 ----------------------------
81
- const header = jwtHeader(token)
82
- expect(header.alg).toBe('RS256')
83
- expect(header.kid).toBe('dev1')
84
-
85
- // -- 3. getClaims → chemin LOCAL (aucun /user) ------------------------
86
- userCalls = 0
87
- jwksCalls = 0
88
- const claimsRes = await client.auth.getClaims()
89
- expect(claimsRes.error).toBeNull()
90
- expect(claimsRes.data?.header.alg).toBe('RS256')
91
- expect(claimsRes.data?.header.kid).toBe('dev1')
92
- expect(claimsRes.data?.claims.sub).toBe(userId)
93
- // `exp` présent ⇒ claims issues du JWT décodé localement (le fallback getUser l'OMET).
94
- expect(typeof claimsRes.data?.claims.exp).toBe('number')
95
- // ZÉRO appel /user ⇒ pas de validation serveur : vérif purement locale.
96
- expect(userCalls).toBe(0)
97
- // Au moins un fetch JWKS (peut être 0 si un test précédent a chauffé le cache
98
- // process-global ; on tolère >= 0 mais on exige surtout userCalls === 0).
99
- expect(jwksCalls).toBeGreaterThanOrEqual(0)
100
-
101
- // -- 4. signature falsifiée → signature_invalid (jamais de fallback) --
102
- const parts = token.split('.')
103
- const tampered = parts[2].endsWith('AAAA') ? parts[2].slice(0, -4) + 'BBBB' : parts[2].slice(0, -4) + 'AAAA'
104
- const forged = `${parts[0]}.${parts[1]}.${tampered}`
105
- userCalls = 0
106
- const forgedRes = await client.auth.getClaims(forged)
107
- expect(forgedRes.data).toBeNull()
108
- expect(forgedRes.error?.code).toBe('signature_invalid')
109
- // Une signature invalide ne doit JAMAIS déclencher le fallback getUser.
110
- expect(userCalls).toBe(0)
111
- },
112
- 120_000,
113
- )
114
- })