@aurabase/aurabase-js 0.3.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.
Files changed (344) hide show
  1. package/.changeset/config.json +11 -0
  2. package/CHANGELOG.md +30 -0
  3. package/LICENSE +21 -0
  4. package/README.md +833 -0
  5. package/package.json +90 -0
  6. package/packages/ai/LICENSE +21 -0
  7. package/packages/ai/README.md +106 -0
  8. package/packages/ai/dist/index.cjs +229 -0
  9. package/packages/ai/dist/index.d.cts +139 -0
  10. package/packages/ai/dist/index.d.ts +139 -0
  11. package/packages/ai/dist/index.js +226 -0
  12. package/packages/ai/node_modules/.bin/esbuild +19 -0
  13. package/packages/ai/node_modules/.bin/tsc +22 -0
  14. package/packages/ai/node_modules/.bin/tsserver +22 -0
  15. package/packages/ai/node_modules/.bin/tsup +22 -0
  16. package/packages/ai/node_modules/.bin/tsup-node +22 -0
  17. package/packages/ai/node_modules/.bin/vite +22 -0
  18. package/packages/ai/node_modules/.bin/vitest +22 -0
  19. package/packages/ai/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
  20. package/packages/ai/package.json +61 -0
  21. package/packages/ai/src/AiService.ts +226 -0
  22. package/packages/ai/src/index.ts +18 -0
  23. package/packages/ai/src/sse.ts +141 -0
  24. package/packages/ai/src/types.ts +30 -0
  25. package/packages/ai/tests/chat-stream.test.ts +151 -0
  26. package/packages/ai/tests/rag-namespaces.test.ts +54 -0
  27. package/packages/ai/tests/sse.test.ts +69 -0
  28. package/packages/ai/tsconfig.json +5 -0
  29. package/packages/ai/tsup.config.ts +11 -0
  30. package/packages/aurabase/LICENSE +21 -0
  31. package/packages/aurabase/README.md +64 -0
  32. package/packages/aurabase/dist/AuraClient-DAtDbfPB.d.cts +77 -0
  33. package/packages/aurabase/dist/AuraClient-DAtDbfPB.d.ts +77 -0
  34. package/packages/aurabase/dist/index.cjs +237 -0
  35. package/packages/aurabase/dist/index.d.cts +46 -0
  36. package/packages/aurabase/dist/index.d.ts +46 -0
  37. package/packages/aurabase/dist/index.js +177 -0
  38. package/packages/aurabase/dist/ssr.cjs +224 -0
  39. package/packages/aurabase/dist/ssr.d.cts +65 -0
  40. package/packages/aurabase/dist/ssr.d.ts +65 -0
  41. package/packages/aurabase/dist/ssr.js +214 -0
  42. package/packages/aurabase/node_modules/.bin/esbuild +19 -0
  43. package/packages/aurabase/node_modules/.bin/tsc +22 -0
  44. package/packages/aurabase/node_modules/.bin/tsserver +22 -0
  45. package/packages/aurabase/node_modules/.bin/tsup +22 -0
  46. package/packages/aurabase/node_modules/.bin/tsup-node +22 -0
  47. package/packages/aurabase/node_modules/.bin/vite +22 -0
  48. package/packages/aurabase/node_modules/.bin/vitest +22 -0
  49. package/packages/aurabase/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
  50. package/packages/aurabase/package.json +80 -0
  51. package/packages/aurabase/src/AuraClient.ts +237 -0
  52. package/packages/aurabase/src/index.ts +152 -0
  53. package/packages/aurabase/src/ssr.ts +156 -0
  54. package/packages/aurabase/tests/ai-streaming-rag-e2e.test.ts +82 -0
  55. package/packages/aurabase/tests/ai.test.ts +111 -0
  56. package/packages/aurabase/tests/auth-admin.test.ts +515 -0
  57. package/packages/aurabase/tests/auth-e2e.test.ts +135 -0
  58. package/packages/aurabase/tests/auth-hardening.test.ts +206 -0
  59. package/packages/aurabase/tests/auth.test.ts +947 -0
  60. package/packages/aurabase/tests/database.test.ts +230 -0
  61. package/packages/aurabase/tests/db-types-e2e.test.ts +217 -0
  62. package/packages/aurabase/tests/dispose.test.ts +23 -0
  63. package/packages/aurabase/tests/foreign-token.e2e.test.ts +129 -0
  64. package/packages/aurabase/tests/functions.test.ts +107 -0
  65. package/packages/aurabase/tests/integration.test.ts +413 -0
  66. package/packages/aurabase/tests/live-production-validation.js +241 -0
  67. package/packages/aurabase/tests/live-sdk-production-validation.js +293 -0
  68. package/packages/aurabase/tests/mocks.ts +139 -0
  69. package/packages/aurabase/tests/notifications.test.ts +314 -0
  70. package/packages/aurabase/tests/observability-backup.test.ts +186 -0
  71. package/packages/aurabase/tests/observability-hook.test.ts +130 -0
  72. package/packages/aurabase/tests/production-robustness.test.ts +268 -0
  73. package/packages/aurabase/tests/public-api-surface.test.ts +53 -0
  74. package/packages/aurabase/tests/query-builder.test.ts +329 -0
  75. package/packages/aurabase/tests/readme-smoke-e2e.test.ts +231 -0
  76. package/packages/aurabase/tests/realtime-filter-e2e.test.ts +344 -0
  77. package/packages/aurabase/tests/refresh-locks.test.ts +210 -0
  78. package/packages/aurabase/tests/retry-option.test.ts +17 -0
  79. package/packages/aurabase/tests/rpc-e2e.test.ts +254 -0
  80. package/packages/aurabase/tests/rs256-local-e2e.test.ts +114 -0
  81. package/packages/aurabase/tests/rt-alias-public-e2e.test.ts +235 -0
  82. package/packages/aurabase/tests/rt-http-broadcast-e2e.test.ts +124 -0
  83. package/packages/aurabase/tests/service-role-guard.test.ts +99 -0
  84. package/packages/aurabase/tests/ssr.test.ts +91 -0
  85. package/packages/aurabase/tests/storage-backend-e2e.test.ts +481 -0
  86. package/packages/aurabase/tests/storage-sdk-parity-e2e.test.ts +104 -0
  87. package/packages/aurabase/tests/storage-transform-e2e.test.ts +684 -0
  88. package/packages/aurabase/tests/storage.test.ts +55 -0
  89. package/packages/aurabase/tests/types/client-generic.test-d.ts +79 -0
  90. package/packages/aurabase/tests/types/docs-snippets.test-d.ts +571 -0
  91. package/packages/aurabase/tests/types/error-code.test-d.ts +53 -0
  92. package/packages/aurabase/tsconfig.json +5 -0
  93. package/packages/aurabase/tsconfig.test-d.json +5 -0
  94. package/packages/aurabase/tsup.config.ts +11 -0
  95. package/packages/aurabase/vitest.config.ts +16 -0
  96. package/packages/aurabase/vitest.e2e.config.ts +9 -0
  97. package/packages/auth/LICENSE +21 -0
  98. package/packages/auth/README.md +215 -0
  99. package/packages/auth/dist/index.cjs +1175 -0
  100. package/packages/auth/dist/index.d.cts +397 -0
  101. package/packages/auth/dist/index.d.ts +397 -0
  102. package/packages/auth/dist/index.js +1170 -0
  103. package/packages/auth/node_modules/.bin/esbuild +19 -0
  104. package/packages/auth/node_modules/.bin/tsc +22 -0
  105. package/packages/auth/node_modules/.bin/tsserver +22 -0
  106. package/packages/auth/node_modules/.bin/tsup +22 -0
  107. package/packages/auth/node_modules/.bin/tsup-node +22 -0
  108. package/packages/auth/node_modules/.bin/vite +22 -0
  109. package/packages/auth/node_modules/.bin/vitest +22 -0
  110. package/packages/auth/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
  111. package/packages/auth/package.json +61 -0
  112. package/packages/auth/src/AuthAdminService.ts +149 -0
  113. package/packages/auth/src/AuthService.ts +1166 -0
  114. package/packages/auth/src/SessionManager.ts +92 -0
  115. package/packages/auth/src/index.ts +17 -0
  116. package/packages/auth/src/jwks.ts +337 -0
  117. package/packages/auth/src/pkce.ts +27 -0
  118. package/packages/auth/src/types.ts +89 -0
  119. package/packages/auth/tests/auth-lifecycle.test.ts +149 -0
  120. package/packages/auth/tests/detect-session.test.ts +361 -0
  121. package/packages/auth/tests/getclaims.test.ts +780 -0
  122. package/packages/auth/tests/is-mfa-challenge.test.ts +27 -0
  123. package/packages/auth/tests/password-recovery.test.ts +124 -0
  124. package/packages/auth/tests/pkce.test.ts +192 -0
  125. package/packages/auth/tests/session-manager.test.ts +103 -0
  126. package/packages/auth/tests/set-session.test.ts +139 -0
  127. package/packages/auth/tests/verify-otp.test.ts +153 -0
  128. package/packages/auth/tsconfig.json +5 -0
  129. package/packages/auth/tsup.config.ts +11 -0
  130. package/packages/core/LICENSE +21 -0
  131. package/packages/core/README.md +132 -0
  132. package/packages/core/dist/index.cjs +695 -0
  133. package/packages/core/dist/index.d.cts +1137 -0
  134. package/packages/core/dist/index.d.ts +1137 -0
  135. package/packages/core/dist/index.js +673 -0
  136. package/packages/core/node_modules/.bin/esbuild +22 -0
  137. package/packages/core/node_modules/.bin/tsc +22 -0
  138. package/packages/core/node_modules/.bin/tsserver +22 -0
  139. package/packages/core/node_modules/.bin/tsup +22 -0
  140. package/packages/core/node_modules/.bin/tsup-node +22 -0
  141. package/packages/core/node_modules/.bin/vite +22 -0
  142. package/packages/core/node_modules/.bin/vitest +22 -0
  143. package/packages/core/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
  144. package/packages/core/package.json +57 -0
  145. package/packages/core/src/AuraError.ts +89 -0
  146. package/packages/core/src/BaseService.ts +44 -0
  147. package/packages/core/src/client.ts +34 -0
  148. package/packages/core/src/cookie-utils.ts +42 -0
  149. package/packages/core/src/fetch.ts +435 -0
  150. package/packages/core/src/index.ts +22 -0
  151. package/packages/core/src/retry.ts +63 -0
  152. package/packages/core/src/serialize.ts +6 -0
  153. package/packages/core/src/stores/BaseAuthStore.ts +164 -0
  154. package/packages/core/src/stores/LocalAuthStore.ts +199 -0
  155. package/packages/core/src/types.ts +1042 -0
  156. package/packages/core/tests/auth-store.test.ts +300 -0
  157. package/packages/core/tests/error-code.test.ts +61 -0
  158. package/packages/core/tests/error.test.ts +24 -0
  159. package/packages/core/tests/fetch.test.ts +286 -0
  160. package/packages/core/tests/mocks.ts +139 -0
  161. package/packages/core/tests/observability.test.ts +86 -0
  162. package/packages/core/tests/refresh-hardening.test.ts +164 -0
  163. package/packages/core/tests/refresh-observability.test.ts +111 -0
  164. package/packages/core/tests/retry.test.ts +106 -0
  165. package/packages/core/tests/serialize.test.ts +12 -0
  166. package/packages/core/tests/timeout.test.ts +79 -0
  167. package/packages/core/tsconfig.json +5 -0
  168. package/packages/core/tsup.config.ts +11 -0
  169. package/packages/db/LICENSE +21 -0
  170. package/packages/db/README.md +152 -0
  171. package/packages/db/VENDORED.md +93 -0
  172. package/packages/db/dist/index.cjs +544 -0
  173. package/packages/db/dist/index.d.cts +1111 -0
  174. package/packages/db/dist/index.d.ts +1111 -0
  175. package/packages/db/dist/index.js +541 -0
  176. package/packages/db/node_modules/.bin/esbuild +19 -0
  177. package/packages/db/node_modules/.bin/tsc +22 -0
  178. package/packages/db/node_modules/.bin/tsserver +22 -0
  179. package/packages/db/node_modules/.bin/tsup +22 -0
  180. package/packages/db/node_modules/.bin/tsup-node +22 -0
  181. package/packages/db/node_modules/.bin/vite +22 -0
  182. package/packages/db/node_modules/.bin/vitest +22 -0
  183. package/packages/db/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
  184. package/packages/db/package.json +62 -0
  185. package/packages/db/src/DatabaseService.ts +170 -0
  186. package/packages/db/src/QueryBuilder.ts +830 -0
  187. package/packages/db/src/index.ts +2 -0
  188. package/packages/db/src/pgrst-types/common/common.ts +86 -0
  189. package/packages/db/src/pgrst-types/common/rpc.ts +151 -0
  190. package/packages/db/src/pgrst-types/feature-flags.ts +20 -0
  191. package/packages/db/src/pgrst-types/types.ts +134 -0
  192. package/packages/db/src/select-query-parser/parser.ts +479 -0
  193. package/packages/db/src/select-query-parser/result.ts +553 -0
  194. package/packages/db/src/select-query-parser/types.ts +132 -0
  195. package/packages/db/src/select-query-parser/utils.ts +707 -0
  196. package/packages/db/tests/filters.test.ts +81 -0
  197. package/packages/db/tests/mutations-filtered.e2e.test.ts +132 -0
  198. package/packages/db/tests/mutations-filtered.test.ts +188 -0
  199. package/packages/db/tests/order.test.ts +45 -0
  200. package/packages/db/tests/rpc.test.ts +41 -0
  201. package/packages/db/tests/scale/build-large-schema.ts +94 -0
  202. package/packages/db/tests/scale/scale-compile.test.ts +167 -0
  203. package/packages/db/tests/schema.test.ts +77 -0
  204. package/packages/db/tests/single-accept.e2e.test.ts +123 -0
  205. package/packages/db/tests/single.test.ts +211 -0
  206. package/packages/db/tests/types/filters.test-d.ts +52 -0
  207. package/packages/db/tests/types/fixtures.ts +40 -0
  208. package/packages/db/tests/types/from-views.test-d.ts +18 -0
  209. package/packages/db/tests/types/mutations.test-d.ts +23 -0
  210. package/packages/db/tests/types/rpc.test-d.ts +60 -0
  211. package/packages/db/tests/types/schema-shape.test-d.ts +8 -0
  212. package/packages/db/tests/types/select-inference.test-d.ts +38 -0
  213. package/packages/db/tests/vendored/fixtures.ts +83 -0
  214. package/packages/db/tests/vendored/get-result.test-d.ts +75 -0
  215. package/packages/db/tsconfig.json +5 -0
  216. package/packages/db/tsconfig.test-d.json +5 -0
  217. package/packages/db/tsup.config.ts +11 -0
  218. package/packages/db/vitest.config.ts +8 -0
  219. package/packages/functions/LICENSE +21 -0
  220. package/packages/functions/README.md +96 -0
  221. package/packages/functions/dist/index.cjs +173 -0
  222. package/packages/functions/dist/index.d.cts +81 -0
  223. package/packages/functions/dist/index.d.ts +81 -0
  224. package/packages/functions/dist/index.js +168 -0
  225. package/packages/functions/node_modules/.bin/esbuild +19 -0
  226. package/packages/functions/node_modules/.bin/tsc +22 -0
  227. package/packages/functions/node_modules/.bin/tsserver +22 -0
  228. package/packages/functions/node_modules/.bin/tsup +22 -0
  229. package/packages/functions/node_modules/.bin/tsup-node +22 -0
  230. package/packages/functions/node_modules/.bin/vite +22 -0
  231. package/packages/functions/node_modules/.bin/vitest +22 -0
  232. package/packages/functions/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
  233. package/packages/functions/package.json +60 -0
  234. package/packages/functions/src/FunctionsService.ts +188 -0
  235. package/packages/functions/src/errors.ts +70 -0
  236. package/packages/functions/src/index.ts +6 -0
  237. package/packages/functions/tests/invoke.test.ts +97 -0
  238. package/packages/functions/tests/response-type-errors.test.ts +135 -0
  239. package/packages/functions/tsconfig.json +5 -0
  240. package/packages/functions/tsup.config.ts +11 -0
  241. package/packages/gen/LICENSE +21 -0
  242. package/packages/gen/README.md +84 -0
  243. package/packages/gen/dist/cli.cjs +334 -0
  244. package/packages/gen/dist/cli.d.cts +1 -0
  245. package/packages/gen/dist/cli.d.ts +1 -0
  246. package/packages/gen/dist/cli.js +332 -0
  247. package/packages/gen/dist/index.cjs +257 -0
  248. package/packages/gen/dist/index.d.cts +12 -0
  249. package/packages/gen/dist/index.d.ts +12 -0
  250. package/packages/gen/dist/index.js +255 -0
  251. package/packages/gen/node_modules/.bin/esbuild +19 -0
  252. package/packages/gen/node_modules/.bin/tsc +22 -0
  253. package/packages/gen/node_modules/.bin/tsserver +22 -0
  254. package/packages/gen/node_modules/.bin/tsup +22 -0
  255. package/packages/gen/node_modules/.bin/tsup-node +22 -0
  256. package/packages/gen/node_modules/.bin/vite +22 -0
  257. package/packages/gen/node_modules/.bin/vitest +22 -0
  258. package/packages/gen/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
  259. package/packages/gen/package.json +66 -0
  260. package/packages/gen/src/cli.ts +67 -0
  261. package/packages/gen/src/generate.ts +0 -0
  262. package/packages/gen/src/index.ts +2 -0
  263. package/packages/gen/tests/compile.test.ts +213 -0
  264. package/packages/gen/tests/e2e.test.ts +130 -0
  265. package/packages/gen/tests/generate.test.ts +396 -0
  266. package/packages/gen/tsconfig.json +5 -0
  267. package/packages/gen/tsup.config.ts +11 -0
  268. package/packages/notifications/LICENSE +21 -0
  269. package/packages/notifications/README.md +134 -0
  270. package/packages/notifications/dist/index.cjs +128 -0
  271. package/packages/notifications/dist/index.d.cts +81 -0
  272. package/packages/notifications/dist/index.d.ts +81 -0
  273. package/packages/notifications/dist/index.js +125 -0
  274. package/packages/notifications/node_modules/.bin/esbuild +19 -0
  275. package/packages/notifications/node_modules/.bin/tsc +22 -0
  276. package/packages/notifications/node_modules/.bin/tsserver +22 -0
  277. package/packages/notifications/node_modules/.bin/tsup +22 -0
  278. package/packages/notifications/node_modules/.bin/tsup-node +22 -0
  279. package/packages/notifications/node_modules/.bin/vite +22 -0
  280. package/packages/notifications/node_modules/.bin/vitest +22 -0
  281. package/packages/notifications/package.json +60 -0
  282. package/packages/notifications/src/NotificationsService.ts +230 -0
  283. package/packages/notifications/src/index.ts +19 -0
  284. package/packages/notifications/tsconfig.json +5 -0
  285. package/packages/notifications/tsup.config.ts +11 -0
  286. package/packages/realtime/LICENSE +21 -0
  287. package/packages/realtime/README.md +183 -0
  288. package/packages/realtime/dist/index.cjs +977 -0
  289. package/packages/realtime/dist/index.d.cts +259 -0
  290. package/packages/realtime/dist/index.d.ts +259 -0
  291. package/packages/realtime/dist/index.js +973 -0
  292. package/packages/realtime/node_modules/.bin/esbuild +19 -0
  293. package/packages/realtime/node_modules/.bin/tsc +22 -0
  294. package/packages/realtime/node_modules/.bin/tsserver +22 -0
  295. package/packages/realtime/node_modules/.bin/tsup +22 -0
  296. package/packages/realtime/node_modules/.bin/tsup-node +22 -0
  297. package/packages/realtime/node_modules/.bin/vite +22 -0
  298. package/packages/realtime/node_modules/.bin/vitest +22 -0
  299. package/packages/realtime/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
  300. package/packages/realtime/package.json +60 -0
  301. package/packages/realtime/src/AuraChannel.ts +485 -0
  302. package/packages/realtime/src/HeartbeatManager.ts +73 -0
  303. package/packages/realtime/src/RealtimeService.ts +505 -0
  304. package/packages/realtime/src/columnFilter.ts +301 -0
  305. package/packages/realtime/src/index.ts +4 -0
  306. package/packages/realtime/tests/ack-routing.test.ts +81 -0
  307. package/packages/realtime/tests/column-filter.test.ts +144 -0
  308. package/packages/realtime/tests/dispatch.test.ts +191 -0
  309. package/packages/realtime/tests/heartbeat.test.ts +67 -0
  310. package/packages/realtime/tests/http-broadcast.test.ts +68 -0
  311. package/packages/realtime/tests/realtime-token.test.ts +43 -0
  312. package/packages/realtime/tests/reconnect-race.test.ts +149 -0
  313. package/packages/realtime/tests/ref-id.test.ts +34 -0
  314. package/packages/realtime/tests/set-auth.test.ts +70 -0
  315. package/packages/realtime/tsconfig.json +5 -0
  316. package/packages/realtime/tsup.config.ts +11 -0
  317. package/packages/storage/LICENSE +21 -0
  318. package/packages/storage/README.md +141 -0
  319. package/packages/storage/dist/index.cjs +333 -0
  320. package/packages/storage/dist/index.d.cts +185 -0
  321. package/packages/storage/dist/index.d.ts +185 -0
  322. package/packages/storage/dist/index.js +329 -0
  323. package/packages/storage/node_modules/.bin/esbuild +19 -0
  324. package/packages/storage/node_modules/.bin/tsc +22 -0
  325. package/packages/storage/node_modules/.bin/tsserver +22 -0
  326. package/packages/storage/node_modules/.bin/tsup +22 -0
  327. package/packages/storage/node_modules/.bin/tsup-node +22 -0
  328. package/packages/storage/node_modules/.bin/vite +22 -0
  329. package/packages/storage/node_modules/.bin/vitest +22 -0
  330. package/packages/storage/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
  331. package/packages/storage/package.json +60 -0
  332. package/packages/storage/src/DownloadBuilder.ts +60 -0
  333. package/packages/storage/src/StorageService.ts +323 -0
  334. package/packages/storage/src/index.ts +2 -0
  335. package/packages/storage/src/path.ts +35 -0
  336. package/packages/storage/src/transform.ts +15 -0
  337. package/packages/storage/tests/download.test.ts +64 -0
  338. package/packages/storage/tests/parity.test.ts +154 -0
  339. package/packages/storage/tests/path.test.ts +87 -0
  340. package/packages/storage/tests/transform.test.ts +79 -0
  341. package/packages/storage/tsconfig.json +5 -0
  342. package/packages/storage/tsup.config.ts +11 -0
  343. package/pnpm-workspace.yaml +24 -0
  344. package/tsconfig.base.json +15 -0
