@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,301 +0,0 @@
1
- /**
2
- * Fallback client (second rideau) du filtre colonne `postgres_changes`.
3
- *
4
- * Le serveur `aura-realtime` est **autoritaire** : il filtre déjà les events CDC
5
- * par colonne avant de les envoyer (`services/aura-realtime/src/protocol.rs`).
6
- * Ce module reproduit fidèlement la même sémantique côté client, en defense-in-depth :
7
- * il ne peut que sur-filtrer (jamais laisser passer un event que le serveur aurait dû
8
- * exclure).
9
- *
10
- * Sémantique portée depuis le backend Rust (parité stricte) :
11
- * - Opérateurs : eq, neq, lt, lte, gt, gte, in, like, ilike, is, match, imatch, isdistinct.
12
- * - Syntaxe : `column=operator.value`, négation `column=not.operator.value`.
13
- * - AND par virgules : `a=eq.1,b=gt.2` (les virgules internes d'un `in.(a,b,c)` ne
14
- * séparent PAS les conditions — split respectant la profondeur des parenthèses).
15
- * - **Fail-CLOSED** : toute condition non parsable (syntaxe invalide, opérateur inconnu,
16
- * regex invalide, trop de valeurs `in`, pattern `match`/`imatch` trop long) => `false`,
17
- * jamais laissé passer.
18
- * - Champ absent du record => résultat non évaluable => exclusion ferme, **jamais
19
- * inversée** par `not.` (sémantique SQL : `NOT (col = x)` avec `col` NULL/absent
20
- * => NULL => exclu). Seul un résultat sémantique évaluable est inversé par `not.`.
21
- */
22
-
23
- type FilterOp =
24
- | 'eq'
25
- | 'neq'
26
- | 'lt'
27
- | 'lte'
28
- | 'gt'
29
- | 'gte'
30
- | 'in'
31
- | 'like'
32
- | 'ilike'
33
- | 'is'
34
- | 'match'
35
- | 'imatch'
36
- | 'isdistinct'
37
-
38
- const VALID_OPS: ReadonlySet<string> = new Set([
39
- 'eq',
40
- 'neq',
41
- 'lt',
42
- 'lte',
43
- 'gt',
44
- 'gte',
45
- 'in',
46
- 'like',
47
- 'ilike',
48
- 'is',
49
- 'match',
50
- 'imatch',
51
- 'isdistinct',
52
- ])
53
-
54
- /** Longueur max d'un pattern `match`/`imatch` — borne anti-ReDoS (parité backend). */
55
- const MAX_REGEX_PATTERN_LEN = 512
56
-
57
- /** Nombre max de valeurs dans un `in.(...)` — parité backend/PostgREST. */
58
- const MAX_IN_VALUES = 100
59
-
60
- interface ParsedColumnFilter {
61
- column: string
62
- op: FilterOp
63
- value: string
64
- /** `true` si le filtre était préfixé par `not.` — le résultat sémantique est inversé. */
65
- negated: boolean
66
- }
67
-
68
- /**
69
- * Parse une condition unique "column=op.value" ou "column=not.op.value".
70
- * Retourne `null` si non parsable (fail-closed — voir `matchesColumnFilter`).
71
- */
72
- function parseColumnFilter(filterStr: string): ParsedColumnFilter | null {
73
- const eqIdx = filterStr.indexOf('=')
74
- if (eqIdx === -1) return null
75
- const column = filterStr.slice(0, eqIdx)
76
- if (!column) return null
77
- let rest = filterStr.slice(eqIdx + 1)
78
-
79
- let negated = false
80
- if (rest.startsWith('not.')) {
81
- negated = true
82
- rest = rest.slice(4)
83
- }
84
-
85
- const dotIdx = rest.indexOf('.')
86
- if (dotIdx === -1) return null
87
- const opStr = rest.slice(0, dotIdx)
88
- let value = rest.slice(dotIdx + 1)
89
-
90
- if (!VALID_OPS.has(opStr)) return null
91
- const op = opStr as FilterOp
92
-
93
- if (op === 'in') {
94
- const stripped = value.replace(/^\(+/, '').replace(/\)+$/, '')
95
- if (stripped.split(',').length > MAX_IN_VALUES) return null
96
- value = stripped
97
- } else if (op === 'is') {
98
- // `is` n'accepte que null/true/false (parité PostgREST) : toute autre valeur est
99
- // rejetée DÈS LE PARSE. Sinon `is.<garbage>` serait accepté et `matchesRaw`
100
- // renverrait `false` (défaut) — un `not.is.<garbage>` inverserait ce `false` en
101
- // `true` => fail-OPEN (matche tout). On refuse ce filtre en amont (parité Rust).
102
- if (value !== 'null' && value !== 'true' && value !== 'false') return null
103
- } else if (op === 'match' || op === 'imatch') {
104
- if (value.length > MAX_REGEX_PATTERN_LEN) return null
105
- // Compiler la regex DÈS LE PARSE : un pattern invalide => filtre rejeté (null).
106
- // Sinon un `not.match.(invalide` verrait son échec de compilation transformé
107
- // en un `true` inversé => fail-OPEN. On refuse ce filtre en amont (parité Rust).
108
- try {
109
- // eslint-disable-next-line no-new
110
- new RegExp(value, op === 'imatch' ? 'i' : undefined)
111
- } catch {
112
- return null
113
- }
114
- }
115
-
116
- return { column, op, value, negated }
117
- }
118
-
119
- /**
120
- * Découpe un ensemble de conditions AND ("a=eq.1,b=in.(x,y)") en conditions
121
- * individuelles, en respectant les virgules internes d'un `in.(...)` (profondeur
122
- * de parenthèses). Ne fait PAS de parsing ici — juste le split structurel.
123
- */
124
- function splitTopLevel(s: string): string[] {
125
- const parts: string[] = []
126
- let depth = 0
127
- let start = 0
128
- for (let i = 0; i < s.length; i++) {
129
- const c = s[i]
130
- if (c === '(') depth += 1
131
- else if (c === ')') depth = Math.max(0, depth - 1)
132
- else if (c === ',' && depth === 0) {
133
- parts.push(s.slice(start, i))
134
- start = i + 1
135
- }
136
- }
137
- parts.push(s.slice(start))
138
- return parts
139
- }
140
-
141
- /**
142
- * Découpe puis parse un ensemble de conditions AND. **Fail-closed** : retourne
143
- * `null` dès qu'UNE condition est invalide (propage l'échec, n'ignore jamais
144
- * silencieusement une condition malformée).
145
- */
146
- function parseFilterSet(s: string): ParsedColumnFilter[] | null {
147
- const parts = splitTopLevel(s)
148
- const out: ParsedColumnFilter[] = []
149
- for (const part of parts) {
150
- const cf = parseColumnFilter(part.trim())
151
- if (!cf) return null
152
- out.push(cf)
153
- }
154
- return out
155
- }
156
-
157
- /** Convertit une valeur JSON en sa représentation string (comparaison textuelle/numérique). */
158
- function jsonValueToString(field: unknown): string {
159
- if (typeof field === 'string') return field
160
- if (typeof field === 'number' || typeof field === 'boolean') return String(field)
161
- if (field === null) return 'null'
162
- try {
163
- return JSON.stringify(field) ?? ''
164
- } catch {
165
- return ''
166
- }
167
- }
168
-
169
- /** Parse une valeur en `number` strict (rejette les chaînes vides/non-numériques). */
170
- function parseStrictNumber(s: string): number | null {
171
- if (s.trim() === '') return null
172
- const n = Number(s)
173
- return Number.isFinite(n) ? n : null
174
- }
175
-
176
- function compareNumeric(a: string, b: string, cmp: (a: number, b: number) => boolean): boolean {
177
- const an = parseStrictNumber(a)
178
- const bn = parseStrictNumber(b)
179
- if (an === null || bn === null) return false // types incompatibles => fail-closed
180
- return cmp(an, bn)
181
- }
182
-
183
- /** Convertit un motif SQL like/ilike (`%`→sous-chaîne, `_`→1 caractère) en RegExp ancrée. */
184
- function likeToRegExp(pattern: string, caseInsensitive: boolean): RegExp | null {
185
- let re = '^'
186
- for (const ch of pattern) {
187
- if (ch === '%') re += '.*'
188
- else if (ch === '_') re += '.'
189
- else re += ch.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
190
- }
191
- re += '$'
192
- try {
193
- return new RegExp(re, caseInsensitive ? 'i' : undefined)
194
- } catch {
195
- return null
196
- }
197
- }
198
-
199
- /**
200
- * Évalue le prédicat brut (sans tenir compte de `negated`).
201
- * Retourne `null` si non évaluable (champ absent, regex invalide) — distinct
202
- * d'un `false` qui est un « faux » sémantique (inversable par `not.`).
203
- */
204
- function matchesRaw(cf: ParsedColumnFilter, record: Record<string, unknown>): boolean | null {
205
- // `is` compare la valeur JSON réelle (null/true/false) — toujours évaluable.
206
- if (cf.op === 'is') {
207
- const field = record[cf.column]
208
- switch (cf.value) {
209
- case 'null':
210
- return field === undefined || field === null
211
- case 'true':
212
- return field === true
213
- case 'false':
214
- return field === false
215
- default:
216
- // Invariant garanti par `parseColumnFilter` : `is.<autre>` est rejeté au parse
217
- // (null), ce bras est donc défensif uniquement (jamais atteint en pratique).
218
- return false
219
- }
220
- }
221
-
222
- // `isdistinct` : NULL est distinct de toute valeur non-NULL — toujours évaluable.
223
- if (cf.op === 'isdistinct') {
224
- const field = record[cf.column]
225
- const fieldIsNull = field === undefined || field === null
226
- if (fieldIsNull) return true
227
- return jsonValueToString(field) !== cf.value
228
- }
229
-
230
- // Champ absent => non évaluable (null), JAMAIS un false inversable.
231
- if (!(cf.column in record) || record[cf.column] === undefined) return null
232
- const field = record[cf.column]
233
- const fieldStr = jsonValueToString(field)
234
-
235
- switch (cf.op) {
236
- case 'eq':
237
- return fieldStr === cf.value
238
- case 'neq':
239
- return fieldStr !== cf.value
240
- case 'lt':
241
- return compareNumeric(fieldStr, cf.value, (a, b) => a < b)
242
- case 'lte':
243
- return compareNumeric(fieldStr, cf.value, (a, b) => a <= b)
244
- case 'gt':
245
- return compareNumeric(fieldStr, cf.value, (a, b) => a > b)
246
- case 'gte':
247
- return compareNumeric(fieldStr, cf.value, (a, b) => a >= b)
248
- case 'in':
249
- return cf.value
250
- .split(',')
251
- .map((v) => v.trim())
252
- .some((v) => v === fieldStr)
253
- case 'like': {
254
- const re = likeToRegExp(cf.value, false)
255
- return re ? re.test(fieldStr) : null
256
- }
257
- case 'ilike': {
258
- const re = likeToRegExp(cf.value, true)
259
- return re ? re.test(fieldStr) : null
260
- }
261
- case 'match': {
262
- try {
263
- return new RegExp(cf.value).test(fieldStr)
264
- } catch {
265
- return null
266
- }
267
- }
268
- case 'imatch': {
269
- try {
270
- return new RegExp(cf.value, 'i').test(fieldStr)
271
- } catch {
272
- return null
273
- }
274
- }
275
- default:
276
- return null
277
- }
278
- }
279
-
280
- /** Applique la négation UNIQUEMENT au résultat sémantique évaluable (jamais à `null`). */
281
- function matchesSingle(cf: ParsedColumnFilter, record: Record<string, unknown>): boolean {
282
- const raw = matchesRaw(cf, record)
283
- if (raw === null) return false // non évaluable => exclusion ferme, pas d'inversion
284
- return cf.negated ? !raw : raw
285
- }
286
-
287
- /**
288
- * Vérifie si `record` matche le filtre colonne `filter` (syntaxe Supabase/PostgREST) :
289
- * `column=operator.value`, négation `not.`, conditions AND séparées par virgules.
290
- *
291
- * **Fail-CLOSED** : si `filter` n'est pas parsable dans son ensemble (une seule
292
- * condition invalide suffit), retourne `false` — jamais un match par défaut.
293
- *
294
- * Ce helper est un second rideau client (defense-in-depth) : le serveur
295
- * `aura-realtime` a déjà filtré l'event de façon autoritaire.
296
- */
297
- export function matchesColumnFilter(filter: string, record: Record<string, unknown>): boolean {
298
- const set = parseFilterSet(filter)
299
- if (!set) return false
300
- return set.every((cf) => matchesSingle(cf, record))
301
- }
@@ -1,4 +0,0 @@
1
- export { RealtimeService } from './RealtimeService'
2
- export type { ConnectionState } from './RealtimeService'
3
- export { AuraChannel } from './AuraChannel'
4
- export { HeartbeatManager } from './HeartbeatManager'
@@ -1,81 +0,0 @@
1
- import { describe, it, expect, vi } from 'vitest'
2
- import { RealtimeService } from '../src/RealtimeService'
3
- import { AuraChannel } from '../src/AuraChannel'
4
- import { BaseAuthStore } from '@aurabase/core'
5
- import type { AuraClientLike } from '@aurabase/core'
6
-
7
- function client(): AuraClientLike {
8
- return {
9
- baseUrl: 'http://x',
10
- projectId: 'proj',
11
- apiKey: 'k',
12
- authStore: new BaseAuthStore(),
13
- fetchFn: (async () => new Response('{}')) as unknown as typeof globalThis.fetch,
14
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
15
- auth: { refreshSession: async () => ({ data: null, error: null }) as any },
16
- }
17
- }
18
-
19
- function seedChannels(rt: RealtimeService, names: string[]): Map<string, AuraChannel> {
20
- const map = (rt as unknown as { _channels: Map<string, AuraChannel> })._channels
21
- for (const n of names) map.set(n, new AuraChannel(n, 'proj'))
22
- return map
23
- }
24
-
25
- function dispatch(rt: RealtimeService, msg: unknown): void {
26
- ;(rt as unknown as { _dispatch: (m: unknown) => void })._dispatch(msg)
27
- }
28
-
29
- describe('RealtimeService._dispatch — routage broadcast_ack par ref_id', () => {
30
- it("un broadcast_ack n'est dispatché qu'au channel émetteur (extrait du préfixe ref_id)", () => {
31
- const rt = new RealtimeService(client())
32
- const map = seedChannels(rt, ['room-1', 'room-2'])
33
- const spy1 = vi.spyOn(map.get('room-1')!, '_onMessage').mockImplementation(() => {})
34
- const spy2 = vi.spyOn(map.get('room-2')!, '_onMessage').mockImplementation(() => {})
35
-
36
- dispatch(rt, { type: 'broadcast_ack', message_id: 'm1', ref_id: 'proj:room-1:r3' })
37
-
38
- expect(spy1).toHaveBeenCalledTimes(1)
39
- expect(spy2).not.toHaveBeenCalled()
40
- })
41
-
42
- it('résout par message_id absent de ref_id ? — fallback dispatch à tous si ref_id non résoluble', () => {
43
- const rt = new RealtimeService(client())
44
- const map = seedChannels(rt, ['room-1', 'room-2'])
45
- const spy1 = vi.spyOn(map.get('room-1')!, '_onMessage').mockImplementation(() => {})
46
- const spy2 = vi.spyOn(map.get('room-2')!, '_onMessage').mockImplementation(() => {})
47
-
48
- // Pas de ref_id (message_id brut, aucun ':r') → non résoluble → fallback à tous.
49
- dispatch(rt, { type: 'broadcast_ack', message_id: 'uuid-sans-prefixe' })
50
-
51
- expect(spy1).toHaveBeenCalledTimes(1)
52
- expect(spy2).toHaveBeenCalledTimes(1)
53
- })
54
-
55
- it("la promesse channel.send(ack:true) se résout 'ok' sur réception de l'ACK routé", async () => {
56
- const rt = new RealtimeService(client())
57
- const map = (rt as unknown as { _channels: Map<string, AuraChannel> })._channels
58
- const ch = new AuraChannel('room-1', 'proj', { broadcast: { ack: true } })
59
- const sent: string[] = []
60
- ch._bindSend((d) => sent.push(d))
61
- map.set('room-1', ch)
62
-
63
- const p = ch.send({ type: 'broadcast', event: 'ping', payload: { a: 1 } })
64
-
65
- // Récupérer le ref_id réellement émis par le channel.
66
- const outbound = JSON.parse(sent[0]!) as { ref_id: string }
67
- dispatch(rt, { type: 'broadcast_ack', message_id: 'm', ref_id: outbound.ref_id })
68
-
69
- await expect(p).resolves.toBe('ok')
70
- })
71
-
72
- it('nom de channel contenant ":r" — lastIndexOf isole bien le suffixe :r{n}', () => {
73
- const rt = new RealtimeService(client())
74
- const map = seedChannels(rt, ['weird:r-name'])
75
- const spy = vi.spyOn(map.get('weird:r-name')!, '_onMessage').mockImplementation(() => {})
76
-
77
- dispatch(rt, { type: 'broadcast_ack', message_id: 'm', ref_id: 'proj:weird:r-name:r7' })
78
-
79
- expect(spy).toHaveBeenCalledTimes(1)
80
- })
81
- })
@@ -1,144 +0,0 @@
1
- import { describe, it, expect } from 'vitest'
2
- import { matchesColumnFilter } from '../src/columnFilter'
3
-
4
- // Ces tests portent la même sémantique que le backend Rust
5
- // (services/aura-realtime/src/protocol.rs) : eq/neq/lt/lte/gt/gte/in/like/ilike/
6
- // is/match/imatch/isdistinct, négation `not.`, AND par virgules (hors `in.(...)`),
7
- // fail-closed sur filtre non parsable, champ absent = exclusion ferme (jamais inversée).
8
-
9
- describe('matchesColumnFilter — opérateurs de base', () => {
10
- it('eq', () => {
11
- expect(matchesColumnFilter('room_id=eq.42', { room_id: 42 })).toBe(true)
12
- expect(matchesColumnFilter('room_id=eq.42', { room_id: 7 })).toBe(false)
13
- })
14
-
15
- it('neq', () => {
16
- expect(matchesColumnFilter('status=neq.deleted', { status: 'active' })).toBe(true)
17
- expect(matchesColumnFilter('status=neq.deleted', { status: 'deleted' })).toBe(false)
18
- })
19
-
20
- it('lt/lte/gt/gte numérique', () => {
21
- expect(matchesColumnFilter('age=lt.18', { age: 10 })).toBe(true)
22
- expect(matchesColumnFilter('age=lt.18', { age: 18 })).toBe(false)
23
- expect(matchesColumnFilter('age=lte.18', { age: 18 })).toBe(true)
24
- expect(matchesColumnFilter('qty=gt.5', { qty: 10 })).toBe(true)
25
- expect(matchesColumnFilter('qty=gt.5', { qty: 5 })).toBe(false)
26
- expect(matchesColumnFilter('score=gte.50', { score: 50 })).toBe(true)
27
- expect(matchesColumnFilter('score=gte.50', { score: 49 })).toBe(false)
28
- })
29
-
30
- it('lt/gt sur types incompatibles => false (fail-closed)', () => {
31
- expect(matchesColumnFilter('name=gt.100', { name: 'hello' })).toBe(false)
32
- })
33
-
34
- it('in — respecte les parenthèses et les espaces', () => {
35
- expect(matchesColumnFilter('status=in.(a,b)', { status: 'b' })).toBe(true)
36
- expect(matchesColumnFilter('status=in.(a,b)', { status: 'c' })).toBe(false)
37
- expect(matchesColumnFilter('status=in.(active, pending, done)', { status: 'pending' })).toBe(true)
38
- })
39
-
40
- it('like / ilike', () => {
41
- expect(matchesColumnFilter('name=like.jo%', { name: 'john' })).toBe(true)
42
- expect(matchesColumnFilter('name=like.jo%', { name: 'alice' })).toBe(false)
43
- expect(matchesColumnFilter('name=ilike.JO%', { name: 'john' })).toBe(true)
44
- expect(matchesColumnFilter('name=ilike.JO%', { name: 'JOHN' })).toBe(true)
45
- expect(matchesColumnFilter('name=ilike.JO%', { name: 'alice' })).toBe(false)
46
- })
47
-
48
- it('is — null/true/false sur la valeur réelle', () => {
49
- expect(matchesColumnFilter('deleted_at=is.null', { deleted_at: null })).toBe(true)
50
- expect(matchesColumnFilter('deleted_at=is.null', { deleted_at: '2026' })).toBe(false)
51
- expect(matchesColumnFilter('active=is.true', { active: true })).toBe(true)
52
- expect(matchesColumnFilter('active=is.true', { active: false })).toBe(false)
53
- expect(matchesColumnFilter('active=is.false', { active: false })).toBe(true)
54
- expect(matchesColumnFilter('active=is.false', { active: true })).toBe(false)
55
- })
56
-
57
- it('match / imatch — RegExp', () => {
58
- expect(matchesColumnFilter('email=match.^[a-z]+@example\\.com$', { email: 'alice@example.com' })).toBe(true)
59
- expect(matchesColumnFilter('email=match.^[a-z]+@example\\.com$', { email: 'ALICE@example.com' })).toBe(false)
60
- expect(matchesColumnFilter('email=imatch.^[a-z]+@example\\.com$', { email: 'ALICE@example.com' })).toBe(true)
61
- })
62
-
63
- it('isdistinct — NULL distinct de toute valeur non-NULL', () => {
64
- expect(matchesColumnFilter('status=isdistinct.done', { status: 'open' })).toBe(true)
65
- expect(matchesColumnFilter('status=isdistinct.done', { status: 'done' })).toBe(false)
66
- expect(matchesColumnFilter('status=isdistinct.done', { status: null })).toBe(true)
67
- })
68
- })
69
-
70
- describe('matchesColumnFilter — négation not.', () => {
71
- it('inverse le résultat sémantique', () => {
72
- expect(matchesColumnFilter('status=not.eq.done', { status: 'open' })).toBe(true)
73
- expect(matchesColumnFilter('status=not.eq.done', { status: 'done' })).toBe(false)
74
- })
75
-
76
- it('ne fail-open jamais sur champ absent (non évaluable => exclusion ferme)', () => {
77
- // Sémantique SQL : NOT (col = x) avec col NULL/absent => NULL => exclu, pas inversé en true.
78
- expect(matchesColumnFilter('status=not.eq.done', { id: 1 })).toBe(false)
79
- })
80
- })
81
-
82
- describe('matchesColumnFilter — AND (virgules)', () => {
83
- it('toutes les conditions doivent matcher', () => {
84
- expect(matchesColumnFilter('room_id=eq.42,qty=gt.5', { room_id: 42, qty: 9 })).toBe(true)
85
- expect(matchesColumnFilter('room_id=eq.42,qty=gt.5', { room_id: 42, qty: 1 })).toBe(false)
86
- })
87
-
88
- it("respecte les virgules internes d'un in.(...)", () => {
89
- expect(matchesColumnFilter('status=in.(a,b),qty=gt.5', { status: 'b', qty: 9 })).toBe(true)
90
- expect(matchesColumnFilter('status=in.(a,b),qty=gt.5', { status: 'c', qty: 9 })).toBe(false)
91
- })
92
- })
93
-
94
- describe('matchesColumnFilter — fail-closed', () => {
95
- it('filtre non parsable (syntaxe invalide) => false', () => {
96
- expect(matchesColumnFilter('garbage', { a: 1 })).toBe(false)
97
- })
98
-
99
- it('opérateur inconnu => false', () => {
100
- expect(matchesColumnFilter('col=unknown.val', { col: 'val' })).toBe(false)
101
- })
102
-
103
- it('une condition AND invalide invalide tout le set => false', () => {
104
- expect(matchesColumnFilter('room_id=eq.42,garbage', { room_id: 42 })).toBe(false)
105
- })
106
-
107
- it('regex match/imatch invalide => filtre rejeté (false), jamais inversé par not.', () => {
108
- expect(matchesColumnFilter('name=match.(unclosed', { name: 'anything' })).toBe(false)
109
- expect(matchesColumnFilter('name=not.match.(unclosed', { name: 'anything' })).toBe(false)
110
- })
111
-
112
- it('pattern match trop long (>512) => rejeté', () => {
113
- const longPattern = 'a'.repeat(513)
114
- expect(matchesColumnFilter(`name=match.${longPattern}`, { name: 'a'.repeat(513) })).toBe(false)
115
- })
116
-
117
- it('in avec plus de 100 valeurs => rejeté', () => {
118
- const values = Array.from({ length: 101 }, (_, i) => String(i + 1)).join(',')
119
- expect(matchesColumnFilter(`id=in.(${values})`, { id: 1 })).toBe(false)
120
- })
121
-
122
- it('is.<valeur invalide> => rejeté au parse (pas null/true/false)', () => {
123
- expect(matchesColumnFilter('active=is.garbage', { active: true })).toBe(false)
124
- expect(matchesColumnFilter('active=is.garbage', { active: false })).toBe(false)
125
- })
126
-
127
- it('not.is.<valeur invalide> ne fail-open jamais (recoin fermé)', () => {
128
- // Avant le fix : is.garbage était accepté au parse, matchesRaw renvoyait false,
129
- // et `not.` inversait ce false en true => le filtre matchait tout event.
130
- expect(matchesColumnFilter('active=not.is.garbage', { active: true })).toBe(false)
131
- expect(matchesColumnFilter('active=not.is.garbage', { active: false })).toBe(false)
132
- })
133
- })
134
-
135
- describe('matchesColumnFilter — champ absent', () => {
136
- it('champ absent => exclusion ferme (jamais un match)', () => {
137
- expect(matchesColumnFilter('xyz=eq.1', { other: 1 })).toBe(false)
138
- })
139
-
140
- it('champ null (présent) sur eq => comparaison textuelle "null", pas une exclusion', () => {
141
- expect(matchesColumnFilter('name=eq.test', { name: null })).toBe(false)
142
- expect(matchesColumnFilter('name=eq.null', { name: null })).toBe(true)
143
- })
144
- })