@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,485 +0,0 @@
1
- import type {
2
- RealtimeEvent,
3
- RealtimeListenType,
4
- PostgresChangesFilter,
5
- BroadcastFilter,
6
- PresenceFilter,
7
- SubscribeCallback,
8
- BroadcastPayload,
9
- PresenceState,
10
- PresenceInfo,
11
- ServerMessage,
12
- ServerSubscribedMsg,
13
- ServerEventMsg,
14
- ServerPresenceStateMsg,
15
- ServerErrorMsg,
16
- ChannelConfig,
17
- } from '@aurabase/core'
18
- import { matchesColumnFilter } from './columnFilter'
19
-
20
- // ─── Internal types ─────────────────────────────────────────────────────────
21
-
22
- type ListenerCallback = (payload: RealtimeEvent | Record<string, unknown>) => void
23
-
24
- type ListenerEntry = {
25
- type: RealtimeListenType
26
- filter: PostgresChangesFilter | BroadcastFilter | PresenceFilter
27
- callback: ListenerCallback
28
- }
29
-
30
- type SendFn = (data: string) => void
31
-
32
- // ─── AuraChannel ────────────────────────────────────────────────────────────
33
-
34
- /**
35
- * A realtime channel with Supabase-compatible chainable API.
36
- *
37
- * @example
38
- * ```ts
39
- * const channel = aura.channel('room-1')
40
- * .on('postgres_changes', { event: 'INSERT', schema: 'public', table: 'messages' }, (payload) => {
41
- * console.log('New message:', payload)
42
- * })
43
- * .on('broadcast', { event: 'cursor-pos' }, (payload) => {
44
- * console.log('Cursor:', payload)
45
- * })
46
- * .on('presence', { event: 'sync' }, () => {
47
- * console.log('Presence synced:', channel.presenceState())
48
- * })
49
- * .subscribe((status) => {
50
- * if (status === 'SUBSCRIBED') console.log('Connected!')
51
- * })
52
- * ```
53
- */
54
- export class AuraChannel {
55
- readonly name: string
56
- private readonly _projectId: string
57
- private readonly _listeners: ListenerEntry[] = []
58
- private _subscribed = false
59
- private _sendFn: SendFn | null = null
60
- private _subscribeCallback: SubscribeCallback | null = null
61
- private _presenceState: PresenceState = {}
62
- private _isPrivate = false
63
- private _broadcastAck = false
64
- private _broadcastSelf = false
65
- private _presenceKey = ''
66
- /** ID de subscription retourné par le serveur (utilisé pour le filtre RLS allowed_sub_ids) */
67
- private _subscriptionId: string | null = null
68
- /** Compteur pour générer des ref_id uniques par connexion */
69
- private _refCounter = 0
70
- /** Dernier event ID recu — utilise pour since_id au reconnect */
71
- private _lastEventId: string | null = null
72
- /** Pending ACK callbacks : ref_id → resolve */
73
- private _pendingAcks = new Map<string, () => void>()
74
-
75
- constructor(name: string, projectId: string, options?: ChannelConfig) {
76
- this.name = name
77
- this._projectId = projectId
78
- this._isPrivate = options?.private ?? false
79
- this._broadcastAck = options?.broadcast?.ack ?? false
80
- this._broadcastSelf = options?.broadcast?.self ?? false
81
- this._presenceKey = options?.config?.presence?.key ?? ''
82
- }
83
-
84
- /** Génère un ref_id unique pour la corrélation request/response. */
85
- private _nextRef(): string {
86
- return `${this.fullName}:r${++this._refCounter}`
87
- }
88
-
89
- /** Full channel name with project prefix (for server protocol) */
90
- get fullName(): string {
91
- return `${this._projectId}:${this.name}`
92
- }
93
-
94
- // ─── Chainable .on() ────────────────────────────────────────────────────
95
-
96
- /**
97
- * Register a listener for a specific event type.
98
- * Returns `this` for chaining.
99
- */
100
- on(
101
- type: 'postgres_changes',
102
- filter: PostgresChangesFilter,
103
- callback: (payload: RealtimeEvent) => void,
104
- ): this
105
- on(
106
- type: 'broadcast',
107
- filter: BroadcastFilter,
108
- callback: (payload: RealtimeEvent) => void,
109
- ): this
110
- on(
111
- type: 'presence',
112
- filter: PresenceFilter,
113
- callback: (payload: any) => void,
114
- ): this
115
- on(
116
- type: RealtimeListenType,
117
- filter: PostgresChangesFilter | BroadcastFilter | PresenceFilter,
118
- callback: (payload: any) => void,
119
- ): this {
120
- this._listeners.push({ type, filter, callback })
121
- return this
122
- }
123
-
124
- // ─── Subscribe ──────────────────────────────────────────────────────────
125
-
126
- /**
127
- * Subscribe to the channel. Sends the Subscribe message to the server
128
- * with the full config (postgres_changes, broadcast, presence).
129
- */
130
- subscribe(callback?: SubscribeCallback): this {
131
- this._subscribeCallback = callback ?? null
132
- this._subscribed = true
133
-
134
- // Si le WS est déjà connecté (canal créé après une connexion existante),
135
- // s'abonner immédiatement. Sinon, RealtimeService appellera _bindSend()
136
- // puis _doSubscribe() depuis son handler ws.onopen une fois connecté.
137
- if (this._sendFn) {
138
- this._doSubscribe()
139
- }
140
-
141
- return this
142
- }
143
-
144
- /** Called by RealtimeService once the WS connection is ready. */
145
- _bindSend(sendFn: SendFn): void {
146
- this._sendFn = sendFn
147
- }
148
-
149
- /** Send the subscribe message to the server. */
150
- _doSubscribe(): void {
151
- if (!this._sendFn) return
152
-
153
- // Build postgres_changes config from listeners
154
- const pgFilters = this._listeners
155
- .filter((l) => l.type === 'postgres_changes')
156
- .map((l) => l.filter as PostgresChangesFilter)
157
-
158
- const msg = {
159
- type: 'subscribe',
160
- channel: this.fullName,
161
- ref_id: this._nextRef(),
162
- since_id: this._lastEventId ?? undefined,
163
- config: {
164
- postgres_changes: pgFilters,
165
- broadcast: {
166
- self_broadcast: this._broadcastSelf, // ← depuis les options du channel
167
- ack: this._broadcastAck, // ← depuis les options du channel
168
- },
169
- presence: { key: this._presenceKey }, // ← depuis les options du channel
170
- private: this._isPrivate,
171
- },
172
- }
173
-
174
- this._sendFn(JSON.stringify(msg))
175
- }
176
-
177
- /** Unsubscribe from the channel. */
178
- unsubscribe(): void {
179
- if (this._sendFn) {
180
- this._sendFn(JSON.stringify({ type: 'unsubscribe', channel: this.fullName }))
181
- }
182
- this._subscribed = false
183
- this._listeners.length = 0
184
- this._presenceState = {}
185
- }
186
-
187
- // ─── Broadcast ──────────────────────────────────────────────────────────
188
-
189
- /**
190
- * Send a broadcast message to the channel.
191
- * If broadcast.ack=true, returns a Promise that resolves when the server ACKs.
192
- *
193
- * @example
194
- * ```ts
195
- * // Without ACK (fire & forget)
196
- * channel.send({ type: 'broadcast', event: 'cursor-pos', payload: { x: 100, y: 200 } })
197
- *
198
- * // With ACK (wait for server confirmation)
199
- * await channel.send({ type: 'broadcast', event: 'cursor-pos', payload: { x: 100 } })
200
- * ```
201
- */
202
- send(message: BroadcastPayload): Promise<'ok' | 'timeout' | 'error'> {
203
- return new Promise((resolve) => {
204
- if (!this._sendFn) {
205
- resolve('error')
206
- return
207
- }
208
- const ref_id = message.ref_id ?? this._nextRef()
209
-
210
- // Si ack demandé, enregistrer un callback en attente
211
- if (this._broadcastAck) {
212
- const timer = setTimeout(() => {
213
- this._pendingAcks.delete(ref_id)
214
- resolve('timeout')
215
- }, 5000)
216
-
217
- this._pendingAcks.set(ref_id, () => {
218
- clearTimeout(timer)
219
- resolve('ok')
220
- })
221
- } else {
222
- resolve('ok')
223
- }
224
-
225
- this._sendFn(
226
- JSON.stringify({
227
- type: 'broadcast',
228
- channel: this.fullName,
229
- ref_id,
230
- payload: { event: message.event, ...message.payload },
231
- }),
232
- )
233
- })
234
- }
235
-
236
- // ─── Presence ───────────────────────────────────────────────────────────
237
-
238
- /**
239
- * Track presence state for this client.
240
- * Returns a Promise that resolves with 'ok' once tracked (compatible Supabase).
241
- *
242
- * @example
243
- * ```ts
244
- * await channel.track({ user: 'user-1', online_at: new Date().toISOString() })
245
- * ```
246
- */
247
- async track(state: Record<string, unknown>): Promise<'ok' | 'error'> {
248
- if (!this._sendFn) return 'error'
249
- this._sendFn(
250
- JSON.stringify({
251
- type: 'presence_update',
252
- channel: this.fullName,
253
- meta: state,
254
- }),
255
- )
256
- return 'ok'
257
- }
258
-
259
- /** Stop tracking presence. */
260
- async untrack(): Promise<'ok' | 'error'> {
261
- return this.track({})
262
- }
263
-
264
- /** Get the current presence state for this channel. */
265
- presenceState(): PresenceState {
266
- return { ...this._presenceState }
267
- }
268
-
269
- // ─── Internal: dispatch server messages ──────────────────────────────────
270
-
271
- /** Called by RealtimeService when a server message arrives for this channel. */
272
- _onMessage(msg: ServerMessage): void {
273
- switch (msg.type) {
274
- case 'subscribed': {
275
- const subMsg = msg as ServerSubscribedMsg
276
- if (subMsg.subscription_id) this._subscriptionId = subMsg.subscription_id
277
- this._subscribeCallback?.('SUBSCRIBED')
278
- break
279
- }
280
-
281
- case 'event': {
282
- // Serde flatten : les champs de RealtimeEvent sont au top-level du JSON
283
- // ServerEventMsg a les champs id, event, topic, payload, timestamp directement
284
- const evtMsg = msg as ServerEventMsg
285
- if (evtMsg.id) {
286
- const event: RealtimeEvent = {
287
- id: evtMsg.id,
288
- event: evtMsg.event as RealtimeEvent['event'],
289
- topic: evtMsg.topic,
290
- payload: evtMsg.payload,
291
- timestamp: evtMsg.timestamp,
292
- allowed_sub_ids: evtMsg.allowed_sub_ids,
293
- }
294
- this._dispatchEvent(event)
295
- }
296
- break
297
- }
298
-
299
- case 'presence_state': {
300
- const presMsg = msg as ServerPresenceStateMsg
301
- if (presMsg.presences) {
302
- this._presenceState = {}
303
- for (const p of presMsg.presences) {
304
- const key = p.user_id || p.client_id
305
- if (!this._presenceState[key]) this._presenceState[key] = []
306
- this._presenceState[key].push(p)
307
- }
308
- for (const l of this._listeners) {
309
- if (l.type === 'presence' && (l.filter as PresenceFilter).event === 'sync') {
310
- l.callback(this._presenceState)
311
- }
312
- }
313
- }
314
- break
315
- }
316
-
317
- case 'error': {
318
- const errMsg = msg as ServerErrorMsg
319
- this._subscribeCallback?.('CHANNEL_ERROR', new Error(errMsg.message ?? 'Unknown error'))
320
- break
321
- }
322
-
323
- case 'token_refreshed':
324
- break
325
-
326
- case 'broadcast_ack': {
327
- // Résoudre le pending ACK si ref_id présent
328
- const ackMsg = msg as { type: 'broadcast_ack'; ref_id?: string; message_id?: string }
329
- const refId = ackMsg.ref_id ?? ackMsg.message_id
330
- if (refId) {
331
- const resolve = this._pendingAcks.get(refId)
332
- if (resolve) {
333
- this._pendingAcks.delete(refId)
334
- resolve()
335
- }
336
- }
337
- break
338
- }
339
- }
340
- }
341
-
342
- /** Dispatch a RealtimeEvent to matching listeners. */
343
- private _dispatchEvent(event: RealtimeEvent): void {
344
- if (!event) return
345
-
346
- // Tracker le dernier event ID pour since_id au reconnect
347
- if (event.id) this._lastEventId = event.id
348
-
349
- // Filtre RLS : si l'event a une liste allowed_sub_ids et que notre subscription_id
350
- // n'y figure pas, ne pas dispatcher (defense-in-depth côté client)
351
- const allowed = event.allowed_sub_ids
352
- if (allowed && this._subscriptionId && !allowed.includes(this._subscriptionId)) {
353
- return
354
- }
355
-
356
- const eventType = event.event // 'insert', 'update', 'delete', 'broadcast', 'presence'
357
-
358
- // Mettre à jour l'état de présence local avant de notifier les écouteurs
359
- if (eventType === 'presence') {
360
- const payload = event.payload as Record<string, unknown>
361
- const action = payload.action as string
362
- const key = (payload.user_id || payload.client_id) as string
363
-
364
- if (action === 'join') {
365
- if (!this._presenceState[key]) this._presenceState[key] = []
366
- if (!this._presenceState[key].some(p => p.client_id === payload.client_id)) {
367
- const presenceInfo: PresenceInfo = {
368
- client_id: payload.client_id as string,
369
- user_id: (payload.user_id as string) ?? undefined,
370
- metadata: (payload.metadata ?? payload.meta ?? {}) as Record<string, unknown>,
371
- joined_at: new Date().toISOString(),
372
- last_seen: new Date().toISOString(),
373
- }
374
- this._presenceState[key].push(presenceInfo)
375
- }
376
- } else if (action === 'leave') {
377
- const arr = this._presenceState[key]
378
- if (arr) {
379
- const idx = arr.findIndex((p) => p.client_id === payload.client_id)
380
- if (idx >= 0) arr.splice(idx, 1)
381
- if (arr.length === 0) delete this._presenceState[key]
382
- }
383
- } else if (action === 'update') {
384
- const arr = this._presenceState[key]
385
- if (arr) {
386
- const idx = arr.findIndex((p) => p.client_id === payload.client_id)
387
- if (idx >= 0) {
388
- arr[idx].metadata = (payload.metadata ?? payload.meta ?? {}) as Record<string, unknown>
389
- arr[idx].last_seen = new Date().toISOString()
390
- } else {
391
- const presenceInfo: PresenceInfo = {
392
- client_id: payload.client_id as string,
393
- user_id: (payload.user_id as string) ?? undefined,
394
- metadata: (payload.metadata ?? payload.meta ?? {}) as Record<string, unknown>,
395
- joined_at: new Date().toISOString(),
396
- last_seen: new Date().toISOString(),
397
- }
398
- this._presenceState[key].push(presenceInfo)
399
- }
400
- } else {
401
- this._presenceState[key] = [{
402
- client_id: payload.client_id as string,
403
- user_id: (payload.user_id as string) ?? undefined,
404
- metadata: (payload.metadata ?? payload.meta ?? {}) as Record<string, unknown>,
405
- joined_at: new Date().toISOString(),
406
- last_seen: new Date().toISOString(),
407
- }]
408
- }
409
- }
410
- }
411
-
412
- for (const listener of this._listeners) {
413
- if (listener.type === 'postgres_changes') {
414
- const filter = listener.filter as PostgresChangesFilter
415
- if (
416
- eventType === 'insert' ||
417
- eventType === 'update' ||
418
- eventType === 'delete'
419
- ) {
420
- const filterEvent = filter.event.toLowerCase()
421
- if (filterEvent === '*' || filterEvent === eventType) {
422
- // Check schema/table if specified
423
- const payload = event.payload as Record<string, unknown>
424
- if (filter.schema && payload.schema !== filter.schema) continue
425
- if (filter.table && payload.table !== filter.table) continue
426
-
427
- // Second rideau client (defense-in-depth) : le serveur a déjà filtré
428
- // par colonne de façon autoritaire. DELETE -> old_record (record CDC
429
- // vide) ; INSERT/UPDATE -> record. Si la cible est absente/non-objet,
430
- // ne pas filtrer (éviter un faux négatif sur un payload sans record).
431
- if (filter.filter) {
432
- const target = eventType === 'delete' ? payload.old_record : payload.record
433
- if (target && typeof target === 'object') {
434
- if (!matchesColumnFilter(filter.filter, target as Record<string, unknown>)) {
435
- continue
436
- }
437
- }
438
- }
439
-
440
- listener.callback(event)
441
- }
442
- }
443
- } else if (listener.type === 'broadcast') {
444
- if (eventType === 'broadcast') {
445
- const filter = listener.filter as BroadcastFilter
446
- const payload = event.payload as Record<string, unknown>
447
- // Match broadcast event name if specified
448
- if (!filter.event || payload.event === filter.event) {
449
- listener.callback(event)
450
- }
451
- }
452
- } else if (listener.type === 'presence') {
453
- if (eventType === 'presence') {
454
- const filter = listener.filter as PresenceFilter
455
- const payload = event.payload as Record<string, unknown>
456
- const action = payload.action as string
457
-
458
- if (filter.event === 'join' && action === 'join') {
459
- listener.callback({
460
- key: payload.user_id || payload.client_id,
461
- newPresences: [payload],
462
- })
463
- } else if (filter.event === 'leave' && action === 'leave') {
464
- listener.callback({
465
- key: payload.user_id || payload.client_id,
466
- leftPresences: [payload],
467
- })
468
- } else if (filter.event === 'sync') {
469
- listener.callback(this.presenceState())
470
- }
471
- }
472
- }
473
- }
474
- }
475
-
476
- /** Whether this channel has been subscribed. */
477
- get isSubscribed(): boolean {
478
- return this._subscribed
479
- }
480
-
481
- /** Get listeners (for re-subscribe after reconnect). */
482
- _getListeners(): ListenerEntry[] {
483
- return this._listeners
484
- }
485
- }
@@ -1,73 +0,0 @@
1
- /**
2
- * HeartbeatManager — pings périodiques pour maintenir la connexion WS.
3
- * Utilise un Worker dédié (anti-throttle des onglets en arrière-plan) ; fallback setInterval.
4
- */
5
- export class HeartbeatManager {
6
- private _worker: Worker | null = null
7
- private _workerUrl: string | null = null
8
- private _interval: ReturnType<typeof setInterval> | null = null
9
- private _sendFn: (() => void) | null = null
10
-
11
- start(sendPing: () => void, intervalMs = 25000): void {
12
- this.stop()
13
- this._sendFn = sendPing
14
-
15
- if (typeof Worker !== 'undefined' && typeof URL !== 'undefined' && typeof URL.createObjectURL === 'function') {
16
- try {
17
- const code = `let i=null;onmessage=(e)=>{if(e.data.type==='start'){if(i)clearInterval(i);i=setInterval(()=>postMessage('ping'),e.data.interval||25000);}else if(e.data.type==='stop'){if(i){clearInterval(i);i=null;}}};`
18
- const blob = new Blob([code], { type: 'application/javascript' })
19
- this._workerUrl = URL.createObjectURL(blob)
20
- this._worker = new Worker(this._workerUrl)
21
- this._worker.onmessage = () => {
22
- this._sendFn?.()
23
- }
24
- this._worker.postMessage({ type: 'start', interval: intervalMs })
25
- return
26
- } catch {
27
- // Worker indispo/CSP → fallback
28
- this._worker = null
29
- if (this._workerUrl) {
30
- try {
31
- URL.revokeObjectURL(this._workerUrl)
32
- } catch {
33
- /* ignore */
34
- }
35
- this._workerUrl = null
36
- }
37
- }
38
- }
39
-
40
- this._interval = setInterval(() => {
41
- this._sendFn?.()
42
- }, intervalMs)
43
- }
44
-
45
- stop(): void {
46
- if (this._worker) {
47
- try {
48
- this._worker.postMessage({ type: 'stop' })
49
- this._worker.terminate()
50
- } catch {
51
- /* ignore */
52
- }
53
- this._worker = null
54
- }
55
- if (this._workerUrl) {
56
- try {
57
- URL.revokeObjectURL(this._workerUrl)
58
- } catch {
59
- /* ignore */
60
- }
61
- this._workerUrl = null
62
- }
63
- if (this._interval) {
64
- clearInterval(this._interval)
65
- this._interval = null
66
- }
67
- this._sendFn = null
68
- }
69
-
70
- get isActive(): boolean {
71
- return this._worker !== null || this._interval !== null
72
- }
73
- }