@@ -0,0 +1,314 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest'
2
+ import { AuraClient } from '../src/AuraClient'
3
+ import { FetchMock, fakeTokenPair } from './mocks'
4
+
5
+ const BASE = 'http://localhost:8080'
6
+ const PROJECT = '00000000-0000-0000-0000-000000000001'
7
+ const PREFIX = `${BASE}/v1/notifications/${PROJECT}`
8
+
9
+ let fetchMock: FetchMock
10
+ let client: AuraClient
11
+
12
+ beforeEach(() => {
13
+ fetchMock = new FetchMock()
14
+ fetchMock.init()
15
+ client = new AuraClient(BASE, PROJECT, 'aura_anon_test', {
16
+ auth: { persistSession: false },
17
+ })
18
+ const tp = fakeTokenPair()
19
+ client.authStore.save(tp.access_token, tp.refresh_token, tp.user)
20
+ })
21
+
22
+ afterEach(() => {
23
+ fetchMock.restore()
24
+ fetchMock.clearMocks()
25
+ })
26
+
27
+ describe('NotificationsService route contract', () => {
28
+ it('sends notifications to /send with backend payload names', async () => {
29
+ fetchMock.on({
30
+ method: 'POST',
31
+ url: `${PREFIX}/send`,
32
+ replyCode: 202,
33
+ replyBody: { data: { id: 'notif-1', status: 'queued' } },
34
+ })
35
+
36
+ await client.notifications.send('email', 'user@example.com', 'Hello', {
37
+ subject: 'Hi',
38
+ templateId: 'tpl-1',
39
+ variables: { name: 'Ada' },
40
+ metadata: { campaign: 'welcome' },
41
+ idempotencyKey: 'notif-key-1',
42
+ })
43
+
44
+ expect(fetchMock.calls[0].body).toEqual({
45
+ channel: 'email',
46
+ recipient: 'user@example.com',
47
+ body: 'Hello',
48
+ subject: 'Hi',
49
+ template_id: 'tpl-1',
50
+ variables: { name: 'Ada' },
51
+ metadata: { campaign: 'welcome' },
52
+ idempotency_key: 'notif-key-1',
53
+ })
54
+ })
55
+
56
+ it('queues notification batches', async () => {
57
+ fetchMock.on({
58
+ method: 'POST',
59
+ url: `${PREFIX}/send/batch`,
60
+ replyCode: 202,
61
+ replyBody: { data: { items: [{ index: 0, status: 'queued' }] } },
62
+ })
63
+
64
+ await client.notifications.sendBatch([
65
+ {
66
+ channel: 'email',
67
+ recipient: 'user@example.com',
68
+ subject: 'Hi',
69
+ body: 'Hello',
70
+ idempotency_key: 'batch-1',
71
+ },
72
+ ])
73
+
74
+ expect(fetchMock.calls[0].body).toEqual({
75
+ items: [
76
+ {
77
+ channel: 'email',
78
+ recipient: 'user@example.com',
79
+ subject: 'Hi',
80
+ body: 'Hello',
81
+ idempotency_key: 'batch-1',
82
+ },
83
+ ],
84
+ })
85
+ })
86
+
87
+ it('lists and gets notifications using backend routes', async () => {
88
+ fetchMock.on({
89
+ method: 'GET',
90
+ url: `${PREFIX}?limit=10&offset=5`,
91
+ replyCode: 200,
92
+ replyBody: { data: [] },
93
+ })
94
+ fetchMock.on({
95
+ method: 'GET',
96
+ url: `${PREFIX}/notif-1`,
97
+ replyCode: 200,
98
+ replyBody: { data: { id: 'notif-1' } },
99
+ })
100
+
101
+ await client.notifications.list({ limit: 10, offset: 5 })
102
+ await client.notifications.get('notif-1')
103
+
104
+ expect(fetchMock.calls[0].url).toBe(`${PREFIX}?limit=10&offset=5`)
105
+ expect(fetchMock.calls[1].url).toBe(`${PREFIX}/notif-1`)
106
+ })
107
+
108
+ it('uses template routes including PATCH update', async () => {
109
+ fetchMock.on({
110
+ method: 'GET',
111
+ url: `${PREFIX}/templates`,
112
+ replyCode: 200,
113
+ replyBody: { data: [] },
114
+ })
115
+ fetchMock.on({
116
+ method: 'GET',
117
+ url: `${PREFIX}/templates/tpl-1`,
118
+ replyCode: 200,
119
+ replyBody: { data: { id: 'tpl-1' } },
120
+ })
121
+ fetchMock.on({
122
+ method: 'POST',
123
+ url: `${PREFIX}/templates`,
124
+ replyCode: 200,
125
+ replyBody: { data: { id: 'tpl-1' } },
126
+ })
127
+ fetchMock.on({
128
+ method: 'PATCH',
129
+ url: `${PREFIX}/templates/tpl-1`,
130
+ replyCode: 200,
131
+ replyBody: { data: { id: 'tpl-1' } },
132
+ })
133
+ fetchMock.on({
134
+ method: 'DELETE',
135
+ url: `${PREFIX}/templates/tpl-1`,
136
+ replyCode: 204,
137
+ })
138
+
139
+ await client.notifications.listTemplates()
140
+ await client.notifications.getTemplate('tpl-1')
141
+ await client.notifications.createTemplate({
142
+ name: 'Welcome',
143
+ channel: 'email',
144
+ subject: 'Hi',
145
+ body: 'Hello',
146
+ variables: ['name'],
147
+ })
148
+ await client.notifications.updateTemplate('tpl-1', { body: 'Updated' })
149
+ await client.notifications.deleteTemplate('tpl-1')
150
+
151
+ expect(fetchMock.calls.map((call) => call.method)).toEqual([
152
+ 'GET',
153
+ 'GET',
154
+ 'POST',
155
+ 'PATCH',
156
+ 'DELETE',
157
+ ])
158
+ expect(fetchMock.calls[3].body).toEqual({ body: 'Updated' })
159
+ })
160
+
161
+ it('exposes admin template preview and observability routes', async () => {
162
+ fetchMock.on({
163
+ method: 'POST',
164
+ url: `${PREFIX}/templates/tpl-1/preview`,
165
+ replyCode: 200,
166
+ replyBody: { data: { subject: 'Hi Ada', body: 'Hello Ada' } },
167
+ })
168
+ fetchMock.on({
169
+ method: 'GET',
170
+ url: `${PREFIX}/settings`,
171
+ replyCode: 200,
172
+ replyBody: { data: { project_id: PROJECT } },
173
+ })
174
+ fetchMock.on({
175
+ method: 'PATCH',
176
+ url: `${PREFIX}/settings`,
177
+ replyCode: 200,
178
+ replyBody: { data: { project_id: PROJECT } },
179
+ })
180
+ fetchMock.on({
181
+ method: 'GET',
182
+ url: `${PREFIX}/stats?window=24h&channel=email`,
183
+ replyCode: 200,
184
+ replyBody: { data: { total: 0 } },
185
+ })
186
+ fetchMock.on({
187
+ method: 'GET',
188
+ url: `${PREFIX}/queue`,
189
+ replyCode: 200,
190
+ replyBody: { data: { queued_count: 0 } },
191
+ })
192
+ fetchMock.on({
193
+ method: 'GET',
194
+ url: `${PREFIX}/notif-1/attempts`,
195
+ replyCode: 200,
196
+ replyBody: { data: [] },
197
+ })
198
+ fetchMock.on({
199
+ method: 'GET',
200
+ url: `${PREFIX}/dead-letters?channel=email&limit=5&offset=0`,
201
+ replyCode: 200,
202
+ replyBody: { data: [] },
203
+ })
204
+ fetchMock.on({
205
+ method: 'POST',
206
+ url: `${PREFIX}/dead-letters/dlq-1/retry`,
207
+ replyCode: 202,
208
+ replyBody: { data: { id: 'notif-1' } },
209
+ })
210
+ fetchMock.on({
211
+ method: 'GET',
212
+ url: `${PREFIX}/events?status=failed&limit=10`,
213
+ replyCode: 200,
214
+ replyBody: { data: [] },
215
+ })
216
+ fetchMock.on({
217
+ method: 'GET',
218
+ url: `${PREFIX}/providers/logs?status=sent&limit=10`,
219
+ replyCode: 200,
220
+ replyBody: { data: [] },
221
+ })
222
+
223
+ await client.notifications.admin.previewTemplate('tpl-1', { name: 'Ada' })
224
+ await client.notifications.admin.getSettings()
225
+ await client.notifications.admin.updateSettings({ webhook: { timeout_secs: 2 } })
226
+ await client.notifications.admin.getStats({ window: '24h', channel: 'email' })
227
+ await client.notifications.admin.getQueue()
228
+ await client.notifications.admin.listAttempts('notif-1')
229
+ await client.notifications.admin.listDeadLetters({ channel: 'email', limit: 5, offset: 0 })
230
+ await client.notifications.admin.retryDeadLetter('dlq-1')
231
+ await client.notifications.admin.listEvents({ status: 'failed', limit: 10 })
232
+ await client.notifications.admin.listProviderLogs({ status: 'sent', limit: 10 })
233
+
234
+ expect(fetchMock.calls.map((call) => call.url)).toEqual([
235
+ `${PREFIX}/templates/tpl-1/preview`,
236
+ `${PREFIX}/settings`,
237
+ `${PREFIX}/settings`,
238
+ `${PREFIX}/stats?window=24h&channel=email`,
239
+ `${PREFIX}/queue`,
240
+ `${PREFIX}/notif-1/attempts`,
241
+ `${PREFIX}/dead-letters?channel=email&limit=5&offset=0`,
242
+ `${PREFIX}/dead-letters/dlq-1/retry`,
243
+ `${PREFIX}/events?status=failed&limit=10`,
244
+ `${PREFIX}/providers/logs?status=sent&limit=10`,
245
+ ])
246
+ expect(fetchMock.calls[0].body).toEqual({ name: 'Ada' })
247
+ expect(fetchMock.calls[2].body).toEqual({ webhook: { timeout_secs: 2 } })
248
+ })
249
+
250
+ it('exposes admin rules routes', async () => {
251
+ fetchMock.on({
252
+ method: 'GET',
253
+ url: `${PREFIX}/rules`,
254
+ replyCode: 200,
255
+ replyBody: { data: [] },
256
+ })
257
+ fetchMock.on({
258
+ method: 'POST',
259
+ url: `${PREFIX}/rules`,
260
+ replyCode: 200,
261
+ replyBody: { data: { id: 'rule-1' } },
262
+ })
263
+ fetchMock.on({
264
+ method: 'GET',
265
+ url: `${PREFIX}/rules/rule-1`,
266
+ replyCode: 200,
267
+ replyBody: { data: { id: 'rule-1' } },
268
+ })
269
+ fetchMock.on({
270
+ method: 'PATCH',
271
+ url: `${PREFIX}/rules/rule-1`,
272
+ replyCode: 200,
273
+ replyBody: { data: { id: 'rule-1' } },
274
+ })
275
+ fetchMock.on({
276
+ method: 'DELETE',
277
+ url: `${PREFIX}/rules/rule-1`,
278
+ replyCode: 200,
279
+ replyBody: { data: null },
280
+ })
281
+
282
+ await client.notifications.admin.listRules()
283
+ await client.notifications.admin.createRule({
284
+ name: 'User created',
285
+ trigger_type: 'auth.user.created',
286
+ condition: {},
287
+ template_id: 'tpl-1',
288
+ channel: 'email',
289
+ recipient_expr: 'email',
290
+ delay_seconds: 0,
291
+ })
292
+ await client.notifications.admin.getRule('rule-1')
293
+ await client.notifications.admin.updateRule('rule-1', { enabled: false })
294
+ await client.notifications.admin.deleteRule('rule-1')
295
+
296
+ expect(fetchMock.calls.map((call) => call.method)).toEqual([
297
+ 'GET',
298
+ 'POST',
299
+ 'GET',
300
+ 'PATCH',
301
+ 'DELETE',
302
+ ])
303
+ expect(fetchMock.calls[1].body).toEqual({
304
+ name: 'User created',
305
+ trigger_type: 'auth.user.created',
306
+ condition: {},
307
+ template_id: 'tpl-1',
308
+ channel: 'email',
309
+ recipient_expr: 'email',
310
+ delay_seconds: 0,
311
+ })
312
+ expect(fetchMock.calls[3].body).toEqual({ enabled: false })
313
+ })
314
+ })
@@ -0,0 +1,186 @@
1
+ import { describe, it, expect, beforeAll, afterAll } from 'vitest'
2
+
3
+ const GATEWAY_URL = 'http://localhost:8080'
4
+ const MGMT_URL = 'http://localhost:8090'
5
+
6
+ describe('Tests d\'intégration Observabilité et Backups Database (E2E)', () => {
7
+ let devToken: string
8
+ let projectId: string
9
+ let anonKey: string
10
+
11
+ const runId = Math.random().toString(36).substring(2, 10)
12
+ const devEmail = `observability-dev-${runId}@aurabase.dev`
13
+ const devPassword = 'DevPasswordE2E123!'
14
+ const projectName = `observability-proj-${runId}`
15
+
16
+ beforeAll(async () => {
17
+ // 1. Enregistrement développeur
18
+ const regRes = await fetch(`${MGMT_URL}/v1/control/accounts/register`, {
19
+ method: 'POST',
20
+ headers: { 'Content-Type': 'application/json' },
21
+ body: JSON.stringify({ email: devEmail, password: devPassword }),
22
+ })
23
+ expect(regRes.status).toBe(200)
24
+ const regData = await regRes.json()
25
+ devToken = regData.data.access_token
26
+
27
+ // 2. Création organisation
28
+ const orgRes = await fetch(`${MGMT_URL}/v1/control/orgs`, {
29
+ method: 'POST',
30
+ headers: {
31
+ 'Authorization': `Bearer ${devToken}`,
32
+ 'Content-Type': 'application/json',
33
+ },
34
+ body: JSON.stringify({ name: 'Observability Org', slug: `obs-org-${runId}` }),
35
+ })
36
+ expect(orgRes.status).toBeLessThan(300)
37
+ const orgData = await orgRes.json()
38
+ const orgId = orgData.id
39
+
40
+ // 3. Création projet
41
+ const projRes = await fetch(`${MGMT_URL}/v1/control/orgs/${orgId}/projects`, {
42
+ method: 'POST',
43
+ headers: {
44
+ 'Authorization': `Bearer ${devToken}`,
45
+ 'Content-Type': 'application/json',
46
+ },
47
+ body: JSON.stringify({ name: projectName, engine: 'postgres', region: 'eu-west-1' }),
48
+ })
49
+ expect(projRes.status).toBe(202)
50
+ const projData = await projRes.json()
51
+ projectId = projData.id
52
+
53
+ // Attente du provisioning
54
+ let active = false
55
+ for (let i = 0; i < 50; i++) {
56
+ const statusRes = await fetch(`${MGMT_URL}/v1/control/projects/${projectId}`, {
57
+ headers: { 'Authorization': `Bearer ${devToken}` },
58
+ })
59
+ const statusData = await statusRes.json()
60
+ if (statusData && statusData.status === 'active') {
61
+ active = true
62
+ break
63
+ }
64
+ await new Promise((resolve) => setTimeout(resolve, 1500))
65
+ }
66
+ expect(active).toBe(true)
67
+
68
+ // Récupération clés API
69
+ const keysRes = await fetch(`${MGMT_URL}/v1/control/projects/${projectId}/initial-keys`, {
70
+ headers: { 'Authorization': `Bearer ${devToken}` },
71
+ })
72
+ expect(keysRes.status).toBe(200)
73
+ const keysData = await keysRes.json()
74
+ const keys = keysData.data || keysData
75
+ anonKey = keys.find((k: any) => k.scope === 'anon').key
76
+ expect(anonKey).toBeDefined()
77
+ }, 50000)
78
+
79
+ afterAll(async () => {
80
+ // Suppression du projet de test
81
+ if (projectId && devToken) {
82
+ const delRes = await fetch(`${MGMT_URL}/v1/control/projects/${projectId}`, {
83
+ method: 'DELETE',
84
+ headers: { 'Authorization': `Bearer ${devToken}` },
85
+ })
86
+ expect(delRes.status).toBe(204)
87
+ }
88
+ })
89
+
90
+ it('devrait interroger les APIs d\'observabilité (db-stats, active-queries, slow-queries) avec succès', async () => {
91
+ // 1. Test des statistiques de la base de données
92
+ const statsRes = await fetch(`${MGMT_URL}/v1/db/${projectId}/observability/db-stats`, {
93
+ headers: { 'Authorization': `Bearer ${devToken}` }
94
+ })
95
+ expect(statsRes.status).toBe(200)
96
+ const stats = await statsRes.json()
97
+ expect(stats.data).toBeDefined()
98
+ expect(stats.data.active_connections).toBeGreaterThanOrEqual(0)
99
+ expect(stats.data.max_connections).toBeGreaterThan(0)
100
+ expect(stats.data.db_size_bytes).toBeGreaterThan(0)
101
+
102
+ // 2. Test des processus SQL actifs
103
+ const activeRes = await fetch(`${MGMT_URL}/v1/db/${projectId}/observability/active-queries`, {
104
+ headers: { 'Authorization': `Bearer ${devToken}` }
105
+ })
106
+ expect(activeRes.status).toBe(200)
107
+ const activeQueries = await activeRes.json()
108
+ expect(activeQueries.data).toBeInstanceOf(Array)
109
+
110
+ // 3. Test des slow-queries (pg_stat_statements)
111
+ const slowRes = await fetch(`${MGMT_URL}/v1/db/${projectId}/observability/slow-queries`, {
112
+ headers: { 'Authorization': `Bearer ${devToken}` }
113
+ })
114
+ expect(slowRes.status).toBe(200)
115
+ const slowQueries = await slowRes.json()
116
+ expect(slowQueries.data).toBeDefined()
117
+ expect(slowQueries.data.extension_available).toBeDefined()
118
+ })
119
+
120
+ it('devrait créer, lister, télécharger, restaurer et supprimer un backup logique SQL avec succès', async () => {
121
+ const backupLabel = `backup_e2e_${runId}`
122
+
123
+ // 1. Création d'un backup
124
+ const createRes = await fetch(`${MGMT_URL}/v1/db/${projectId}/backups`, {
125
+ method: 'POST',
126
+ headers: {
127
+ 'Authorization': `Bearer ${devToken}`,
128
+ 'Content-Type': 'application/json',
129
+ },
130
+ body: JSON.stringify({ label: backupLabel })
131
+ })
132
+ if (createRes.status !== 200) {
133
+ console.log('CREATE BACKUP ERROR STATUS:', createRes.status, 'BODY:', await createRes.text())
134
+ }
135
+ expect(createRes.status).toBe(200)
136
+ const createData = await createRes.json()
137
+ expect(createData.data).toBeDefined()
138
+ const backupId = createData.data.id
139
+ expect(backupId).toBeDefined()
140
+ expect(createData.data.label).toBe(backupLabel)
141
+
142
+ // 2. Liste des backups
143
+ const listRes = await fetch(`${MGMT_URL}/v1/db/${projectId}/backups`, {
144
+ headers: { 'Authorization': `Bearer ${devToken}` }
145
+ })
146
+ expect(listRes.status).toBe(200)
147
+ const listData = await listRes.json()
148
+ expect(listData.data).toBeInstanceOf(Array)
149
+ const found = listData.data.find((b: any) => b.id === backupId)
150
+ expect(found).toBeDefined()
151
+ expect(found.label).toBe(backupLabel)
152
+
153
+ // 3. Téléchargement du dump SQL
154
+ const getRes = await fetch(`${MGMT_URL}/v1/db/${projectId}/backups/${backupId}`, {
155
+ headers: { 'Authorization': `Bearer ${devToken}` }
156
+ })
157
+ expect(getRes.status).toBe(200)
158
+ const getData = await getRes.json()
159
+ expect(getData.data).toBeDefined()
160
+ expect(getData.data.sql_dump).toBeDefined()
161
+ expect(getData.data.sql_dump).toContain('Aurabase backup')
162
+ expect(getData.data.sql_dump).toContain(projectId)
163
+
164
+ // 4. Restauration du backup
165
+ const restoreRes = await fetch(`${MGMT_URL}/v1/db/${projectId}/backups/${backupId}/restore`, {
166
+ method: 'POST',
167
+ headers: { 'Authorization': `Bearer ${devToken}` }
168
+ })
169
+ expect(restoreRes.status).toBe(200)
170
+
171
+ // 5. Suppression du backup
172
+ const delRes = await fetch(`${MGMT_URL}/v1/db/${projectId}/backups/${backupId}`, {
173
+ method: 'DELETE',
174
+ headers: { 'Authorization': `Bearer ${devToken}` }
175
+ })
176
+ expect(delRes.status).toBe(200)
177
+
178
+ // 6. Vérification de la suppression de la liste
179
+ const listPostDelRes = await fetch(`${MGMT_URL}/v1/db/${projectId}/backups`, {
180
+ headers: { 'Authorization': `Bearer ${devToken}` }
181
+ })
182
+ const listPostDelData = await listPostDelRes.json()
183
+ const stillExists = listPostDelData.data.some((b: any) => b.id === backupId)
184
+ expect(stillExists).toBe(false)
185
+ })
186
+ })
@@ -0,0 +1,130 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Câblage bout-en-bout du hook d'observabilité (LOT 3, finding I8) :
3
+ // AuraClientOptions.onEvent/debug -> AuraClient -> fetchWithAuth / LocalAuthStore.
4
+ // Valide que le threading ajouté dans AuraClient.ts (hors périmètre strict du lot,
5
+ // mais indispensable — voir rapport) fonctionne réellement de bout en bout, pas
6
+ // seulement au niveau unitaire de `@aurabase/core`.
7
+ // ---------------------------------------------------------------------------
8
+
9
+ import { describe, it, expect, vi, afterEach } from 'vitest'
10
+ import { AuraClient } from '../src/AuraClient'
11
+ import { LocalAuthStore } from '@aurabase/core'
12
+ import type { AuraObservabilityEvent } from '@aurabase/core'
13
+
14
+ afterEach(() => {
15
+ vi.useRealTimers()
16
+ delete (globalThis as { localStorage?: unknown }).localStorage
17
+ })
18
+
19
+ /** fetch séquentiel minimal (mêmes réponses dans l'ordre, quel que soit le call count). */
20
+ function seqFetch(responses: Array<{ status: number; body?: unknown }>): typeof globalThis.fetch {
21
+ let i = 0
22
+ return (async () => {
23
+ const r = responses[Math.min(i, responses.length - 1)]
24
+ i++
25
+ return new Response(r.body !== undefined ? JSON.stringify(r.body) : null, {
26
+ status: r.status,
27
+ headers: { 'Content-Type': 'application/json' },
28
+ })
29
+ }) as unknown as typeof globalThis.fetch
30
+ }
31
+
32
+ describe('AuraClientOptions.onEvent/debug — câblage bout-en-bout', () => {
33
+ it('un retry transitoire réel (via client.auth.getSettings()) émet {kind:"retry"}', async () => {
34
+ vi.useFakeTimers()
35
+ const events: AuraObservabilityEvent[] = []
36
+ const fetchFn = seqFetch([
37
+ { status: 503 },
38
+ { status: 200, body: { data: { disable_signup: false, external: {}, mailer_autoconfirm: true, mfa_enabled: false } } },
39
+ ])
40
+ const client = new AuraClient('http://localhost:8080', 'proj-1', 'aura_anon_x', {
41
+ fetch: fetchFn,
42
+ onEvent: (e) => events.push(e),
43
+ auth: { persistSession: false },
44
+ })
45
+
46
+ const p = client.auth.getSettings()
47
+ await vi.advanceTimersByTimeAsync(600)
48
+ const res = await p
49
+
50
+ expect(res.error).toBeNull()
51
+ expect(events).toContainEqual(
52
+ expect.objectContaining({ kind: 'retry', status: 503 }),
53
+ )
54
+ })
55
+
56
+ it('debug:true journalise les événements via console.debug', async () => {
57
+ const spy = vi.spyOn(console, 'debug').mockImplementation(() => {})
58
+ vi.useFakeTimers()
59
+ const fetchFn = seqFetch([{ status: 503 }, { status: 200, body: { data: {} } }])
60
+ const client = new AuraClient('http://localhost:8080', 'proj-1', 'aura_anon_x', {
61
+ fetch: fetchFn,
62
+ debug: true,
63
+ auth: { persistSession: false },
64
+ })
65
+
66
+ const p = client.auth.getSettings()
67
+ await vi.advanceTimersByTimeAsync(600)
68
+ await p
69
+
70
+ expect(spy).toHaveBeenCalled()
71
+ spy.mockRestore()
72
+ })
73
+
74
+ it('un store custom (LocalAuthStore fourni via auth.storage) émet storage-blocked via le client', () => {
75
+ // localStorage cassé (quota/navigation privée) — même mock que auth-store.test.ts.
76
+ const storage = {
77
+ getItem: vi.fn(() => null),
78
+ setItem: vi.fn(() => {
79
+ throw new Error('QuotaExceededError')
80
+ }),
81
+ removeItem: vi.fn(),
82
+ clear: vi.fn(),
83
+ length: 0,
84
+ key: vi.fn(() => null),
85
+ }
86
+ Object.defineProperty(globalThis, 'localStorage', {
87
+ value: storage,
88
+ writable: true,
89
+ configurable: true,
90
+ })
91
+
92
+ const events: AuraObservabilityEvent[] = []
93
+ const customStore = new LocalAuthStore('custom-key')
94
+ const client = new AuraClient('http://localhost:8080', 'proj-1', 'aura_anon_x', {
95
+ onEvent: (e) => events.push(e),
96
+ auth: { persistSession: true, storage: customStore },
97
+ })
98
+
99
+ expect(() =>
100
+ client.authStore.save('tok', 'rt', {
101
+ id: 'u1',
102
+ email: 't@e.com',
103
+ role: 'authenticated',
104
+ email_verified: true,
105
+ is_banned: false,
106
+ is_anonymous: false,
107
+ mfa_enabled: false,
108
+ user_metadata: {},
109
+ app_metadata: {},
110
+ created_at: '2025-01-01T00:00:00Z',
111
+ }),
112
+ ).not.toThrow()
113
+
114
+ // save() persiste via `_persist()` (op 'persist'), pas via `setItem` (générique).
115
+ expect(events).toContainEqual({ kind: 'storage-blocked', op: 'persist' })
116
+ })
117
+
118
+ it('sans onEvent ni debug (défaut) : comportement inchangé, aucune émission', async () => {
119
+ vi.useFakeTimers()
120
+ const fetchFn = seqFetch([{ status: 503 }, { status: 200, body: { data: {} } }])
121
+ const client = new AuraClient('http://localhost:8080', 'proj-1', 'aura_anon_x', {
122
+ fetch: fetchFn,
123
+ auth: { persistSession: false },
124
+ })
125
+ const p = client.auth.getSettings()
126
+ await vi.advanceTimersByTimeAsync(600)
127
+ const res = await p
128
+ expect(res.error).toBeNull()
129
+ })
130
+ })