@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.
- package/.changeset/config.json +11 -0
- package/CHANGELOG.md +30 -0
- package/LICENSE +21 -0
- package/README.md +833 -0
- package/package.json +90 -0
- package/packages/ai/LICENSE +21 -0
- package/packages/ai/README.md +106 -0
- package/packages/ai/dist/index.cjs +229 -0
- package/packages/ai/dist/index.d.cts +139 -0
- package/packages/ai/dist/index.d.ts +139 -0
- package/packages/ai/dist/index.js +226 -0
- package/packages/ai/node_modules/.bin/esbuild +19 -0
- package/packages/ai/node_modules/.bin/tsc +22 -0
- package/packages/ai/node_modules/.bin/tsserver +22 -0
- package/packages/ai/node_modules/.bin/tsup +22 -0
- package/packages/ai/node_modules/.bin/tsup-node +22 -0
- package/packages/ai/node_modules/.bin/vite +22 -0
- package/packages/ai/node_modules/.bin/vitest +22 -0
- package/packages/ai/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/packages/ai/package.json +61 -0
- package/packages/ai/src/AiService.ts +226 -0
- package/packages/ai/src/index.ts +18 -0
- package/packages/ai/src/sse.ts +141 -0
- package/packages/ai/src/types.ts +30 -0
- package/packages/ai/tests/chat-stream.test.ts +151 -0
- package/packages/ai/tests/rag-namespaces.test.ts +54 -0
- package/packages/ai/tests/sse.test.ts +69 -0
- package/packages/ai/tsconfig.json +5 -0
- package/packages/ai/tsup.config.ts +11 -0
- package/packages/aurabase/LICENSE +21 -0
- package/packages/aurabase/README.md +64 -0
- package/packages/aurabase/dist/AuraClient-DAtDbfPB.d.cts +77 -0
- package/packages/aurabase/dist/AuraClient-DAtDbfPB.d.ts +77 -0
- package/packages/aurabase/dist/index.cjs +237 -0
- package/packages/aurabase/dist/index.d.cts +46 -0
- package/packages/aurabase/dist/index.d.ts +46 -0
- package/packages/aurabase/dist/index.js +177 -0
- package/packages/aurabase/dist/ssr.cjs +224 -0
- package/packages/aurabase/dist/ssr.d.cts +65 -0
- package/packages/aurabase/dist/ssr.d.ts +65 -0
- package/packages/aurabase/dist/ssr.js +214 -0
- package/packages/aurabase/node_modules/.bin/esbuild +19 -0
- package/packages/aurabase/node_modules/.bin/tsc +22 -0
- package/packages/aurabase/node_modules/.bin/tsserver +22 -0
- package/packages/aurabase/node_modules/.bin/tsup +22 -0
- package/packages/aurabase/node_modules/.bin/tsup-node +22 -0
- package/packages/aurabase/node_modules/.bin/vite +22 -0
- package/packages/aurabase/node_modules/.bin/vitest +22 -0
- package/packages/aurabase/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/packages/aurabase/package.json +80 -0
- package/packages/aurabase/src/AuraClient.ts +237 -0
- package/packages/aurabase/src/index.ts +152 -0
- package/packages/aurabase/src/ssr.ts +156 -0
- package/packages/aurabase/tests/ai-streaming-rag-e2e.test.ts +82 -0
- package/packages/aurabase/tests/ai.test.ts +111 -0
- package/packages/aurabase/tests/auth-admin.test.ts +515 -0
- package/packages/aurabase/tests/auth-e2e.test.ts +135 -0
- package/packages/aurabase/tests/auth-hardening.test.ts +206 -0
- package/packages/aurabase/tests/auth.test.ts +947 -0
- package/packages/aurabase/tests/database.test.ts +230 -0
- package/packages/aurabase/tests/db-types-e2e.test.ts +217 -0
- package/packages/aurabase/tests/dispose.test.ts +23 -0
- package/packages/aurabase/tests/foreign-token.e2e.test.ts +129 -0
- package/packages/aurabase/tests/functions.test.ts +107 -0
- package/packages/aurabase/tests/integration.test.ts +413 -0
- package/packages/aurabase/tests/live-production-validation.js +241 -0
- package/packages/aurabase/tests/live-sdk-production-validation.js +293 -0
- package/packages/aurabase/tests/mocks.ts +139 -0
- package/packages/aurabase/tests/notifications.test.ts +314 -0
- package/packages/aurabase/tests/observability-backup.test.ts +186 -0
- package/packages/aurabase/tests/observability-hook.test.ts +130 -0
- package/packages/aurabase/tests/production-robustness.test.ts +268 -0
- package/packages/aurabase/tests/public-api-surface.test.ts +53 -0
- package/packages/aurabase/tests/query-builder.test.ts +329 -0
- package/packages/aurabase/tests/readme-smoke-e2e.test.ts +231 -0
- package/packages/aurabase/tests/realtime-filter-e2e.test.ts +344 -0
- package/packages/aurabase/tests/refresh-locks.test.ts +210 -0
- package/packages/aurabase/tests/retry-option.test.ts +17 -0
- package/packages/aurabase/tests/rpc-e2e.test.ts +254 -0
- package/packages/aurabase/tests/rs256-local-e2e.test.ts +114 -0
- package/packages/aurabase/tests/rt-alias-public-e2e.test.ts +235 -0
- package/packages/aurabase/tests/rt-http-broadcast-e2e.test.ts +124 -0
- package/packages/aurabase/tests/service-role-guard.test.ts +99 -0
- package/packages/aurabase/tests/ssr.test.ts +91 -0
- package/packages/aurabase/tests/storage-backend-e2e.test.ts +481 -0
- package/packages/aurabase/tests/storage-sdk-parity-e2e.test.ts +104 -0
- package/packages/aurabase/tests/storage-transform-e2e.test.ts +684 -0
- package/packages/aurabase/tests/storage.test.ts +55 -0
- package/packages/aurabase/tests/types/client-generic.test-d.ts +79 -0
- package/packages/aurabase/tests/types/docs-snippets.test-d.ts +571 -0
- package/packages/aurabase/tests/types/error-code.test-d.ts +53 -0
- package/packages/aurabase/tsconfig.json +5 -0
- package/packages/aurabase/tsconfig.test-d.json +5 -0
- package/packages/aurabase/tsup.config.ts +11 -0
- package/packages/aurabase/vitest.config.ts +16 -0
- package/packages/aurabase/vitest.e2e.config.ts +9 -0
- package/packages/auth/LICENSE +21 -0
- package/packages/auth/README.md +215 -0
- package/packages/auth/dist/index.cjs +1175 -0
- package/packages/auth/dist/index.d.cts +397 -0
- package/packages/auth/dist/index.d.ts +397 -0
- package/packages/auth/dist/index.js +1170 -0
- package/packages/auth/node_modules/.bin/esbuild +19 -0
- package/packages/auth/node_modules/.bin/tsc +22 -0
- package/packages/auth/node_modules/.bin/tsserver +22 -0
- package/packages/auth/node_modules/.bin/tsup +22 -0
- package/packages/auth/node_modules/.bin/tsup-node +22 -0
- package/packages/auth/node_modules/.bin/vite +22 -0
- package/packages/auth/node_modules/.bin/vitest +22 -0
- package/packages/auth/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/packages/auth/package.json +61 -0
- package/packages/auth/src/AuthAdminService.ts +149 -0
- package/packages/auth/src/AuthService.ts +1166 -0
- package/packages/auth/src/SessionManager.ts +92 -0
- package/packages/auth/src/index.ts +17 -0
- package/packages/auth/src/jwks.ts +337 -0
- package/packages/auth/src/pkce.ts +27 -0
- package/packages/auth/src/types.ts +89 -0
- package/packages/auth/tests/auth-lifecycle.test.ts +149 -0
- package/packages/auth/tests/detect-session.test.ts +361 -0
- package/packages/auth/tests/getclaims.test.ts +780 -0
- package/packages/auth/tests/is-mfa-challenge.test.ts +27 -0
- package/packages/auth/tests/password-recovery.test.ts +124 -0
- package/packages/auth/tests/pkce.test.ts +192 -0
- package/packages/auth/tests/session-manager.test.ts +103 -0
- package/packages/auth/tests/set-session.test.ts +139 -0
- package/packages/auth/tests/verify-otp.test.ts +153 -0
- package/packages/auth/tsconfig.json +5 -0
- package/packages/auth/tsup.config.ts +11 -0
- package/packages/core/LICENSE +21 -0
- package/packages/core/README.md +132 -0
- package/packages/core/dist/index.cjs +695 -0
- package/packages/core/dist/index.d.cts +1137 -0
- package/packages/core/dist/index.d.ts +1137 -0
- package/packages/core/dist/index.js +673 -0
- package/packages/core/node_modules/.bin/esbuild +22 -0
- package/packages/core/node_modules/.bin/tsc +22 -0
- package/packages/core/node_modules/.bin/tsserver +22 -0
- package/packages/core/node_modules/.bin/tsup +22 -0
- package/packages/core/node_modules/.bin/tsup-node +22 -0
- package/packages/core/node_modules/.bin/vite +22 -0
- package/packages/core/node_modules/.bin/vitest +22 -0
- package/packages/core/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/packages/core/package.json +57 -0
- package/packages/core/src/AuraError.ts +89 -0
- package/packages/core/src/BaseService.ts +44 -0
- package/packages/core/src/client.ts +34 -0
- package/packages/core/src/cookie-utils.ts +42 -0
- package/packages/core/src/fetch.ts +435 -0
- package/packages/core/src/index.ts +22 -0
- package/packages/core/src/retry.ts +63 -0
- package/packages/core/src/serialize.ts +6 -0
- package/packages/core/src/stores/BaseAuthStore.ts +164 -0
- package/packages/core/src/stores/LocalAuthStore.ts +199 -0
- package/packages/core/src/types.ts +1042 -0
- package/packages/core/tests/auth-store.test.ts +300 -0
- package/packages/core/tests/error-code.test.ts +61 -0
- package/packages/core/tests/error.test.ts +24 -0
- package/packages/core/tests/fetch.test.ts +286 -0
- package/packages/core/tests/mocks.ts +139 -0
- package/packages/core/tests/observability.test.ts +86 -0
- package/packages/core/tests/refresh-hardening.test.ts +164 -0
- package/packages/core/tests/refresh-observability.test.ts +111 -0
- package/packages/core/tests/retry.test.ts +106 -0
- package/packages/core/tests/serialize.test.ts +12 -0
- package/packages/core/tests/timeout.test.ts +79 -0
- package/packages/core/tsconfig.json +5 -0
- package/packages/core/tsup.config.ts +11 -0
- package/packages/db/LICENSE +21 -0
- package/packages/db/README.md +152 -0
- package/packages/db/VENDORED.md +93 -0
- package/packages/db/dist/index.cjs +544 -0
- package/packages/db/dist/index.d.cts +1111 -0
- package/packages/db/dist/index.d.ts +1111 -0
- package/packages/db/dist/index.js +541 -0
- package/packages/db/node_modules/.bin/esbuild +19 -0
- package/packages/db/node_modules/.bin/tsc +22 -0
- package/packages/db/node_modules/.bin/tsserver +22 -0
- package/packages/db/node_modules/.bin/tsup +22 -0
- package/packages/db/node_modules/.bin/tsup-node +22 -0
- package/packages/db/node_modules/.bin/vite +22 -0
- package/packages/db/node_modules/.bin/vitest +22 -0
- package/packages/db/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/packages/db/package.json +62 -0
- package/packages/db/src/DatabaseService.ts +170 -0
- package/packages/db/src/QueryBuilder.ts +830 -0
- package/packages/db/src/index.ts +2 -0
- package/packages/db/src/pgrst-types/common/common.ts +86 -0
- package/packages/db/src/pgrst-types/common/rpc.ts +151 -0
- package/packages/db/src/pgrst-types/feature-flags.ts +20 -0
- package/packages/db/src/pgrst-types/types.ts +134 -0
- package/packages/db/src/select-query-parser/parser.ts +479 -0
- package/packages/db/src/select-query-parser/result.ts +553 -0
- package/packages/db/src/select-query-parser/types.ts +132 -0
- package/packages/db/src/select-query-parser/utils.ts +707 -0
- package/packages/db/tests/filters.test.ts +81 -0
- package/packages/db/tests/mutations-filtered.e2e.test.ts +132 -0
- package/packages/db/tests/mutations-filtered.test.ts +188 -0
- package/packages/db/tests/order.test.ts +45 -0
- package/packages/db/tests/rpc.test.ts +41 -0
- package/packages/db/tests/scale/build-large-schema.ts +94 -0
- package/packages/db/tests/scale/scale-compile.test.ts +167 -0
- package/packages/db/tests/schema.test.ts +77 -0
- package/packages/db/tests/single-accept.e2e.test.ts +123 -0
- package/packages/db/tests/single.test.ts +211 -0
- package/packages/db/tests/types/filters.test-d.ts +52 -0
- package/packages/db/tests/types/fixtures.ts +40 -0
- package/packages/db/tests/types/from-views.test-d.ts +18 -0
- package/packages/db/tests/types/mutations.test-d.ts +23 -0
- package/packages/db/tests/types/rpc.test-d.ts +60 -0
- package/packages/db/tests/types/schema-shape.test-d.ts +8 -0
- package/packages/db/tests/types/select-inference.test-d.ts +38 -0
- package/packages/db/tests/vendored/fixtures.ts +83 -0
- package/packages/db/tests/vendored/get-result.test-d.ts +75 -0
- package/packages/db/tsconfig.json +5 -0
- package/packages/db/tsconfig.test-d.json +5 -0
- package/packages/db/tsup.config.ts +11 -0
- package/packages/db/vitest.config.ts +8 -0
- package/packages/functions/LICENSE +21 -0
- package/packages/functions/README.md +96 -0
- package/packages/functions/dist/index.cjs +173 -0
- package/packages/functions/dist/index.d.cts +81 -0
- package/packages/functions/dist/index.d.ts +81 -0
- package/packages/functions/dist/index.js +168 -0
- package/packages/functions/node_modules/.bin/esbuild +19 -0
- package/packages/functions/node_modules/.bin/tsc +22 -0
- package/packages/functions/node_modules/.bin/tsserver +22 -0
- package/packages/functions/node_modules/.bin/tsup +22 -0
- package/packages/functions/node_modules/.bin/tsup-node +22 -0
- package/packages/functions/node_modules/.bin/vite +22 -0
- package/packages/functions/node_modules/.bin/vitest +22 -0
- package/packages/functions/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/packages/functions/package.json +60 -0
- package/packages/functions/src/FunctionsService.ts +188 -0
- package/packages/functions/src/errors.ts +70 -0
- package/packages/functions/src/index.ts +6 -0
- package/packages/functions/tests/invoke.test.ts +97 -0
- package/packages/functions/tests/response-type-errors.test.ts +135 -0
- package/packages/functions/tsconfig.json +5 -0
- package/packages/functions/tsup.config.ts +11 -0
- package/packages/gen/LICENSE +21 -0
- package/packages/gen/README.md +84 -0
- package/packages/gen/dist/cli.cjs +334 -0
- package/packages/gen/dist/cli.d.cts +1 -0
- package/packages/gen/dist/cli.d.ts +1 -0
- package/packages/gen/dist/cli.js +332 -0
- package/packages/gen/dist/index.cjs +257 -0
- package/packages/gen/dist/index.d.cts +12 -0
- package/packages/gen/dist/index.d.ts +12 -0
- package/packages/gen/dist/index.js +255 -0
- package/packages/gen/node_modules/.bin/esbuild +19 -0
- package/packages/gen/node_modules/.bin/tsc +22 -0
- package/packages/gen/node_modules/.bin/tsserver +22 -0
- package/packages/gen/node_modules/.bin/tsup +22 -0
- package/packages/gen/node_modules/.bin/tsup-node +22 -0
- package/packages/gen/node_modules/.bin/vite +22 -0
- package/packages/gen/node_modules/.bin/vitest +22 -0
- package/packages/gen/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/packages/gen/package.json +66 -0
- package/packages/gen/src/cli.ts +67 -0
- package/packages/gen/src/generate.ts +0 -0
- package/packages/gen/src/index.ts +2 -0
- package/packages/gen/tests/compile.test.ts +213 -0
- package/packages/gen/tests/e2e.test.ts +130 -0
- package/packages/gen/tests/generate.test.ts +396 -0
- package/packages/gen/tsconfig.json +5 -0
- package/packages/gen/tsup.config.ts +11 -0
- package/packages/notifications/LICENSE +21 -0
- package/packages/notifications/README.md +134 -0
- package/packages/notifications/dist/index.cjs +128 -0
- package/packages/notifications/dist/index.d.cts +81 -0
- package/packages/notifications/dist/index.d.ts +81 -0
- package/packages/notifications/dist/index.js +125 -0
- package/packages/notifications/node_modules/.bin/esbuild +19 -0
- package/packages/notifications/node_modules/.bin/tsc +22 -0
- package/packages/notifications/node_modules/.bin/tsserver +22 -0
- package/packages/notifications/node_modules/.bin/tsup +22 -0
- package/packages/notifications/node_modules/.bin/tsup-node +22 -0
- package/packages/notifications/node_modules/.bin/vite +22 -0
- package/packages/notifications/node_modules/.bin/vitest +22 -0
- package/packages/notifications/package.json +60 -0
- package/packages/notifications/src/NotificationsService.ts +230 -0
- package/packages/notifications/src/index.ts +19 -0
- package/packages/notifications/tsconfig.json +5 -0
- package/packages/notifications/tsup.config.ts +11 -0
- package/packages/realtime/LICENSE +21 -0
- package/packages/realtime/README.md +183 -0
- package/packages/realtime/dist/index.cjs +977 -0
- package/packages/realtime/dist/index.d.cts +259 -0
- package/packages/realtime/dist/index.d.ts +259 -0
- package/packages/realtime/dist/index.js +973 -0
- package/packages/realtime/node_modules/.bin/esbuild +19 -0
- package/packages/realtime/node_modules/.bin/tsc +22 -0
- package/packages/realtime/node_modules/.bin/tsserver +22 -0
- package/packages/realtime/node_modules/.bin/tsup +22 -0
- package/packages/realtime/node_modules/.bin/tsup-node +22 -0
- package/packages/realtime/node_modules/.bin/vite +22 -0
- package/packages/realtime/node_modules/.bin/vitest +22 -0
- package/packages/realtime/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/packages/realtime/package.json +60 -0
- package/packages/realtime/src/AuraChannel.ts +485 -0
- package/packages/realtime/src/HeartbeatManager.ts +73 -0
- package/packages/realtime/src/RealtimeService.ts +505 -0
- package/packages/realtime/src/columnFilter.ts +301 -0
- package/packages/realtime/src/index.ts +4 -0
- package/packages/realtime/tests/ack-routing.test.ts +81 -0
- package/packages/realtime/tests/column-filter.test.ts +144 -0
- package/packages/realtime/tests/dispatch.test.ts +191 -0
- package/packages/realtime/tests/heartbeat.test.ts +67 -0
- package/packages/realtime/tests/http-broadcast.test.ts +68 -0
- package/packages/realtime/tests/realtime-token.test.ts +43 -0
- package/packages/realtime/tests/reconnect-race.test.ts +149 -0
- package/packages/realtime/tests/ref-id.test.ts +34 -0
- package/packages/realtime/tests/set-auth.test.ts +70 -0
- package/packages/realtime/tsconfig.json +5 -0
- package/packages/realtime/tsup.config.ts +11 -0
- package/packages/storage/LICENSE +21 -0
- package/packages/storage/README.md +141 -0
- package/packages/storage/dist/index.cjs +333 -0
- package/packages/storage/dist/index.d.cts +185 -0
- package/packages/storage/dist/index.d.ts +185 -0
- package/packages/storage/dist/index.js +329 -0
- package/packages/storage/node_modules/.bin/esbuild +19 -0
- package/packages/storage/node_modules/.bin/tsc +22 -0
- package/packages/storage/node_modules/.bin/tsserver +22 -0
- package/packages/storage/node_modules/.bin/tsup +22 -0
- package/packages/storage/node_modules/.bin/tsup-node +22 -0
- package/packages/storage/node_modules/.bin/vite +22 -0
- package/packages/storage/node_modules/.bin/vitest +22 -0
- package/packages/storage/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/packages/storage/package.json +60 -0
- package/packages/storage/src/DownloadBuilder.ts +60 -0
- package/packages/storage/src/StorageService.ts +323 -0
- package/packages/storage/src/index.ts +2 -0
- package/packages/storage/src/path.ts +35 -0
- package/packages/storage/src/transform.ts +15 -0
- package/packages/storage/tests/download.test.ts +64 -0
- package/packages/storage/tests/parity.test.ts +154 -0
- package/packages/storage/tests/path.test.ts +87 -0
- package/packages/storage/tests/transform.test.ts +79 -0
- package/packages/storage/tsconfig.json +5 -0
- package/packages/storage/tsup.config.ts +11 -0
- package/pnpm-workspace.yaml +24 -0
- package/tsconfig.base.json +15 -0
|
@@ -0,0 +1,505 @@
|
|
|
1
|
+
import type { AuraClientLike, AuraResponse } from '@aurabase/core'
|
|
2
|
+
import type { ServerMessage, ChannelConfig, BroadcastAckMessage } from '@aurabase/core'
|
|
3
|
+
import { fetchWithAuth } from '@aurabase/core'
|
|
4
|
+
import { AuraChannel } from './AuraChannel'
|
|
5
|
+
import { HeartbeatManager } from './HeartbeatManager'
|
|
6
|
+
|
|
7
|
+
/** Connection state machine */
|
|
8
|
+
export type ConnectionState = 'disconnected' | 'connecting' | 'connected' | 'reconnecting'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Realtime service — manages a single WebSocket connection and multiplexes
|
|
12
|
+
* channels on top of it. Supabase-compatible API.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* const aura = createClient('http://localhost:8080', 'my-project')
|
|
17
|
+
*
|
|
18
|
+
* const channel = aura.realtime.channel('room-1')
|
|
19
|
+
* .on('postgres_changes', { event: 'INSERT', schema: 'public', table: 'messages' }, (payload) => {
|
|
20
|
+
* console.log('New message:', payload)
|
|
21
|
+
* })
|
|
22
|
+
* .on('broadcast', { event: 'cursor-pos' }, (payload) => {
|
|
23
|
+
* console.log('Cursor:', payload)
|
|
24
|
+
* })
|
|
25
|
+
* .subscribe((status) => {
|
|
26
|
+
* if (status === 'SUBSCRIBED') {
|
|
27
|
+
* channel.send({ type: 'broadcast', event: 'cursor-pos', payload: { x: 100, y: 200 } })
|
|
28
|
+
* }
|
|
29
|
+
* })
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export class RealtimeService {
|
|
33
|
+
private readonly client: AuraClientLike
|
|
34
|
+
private _ws: WebSocket | null = null
|
|
35
|
+
private _channels = new Map<string, AuraChannel>()
|
|
36
|
+
private _state: ConnectionState = 'disconnected'
|
|
37
|
+
private _stateListeners = new Set<(state: ConnectionState) => void>()
|
|
38
|
+
|
|
39
|
+
// ─── Reconnection state ──────────────────────────────────────────────
|
|
40
|
+
private _autoReconnect = true
|
|
41
|
+
private _reconnectAttempts = 0
|
|
42
|
+
private _maxReconnectAttempts = Infinity
|
|
43
|
+
private _reconnectTimer: ReturnType<typeof setTimeout> | null = null
|
|
44
|
+
private _intentionalClose = false
|
|
45
|
+
private _heartbeat = new HeartbeatManager()
|
|
46
|
+
private _tokenRefreshTimer: ReturnType<typeof setTimeout> | null = null
|
|
47
|
+
/** Dernier access_token propagé au serveur — évite les pushs redondants (propagation différentielle). */
|
|
48
|
+
private _lastSentToken: string | null = null
|
|
49
|
+
|
|
50
|
+
/** Backoff delays in ms: 1s, 2s, 4s, 8s, 16s, 30s (cap) */
|
|
51
|
+
private static readonly BACKOFF_MS = [1000, 2000, 4000, 8000, 16000, 30000]
|
|
52
|
+
|
|
53
|
+
constructor(client: AuraClientLike) {
|
|
54
|
+
this.client = client
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Current connection state. */
|
|
58
|
+
get connectionState(): ConnectionState {
|
|
59
|
+
return this._state
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Listen for connection state changes. */
|
|
63
|
+
onStateChange(callback: (state: ConnectionState) => void): () => void {
|
|
64
|
+
this._stateListeners.add(callback)
|
|
65
|
+
return () => this._stateListeners.delete(callback)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
private _setState(state: ConnectionState): void {
|
|
69
|
+
this._state = state
|
|
70
|
+
for (const cb of this._stateListeners) {
|
|
71
|
+
try { cb(state) } catch { /* ignore */ }
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// ─── WebSocket URL ──────────────────────────────────────────────────────
|
|
76
|
+
|
|
77
|
+
private get _wsUrl(): string {
|
|
78
|
+
const base = this.client.baseUrl.replace(/^http/, 'ws')
|
|
79
|
+
return `${base}/v1/realtime/ws`
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ─── Connection management ──────────────────────────────────────────────
|
|
83
|
+
|
|
84
|
+
/** Ensure the WebSocket connection is open. */
|
|
85
|
+
connect(): void {
|
|
86
|
+
if (this._ws && (this._ws.readyState === WebSocket.OPEN || this._ws.readyState === WebSocket.CONNECTING)) {
|
|
87
|
+
return
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Toute nouvelle tentative de connexion repart d'un état « non-intentionnel » :
|
|
91
|
+
// évite qu'un flag `_intentionalClose` laissé à true (car le onclose de
|
|
92
|
+
// l'ancien socket est désormais court-circuité par la garde anti-race) ne
|
|
93
|
+
// fasse traiter une future coupure inattendue comme volontaire.
|
|
94
|
+
this._intentionalClose = false
|
|
95
|
+
|
|
96
|
+
this._setState('connecting')
|
|
97
|
+
|
|
98
|
+
const token = this.client.authStore.token
|
|
99
|
+
const params = new URLSearchParams()
|
|
100
|
+
if (token) params.set('token', token)
|
|
101
|
+
// Clé API en query param (les navigateurs ne permettent pas de headers custom sur WebSocket)
|
|
102
|
+
if (this.client.apiKey) params.set('apikey', this.client.apiKey)
|
|
103
|
+
|
|
104
|
+
const url = `${this._wsUrl}?${params.toString()}`
|
|
105
|
+
// On capture `ws` dans les closures des handlers : un ancien socket dont
|
|
106
|
+
// l'événement `onclose`/`onopen` se déclenche de façon asynchrone APRÈS
|
|
107
|
+
// qu'un nouveau socket ait été créé ne doit pas écraser la connexion
|
|
108
|
+
// courante (race « stale onclose »). Chaque handler se garde donc par
|
|
109
|
+
// `if (ws !== this._ws) return`.
|
|
110
|
+
const ws = new WebSocket(url)
|
|
111
|
+
this._ws = ws
|
|
112
|
+
|
|
113
|
+
ws.onopen = () => {
|
|
114
|
+
// Socket périmé (superseded par un connect()/reconnexion) ⇒ on le ferme
|
|
115
|
+
// proprement et on n'active rien dessus.
|
|
116
|
+
if (ws !== this._ws) {
|
|
117
|
+
try { ws.close() } catch { /* ignore */ }
|
|
118
|
+
return
|
|
119
|
+
}
|
|
120
|
+
this._reconnectAttempts = 0
|
|
121
|
+
this._setState('connected')
|
|
122
|
+
|
|
123
|
+
// Démarrer le heartbeat proactif (anti-throttle navigateur)
|
|
124
|
+
this._heartbeat.start(() => this._send(JSON.stringify({ type: 'ping' })), 25000)
|
|
125
|
+
|
|
126
|
+
// Planifier le refresh proactif du token (à 80% du TTL)
|
|
127
|
+
this._scheduleTokenRefresh()
|
|
128
|
+
|
|
129
|
+
// Bind send function and re-subscribe all pending channels
|
|
130
|
+
for (const channel of this._channels.values()) {
|
|
131
|
+
channel._bindSend((data) => this._send(data))
|
|
132
|
+
if (channel.isSubscribed) {
|
|
133
|
+
channel._doSubscribe()
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
ws.onmessage = (evt) => {
|
|
139
|
+
// Ignore les messages d'un socket périmé.
|
|
140
|
+
if (ws !== this._ws) return
|
|
141
|
+
try {
|
|
142
|
+
const raw = typeof evt.data === 'string' ? evt.data : ''
|
|
143
|
+
const msg: ServerMessage = JSON.parse(raw)
|
|
144
|
+
this._dispatch(msg)
|
|
145
|
+
} catch (e) {
|
|
146
|
+
if (typeof console !== 'undefined') {
|
|
147
|
+
console.warn('[aura-realtime] Failed to parse WS message:', evt.data, e)
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
ws.onclose = () => {
|
|
153
|
+
// Garde anti-race : si ce socket n'est plus le socket courant (une
|
|
154
|
+
// nouvelle connexion l'a remplacé, ou disconnect() a déjà nettoyé),
|
|
155
|
+
// son `onclose` tardif ne doit PAS toucher l'état partagé.
|
|
156
|
+
if (ws !== this._ws) return
|
|
157
|
+
this._ws = null
|
|
158
|
+
// Nouvelle connexion (après reconnexion auto) ⇒ le token devra être renvoyé.
|
|
159
|
+
this._lastSentToken = null
|
|
160
|
+
this._heartbeat.stop()
|
|
161
|
+
if (this._intentionalClose) {
|
|
162
|
+
this._setState('disconnected')
|
|
163
|
+
this._intentionalClose = false
|
|
164
|
+
} else if (this._autoReconnect) {
|
|
165
|
+
this._scheduleReconnect()
|
|
166
|
+
} else {
|
|
167
|
+
this._setState('disconnected')
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
ws.onerror = (evt) => {
|
|
172
|
+
if (ws !== this._ws) return
|
|
173
|
+
if (typeof console !== 'undefined') {
|
|
174
|
+
console.warn('[aura-realtime] WebSocket error', evt)
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/** Send raw data through the WebSocket. */
|
|
180
|
+
private _send(data: string): void {
|
|
181
|
+
if (this._ws && this._ws.readyState === WebSocket.OPEN) {
|
|
182
|
+
this._ws.send(data)
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/** Refresh the JWT token on an existing connection (no disconnect). */
|
|
187
|
+
refreshToken(token: string): void {
|
|
188
|
+
if (token === this._lastSentToken) {
|
|
189
|
+
this._scheduleTokenRefresh()
|
|
190
|
+
return
|
|
191
|
+
}
|
|
192
|
+
this._lastSentToken = token
|
|
193
|
+
this._send(JSON.stringify({ type: 'access_token', token }))
|
|
194
|
+
this._scheduleTokenRefresh()
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Définit/rafraîchit explicitement le token utilisé pour l'autorisation des
|
|
199
|
+
* canaux privés et broadcast (parité `@supabase/realtime-js` `setAuth`).
|
|
200
|
+
*
|
|
201
|
+
* - Avec `token` : utilise ce token. Sinon, reprend `client.authStore.token`.
|
|
202
|
+
* - Propage via le pipeline existant `refreshToken` (push
|
|
203
|
+
* `{ type:'access_token', token }` sur une connexion ouverte, dédupliqué via
|
|
204
|
+
* `_lastSentToken`). N'ouvre ni ne ferme aucune connexion WS.
|
|
205
|
+
* - Hors-ligne : `connect()` lit `client.authStore.token` au moment de l'open,
|
|
206
|
+
* donc `setAuth()` doit être appelé avec un token présent dans le store (ou
|
|
207
|
+
* après authentification) pour prendre effet sur une future connexion.
|
|
208
|
+
*
|
|
209
|
+
* À appeler AVANT de s'abonner à un canal privé (`channel(name,{private:true})`).
|
|
210
|
+
*
|
|
211
|
+
* @example
|
|
212
|
+
* ```ts
|
|
213
|
+
* aura.realtime.setAuth(session.access_token)
|
|
214
|
+
* const ch = aura.realtime.channel('secret', { private: true }).subscribe()
|
|
215
|
+
* ```
|
|
216
|
+
*/
|
|
217
|
+
setAuth(token?: string): void {
|
|
218
|
+
const resolved = token ?? this.client.authStore.token
|
|
219
|
+
if (resolved == null) return
|
|
220
|
+
this.refreshToken(resolved)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Envoie un broadcast par HTTP (REST), sans connexion WebSocket ouverte
|
|
225
|
+
* (parité `@supabase/realtime-js`). Utile côté serveur/edge.
|
|
226
|
+
*
|
|
227
|
+
* POST `/v1/realtime/{projectId}/broadcast` avec le contrat du handler backend
|
|
228
|
+
* (`broadcast.rs`) : `{ channel, payload }`, `channel` préfixé par le projet,
|
|
229
|
+
* `event` replié dans `payload` (aligné sur le broadcast WS).
|
|
230
|
+
*
|
|
231
|
+
* @param channel Nom du canal (préfixé projet automatiquement si absent).
|
|
232
|
+
* @param event Nom de l'événement broadcast.
|
|
233
|
+
* @param payload Données de l'événement.
|
|
234
|
+
* @returns `AuraResponse<null>` — `error` non nul en cas de 4xx/rate-limit.
|
|
235
|
+
*
|
|
236
|
+
* @example
|
|
237
|
+
* ```ts
|
|
238
|
+
* await aura.realtime.httpBroadcast('room-1', 'cursor', { x: 10, y: 20 })
|
|
239
|
+
* ```
|
|
240
|
+
*/
|
|
241
|
+
httpBroadcast(
|
|
242
|
+
channel: string,
|
|
243
|
+
event: string,
|
|
244
|
+
payload: Record<string, unknown> = {},
|
|
245
|
+
): Promise<AuraResponse<null>> {
|
|
246
|
+
const prefix = `${this.client.projectId}:`
|
|
247
|
+
const fullChannel = channel.startsWith(prefix) ? channel : `${prefix}${channel}`
|
|
248
|
+
const body = {
|
|
249
|
+
channel: fullChannel,
|
|
250
|
+
payload: { event, ...payload },
|
|
251
|
+
}
|
|
252
|
+
return fetchWithAuth<null>(
|
|
253
|
+
`${this.client.baseUrl}/v1/realtime/${this.client.projectId}/broadcast`,
|
|
254
|
+
{ method: 'POST', body },
|
|
255
|
+
this.client,
|
|
256
|
+
)
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Schedule proactive token refresh at 80% of the JWT TTL.
|
|
261
|
+
* Prevents token expiration during an active WebSocket session.
|
|
262
|
+
*/
|
|
263
|
+
private _scheduleTokenRefresh(): void {
|
|
264
|
+
if (this._tokenRefreshTimer) {
|
|
265
|
+
clearTimeout(this._tokenRefreshTimer)
|
|
266
|
+
this._tokenRefreshTimer = null
|
|
267
|
+
}
|
|
268
|
+
const token = this.client.authStore.token
|
|
269
|
+
if (!token) return
|
|
270
|
+
try {
|
|
271
|
+
const payload = JSON.parse(atob(token.split('.')[1]))
|
|
272
|
+
const exp = payload.exp * 1000
|
|
273
|
+
const ttl = exp - Date.now()
|
|
274
|
+
if (ttl <= 0) return
|
|
275
|
+
// Refresh at 80% of the remaining TTL
|
|
276
|
+
const refreshIn = ttl * 0.8
|
|
277
|
+
this._tokenRefreshTimer = setTimeout(async () => {
|
|
278
|
+
this._tokenRefreshTimer = null
|
|
279
|
+
try {
|
|
280
|
+
const res = await this.client.auth.refreshSession()
|
|
281
|
+
if (res.data) {
|
|
282
|
+
this.refreshToken(res.data.access_token)
|
|
283
|
+
}
|
|
284
|
+
} catch {
|
|
285
|
+
// Refresh failed — the server will eventually disconnect on token expiry
|
|
286
|
+
}
|
|
287
|
+
}, refreshIn)
|
|
288
|
+
} catch {
|
|
289
|
+
// JWT decode failed — skip proactive refresh
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/** Schedule a reconnection attempt with exponential backoff + jitter. */
|
|
294
|
+
private _scheduleReconnect(): void {
|
|
295
|
+
// Clear any existing timer to avoid double-reconnect
|
|
296
|
+
if (this._reconnectTimer) {
|
|
297
|
+
clearTimeout(this._reconnectTimer)
|
|
298
|
+
this._reconnectTimer = null
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
if (this._reconnectAttempts >= this._maxReconnectAttempts) {
|
|
302
|
+
this._setState('disconnected')
|
|
303
|
+
return
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
this._reconnectAttempts++
|
|
307
|
+
this._setState('reconnecting')
|
|
308
|
+
|
|
309
|
+
const idx = Math.min(this._reconnectAttempts - 1, RealtimeService.BACKOFF_MS.length - 1)
|
|
310
|
+
const baseDelay = RealtimeService.BACKOFF_MS[idx]
|
|
311
|
+
const jitter = Math.random() * baseDelay * 0.25
|
|
312
|
+
const delay = baseDelay + jitter
|
|
313
|
+
|
|
314
|
+
this._reconnectTimer = setTimeout(() => {
|
|
315
|
+
this._reconnectTimer = null
|
|
316
|
+
this.connect()
|
|
317
|
+
}, delay)
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/** Enable/disable auto-reconnect (enabled by default). */
|
|
321
|
+
setAutoReconnect(enabled: boolean, maxAttempts?: number): void {
|
|
322
|
+
this._autoReconnect = enabled
|
|
323
|
+
if (maxAttempts !== undefined) this._maxReconnectAttempts = maxAttempts
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/** Disconnect the WebSocket and clean up all channels. */
|
|
327
|
+
disconnect(): void {
|
|
328
|
+
this._intentionalClose = true
|
|
329
|
+
this._heartbeat.stop()
|
|
330
|
+
if (this._tokenRefreshTimer) {
|
|
331
|
+
clearTimeout(this._tokenRefreshTimer)
|
|
332
|
+
this._tokenRefreshTimer = null
|
|
333
|
+
}
|
|
334
|
+
if (this._reconnectTimer) {
|
|
335
|
+
clearTimeout(this._reconnectTimer)
|
|
336
|
+
this._reconnectTimer = null
|
|
337
|
+
}
|
|
338
|
+
this._reconnectAttempts = 0
|
|
339
|
+
for (const channel of this._channels.values()) {
|
|
340
|
+
channel.unsubscribe()
|
|
341
|
+
}
|
|
342
|
+
this._channels.clear()
|
|
343
|
+
if (this._ws) {
|
|
344
|
+
this._ws.close()
|
|
345
|
+
this._ws = null
|
|
346
|
+
}
|
|
347
|
+
this._setState('disconnected')
|
|
348
|
+
this._lastSentToken = null
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// ─── Channel factory ────────────────────────────────────────────────────
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Create or retrieve a channel by name.
|
|
355
|
+
* Returns an `AuraChannel` with chainable `.on()` / `.subscribe()` API.
|
|
356
|
+
*
|
|
357
|
+
* @example
|
|
358
|
+
* ```ts
|
|
359
|
+
* const channel = aura.realtime.channel('room-1')
|
|
360
|
+
* ```
|
|
361
|
+
*/
|
|
362
|
+
channel(name: string, options?: ChannelConfig): AuraChannel {
|
|
363
|
+
const existing = this._channels.get(name)
|
|
364
|
+
if (existing) return existing
|
|
365
|
+
|
|
366
|
+
const ch = new AuraChannel(name, this.client.projectId, options)
|
|
367
|
+
this._channels.set(name, ch)
|
|
368
|
+
|
|
369
|
+
// Établit la connexion WS si nécessaire (idempotent — connect() vérifie
|
|
370
|
+
// déjà readyState avant d'ouvrir un nouveau socket).
|
|
371
|
+
this.connect()
|
|
372
|
+
|
|
373
|
+
// If already connected, bind immediately
|
|
374
|
+
if (this._ws && this._ws.readyState === WebSocket.OPEN) {
|
|
375
|
+
ch._bindSend((data) => this._send(data))
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
return ch
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/** Remove a channel. */
|
|
382
|
+
removeChannel(name: string): void {
|
|
383
|
+
const ch = this._channels.get(name)
|
|
384
|
+
if (ch) {
|
|
385
|
+
ch.unsubscribe()
|
|
386
|
+
this._channels.delete(name)
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/** List active channels. */
|
|
391
|
+
getChannels(): AuraChannel[] {
|
|
392
|
+
return Array.from(this._channels.values())
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/** Remove all channels and subscriptions. */
|
|
396
|
+
removeAllChannels(): void {
|
|
397
|
+
for (const ch of this._channels.values()) {
|
|
398
|
+
ch.unsubscribe()
|
|
399
|
+
}
|
|
400
|
+
this._channels.clear()
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// ─── Message dispatch ───────────────────────────────────────────────────
|
|
404
|
+
|
|
405
|
+
/** Route incoming server messages to the correct channel. */
|
|
406
|
+
private _dispatch(msg: ServerMessage): void {
|
|
407
|
+
// Extract channel name — present in subscribed, presence_state, OR topic in event messages
|
|
408
|
+
// ServerMessage::Event uses 'topic' (serde flatten de RealtimeEvent), pas 'channel'
|
|
409
|
+
const channelFull = 'channel' in msg
|
|
410
|
+
? (msg as { channel?: string }).channel
|
|
411
|
+
: 'topic' in msg
|
|
412
|
+
? (msg as { topic?: string }).topic
|
|
413
|
+
: undefined
|
|
414
|
+
|
|
415
|
+
if (channelFull) {
|
|
416
|
+
// Strip project prefix to find channel
|
|
417
|
+
const prefix = `${this.client.projectId}:`
|
|
418
|
+
const name = channelFull.startsWith(prefix) ? channelFull.slice(prefix.length) : channelFull
|
|
419
|
+
const ch = this._channels.get(name)
|
|
420
|
+
if (ch) {
|
|
421
|
+
ch._onMessage(msg)
|
|
422
|
+
return
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// Try matching by full name (for CDC channels like proj:db:schema.table)
|
|
426
|
+
// The channel might have been created with a short name but CDC sends full topic
|
|
427
|
+
for (const [, channel] of this._channels) {
|
|
428
|
+
if (channel.fullName === channelFull || channelFull.startsWith(channel.fullName + ':')) {
|
|
429
|
+
channel._onMessage(msg)
|
|
430
|
+
return
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// Messages without channel (pong, token_refreshed, global errors)
|
|
436
|
+
if (msg.type === 'token_refreshed') {
|
|
437
|
+
// Global token refresh ACK — no action needed
|
|
438
|
+
return
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
if (msg.type === 'pong') {
|
|
442
|
+
// Keepalive — no action needed
|
|
443
|
+
return
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
if (msg.type === 'broadcast_ack') {
|
|
447
|
+
// Router l'ACK vers le SEUL channel émetteur. Le `ref_id` est préfixé par
|
|
448
|
+
// le `fullName` du channel (`{fullName}:r{n}`, cf. AuraChannel._nextRef) :
|
|
449
|
+
// on récupère le suffixe `:r{n}` via lastIndexOf (robuste à un nom de
|
|
450
|
+
// channel contenant lui-même « :r ») et on résout par `fullName`.
|
|
451
|
+
const ack = msg as BroadcastAckMessage
|
|
452
|
+
const refId = ack.ref_id ?? ack.message_id
|
|
453
|
+
if (refId) {
|
|
454
|
+
const marker = refId.lastIndexOf(':r')
|
|
455
|
+
if (marker > 0) {
|
|
456
|
+
const fullName = refId.slice(0, marker)
|
|
457
|
+
for (const ch of this._channels.values()) {
|
|
458
|
+
if (ch.fullName === fullName) {
|
|
459
|
+
ch._onMessage(msg)
|
|
460
|
+
return
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
// Fallback prudent : `ref_id` non résoluble (préfixe inattendu ou
|
|
466
|
+
// `message_id` brut sans « :r ») → dispatch à tous pour ne pas perdre
|
|
467
|
+
// l'ACK. Chaque channel n'agit que sur un `ref_id`/`message_id` présent
|
|
468
|
+
// dans ses `_pendingAcks`, donc aucun faux positif.
|
|
469
|
+
for (const ch of this._channels.values()) {
|
|
470
|
+
ch._onMessage(msg)
|
|
471
|
+
}
|
|
472
|
+
return
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// Broadcast to all channels for unroutable messages
|
|
476
|
+
if (msg.type === 'error') {
|
|
477
|
+
for (const ch of this._channels.values()) {
|
|
478
|
+
ch._onMessage(msg)
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
// ─── Backward compatibility ─────────────────────────────────────────────
|
|
484
|
+
|
|
485
|
+
/**
|
|
486
|
+
* Simple callback-based subscription (legacy API).
|
|
487
|
+
* Prefer using `.channel().on().subscribe()` instead.
|
|
488
|
+
*
|
|
489
|
+
* @deprecated Use `channel().on().subscribe()` instead.
|
|
490
|
+
*/
|
|
491
|
+
on<T = Record<string, unknown>>(
|
|
492
|
+
channelName: string,
|
|
493
|
+
callback: (event: any) => void,
|
|
494
|
+
): { unsubscribe: () => void } {
|
|
495
|
+
this.connect()
|
|
496
|
+
|
|
497
|
+
const ch = this.channel(channelName)
|
|
498
|
+
ch.on('postgres_changes', { event: '*' }, callback)
|
|
499
|
+
ch.subscribe()
|
|
500
|
+
|
|
501
|
+
return {
|
|
502
|
+
unsubscribe: () => this.removeChannel(channelName),
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
}
|