alepha 0.13.0 → 0.13.2
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/README.md +1 -1
- package/dist/api-files/index.d.ts +28 -91
- package/dist/api-files/index.js +10 -755
- package/dist/api-files/index.js.map +1 -1
- package/dist/api-jobs/index.d.ts +67 -67
- package/dist/api-jobs/index.js +13 -13
- package/dist/api-jobs/index.js.map +1 -1
- package/dist/api-notifications/index.d.ts +129 -146
- package/dist/api-notifications/index.js +17 -39
- package/dist/api-notifications/index.js.map +1 -1
- package/dist/api-parameters/index.d.ts +21 -22
- package/dist/api-parameters/index.js +22 -22
- package/dist/api-parameters/index.js.map +1 -1
- package/dist/api-users/index.d.ts +224 -2001
- package/dist/api-users/index.js +914 -4787
- package/dist/api-users/index.js.map +1 -1
- package/dist/api-verifications/index.d.ts +96 -96
- package/dist/batch/index.d.ts +13 -13
- package/dist/batch/index.js +8 -8
- package/dist/batch/index.js.map +1 -1
- package/dist/bucket/index.d.ts +14 -14
- package/dist/bucket/index.js +12 -12
- package/dist/bucket/index.js.map +1 -1
- package/dist/cache/index.d.ts +11 -11
- package/dist/cache/index.js +9 -9
- package/dist/cache/index.js.map +1 -1
- package/dist/cli/{dist-Sz2EXvQX.cjs → dist-Dl9Vl7Ur.js} +17 -13
- package/dist/cli/{dist-BBPjuQ56.js.map → dist-Dl9Vl7Ur.js.map} +1 -1
- package/dist/cli/index.d.ts +31 -37
- package/dist/cli/index.js +152 -83
- package/dist/cli/index.js.map +1 -1
- package/dist/command/index.d.ts +19 -19
- package/dist/command/index.js +25 -25
- package/dist/command/index.js.map +1 -1
- package/dist/core/index.browser.js +218 -218
- package/dist/core/index.browser.js.map +1 -1
- package/dist/core/index.d.ts +232 -232
- package/dist/core/index.js +218 -218
- package/dist/core/index.js.map +1 -1
- package/dist/core/{index.cjs → index.native.js} +304 -455
- package/dist/core/index.native.js.map +1 -0
- package/dist/datetime/index.d.ts +9 -9
- package/dist/datetime/index.js +7 -7
- package/dist/datetime/index.js.map +1 -1
- package/dist/email/index.d.ts +16 -16
- package/dist/email/index.js +80 -82
- package/dist/email/index.js.map +1 -1
- package/dist/file/index.js +1 -1
- package/dist/file/index.js.map +1 -1
- package/dist/lock/index.d.ts +9 -9
- package/dist/lock/index.js +8 -8
- package/dist/lock/index.js.map +1 -1
- package/dist/lock-redis/index.js +3 -66
- package/dist/lock-redis/index.js.map +1 -1
- package/dist/logger/index.d.ts +5 -5
- package/dist/logger/index.js +8 -8
- package/dist/logger/index.js.map +1 -1
- package/dist/orm/index.browser.js +114 -114
- package/dist/orm/index.browser.js.map +1 -1
- package/dist/orm/index.d.ts +219 -219
- package/dist/orm/index.js +46 -46
- package/dist/orm/index.js.map +1 -1
- package/dist/queue/index.d.ts +25 -25
- package/dist/queue/index.js +20 -20
- package/dist/queue/index.js.map +1 -1
- package/dist/queue-redis/index.d.ts +2 -2
- package/dist/redis/index.d.ts +10 -10
- package/dist/retry/index.d.ts +20 -20
- package/dist/retry/index.js +9 -9
- package/dist/retry/index.js.map +1 -1
- package/dist/scheduler/index.d.ts +12 -12
- package/dist/scheduler/index.js +9 -9
- package/dist/scheduler/index.js.map +1 -1
- package/dist/security/index.d.ts +53 -53
- package/dist/security/index.js +32 -32
- package/dist/security/index.js.map +1 -1
- package/dist/server/index.browser.js +1 -1
- package/dist/server/index.browser.js.map +1 -1
- package/dist/server/index.d.ts +101 -101
- package/dist/server/index.js +17 -17
- package/dist/server/index.js.map +1 -1
- package/dist/server-auth/index.browser.js +4 -982
- package/dist/server-auth/index.browser.js.map +1 -1
- package/dist/server-auth/index.d.ts +204 -785
- package/dist/server-auth/index.js +47 -1239
- package/dist/server-auth/index.js.map +1 -1
- package/dist/server-cache/index.d.ts +10 -10
- package/dist/server-cache/index.js +2 -2
- package/dist/server-cache/index.js.map +1 -1
- package/dist/server-compress/index.d.ts +4 -4
- package/dist/server-compress/index.js +1 -1
- package/dist/server-compress/index.js.map +1 -1
- package/dist/server-cookies/index.browser.js +8 -8
- package/dist/server-cookies/index.browser.js.map +1 -1
- package/dist/server-cookies/index.d.ts +17 -17
- package/dist/server-cookies/index.js +10 -10
- package/dist/server-cookies/index.js.map +1 -1
- package/dist/server-cors/index.d.ts +17 -17
- package/dist/server-cors/index.js +9 -9
- package/dist/server-cors/index.js.map +1 -1
- package/dist/server-health/index.d.ts +2 -2
- package/dist/server-helmet/index.d.ts +1 -1
- package/dist/server-links/index.browser.js +12 -12
- package/dist/server-links/index.browser.js.map +1 -1
- package/dist/server-links/index.d.ts +59 -251
- package/dist/server-links/index.js +23 -502
- package/dist/server-links/index.js.map +1 -1
- package/dist/server-metrics/index.d.ts +4 -4
- package/dist/server-metrics/index.js +170 -174
- package/dist/server-metrics/index.js.map +1 -1
- package/dist/server-multipart/index.d.ts +2 -2
- package/dist/server-proxy/index.d.ts +12 -12
- package/dist/server-proxy/index.js +10 -10
- package/dist/server-proxy/index.js.map +1 -1
- package/dist/server-rate-limit/index.d.ts +22 -22
- package/dist/server-rate-limit/index.js +12 -12
- package/dist/server-rate-limit/index.js.map +1 -1
- package/dist/server-security/index.d.ts +24 -24
- package/dist/server-security/index.js +15 -15
- package/dist/server-security/index.js.map +1 -1
- package/dist/server-static/index.d.ts +14 -14
- package/dist/server-static/index.js +8 -8
- package/dist/server-static/index.js.map +1 -1
- package/dist/server-swagger/index.d.ts +25 -184
- package/dist/server-swagger/index.js +21 -724
- package/dist/server-swagger/index.js.map +1 -1
- package/dist/sms/index.d.ts +14 -14
- package/dist/sms/index.js +9 -9
- package/dist/sms/index.js.map +1 -1
- package/dist/thread/index.d.ts +11 -11
- package/dist/thread/index.js +17 -17
- package/dist/thread/index.js.map +1 -1
- package/dist/topic/index.d.ts +26 -26
- package/dist/topic/index.js +16 -16
- package/dist/topic/index.js.map +1 -1
- package/dist/topic-redis/index.d.ts +1 -1
- package/dist/vite/index.d.ts +3 -3
- package/dist/vite/index.js +12 -13
- package/dist/vite/index.js.map +1 -1
- package/dist/websocket/index.browser.js +11 -11
- package/dist/websocket/index.browser.js.map +1 -1
- package/dist/websocket/index.d.ts +51 -51
- package/dist/websocket/index.js +13 -13
- package/dist/websocket/index.js.map +1 -1
- package/package.json +62 -52
- package/src/api-files/services/FileService.ts +5 -7
- package/src/api-jobs/index.ts +1 -1
- package/src/api-jobs/{descriptors → primitives}/$job.ts +8 -8
- package/src/api-jobs/providers/JobProvider.ts +9 -9
- package/src/api-jobs/services/JobService.ts +5 -5
- package/src/api-notifications/index.ts +5 -15
- package/src/api-notifications/{descriptors → primitives}/$notification.ts +10 -10
- package/src/api-notifications/services/NotificationSenderService.ts +3 -3
- package/src/api-parameters/index.ts +1 -1
- package/src/api-parameters/{descriptors → primitives}/$config.ts +7 -12
- package/src/api-users/index.ts +1 -1
- package/src/api-users/{descriptors → primitives}/$userRealm.ts +8 -8
- package/src/api-users/providers/UserRealmProvider.ts +1 -1
- package/src/batch/index.ts +3 -3
- package/src/batch/{descriptors → primitives}/$batch.ts +13 -16
- package/src/bucket/index.ts +8 -8
- package/src/bucket/{descriptors → primitives}/$bucket.ts +8 -8
- package/src/bucket/providers/LocalFileStorageProvider.ts +3 -3
- package/src/cache/index.ts +4 -4
- package/src/cache/{descriptors → primitives}/$cache.ts +15 -15
- package/src/cli/apps/AlephaPackageBuilderCli.ts +30 -3
- package/src/cli/assets/appRouterTs.ts +9 -0
- package/src/cli/assets/indexHtml.ts +2 -1
- package/src/cli/assets/mainBrowserTs.ts +10 -0
- package/src/cli/commands/CoreCommands.ts +6 -5
- package/src/cli/commands/DrizzleCommands.ts +69 -61
- package/src/cli/commands/VerifyCommands.ts +2 -2
- package/src/cli/commands/ViteCommands.ts +6 -1
- package/src/cli/services/ProjectUtils.ts +78 -41
- package/src/command/index.ts +5 -5
- package/src/command/{descriptors → primitives}/$command.ts +9 -12
- package/src/command/providers/CliProvider.ts +10 -10
- package/src/core/Alepha.ts +30 -33
- package/src/core/constants/KIND.ts +1 -1
- package/src/core/constants/OPTIONS.ts +1 -1
- package/src/core/helpers/{descriptor.ts → primitive.ts} +18 -18
- package/src/core/helpers/ref.ts +1 -1
- package/src/core/index.shared.ts +8 -8
- package/src/core/{descriptors → primitives}/$context.ts +5 -5
- package/src/core/{descriptors → primitives}/$hook.ts +4 -4
- package/src/core/{descriptors → primitives}/$inject.ts +2 -2
- package/src/core/{descriptors → primitives}/$module.ts +9 -9
- package/src/core/{descriptors → primitives}/$use.ts +2 -2
- package/src/core/providers/CodecManager.ts +1 -1
- package/src/core/providers/JsonSchemaCodec.ts +1 -1
- package/src/core/providers/StateManager.ts +2 -2
- package/src/datetime/index.ts +3 -3
- package/src/datetime/{descriptors → primitives}/$interval.ts +6 -6
- package/src/email/index.ts +4 -4
- package/src/email/{descriptors → primitives}/$email.ts +8 -8
- package/src/file/index.ts +1 -1
- package/src/lock/index.ts +3 -3
- package/src/lock/{descriptors → primitives}/$lock.ts +10 -10
- package/src/logger/index.ts +8 -8
- package/src/logger/{descriptors → primitives}/$logger.ts +2 -2
- package/src/logger/services/Logger.ts +1 -1
- package/src/orm/constants/PG_SYMBOLS.ts +2 -2
- package/src/orm/index.browser.ts +2 -2
- package/src/orm/index.ts +8 -8
- package/src/orm/{descriptors → primitives}/$entity.ts +11 -11
- package/src/orm/{descriptors → primitives}/$repository.ts +2 -2
- package/src/orm/{descriptors → primitives}/$sequence.ts +8 -8
- package/src/orm/{descriptors → primitives}/$transaction.ts +4 -4
- package/src/orm/providers/DrizzleKitProvider.ts +1 -1
- package/src/orm/providers/PostgresTypeProvider.ts +3 -3
- package/src/orm/providers/RepositoryProvider.ts +4 -4
- package/src/orm/providers/drivers/DatabaseProvider.ts +7 -7
- package/src/orm/services/ModelBuilder.ts +9 -9
- package/src/orm/services/PgRelationManager.ts +2 -2
- package/src/orm/services/PostgresModelBuilder.ts +5 -5
- package/src/orm/services/Repository.ts +7 -7
- package/src/orm/services/SqliteModelBuilder.ts +5 -5
- package/src/queue/index.ts +7 -7
- package/src/queue/{descriptors → primitives}/$consumer.ts +15 -15
- package/src/queue/{descriptors → primitives}/$queue.ts +12 -12
- package/src/queue/providers/WorkerProvider.ts +7 -7
- package/src/retry/index.ts +3 -3
- package/src/retry/{descriptors → primitives}/$retry.ts +19 -17
- package/src/scheduler/index.ts +3 -3
- package/src/scheduler/{descriptors → primitives}/$scheduler.ts +9 -9
- package/src/scheduler/providers/CronProvider.ts +1 -1
- package/src/security/index.ts +9 -9
- package/src/security/{descriptors → primitives}/$permission.ts +7 -7
- package/src/security/{descriptors → primitives}/$realm.ts +6 -12
- package/src/security/{descriptors → primitives}/$role.ts +12 -12
- package/src/security/{descriptors → primitives}/$serviceAccount.ts +8 -8
- package/src/server/index.browser.ts +1 -1
- package/src/server/index.ts +14 -14
- package/src/server/{descriptors → primitives}/$action.ts +13 -13
- package/src/server/{descriptors → primitives}/$route.ts +9 -9
- package/src/server/providers/NodeHttpServerProvider.ts +2 -2
- package/src/server/services/HttpClient.ts +1 -1
- package/src/server-auth/index.browser.ts +1 -1
- package/src/server-auth/index.ts +6 -6
- package/src/server-auth/{descriptors → primitives}/$auth.ts +10 -10
- package/src/server-auth/{descriptors → primitives}/$authCredentials.ts +4 -4
- package/src/server-auth/{descriptors → primitives}/$authGithub.ts +4 -4
- package/src/server-auth/{descriptors → primitives}/$authGoogle.ts +4 -4
- package/src/server-auth/providers/ServerAuthProvider.ts +4 -4
- package/src/server-cache/providers/ServerCacheProvider.ts +7 -7
- package/src/server-compress/providers/ServerCompressProvider.ts +3 -3
- package/src/server-cookies/index.browser.ts +2 -2
- package/src/server-cookies/index.ts +5 -5
- package/src/server-cookies/{descriptors → primitives}/$cookie.browser.ts +12 -12
- package/src/server-cookies/{descriptors → primitives}/$cookie.ts +13 -13
- package/src/server-cookies/providers/ServerCookiesProvider.ts +4 -4
- package/src/server-cookies/services/CookieParser.ts +1 -1
- package/src/server-cors/index.ts +3 -3
- package/src/server-cors/{descriptors → primitives}/$cors.ts +11 -13
- package/src/server-cors/providers/ServerCorsProvider.ts +5 -5
- package/src/server-links/index.browser.ts +5 -5
- package/src/server-links/index.ts +9 -9
- package/src/server-links/{descriptors → primitives}/$remote.ts +11 -11
- package/src/server-links/providers/LinkProvider.ts +7 -7
- package/src/server-links/providers/{RemoteDescriptorProvider.ts → RemotePrimitiveProvider.ts} +6 -6
- package/src/server-links/providers/ServerLinksProvider.ts +3 -3
- package/src/server-proxy/index.ts +3 -3
- package/src/server-proxy/{descriptors → primitives}/$proxy.ts +8 -8
- package/src/server-proxy/providers/ServerProxyProvider.ts +4 -4
- package/src/server-rate-limit/index.ts +6 -6
- package/src/server-rate-limit/{descriptors → primitives}/$rateLimit.ts +13 -13
- package/src/server-rate-limit/providers/ServerRateLimitProvider.ts +5 -5
- package/src/server-security/index.ts +3 -3
- package/src/server-security/{descriptors → primitives}/$basicAuth.ts +13 -13
- package/src/server-security/providers/ServerBasicAuthProvider.ts +5 -5
- package/src/server-security/providers/ServerSecurityProvider.ts +4 -4
- package/src/server-static/index.ts +3 -3
- package/src/server-static/{descriptors → primitives}/$serve.ts +8 -10
- package/src/server-static/providers/ServerStaticProvider.ts +6 -6
- package/src/server-swagger/index.ts +5 -5
- package/src/server-swagger/{descriptors → primitives}/$swagger.ts +9 -9
- package/src/server-swagger/providers/ServerSwaggerProvider.ts +11 -10
- package/src/sms/index.ts +4 -4
- package/src/sms/{descriptors → primitives}/$sms.ts +8 -8
- package/src/thread/index.ts +3 -3
- package/src/thread/{descriptors → primitives}/$thread.ts +13 -13
- package/src/thread/providers/ThreadProvider.ts +7 -9
- package/src/topic/index.ts +5 -5
- package/src/topic/{descriptors → primitives}/$subscriber.ts +14 -14
- package/src/topic/{descriptors → primitives}/$topic.ts +10 -10
- package/src/topic/providers/TopicProvider.ts +4 -4
- package/src/vite/helpers/boot.ts +3 -3
- package/src/vite/tasks/copyAssets.ts +1 -1
- package/src/vite/tasks/generateSitemap.ts +3 -3
- package/src/vite/tasks/prerenderPages.ts +2 -2
- package/src/vite/tasks/runAlepha.ts +2 -2
- package/src/websocket/index.browser.ts +3 -3
- package/src/websocket/index.shared.ts +2 -2
- package/src/websocket/index.ts +4 -4
- package/src/websocket/interfaces/WebSocketInterfaces.ts +3 -3
- package/src/websocket/{descriptors → primitives}/$channel.ts +10 -10
- package/src/websocket/{descriptors → primitives}/$websocket.ts +8 -8
- package/src/websocket/providers/NodeWebSocketServerProvider.ts +7 -7
- package/src/websocket/providers/WebSocketServerProvider.ts +3 -3
- package/src/websocket/services/WebSocketClient.ts +5 -5
- package/dist/api-files/index.cjs +0 -1293
- package/dist/api-files/index.cjs.map +0 -1
- package/dist/api-files/index.d.cts +0 -829
- package/dist/api-jobs/index.cjs +0 -274
- package/dist/api-jobs/index.cjs.map +0 -1
- package/dist/api-jobs/index.d.cts +0 -654
- package/dist/api-notifications/index.cjs +0 -380
- package/dist/api-notifications/index.cjs.map +0 -1
- package/dist/api-notifications/index.d.cts +0 -289
- package/dist/api-parameters/index.cjs +0 -66
- package/dist/api-parameters/index.cjs.map +0 -1
- package/dist/api-parameters/index.d.cts +0 -84
- package/dist/api-users/index.cjs +0 -6009
- package/dist/api-users/index.cjs.map +0 -1
- package/dist/api-users/index.d.cts +0 -4740
- package/dist/api-verifications/index.cjs +0 -407
- package/dist/api-verifications/index.cjs.map +0 -1
- package/dist/api-verifications/index.d.cts +0 -207
- package/dist/batch/index.cjs +0 -408
- package/dist/batch/index.cjs.map +0 -1
- package/dist/batch/index.d.cts +0 -330
- package/dist/bin/index.cjs +0 -17
- package/dist/bin/index.cjs.map +0 -1
- package/dist/bin/index.d.cts +0 -1
- package/dist/bucket/index.cjs +0 -303
- package/dist/bucket/index.cjs.map +0 -1
- package/dist/bucket/index.d.cts +0 -355
- package/dist/cache/index.cjs +0 -241
- package/dist/cache/index.cjs.map +0 -1
- package/dist/cache/index.d.cts +0 -202
- package/dist/cache-redis/index.cjs +0 -84
- package/dist/cache-redis/index.cjs.map +0 -1
- package/dist/cache-redis/index.d.cts +0 -40
- package/dist/cli/chunk-DSlc6foC.cjs +0 -43
- package/dist/cli/dist-BBPjuQ56.js +0 -2778
- package/dist/cli/dist-Sz2EXvQX.cjs.map +0 -1
- package/dist/cli/index.cjs +0 -1241
- package/dist/cli/index.cjs.map +0 -1
- package/dist/cli/index.d.cts +0 -422
- package/dist/command/index.cjs +0 -693
- package/dist/command/index.cjs.map +0 -1
- package/dist/command/index.d.cts +0 -340
- package/dist/core/index.cjs.map +0 -1
- package/dist/core/index.d.cts +0 -1927
- package/dist/datetime/index.cjs +0 -318
- package/dist/datetime/index.cjs.map +0 -1
- package/dist/datetime/index.d.cts +0 -145
- package/dist/email/index.cjs +0 -10874
- package/dist/email/index.cjs.map +0 -1
- package/dist/email/index.d.cts +0 -186
- package/dist/fake/index.cjs +0 -34641
- package/dist/fake/index.cjs.map +0 -1
- package/dist/fake/index.d.cts +0 -74
- package/dist/file/index.cjs +0 -1212
- package/dist/file/index.cjs.map +0 -1
- package/dist/file/index.d.cts +0 -698
- package/dist/lock/index.cjs +0 -226
- package/dist/lock/index.cjs.map +0 -1
- package/dist/lock/index.d.cts +0 -361
- package/dist/lock-redis/index.cjs +0 -113
- package/dist/lock-redis/index.cjs.map +0 -1
- package/dist/lock-redis/index.d.cts +0 -24
- package/dist/logger/index.cjs +0 -521
- package/dist/logger/index.cjs.map +0 -1
- package/dist/logger/index.d.cts +0 -281
- package/dist/orm/index.cjs +0 -2986
- package/dist/orm/index.cjs.map +0 -1
- package/dist/orm/index.d.cts +0 -2213
- package/dist/queue/index.cjs +0 -1044
- package/dist/queue/index.cjs.map +0 -1
- package/dist/queue/index.d.cts +0 -1265
- package/dist/queue-redis/index.cjs +0 -873
- package/dist/queue-redis/index.cjs.map +0 -1
- package/dist/queue-redis/index.d.cts +0 -82
- package/dist/redis/index.cjs +0 -153
- package/dist/redis/index.cjs.map +0 -1
- package/dist/redis/index.d.cts +0 -82
- package/dist/retry/index.cjs +0 -146
- package/dist/retry/index.cjs.map +0 -1
- package/dist/retry/index.d.cts +0 -172
- package/dist/router/index.cjs +0 -111
- package/dist/router/index.cjs.map +0 -1
- package/dist/router/index.d.cts +0 -46
- package/dist/scheduler/index.cjs +0 -576
- package/dist/scheduler/index.cjs.map +0 -1
- package/dist/scheduler/index.d.cts +0 -145
- package/dist/security/index.cjs +0 -2402
- package/dist/security/index.cjs.map +0 -1
- package/dist/security/index.d.cts +0 -598
- package/dist/server/index.cjs +0 -1680
- package/dist/server/index.cjs.map +0 -1
- package/dist/server/index.d.cts +0 -810
- package/dist/server-auth/index.cjs +0 -3146
- package/dist/server-auth/index.cjs.map +0 -1
- package/dist/server-auth/index.d.cts +0 -1164
- package/dist/server-cache/index.cjs +0 -252
- package/dist/server-cache/index.cjs.map +0 -1
- package/dist/server-cache/index.d.cts +0 -164
- package/dist/server-compress/index.cjs +0 -141
- package/dist/server-compress/index.cjs.map +0 -1
- package/dist/server-compress/index.d.cts +0 -38
- package/dist/server-cookies/index.cjs +0 -234
- package/dist/server-cookies/index.cjs.map +0 -1
- package/dist/server-cookies/index.d.cts +0 -144
- package/dist/server-cors/index.cjs +0 -201
- package/dist/server-cors/index.cjs.map +0 -1
- package/dist/server-cors/index.d.cts +0 -140
- package/dist/server-health/index.cjs +0 -62
- package/dist/server-health/index.cjs.map +0 -1
- package/dist/server-health/index.d.cts +0 -58
- package/dist/server-helmet/index.cjs +0 -131
- package/dist/server-helmet/index.cjs.map +0 -1
- package/dist/server-helmet/index.d.cts +0 -97
- package/dist/server-links/index.cjs +0 -992
- package/dist/server-links/index.cjs.map +0 -1
- package/dist/server-links/index.d.cts +0 -513
- package/dist/server-metrics/index.cjs +0 -4535
- package/dist/server-metrics/index.cjs.map +0 -1
- package/dist/server-metrics/index.d.cts +0 -35
- package/dist/server-multipart/index.cjs +0 -237
- package/dist/server-multipart/index.cjs.map +0 -1
- package/dist/server-multipart/index.d.cts +0 -50
- package/dist/server-proxy/index.cjs +0 -186
- package/dist/server-proxy/index.cjs.map +0 -1
- package/dist/server-proxy/index.d.cts +0 -234
- package/dist/server-rate-limit/index.cjs +0 -241
- package/dist/server-rate-limit/index.cjs.map +0 -1
- package/dist/server-rate-limit/index.d.cts +0 -183
- package/dist/server-security/index.cjs +0 -316
- package/dist/server-security/index.cjs.map +0 -1
- package/dist/server-security/index.d.cts +0 -173
- package/dist/server-static/index.cjs +0 -170
- package/dist/server-static/index.cjs.map +0 -1
- package/dist/server-static/index.d.cts +0 -121
- package/dist/server-swagger/index.cjs +0 -1021
- package/dist/server-swagger/index.cjs.map +0 -1
- package/dist/server-swagger/index.d.cts +0 -382
- package/dist/sms/index.cjs +0 -221
- package/dist/sms/index.cjs.map +0 -1
- package/dist/sms/index.d.cts +0 -130
- package/dist/thread/index.cjs +0 -350
- package/dist/thread/index.cjs.map +0 -1
- package/dist/thread/index.d.cts +0 -260
- package/dist/topic/index.cjs +0 -282
- package/dist/topic/index.cjs.map +0 -1
- package/dist/topic/index.d.cts +0 -523
- package/dist/topic-redis/index.cjs +0 -71
- package/dist/topic-redis/index.cjs.map +0 -1
- package/dist/topic-redis/index.d.cts +0 -42
- package/dist/vite/index.cjs +0 -1077
- package/dist/vite/index.cjs.map +0 -1
- package/dist/vite/index.d.cts +0 -542
- package/dist/websocket/index.cjs +0 -1117
- package/dist/websocket/index.cjs.map +0 -1
- package/dist/websocket/index.d.cts +0 -861
- package/src/api-notifications/providers/MemorySmsProvider.ts +0 -20
- package/src/api-notifications/providers/SmsProvider.ts +0 -8
- /package/src/core/{descriptors → primitives}/$atom.ts +0 -0
- /package/src/core/{descriptors → primitives}/$env.ts +0 -0
- /package/src/server-auth/{descriptors → primitives}/$authApple.ts +0 -0
- /package/src/server-links/{descriptors → primitives}/$client.ts +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.browser.js","names":["channel: ChannelDescriptor<TClient, TServer>","options: {\n url?: string;\n autoReconnect?: boolean;\n reconnectInterval?: number;\n maxReconnectAttempts?: number;\n }","env: Static<typeof envSchema>","url","code?: number"],"sources":["../../src/websocket/descriptors/$channel.ts","../../src/websocket/providers/WebSocketServerProvider.ts","../../src/websocket/descriptors/$websocket.ts","../../src/websocket/services/WebSocketClient.ts","../../src/websocket/errors/WebSocketError.ts","../../src/websocket/interfaces/WebSocketInterfaces.ts","../../src/websocket/services/RoomManager.ts","../../src/websocket/services/WebSocketTopicService.ts","../../src/websocket/index.browser.ts"],"sourcesContent":["import {\n createDescriptor,\n Descriptor,\n KIND,\n type TObject,\n type TString,\n type TUnion,\n} from \"alepha\";\n\nexport type TWSObject = TObject | TUnion;\n\n/**\n * Channel descriptor options\n */\nexport interface ChannelDescriptorOptions<\n TClient extends TWSObject,\n TServer extends TWSObject,\n> {\n /**\n * WebSocket endpoint path (e.g., \"/ws/chat\")\n */\n path: string;\n\n /**\n * Optional description for documentation\n */\n description?: string;\n\n /**\n * Message schemas for bidirectional communication\n */\n schema: {\n /**\n * Optional room ID schema validation\n * Default: t.text() (any string)\n * Can be enforced at application level: t.uuid(), t.regex(/^[a-f0-9\\-]{36}$/)\n */\n roomId?: TString;\n\n /**\n * Messages from server to client\n * This is what clients will receive\n */\n in: TClient;\n\n /**\n * Messages from client to server\n * This is what the server will receive\n */\n out: TServer;\n };\n}\n\n/**\n * Defines a WebSocket channel with specified client and server message schemas.\n *\n * Channels must be defined as class properties to be registered in the Alepha context.\n * They define the \"vocabulary\" for communication - the schema for messages flowing\n * in both directions (server→client and client→server).\n *\n * @example Server-side with $websocket\n * ```typescript\n * class ChatController {\n * // Channel must be defined inside a class\n * chatChannel = $channel({\n * path: \"/ws/chat\",\n * description: \"Real-time chat channel\",\n * schema: {\n * // Server → Client messages\n * in: t.union([\n * t.object({\n * type: t.const(\"append\"),\n * content: t.text(),\n * username: t.text()\n * }),\n * t.object({\n * type: t.const(\"system\"),\n * message: t.text()\n * })\n * ]),\n * // Client → Server messages\n * out: t.object({\n * content: t.text()\n * })\n * }\n * });\n *\n * chat = $websocket({\n * channel: this.chatChannel,\n * handler: async ({ message, reply }) => {\n * await reply({\n * message: { type: \"append\", content: message.content, username: \"user\" }\n * });\n * }\n * });\n * }\n * ```\n *\n * @example Browser-side with useRoom\n * ```typescript\n * // Define channel in a class for browser context\n * class ChatClient {\n * chatChannel = $channel({\n * path: \"/ws/chat\",\n * schema: { in: inSchema, out: outSchema }\n * });\n * }\n *\n * // Use in React component\n * function Chat() {\n * const client = useInject(ChatClient);\n * const chat = useRoom({ roomId: \"lobby\", channel: client.chatChannel, handler: ... }, []);\n * }\n * ```\n */\nexport const $channel = <TClient extends TWSObject, TServer extends TWSObject>(\n options: ChannelDescriptorOptions<TClient, TServer>,\n): ChannelDescriptor<TClient, TServer> => {\n return createDescriptor(ChannelDescriptor<TClient, TServer>, options);\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class ChannelDescriptor<\n TClient extends TWSObject,\n TServer extends TWSObject,\n> extends Descriptor<ChannelDescriptorOptions<TClient, TServer>> {\n // Channels are just schema definitions - no initialization logic needed\n}\n\n$channel[KIND] = ChannelDescriptor;\n","import type { TWSObject } from \"../descriptors/$channel.ts\";\nimport type {\n EmitOptions,\n WebSocketConnection,\n WebSocketDescriptorOptions,\n} from \"../interfaces/WebSocketInterfaces.ts\";\n\n/**\n * Abstract WebSocket server provider\n *\n * This class provides the base interface that must be implemented by\n * platform-specific providers (Node.js, Browser, etc.)\n */\nexport abstract class WebSocketServerProvider {\n /**\n * Register a WebSocket endpoint with its channel configuration\n */\n abstract registerEndpoint<\n TClient extends TWSObject,\n TServer extends TWSObject,\n >(config: WebSocketDescriptorOptions<TClient, TServer>): void;\n\n /**\n * Emit a message to clients based on targeting criteria\n *\n * This method distributes messages across all server instances via pub/sub.\n */\n abstract emit<TClient extends TWSObject>(\n channelPath: string,\n options: EmitOptions<TClient>,\n ): Promise<void>;\n\n /**\n * Get all active connections (local to this server instance)\n */\n abstract getConnections(): WebSocketConnection[];\n\n /**\n * Get connections in a specific room (local to this server instance)\n */\n abstract getRoomConnections(roomId: string): WebSocketConnection[];\n\n /**\n * Get connections for a specific user (local to this server instance)\n */\n abstract getUserConnections(userId: string): WebSocketConnection[];\n\n /**\n * Close a specific connection\n */\n abstract closeConnection(\n connectionId: string,\n code?: number,\n reason?: string,\n ): Promise<void>;\n}\n","import { $inject, createDescriptor, Descriptor, KIND } from \"alepha\";\nimport type {\n EmitOptions,\n WebSocketDescriptorOptions,\n} from \"../interfaces/WebSocketInterfaces.ts\";\nimport { WebSocketServerProvider } from \"../providers/WebSocketServerProvider.ts\";\nimport type { TWSObject } from \"./$channel.ts\";\n\n/**\n * Defines a WebSocket server endpoint for a specific channel.\n *\n * Server-side only. Creates a WebSocket endpoint that:\n * - Accepts connections from clients\n * - Validates incoming messages against the channel schema\n * - Provides room-based messaging\n * - Integrates with alepha/security for authentication (optional)\n * - Supports horizontal scaling via alepha/topic\n *\n * @example\n * ```typescript\n * class ChatController {\n * chat = $websocket({\n * channel: chatChannel,\n * handler: async ({ connectionId, userId, roomId, message, reply }) => {\n * // Broadcast to all in room except sender\n * await reply({\n * message: {\n * type: \"append\",\n * username: userId,\n * content: message.content\n * },\n * exceptSelf: true\n * });\n * }\n * });\n *\n * async broadcastAnnouncement(roomId: string, text: string) {\n * await this.chat.emit({\n * roomId,\n * message: {\n * type: \"append\",\n * username: \"System\",\n * content: text\n * }\n * });\n * }\n * }\n * ```\n */\nexport const $websocket = <\n TClient extends TWSObject,\n TServer extends TWSObject,\n>(\n options: WebSocketDescriptorOptions<TClient, TServer>,\n): WebSocketDescriptor<TClient, TServer> => {\n return createDescriptor(WebSocketDescriptor<TClient, TServer>, options);\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class WebSocketDescriptor<\n TClient extends TWSObject,\n TServer extends TWSObject,\n> extends Descriptor<WebSocketDescriptorOptions<TClient, TServer>> {\n protected readonly webSocketServerProvider = $inject(WebSocketServerProvider);\n\n protected onInit() {\n this.webSocketServerProvider.registerEndpoint(this.options);\n }\n\n /**\n * Emit message to clients\n *\n * Send messages from the server to connected clients based on targeting criteria.\n * Messages are distributed across all server instances via pub/sub.\n *\n * @example\n * ```typescript\n * // Send to specific room\n * await websocket.emit({\n * roomId: \"room-123\",\n * message: { type: \"update\", data: {...} }\n * });\n *\n * // Send to specific user (all their connections)\n * await websocket.emit({\n * userId: \"user-456\",\n * message: { type: \"notification\", text: \"Hello!\" }\n * });\n *\n * // Send to multiple rooms, except certain users\n * await websocket.emit({\n * roomIds: [\"room-1\", \"room-2\"],\n * exceptUserIds: [\"user-123\"],\n * message: { type: \"broadcast\", content: \"System announcement\" }\n * });\n * ```\n */\n public async emit(options: EmitOptions<TClient>): Promise<void> {\n await this.webSocketServerProvider.emit(\n this.options.channel.options.path,\n options,\n );\n }\n}\n\n$websocket[KIND] = WebSocketDescriptor;\n","import {\n $env,\n $inject,\n Alepha,\n AlephaError,\n type Static,\n TypeBoxValue,\n t,\n} from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport type { ChannelDescriptor, TWSObject } from \"../descriptors/$channel.ts\";\n\nconst envSchema = t.object({\n WEBSOCKET_URL: t.text({\n default: \"\",\n description:\n \"WebSocket server URL (e.g., ws://localhost:3001). Leave empty to auto-detect.\",\n }),\n WEBSOCKET_RECONNECT_INTERVAL: t.integer({\n default: 3000,\n description: \"Reconnection interval in milliseconds\",\n }),\n WEBSOCKET_MAX_RECONNECT_ATTEMPTS: t.integer({\n default: 10,\n description:\n \"Maximum number of reconnection attempts. Set to -1 for infinite.\",\n }),\n});\n\ndeclare module \"alepha\" {\n interface Env extends Partial<Static<typeof envSchema>> {}\n}\n\n/**\n * Room subscription\n */\ninterface RoomSubscription<TClient extends TWSObject> {\n roomId: string;\n handler: (message: Static<TClient>) => void;\n}\n\n/**\n * WebSocket channel connection\n *\n * Manages a single WebSocket connection to a channel with multiple room subscriptions.\n * One connection can handle multiple rooms on the same channel.\n */\nexport class WebSocketChannelConnection<\n TClient extends TWSObject,\n TServer extends TWSObject,\n> {\n protected readonly alepha = $inject(Alepha);\n protected readonly log = $logger();\n protected ws?: WebSocket;\n protected reconnectAttempts = 0;\n protected reconnectTimer?: number;\n protected messageQueue: Array<{ roomId: string; message: Static<TServer> }> =\n [];\n\n // Room subscriptions: Map<roomId, handler>\n protected subscriptions = new Map<\n string,\n (message: Static<TClient>) => void\n >();\n\n // Connection state\n public isConnected = false;\n public isConnecting = false;\n public isError = false;\n public error?: Error;\n\n // Connection callbacks\n protected onConnectCallbacks = new Set<() => void>();\n protected onDisconnectCallbacks = new Set<() => void>();\n protected onErrorCallbacks = new Set<(error: Error) => void>();\n\n constructor(\n protected readonly channel: ChannelDescriptor<TClient, TServer>,\n protected readonly options: {\n url?: string;\n autoReconnect?: boolean;\n reconnectInterval?: number;\n maxReconnectAttempts?: number;\n },\n protected readonly env: Static<typeof envSchema>,\n ) {}\n\n /**\n * Build WebSocket URL\n */\n protected buildUrl(): string {\n this.log.trace(\"Building WebSocket URL\", {\n hasCustomUrl: !!this.options.url,\n channelPath: this.channel.options.path,\n });\n\n if (this.options.url) {\n this.log.debug(\"Using custom WebSocket URL\", { url: this.options.url });\n return this.options.url;\n }\n\n // Auto-detect URL from current location (browser only)\n if (typeof window !== \"undefined\") {\n const protocol = window.location.protocol === \"https:\" ? \"wss:\" : \"ws:\";\n const host = window.location.host;\n const path = this.channel.options.path;\n // Send all room IDs as query params\n const roomIds = Array.from(this.subscriptions.keys());\n const roomParam =\n roomIds.length > 0 ? `?roomIds=${roomIds.join(\",\")}` : \"\";\n const url = `${protocol}//${host}${path}${roomParam}`;\n this.log.debug(\"Auto-detected WebSocket URL\", { url, roomIds });\n return url;\n }\n\n // Fallback to env URL\n const url = `${this.env.WEBSOCKET_URL}${this.channel.options.path}`;\n this.log.debug(\"Using env WebSocket URL\", { url });\n return url;\n }\n\n /**\n * Subscribe to a room on this channel\n */\n public subscribe(\n roomId: string,\n handler: (message: Static<TClient>) => void,\n callbacks?: {\n onConnect?: () => void;\n onDisconnect?: () => void;\n onError?: (error: Error) => void;\n },\n ): () => void {\n this.log.debug(\"Subscribing to room\", {\n roomId,\n channelPath: this.channel.options.path,\n existingSubscriptions: this.subscriptions.size,\n });\n\n // Add subscription\n this.subscriptions.set(roomId, handler);\n\n // Add callbacks\n if (callbacks?.onConnect) this.onConnectCallbacks.add(callbacks.onConnect);\n if (callbacks?.onDisconnect)\n this.onDisconnectCallbacks.add(callbacks.onDisconnect);\n if (callbacks?.onError) this.onErrorCallbacks.add(callbacks.onError);\n\n // Connect if not already connected\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\n this.log.trace(\"No active connection, initiating connect\");\n this.connect().catch((error) => {\n this.log.error(\"Failed to connect:\", error);\n });\n } else {\n this.log.trace(\"Already connected, reusing existing connection\");\n }\n\n // Return unsubscribe function\n return () => {\n this.log.debug(\"Unsubscribing from room\", { roomId });\n this.subscriptions.delete(roomId);\n if (callbacks?.onConnect)\n this.onConnectCallbacks.delete(callbacks.onConnect);\n if (callbacks?.onDisconnect)\n this.onDisconnectCallbacks.delete(callbacks.onDisconnect);\n if (callbacks?.onError) this.onErrorCallbacks.delete(callbacks.onError);\n\n // Disconnect if no more subscriptions\n if (this.subscriptions.size === 0) {\n this.log.debug(\"No more subscriptions, disconnecting\");\n this.disconnect();\n }\n };\n }\n\n /**\n * Connect to WebSocket server\n */\n protected async connect(): Promise<void> {\n if (this.ws?.readyState === WebSocket.OPEN) {\n this.log.trace(\"Already connected, skipping connect\");\n return;\n }\n\n this.isConnecting = true;\n this.isError = false;\n this.error = undefined;\n\n const url = this.buildUrl();\n this.log.info(\"Connecting to WebSocket server\", { url });\n\n return new Promise((resolve, reject) => {\n try {\n const ws = new WebSocket(url);\n this.ws = ws;\n\n ws.onopen = () => {\n this.isConnected = true;\n this.isConnecting = false;\n this.isError = false;\n this.error = undefined;\n this.reconnectAttempts = 0;\n\n this.log.info(\"WebSocket connected\", {\n channelPath: this.channel.options.path,\n rooms: Array.from(this.subscriptions.keys()),\n });\n\n // Flush queued messages\n if (this.messageQueue.length > 0) {\n this.log.debug(\"Flushing queued messages\", {\n count: this.messageQueue.length,\n });\n }\n while (this.messageQueue.length > 0) {\n const msg = this.messageQueue.shift();\n if (msg) {\n this.log.trace(\"Sending queued message\", { roomId: msg.roomId });\n ws.send(\n JSON.stringify({\n roomId: msg.roomId,\n message: msg.message,\n }),\n );\n }\n }\n\n // Call all connect callbacks\n for (const callback of this.onConnectCallbacks) {\n callback();\n }\n\n resolve();\n };\n\n ws.onmessage = (event) => {\n this.log.trace(\"Message received\", {\n dataLength: event.data?.length,\n });\n this.handleMessage(event.data);\n };\n\n ws.onclose = (event) => {\n this.isConnected = false;\n this.isConnecting = false;\n this.ws = undefined;\n\n this.log.info(\"WebSocket disconnected\", {\n code: event.code,\n reason: event.reason,\n wasClean: event.wasClean,\n });\n\n // Call all disconnect callbacks\n for (const callback of this.onDisconnectCallbacks) {\n callback();\n }\n\n // Attempt reconnection\n if (this.options.autoReconnect !== false) {\n this.scheduleReconnect();\n }\n };\n\n ws.onerror = () => {\n const err = new Error(\"WebSocket connection error\");\n this.isError = true;\n this.error = err;\n this.isConnecting = false;\n\n this.log.error(\"WebSocket error\", { url });\n\n // Call all error callbacks\n for (const callback of this.onErrorCallbacks) {\n callback(err);\n }\n\n reject(err);\n };\n } catch (err) {\n const error =\n err instanceof Error ? err : new Error(\"Connection failed\");\n this.isError = true;\n this.error = error;\n this.isConnecting = false;\n\n this.log.error(\"Failed to create WebSocket\", { error: error.message });\n\n // Call all error callbacks\n for (const callback of this.onErrorCallbacks) {\n callback(error);\n }\n\n reject(error);\n }\n });\n }\n\n /**\n * Handle incoming message\n */\n protected handleMessage(data: string): void {\n try {\n const parsed = JSON.parse(data);\n this.log.trace(\"Parsed incoming message\", { parsed });\n\n // Validate incoming message against schema\n const inSchema = this.channel.options.schema.in;\n this.alepha.codec.validate(inSchema, parsed);\n\n this.log.debug(\"Dispatching message to handlers\", {\n handlerCount: this.subscriptions.size,\n });\n\n // Extract roomId from message if present (server should send it back)\n // For now, broadcast to all subscribed rooms\n // TODO: Server should include roomId in response\n for (const handler of this.subscriptions.values()) {\n handler(parsed as Static<TClient>);\n }\n } catch (err) {\n this.log.error(\"Error handling message:\", err);\n }\n }\n\n /**\n * Send message to a specific room\n */\n public async send(roomId: string, message: Static<TServer>): Promise<void> {\n this.log.trace(\"Sending message\", { roomId, message });\n\n // Validate outgoing message against schema\n const outSchema = this.channel.options.schema.out;\n if (!TypeBoxValue.Check(outSchema, message)) {\n const errors = Array.from(TypeBoxValue.Errors(outSchema, message));\n this.log.warn(\"Message validation failed\", { errors });\n throw new Error(\n `Message validation failed: ${errors.map((e) => e.message).join(\", \")}`,\n );\n }\n\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\n // Queue message\n this.log.debug(\"Connection not ready, queuing message\", {\n roomId,\n queueSize: this.messageQueue.length + 1,\n });\n this.messageQueue.push({ roomId, message });\n return;\n }\n\n this.log.debug(\"Sending message to server\", { roomId });\n this.ws.send(\n JSON.stringify({\n roomId,\n message,\n }),\n );\n }\n\n /**\n * Schedule reconnection\n */\n protected scheduleReconnect(): void {\n const maxAttempts =\n this.options.maxReconnectAttempts ??\n this.env.WEBSOCKET_MAX_RECONNECT_ATTEMPTS ??\n 10;\n const reconnectInterval =\n this.options.reconnectInterval ??\n this.env.WEBSOCKET_RECONNECT_INTERVAL ??\n 3000;\n\n if (maxAttempts !== -1 && this.reconnectAttempts >= maxAttempts) {\n this.log.warn(\"Max reconnection attempts reached\", {\n attempts: this.reconnectAttempts,\n maxAttempts,\n });\n return;\n }\n\n this.reconnectAttempts++;\n\n this.log.debug(\"Scheduling reconnection\", {\n attempt: this.reconnectAttempts,\n maxAttempts,\n intervalMs: reconnectInterval,\n });\n\n this.reconnectTimer = window.setTimeout(() => {\n this.log.info(\"Reconnecting...\", {\n attempt: this.reconnectAttempts,\n maxAttempts,\n });\n this.connect().catch((error) => {\n this.log.error(\"Reconnection failed:\", error);\n });\n }, reconnectInterval);\n }\n\n /**\n * Disconnect from server\n */\n public disconnect(): void {\n this.log.debug(\"Disconnecting\", {\n hasTimer: !!this.reconnectTimer,\n hasConnection: !!this.ws,\n });\n\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = undefined;\n }\n\n if (this.ws) {\n this.ws.close();\n this.ws = undefined;\n }\n\n this.isConnected = false;\n this.isConnecting = false;\n\n this.log.info(\"Disconnected\");\n }\n\n /**\n * Reconnect manually\n */\n public reconnect(): void {\n this.log.info(\"Manual reconnect requested\");\n this.disconnect();\n this.connect().catch((error) => {\n this.log.error(\"Manual reconnection failed:\", error);\n });\n }\n\n /**\n * Check if subscribed to a room\n */\n public hasRoom(roomId: string): boolean {\n return this.subscriptions.has(roomId);\n }\n\n /**\n * Get all subscribed rooms\n */\n public getRooms(): string[] {\n return Array.from(this.subscriptions.keys());\n }\n}\n\n/**\n * WebSocket Client Service\n *\n * Manages WebSocket connections from the client side (browser).\n * One connection per channel, multiple rooms per connection.\n */\nexport class WebSocketClient {\n protected readonly log = $logger();\n protected readonly alepha = $inject(Alepha);\n protected readonly env = $env(envSchema);\n\n // Map<channelPath, connection>\n protected connections = new Map<\n string,\n WebSocketChannelConnection<any, any>\n >();\n\n /**\n * Subscribe to a room on a channel\n */\n public subscribe<TClient extends TWSObject, TServer extends TWSObject>(\n roomId: string,\n channel: ChannelDescriptor<TClient, TServer>,\n handler: (message: Static<TClient>) => void,\n options: {\n url?: string;\n autoReconnect?: boolean;\n reconnectInterval?: number;\n maxReconnectAttempts?: number;\n onConnect?: () => void;\n onDisconnect?: () => void;\n onError?: (error: Error) => void;\n } = {},\n ): () => void {\n const channelPath = channel.options.path;\n\n this.log.debug(\"WebSocketClient.subscribe\", {\n roomId,\n channelPath,\n existingConnections: this.connections.size,\n });\n\n // Get or create connection for this channel\n let connection = this.connections.get(\n channelPath,\n ) as WebSocketChannelConnection<TClient, TServer>;\n\n if (!connection) {\n this.log.debug(\"Creating new connection for channel\", { channelPath });\n connection = this.alepha.inject(WebSocketChannelConnection, {\n lifetime: \"transient\",\n args: [\n channel,\n {\n url: options.url,\n autoReconnect: options.autoReconnect,\n reconnectInterval: options.reconnectInterval,\n maxReconnectAttempts: options.maxReconnectAttempts,\n },\n this.env,\n ],\n }) as WebSocketChannelConnection<any, any>;\n\n this.connections.set(channelPath, connection);\n } else {\n this.log.trace(\"Reusing existing connection for channel\", {\n channelPath,\n });\n }\n\n // Subscribe to the room on this connection\n const unsubscribe = connection.subscribe(roomId, handler, {\n onConnect: options.onConnect,\n onDisconnect: options.onDisconnect,\n onError: options.onError,\n });\n\n // Return unsubscribe function\n return () => {\n this.log.debug(\"WebSocketClient.unsubscribe\", { roomId, channelPath });\n unsubscribe();\n\n // Clean up connection if no more rooms\n if (connection.getRooms().length === 0) {\n this.log.debug(\"Removing connection for channel (no more rooms)\", {\n channelPath,\n });\n this.connections.delete(channelPath);\n }\n };\n }\n\n /**\n * Send message to a room on a channel\n */\n public async send<TClient extends TWSObject, TServer extends TWSObject>(\n roomId: string,\n channel: ChannelDescriptor<TClient, TServer>,\n message: Static<TServer>,\n ): Promise<void> {\n const channelPath = channel.options.path;\n\n this.log.trace(\"WebSocketClient.send\", { roomId, channelPath });\n\n const connection = this.connections.get(\n channelPath,\n ) as WebSocketChannelConnection<TClient, TServer>;\n\n if (!connection) {\n this.log.warn(\"Attempted to send on unsubscribed channel\", {\n channelPath,\n });\n throw new AlephaError(\n `Not subscribed to channel ${channelPath}. Subscribe first before sending messages.`,\n );\n }\n\n await connection.send(roomId, message);\n }\n\n /**\n * Get connection for a channel\n */\n public getConnection<TClient extends TWSObject, TServer extends TWSObject>(\n channel: ChannelDescriptor<TClient, TServer>,\n ): WebSocketChannelConnection<TClient, TServer> | undefined {\n const channelPath = channel.options.path;\n const connection = this.connections.get(channelPath) as\n | WebSocketChannelConnection<TClient, TServer>\n | undefined;\n\n this.log.trace(\"WebSocketClient.getConnection\", {\n channelPath,\n found: !!connection,\n });\n\n return connection;\n }\n\n /**\n * Disconnect all connections\n */\n public disconnectAll(): void {\n this.log.info(\"Disconnecting all connections\", {\n count: this.connections.size,\n });\n\n for (const connection of this.connections.values()) {\n connection.disconnect();\n }\n this.connections.clear();\n\n this.log.debug(\"All connections disconnected\");\n }\n}\n","/**\n * Base WebSocket error class\n */\nexport class WebSocketError extends Error {\n constructor(\n message: string,\n public readonly code?: number,\n ) {\n super(message);\n this.name = \"WebSocketError\";\n }\n}\n\n/**\n * Error thrown when WebSocket connection fails\n */\nexport class WebSocketConnectionError extends WebSocketError {\n constructor(message: string, code?: number) {\n super(message, code);\n this.name = \"WebSocketConnectionError\";\n }\n}\n\n/**\n * Error thrown when WebSocket message validation fails\n */\nexport class WebSocketValidationError extends WebSocketError {\n constructor(message: string) {\n super(message);\n this.name = \"WebSocketValidationError\";\n }\n}\n","import type { Static } from \"alepha\";\nimport type { ChannelDescriptor, TWSObject } from \"../descriptors/$channel.ts\";\n\n/**\n * WebSocket connection interface\n */\nexport interface WebSocketConnection {\n /**\n * Unique connection ID\n */\n id: string;\n\n /**\n * User ID (if authenticated and security module is used)\n */\n userId?: string;\n\n /**\n * Room IDs that this connection is currently in\n */\n roomIds: string[];\n\n /**\n * Send a message to this connection\n */\n send(message: any): Promise<void>;\n\n /**\n * Close this connection\n */\n close(code?: number, reason?: string): Promise<void>;\n\n /**\n * Connection metadata (custom data)\n */\n metadata?: Record<string, any>;\n\n /**\n * Connection state\n */\n readyState: WebSocketState;\n}\n\n/**\n * WebSocket state enum\n */\nexport enum WebSocketState {\n CONNECTING = 0,\n OPEN = 1,\n CLOSING = 2,\n CLOSED = 3,\n}\n\n/**\n * WebSocket endpoint configuration (server-side)\n */\nexport interface WebSocketDescriptorOptions<\n TClient extends TWSObject,\n TServer extends TWSObject,\n> {\n /**\n * Channel definition with schema and path\n */\n channel: ChannelDescriptor<TClient, TServer>;\n\n /**\n * Handler for incoming messages from clients\n */\n handler: WebSocketHandler<TClient, TServer>;\n\n /**\n * Optional connection handler (called when a client connects)\n */\n onConnect?: (params: {\n /**\n * Unique connection ID of the client\n */\n connectionId: string;\n\n /**\n * User ID of the connected client (if authenticated and security module is used)\n */\n userId?: string;\n\n /**\n * Room IDs that the client is connecting to\n */\n roomIds: string[];\n }) => Promise<void> | void;\n\n /**\n * Optional disconnection handler (called when a client disconnects)\n */\n onDisconnect?: (params: {\n /**\n * Unique connection ID of the client\n */\n connectionId: string;\n\n /**\n * User ID of the connected client (if authenticated and security module is used)\n */\n userId?: string;\n\n /**\n * Room IDs that the client was connected to\n */\n roomIds: string[];\n }) => Promise<void> | void;\n\n /**\n * Change WebSocket server provider (for custom implementations)\n */\n provider?: any;\n\n /**\n * Whether to enforce security (authentication, authorization) on this WebSocket endpoint\n * Requires alepha/security integration\n */\n secure?: boolean;\n\n /**\n * Limit number of connections per user (if authenticated)\n */\n maxConnectionsPerUser?: number;\n}\n\n/**\n * WebSocket message handler\n */\nexport type WebSocketHandler<\n TClient extends TWSObject,\n TServer extends TWSObject,\n> = (\n context: WebSocketHandlerContext<TClient, TServer>,\n) => Promise<void> | void;\n\n/**\n * WebSocket handler context\n */\nexport interface WebSocketHandlerContext<\n TClient extends TWSObject,\n TServer extends TWSObject,\n> {\n /**\n * Unique connection ID of the client\n */\n connectionId: string;\n\n /**\n * User ID of the connected client (if authenticated and security module is used)\n */\n userId?: string;\n\n /**\n * Room ID that the client sent the message from\n */\n roomId: string;\n\n /**\n * The parsed and validated message from the client\n */\n message: Static<TServer>;\n\n /**\n * Reply function tailored to current context (connectionId, roomId)\n */\n reply: (options: {\n /**\n * Message to send\n */\n message: Static<TClient>;\n\n /**\n * Optional: specify room ID to send to (defaults to sender's room ID)\n */\n roomId?: string;\n\n /**\n * Optional: exclude the sender connection from receiving the message\n * Will populate exceptConnectionIds with sender connection ID behind the scenes\n */\n exceptSelf?: boolean;\n\n /**\n * Optional: exclude specific connection IDs from receiving the message\n */\n exceptConnectionIds?: string[];\n\n /**\n * Optional: exclude specific user IDs from receiving the message\n * Requires alepha/security integration\n */\n exceptUserIds?: string[];\n }) => Promise<void>;\n}\n\n/**\n * Emit options for sending messages from the server\n */\nexport interface EmitOptions<TClient extends TWSObject> {\n /**\n * Message to send to clients\n */\n message: Static<TClient>;\n\n /**\n * Room ID to send the message to\n */\n roomId?: string;\n\n /**\n * Room IDs to send the message to\n */\n roomIds?: string[];\n\n /**\n * User ID to send the message to (if authenticated)\n */\n userId?: string;\n\n /**\n * User IDs to send the message to (if authenticated)\n */\n userIds?: string[];\n\n /**\n * Connection ID to send the message to\n */\n connectionId?: string;\n\n /**\n * Connection IDs to send the message to\n */\n connectionIds?: string[];\n\n /**\n * Optional: exclude specific connection IDs from receiving the message\n */\n exceptConnectionIds?: string[];\n\n /**\n * Optional: exclude specific user IDs from receiving the message\n * Requires alepha/security integration\n */\n exceptUserIds?: string[];\n}\n","import { $logger } from \"alepha/logger\";\n\n/**\n * Manages WebSocket room memberships\n *\n * Rooms are logical groupings of connections. A connection can be in multiple rooms,\n * and messages can be targeted to specific rooms.\n */\nexport class RoomManager {\n protected readonly log = $logger();\n\n /**\n * Maps roomId → Set<connectionId>\n */\n protected readonly rooms = new Map<string, Set<string>>();\n\n /**\n * Maps connectionId → Set<roomId>\n * Inverse index for fast lookup of connection's rooms\n */\n protected readonly connectionRooms = new Map<string, Set<string>>();\n\n /**\n * Join a connection to one or more rooms\n */\n public joinRooms(connectionId: string, roomIds: string[]): void {\n for (const roomId of roomIds) {\n this.joinRoom(connectionId, roomId);\n }\n }\n\n /**\n * Join a connection to a room\n */\n public joinRoom(connectionId: string, roomId: string): void {\n // Add to room\n let room = this.rooms.get(roomId);\n if (!room) {\n room = new Set();\n this.rooms.set(roomId, room);\n }\n room.add(connectionId);\n\n // Update inverse index\n let connRooms = this.connectionRooms.get(connectionId);\n if (!connRooms) {\n connRooms = new Set();\n this.connectionRooms.set(connectionId, connRooms);\n }\n connRooms.add(roomId);\n\n this.log.debug(`Connection ${connectionId} joined room ${roomId}`);\n }\n\n /**\n * Leave a connection from a room\n */\n public leaveRoom(connectionId: string, roomId: string): void {\n // Remove from room\n const room = this.rooms.get(roomId);\n if (room) {\n room.delete(connectionId);\n if (room.size === 0) {\n this.rooms.delete(roomId);\n }\n }\n\n // Update inverse index\n const connRooms = this.connectionRooms.get(connectionId);\n if (connRooms) {\n connRooms.delete(roomId);\n if (connRooms.size === 0) {\n this.connectionRooms.delete(connectionId);\n }\n }\n\n this.log.debug(`Connection ${connectionId} left room ${roomId}`);\n }\n\n /**\n * Remove a connection from all rooms\n */\n public leaveAllRooms(connectionId: string): void {\n const connRooms = this.connectionRooms.get(connectionId);\n if (!connRooms) {\n return;\n }\n\n for (const roomId of connRooms) {\n const room = this.rooms.get(roomId);\n if (room) {\n room.delete(connectionId);\n if (room.size === 0) {\n this.rooms.delete(roomId);\n }\n }\n }\n\n this.connectionRooms.delete(connectionId);\n this.log.debug(`Connection ${connectionId} left all rooms`);\n }\n\n /**\n * Get all connection IDs in a room\n */\n public getRoomConnections(roomId: string): string[] {\n const room = this.rooms.get(roomId);\n return room ? Array.from(room) : [];\n }\n\n /**\n * Get all room IDs for a connection\n */\n public getConnectionRooms(connectionId: string): string[] {\n const connRooms = this.connectionRooms.get(connectionId);\n return connRooms ? Array.from(connRooms) : [];\n }\n\n /**\n * Check if a connection is in a room\n */\n public isInRoom(connectionId: string, roomId: string): boolean {\n const connRooms = this.connectionRooms.get(connectionId);\n return connRooms ? connRooms.has(roomId) : false;\n }\n\n /**\n * Get all active rooms\n */\n public getAllRooms(): string[] {\n return Array.from(this.rooms.keys());\n }\n\n /**\n * Get total number of connections across all rooms\n */\n public getTotalConnections(): number {\n return this.connectionRooms.size;\n }\n\n /**\n * Get room statistics\n */\n public getStats(): {\n totalRooms: number;\n totalConnections: number;\n roomSizes: Map<string, number>;\n } {\n const roomSizes = new Map<string, number>();\n for (const [roomId, connections] of this.rooms) {\n roomSizes.set(roomId, connections.size);\n }\n\n return {\n totalRooms: this.rooms.size,\n totalConnections: this.connectionRooms.size,\n roomSizes,\n };\n }\n}\n","import { type Static, t } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport { $topic } from \"alepha/topic\";\n\n/**\n * WebSocket message distribution event\n */\nconst webSocketMessageSchema = {\n payload: t.object({\n /**\n * Channel path (e.g., \"/ws/chat\")\n */\n channelPath: t.text(),\n\n /**\n * Target room ID(s)\n */\n roomIds: t.optional(t.array(t.text())),\n\n /**\n * Target user ID(s)\n */\n userIds: t.optional(t.array(t.text())),\n\n /**\n * Target connection ID(s)\n */\n connectionIds: t.optional(t.array(t.text())),\n\n /**\n * Exclude connection ID(s) from receiving the message\n */\n exceptConnectionIds: t.optional(t.array(t.text())),\n\n /**\n * Exclude user ID(s) from receiving the message\n */\n exceptUserIds: t.optional(t.array(t.text())),\n\n /**\n * The message payload to send\n */\n message: t.any(),\n }),\n};\n\n/**\n * WebSocket Topic Service\n *\n * Manages pub/sub messaging for WebSocket connections across multiple server instances.\n * Uses alepha/topic for cross-instance message distribution, enabling horizontal scaling.\n *\n * When a WebSocket message needs to be sent:\n * 1. Server instance A publishes to the topic\n * 2. All server instances (A, B, C, etc.) receive the message\n * 3. Each instance sends to its local connections that match the criteria\n *\n * This enables:\n * - Multiple server instances handling WebSocket connections\n * - Redis-backed message distribution (with alepha/topic/redis)\n * - Horizontal scaling without losing messages\n */\nexport class WebSocketTopicService {\n protected readonly log = $logger();\n\n /**\n * Handler function to be called when a message is received from the topic\n * This is set by the WebSocket provider during initialization\n */\n public messageHandler?: (\n event: Static<(typeof webSocketMessageSchema)[\"payload\"]>,\n ) => Promise<void>;\n\n /**\n * Topic for distributing WebSocket messages across server instances\n */\n public readonly topic = $topic({\n name: \"websocket:broadcast\",\n description:\n \"Distributes WebSocket messages across server instances for horizontal scaling\",\n schema: webSocketMessageSchema,\n handler: async (message) => {\n if (this.messageHandler) {\n await this.messageHandler(message.payload);\n }\n },\n });\n\n /**\n * Publish a message to be distributed across all server instances\n */\n public async publish(\n event: Static<(typeof webSocketMessageSchema)[\"payload\"]>,\n ): Promise<void> {\n await this.topic.publish(event);\n }\n\n /**\n * Set the handler for incoming messages\n */\n public setMessageHandler(\n handler: (\n event: Static<(typeof webSocketMessageSchema)[\"payload\"]>,\n ) => Promise<void>,\n ): void {\n this.messageHandler = handler;\n }\n}\n","import { $module, type Alepha } from \"alepha\";\nimport { AlephaTopic } from \"alepha/topic\";\nimport { $channel } from \"./descriptors/$channel.ts\";\nimport { $websocket } from \"./descriptors/$websocket.ts\";\nimport { WebSocketClient } from \"./services/WebSocketClient.ts\";\n\n/**\n * alepha/websocket (Browser)\n *\n * Browser-side WebSocket client module. Provides WebSocketClient service\n * for managing WebSocket connections from the browser.\n *\n * For React applications, use alepha/react/websocket with the useRoom hook.\n */\nexport * from \"./index.shared.ts\";\n\nexport const AlephaWebSocket = $module({\n name: \"alepha.websocket\",\n descriptors: [$channel, $websocket],\n services: [WebSocketClient],\n register: (alepha: Alepha) => {\n alepha.with(AlephaTopic);\n alepha.with(WebSocketClient);\n },\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.browser.js","names":["channel: ChannelPrimitive<TClient, TServer>","options: {\n url?: string;\n autoReconnect?: boolean;\n reconnectInterval?: number;\n maxReconnectAttempts?: number;\n }","env: Static<typeof envSchema>","url","code?: number"],"sources":["../../src/websocket/primitives/$channel.ts","../../src/websocket/providers/WebSocketServerProvider.ts","../../src/websocket/primitives/$websocket.ts","../../src/websocket/services/WebSocketClient.ts","../../src/websocket/errors/WebSocketError.ts","../../src/websocket/interfaces/WebSocketInterfaces.ts","../../src/websocket/services/RoomManager.ts","../../src/websocket/services/WebSocketTopicService.ts","../../src/websocket/index.browser.ts"],"sourcesContent":["import {\n createPrimitive,\n KIND,\n Primitive,\n type TObject,\n type TString,\n type TUnion,\n} from \"alepha\";\n\nexport type TWSObject = TObject | TUnion;\n\n/**\n * Channel primitive options\n */\nexport interface ChannelPrimitiveOptions<\n TClient extends TWSObject,\n TServer extends TWSObject,\n> {\n /**\n * WebSocket endpoint path (e.g., \"/ws/chat\")\n */\n path: string;\n\n /**\n * Optional description for documentation\n */\n description?: string;\n\n /**\n * Message schemas for bidirectional communication\n */\n schema: {\n /**\n * Optional room ID schema validation\n * Default: t.text() (any string)\n * Can be enforced at application level: t.uuid(), t.regex(/^[a-f0-9\\-]{36}$/)\n */\n roomId?: TString;\n\n /**\n * Messages from server to client\n * This is what clients will receive\n */\n in: TClient;\n\n /**\n * Messages from client to server\n * This is what the server will receive\n */\n out: TServer;\n };\n}\n\n/**\n * Defines a WebSocket channel with specified client and server message schemas.\n *\n * Channels must be defined as class properties to be registered in the Alepha context.\n * They define the \"vocabulary\" for communication - the schema for messages flowing\n * in both directions (server→client and client→server).\n *\n * @example Server-side with $websocket\n * ```typescript\n * class ChatController {\n * // Channel must be defined inside a class\n * chatChannel = $channel({\n * path: \"/ws/chat\",\n * description: \"Real-time chat channel\",\n * schema: {\n * // Server → Client messages\n * in: t.union([\n * t.object({\n * type: t.const(\"append\"),\n * content: t.text(),\n * username: t.text()\n * }),\n * t.object({\n * type: t.const(\"system\"),\n * message: t.text()\n * })\n * ]),\n * // Client → Server messages\n * out: t.object({\n * content: t.text()\n * })\n * }\n * });\n *\n * chat = $websocket({\n * channel: this.chatChannel,\n * handler: async ({ message, reply }) => {\n * await reply({\n * message: { type: \"append\", content: message.content, username: \"user\" }\n * });\n * }\n * });\n * }\n * ```\n *\n * @example Browser-side with useRoom\n * ```typescript\n * // Define channel in a class for browser context\n * class ChatClient {\n * chatChannel = $channel({\n * path: \"/ws/chat\",\n * schema: { in: inSchema, out: outSchema }\n * });\n * }\n *\n * // Use in React component\n * function Chat() {\n * const client = useInject(ChatClient);\n * const chat = useRoom({ roomId: \"lobby\", channel: client.chatChannel, handler: ... }, []);\n * }\n * ```\n */\nexport const $channel = <TClient extends TWSObject, TServer extends TWSObject>(\n options: ChannelPrimitiveOptions<TClient, TServer>,\n): ChannelPrimitive<TClient, TServer> => {\n return createPrimitive(ChannelPrimitive<TClient, TServer>, options);\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class ChannelPrimitive<\n TClient extends TWSObject,\n TServer extends TWSObject,\n> extends Primitive<ChannelPrimitiveOptions<TClient, TServer>> {\n // Channels are just schema definitions - no initialization logic needed\n}\n\n$channel[KIND] = ChannelPrimitive;\n","import type {\n EmitOptions,\n WebSocketConnection,\n WebSocketPrimitiveOptions,\n} from \"../interfaces/WebSocketInterfaces.ts\";\nimport type { TWSObject } from \"../primitives/$channel.ts\";\n\n/**\n * Abstract WebSocket server provider\n *\n * This class provides the base interface that must be implemented by\n * platform-specific providers (Node.js, Browser, etc.)\n */\nexport abstract class WebSocketServerProvider {\n /**\n * Register a WebSocket endpoint with its channel configuration\n */\n abstract registerEndpoint<\n TClient extends TWSObject,\n TServer extends TWSObject,\n >(config: WebSocketPrimitiveOptions<TClient, TServer>): void;\n\n /**\n * Emit a message to clients based on targeting criteria\n *\n * This method distributes messages across all server instances via pub/sub.\n */\n abstract emit<TClient extends TWSObject>(\n channelPath: string,\n options: EmitOptions<TClient>,\n ): Promise<void>;\n\n /**\n * Get all active connections (local to this server instance)\n */\n abstract getConnections(): WebSocketConnection[];\n\n /**\n * Get connections in a specific room (local to this server instance)\n */\n abstract getRoomConnections(roomId: string): WebSocketConnection[];\n\n /**\n * Get connections for a specific user (local to this server instance)\n */\n abstract getUserConnections(userId: string): WebSocketConnection[];\n\n /**\n * Close a specific connection\n */\n abstract closeConnection(\n connectionId: string,\n code?: number,\n reason?: string,\n ): Promise<void>;\n}\n","import { $inject, createPrimitive, KIND, Primitive } from \"alepha\";\nimport type {\n EmitOptions,\n WebSocketPrimitiveOptions,\n} from \"../interfaces/WebSocketInterfaces.ts\";\nimport { WebSocketServerProvider } from \"../providers/WebSocketServerProvider.ts\";\nimport type { TWSObject } from \"./$channel.ts\";\n\n/**\n * Defines a WebSocket server endpoint for a specific channel.\n *\n * Server-side only. Creates a WebSocket endpoint that:\n * - Accepts connections from clients\n * - Validates incoming messages against the channel schema\n * - Provides room-based messaging\n * - Integrates with alepha/security for authentication (optional)\n * - Supports horizontal scaling via alepha/topic\n *\n * @example\n * ```typescript\n * class ChatController {\n * chat = $websocket({\n * channel: chatChannel,\n * handler: async ({ connectionId, userId, roomId, message, reply }) => {\n * // Broadcast to all in room except sender\n * await reply({\n * message: {\n * type: \"append\",\n * username: userId,\n * content: message.content\n * },\n * exceptSelf: true\n * });\n * }\n * });\n *\n * async broadcastAnnouncement(roomId: string, text: string) {\n * await this.chat.emit({\n * roomId,\n * message: {\n * type: \"append\",\n * username: \"System\",\n * content: text\n * }\n * });\n * }\n * }\n * ```\n */\nexport const $websocket = <\n TClient extends TWSObject,\n TServer extends TWSObject,\n>(\n options: WebSocketPrimitiveOptions<TClient, TServer>,\n): WebSocketPrimitive<TClient, TServer> => {\n return createPrimitive(WebSocketPrimitive<TClient, TServer>, options);\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class WebSocketPrimitive<\n TClient extends TWSObject,\n TServer extends TWSObject,\n> extends Primitive<WebSocketPrimitiveOptions<TClient, TServer>> {\n protected readonly webSocketServerProvider = $inject(WebSocketServerProvider);\n\n protected onInit() {\n this.webSocketServerProvider.registerEndpoint(this.options);\n }\n\n /**\n * Emit message to clients\n *\n * Send messages from the server to connected clients based on targeting criteria.\n * Messages are distributed across all server instances via pub/sub.\n *\n * @example\n * ```typescript\n * // Send to specific room\n * await websocket.emit({\n * roomId: \"room-123\",\n * message: { type: \"update\", data: {...} }\n * });\n *\n * // Send to specific user (all their connections)\n * await websocket.emit({\n * userId: \"user-456\",\n * message: { type: \"notification\", text: \"Hello!\" }\n * });\n *\n * // Send to multiple rooms, except certain users\n * await websocket.emit({\n * roomIds: [\"room-1\", \"room-2\"],\n * exceptUserIds: [\"user-123\"],\n * message: { type: \"broadcast\", content: \"System announcement\" }\n * });\n * ```\n */\n public async emit(options: EmitOptions<TClient>): Promise<void> {\n await this.webSocketServerProvider.emit(\n this.options.channel.options.path,\n options,\n );\n }\n}\n\n$websocket[KIND] = WebSocketPrimitive;\n","import {\n $env,\n $inject,\n Alepha,\n AlephaError,\n type Static,\n TypeBoxValue,\n t,\n} from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport type { ChannelPrimitive, TWSObject } from \"../primitives/$channel.ts\";\n\nconst envSchema = t.object({\n WEBSOCKET_URL: t.text({\n default: \"\",\n description:\n \"WebSocket server URL (e.g., ws://localhost:3001). Leave empty to auto-detect.\",\n }),\n WEBSOCKET_RECONNECT_INTERVAL: t.integer({\n default: 3000,\n description: \"Reconnection interval in milliseconds\",\n }),\n WEBSOCKET_MAX_RECONNECT_ATTEMPTS: t.integer({\n default: 10,\n description:\n \"Maximum number of reconnection attempts. Set to -1 for infinite.\",\n }),\n});\n\ndeclare module \"alepha\" {\n interface Env extends Partial<Static<typeof envSchema>> {}\n}\n\n/**\n * Room subscription\n */\ninterface RoomSubscription<TClient extends TWSObject> {\n roomId: string;\n handler: (message: Static<TClient>) => void;\n}\n\n/**\n * WebSocket channel connection\n *\n * Manages a single WebSocket connection to a channel with multiple room subscriptions.\n * One connection can handle multiple rooms on the same channel.\n */\nexport class WebSocketChannelConnection<\n TClient extends TWSObject,\n TServer extends TWSObject,\n> {\n protected readonly alepha = $inject(Alepha);\n protected readonly log = $logger();\n protected ws?: WebSocket;\n protected reconnectAttempts = 0;\n protected reconnectTimer?: number;\n protected messageQueue: Array<{ roomId: string; message: Static<TServer> }> =\n [];\n\n // Room subscriptions: Map<roomId, handler>\n protected subscriptions = new Map<\n string,\n (message: Static<TClient>) => void\n >();\n\n // Connection state\n public isConnected = false;\n public isConnecting = false;\n public isError = false;\n public error?: Error;\n\n // Connection callbacks\n protected onConnectCallbacks = new Set<() => void>();\n protected onDisconnectCallbacks = new Set<() => void>();\n protected onErrorCallbacks = new Set<(error: Error) => void>();\n\n constructor(\n protected readonly channel: ChannelPrimitive<TClient, TServer>,\n protected readonly options: {\n url?: string;\n autoReconnect?: boolean;\n reconnectInterval?: number;\n maxReconnectAttempts?: number;\n },\n protected readonly env: Static<typeof envSchema>,\n ) {}\n\n /**\n * Build WebSocket URL\n */\n protected buildUrl(): string {\n this.log.trace(\"Building WebSocket URL\", {\n hasCustomUrl: !!this.options.url,\n channelPath: this.channel.options.path,\n });\n\n if (this.options.url) {\n this.log.debug(\"Using custom WebSocket URL\", { url: this.options.url });\n return this.options.url;\n }\n\n // Auto-detect URL from current location (browser only)\n if (typeof window !== \"undefined\") {\n const protocol = window.location.protocol === \"https:\" ? \"wss:\" : \"ws:\";\n const host = window.location.host;\n const path = this.channel.options.path;\n // Send all room IDs as query params\n const roomIds = Array.from(this.subscriptions.keys());\n const roomParam =\n roomIds.length > 0 ? `?roomIds=${roomIds.join(\",\")}` : \"\";\n const url = `${protocol}//${host}${path}${roomParam}`;\n this.log.debug(\"Auto-detected WebSocket URL\", { url, roomIds });\n return url;\n }\n\n // Fallback to env URL\n const url = `${this.env.WEBSOCKET_URL}${this.channel.options.path}`;\n this.log.debug(\"Using env WebSocket URL\", { url });\n return url;\n }\n\n /**\n * Subscribe to a room on this channel\n */\n public subscribe(\n roomId: string,\n handler: (message: Static<TClient>) => void,\n callbacks?: {\n onConnect?: () => void;\n onDisconnect?: () => void;\n onError?: (error: Error) => void;\n },\n ): () => void {\n this.log.debug(\"Subscribing to room\", {\n roomId,\n channelPath: this.channel.options.path,\n existingSubscriptions: this.subscriptions.size,\n });\n\n // Add subscription\n this.subscriptions.set(roomId, handler);\n\n // Add callbacks\n if (callbacks?.onConnect) this.onConnectCallbacks.add(callbacks.onConnect);\n if (callbacks?.onDisconnect)\n this.onDisconnectCallbacks.add(callbacks.onDisconnect);\n if (callbacks?.onError) this.onErrorCallbacks.add(callbacks.onError);\n\n // Connect if not already connected\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\n this.log.trace(\"No active connection, initiating connect\");\n this.connect().catch((error) => {\n this.log.error(\"Failed to connect:\", error);\n });\n } else {\n this.log.trace(\"Already connected, reusing existing connection\");\n }\n\n // Return unsubscribe function\n return () => {\n this.log.debug(\"Unsubscribing from room\", { roomId });\n this.subscriptions.delete(roomId);\n if (callbacks?.onConnect)\n this.onConnectCallbacks.delete(callbacks.onConnect);\n if (callbacks?.onDisconnect)\n this.onDisconnectCallbacks.delete(callbacks.onDisconnect);\n if (callbacks?.onError) this.onErrorCallbacks.delete(callbacks.onError);\n\n // Disconnect if no more subscriptions\n if (this.subscriptions.size === 0) {\n this.log.debug(\"No more subscriptions, disconnecting\");\n this.disconnect();\n }\n };\n }\n\n /**\n * Connect to WebSocket server\n */\n protected async connect(): Promise<void> {\n if (this.ws?.readyState === WebSocket.OPEN) {\n this.log.trace(\"Already connected, skipping connect\");\n return;\n }\n\n this.isConnecting = true;\n this.isError = false;\n this.error = undefined;\n\n const url = this.buildUrl();\n this.log.info(\"Connecting to WebSocket server\", { url });\n\n return new Promise((resolve, reject) => {\n try {\n const ws = new WebSocket(url);\n this.ws = ws;\n\n ws.onopen = () => {\n this.isConnected = true;\n this.isConnecting = false;\n this.isError = false;\n this.error = undefined;\n this.reconnectAttempts = 0;\n\n this.log.info(\"WebSocket connected\", {\n channelPath: this.channel.options.path,\n rooms: Array.from(this.subscriptions.keys()),\n });\n\n // Flush queued messages\n if (this.messageQueue.length > 0) {\n this.log.debug(\"Flushing queued messages\", {\n count: this.messageQueue.length,\n });\n }\n while (this.messageQueue.length > 0) {\n const msg = this.messageQueue.shift();\n if (msg) {\n this.log.trace(\"Sending queued message\", { roomId: msg.roomId });\n ws.send(\n JSON.stringify({\n roomId: msg.roomId,\n message: msg.message,\n }),\n );\n }\n }\n\n // Call all connect callbacks\n for (const callback of this.onConnectCallbacks) {\n callback();\n }\n\n resolve();\n };\n\n ws.onmessage = (event) => {\n this.log.trace(\"Message received\", {\n dataLength: event.data?.length,\n });\n this.handleMessage(event.data);\n };\n\n ws.onclose = (event) => {\n this.isConnected = false;\n this.isConnecting = false;\n this.ws = undefined;\n\n this.log.info(\"WebSocket disconnected\", {\n code: event.code,\n reason: event.reason,\n wasClean: event.wasClean,\n });\n\n // Call all disconnect callbacks\n for (const callback of this.onDisconnectCallbacks) {\n callback();\n }\n\n // Attempt reconnection\n if (this.options.autoReconnect !== false) {\n this.scheduleReconnect();\n }\n };\n\n ws.onerror = () => {\n const err = new Error(\"WebSocket connection error\");\n this.isError = true;\n this.error = err;\n this.isConnecting = false;\n\n this.log.error(\"WebSocket error\", { url });\n\n // Call all error callbacks\n for (const callback of this.onErrorCallbacks) {\n callback(err);\n }\n\n reject(err);\n };\n } catch (err) {\n const error =\n err instanceof Error ? err : new Error(\"Connection failed\");\n this.isError = true;\n this.error = error;\n this.isConnecting = false;\n\n this.log.error(\"Failed to create WebSocket\", { error: error.message });\n\n // Call all error callbacks\n for (const callback of this.onErrorCallbacks) {\n callback(error);\n }\n\n reject(error);\n }\n });\n }\n\n /**\n * Handle incoming message\n */\n protected handleMessage(data: string): void {\n try {\n const parsed = JSON.parse(data);\n this.log.trace(\"Parsed incoming message\", { parsed });\n\n // Validate incoming message against schema\n const inSchema = this.channel.options.schema.in;\n this.alepha.codec.validate(inSchema, parsed);\n\n this.log.debug(\"Dispatching message to handlers\", {\n handlerCount: this.subscriptions.size,\n });\n\n // Extract roomId from message if present (server should send it back)\n // For now, broadcast to all subscribed rooms\n // TODO: Server should include roomId in response\n for (const handler of this.subscriptions.values()) {\n handler(parsed as Static<TClient>);\n }\n } catch (err) {\n this.log.error(\"Error handling message:\", err);\n }\n }\n\n /**\n * Send message to a specific room\n */\n public async send(roomId: string, message: Static<TServer>): Promise<void> {\n this.log.trace(\"Sending message\", { roomId, message });\n\n // Validate outgoing message against schema\n const outSchema = this.channel.options.schema.out;\n if (!TypeBoxValue.Check(outSchema, message)) {\n const errors = Array.from(TypeBoxValue.Errors(outSchema, message));\n this.log.warn(\"Message validation failed\", { errors });\n throw new Error(\n `Message validation failed: ${errors.map((e) => e.message).join(\", \")}`,\n );\n }\n\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\n // Queue message\n this.log.debug(\"Connection not ready, queuing message\", {\n roomId,\n queueSize: this.messageQueue.length + 1,\n });\n this.messageQueue.push({ roomId, message });\n return;\n }\n\n this.log.debug(\"Sending message to server\", { roomId });\n this.ws.send(\n JSON.stringify({\n roomId,\n message,\n }),\n );\n }\n\n /**\n * Schedule reconnection\n */\n protected scheduleReconnect(): void {\n const maxAttempts =\n this.options.maxReconnectAttempts ??\n this.env.WEBSOCKET_MAX_RECONNECT_ATTEMPTS ??\n 10;\n const reconnectInterval =\n this.options.reconnectInterval ??\n this.env.WEBSOCKET_RECONNECT_INTERVAL ??\n 3000;\n\n if (maxAttempts !== -1 && this.reconnectAttempts >= maxAttempts) {\n this.log.warn(\"Max reconnection attempts reached\", {\n attempts: this.reconnectAttempts,\n maxAttempts,\n });\n return;\n }\n\n this.reconnectAttempts++;\n\n this.log.debug(\"Scheduling reconnection\", {\n attempt: this.reconnectAttempts,\n maxAttempts,\n intervalMs: reconnectInterval,\n });\n\n this.reconnectTimer = window.setTimeout(() => {\n this.log.info(\"Reconnecting...\", {\n attempt: this.reconnectAttempts,\n maxAttempts,\n });\n this.connect().catch((error) => {\n this.log.error(\"Reconnection failed:\", error);\n });\n }, reconnectInterval);\n }\n\n /**\n * Disconnect from server\n */\n public disconnect(): void {\n this.log.debug(\"Disconnecting\", {\n hasTimer: !!this.reconnectTimer,\n hasConnection: !!this.ws,\n });\n\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = undefined;\n }\n\n if (this.ws) {\n this.ws.close();\n this.ws = undefined;\n }\n\n this.isConnected = false;\n this.isConnecting = false;\n\n this.log.info(\"Disconnected\");\n }\n\n /**\n * Reconnect manually\n */\n public reconnect(): void {\n this.log.info(\"Manual reconnect requested\");\n this.disconnect();\n this.connect().catch((error) => {\n this.log.error(\"Manual reconnection failed:\", error);\n });\n }\n\n /**\n * Check if subscribed to a room\n */\n public hasRoom(roomId: string): boolean {\n return this.subscriptions.has(roomId);\n }\n\n /**\n * Get all subscribed rooms\n */\n public getRooms(): string[] {\n return Array.from(this.subscriptions.keys());\n }\n}\n\n/**\n * WebSocket Client Service\n *\n * Manages WebSocket connections from the client side (browser).\n * One connection per channel, multiple rooms per connection.\n */\nexport class WebSocketClient {\n protected readonly log = $logger();\n protected readonly alepha = $inject(Alepha);\n protected readonly env = $env(envSchema);\n\n // Map<channelPath, connection>\n protected connections = new Map<\n string,\n WebSocketChannelConnection<any, any>\n >();\n\n /**\n * Subscribe to a room on a channel\n */\n public subscribe<TClient extends TWSObject, TServer extends TWSObject>(\n roomId: string,\n channel: ChannelPrimitive<TClient, TServer>,\n handler: (message: Static<TClient>) => void,\n options: {\n url?: string;\n autoReconnect?: boolean;\n reconnectInterval?: number;\n maxReconnectAttempts?: number;\n onConnect?: () => void;\n onDisconnect?: () => void;\n onError?: (error: Error) => void;\n } = {},\n ): () => void {\n const channelPath = channel.options.path;\n\n this.log.debug(\"WebSocketClient.subscribe\", {\n roomId,\n channelPath,\n existingConnections: this.connections.size,\n });\n\n // Get or create connection for this channel\n let connection = this.connections.get(\n channelPath,\n ) as WebSocketChannelConnection<TClient, TServer>;\n\n if (!connection) {\n this.log.debug(\"Creating new connection for channel\", { channelPath });\n connection = this.alepha.inject(WebSocketChannelConnection, {\n lifetime: \"transient\",\n args: [\n channel,\n {\n url: options.url,\n autoReconnect: options.autoReconnect,\n reconnectInterval: options.reconnectInterval,\n maxReconnectAttempts: options.maxReconnectAttempts,\n },\n this.env,\n ],\n }) as WebSocketChannelConnection<any, any>;\n\n this.connections.set(channelPath, connection);\n } else {\n this.log.trace(\"Reusing existing connection for channel\", {\n channelPath,\n });\n }\n\n // Subscribe to the room on this connection\n const unsubscribe = connection.subscribe(roomId, handler, {\n onConnect: options.onConnect,\n onDisconnect: options.onDisconnect,\n onError: options.onError,\n });\n\n // Return unsubscribe function\n return () => {\n this.log.debug(\"WebSocketClient.unsubscribe\", { roomId, channelPath });\n unsubscribe();\n\n // Clean up connection if no more rooms\n if (connection.getRooms().length === 0) {\n this.log.debug(\"Removing connection for channel (no more rooms)\", {\n channelPath,\n });\n this.connections.delete(channelPath);\n }\n };\n }\n\n /**\n * Send message to a room on a channel\n */\n public async send<TClient extends TWSObject, TServer extends TWSObject>(\n roomId: string,\n channel: ChannelPrimitive<TClient, TServer>,\n message: Static<TServer>,\n ): Promise<void> {\n const channelPath = channel.options.path;\n\n this.log.trace(\"WebSocketClient.send\", { roomId, channelPath });\n\n const connection = this.connections.get(\n channelPath,\n ) as WebSocketChannelConnection<TClient, TServer>;\n\n if (!connection) {\n this.log.warn(\"Attempted to send on unsubscribed channel\", {\n channelPath,\n });\n throw new AlephaError(\n `Not subscribed to channel ${channelPath}. Subscribe first before sending messages.`,\n );\n }\n\n await connection.send(roomId, message);\n }\n\n /**\n * Get connection for a channel\n */\n public getConnection<TClient extends TWSObject, TServer extends TWSObject>(\n channel: ChannelPrimitive<TClient, TServer>,\n ): WebSocketChannelConnection<TClient, TServer> | undefined {\n const channelPath = channel.options.path;\n const connection = this.connections.get(channelPath) as\n | WebSocketChannelConnection<TClient, TServer>\n | undefined;\n\n this.log.trace(\"WebSocketClient.getConnection\", {\n channelPath,\n found: !!connection,\n });\n\n return connection;\n }\n\n /**\n * Disconnect all connections\n */\n public disconnectAll(): void {\n this.log.info(\"Disconnecting all connections\", {\n count: this.connections.size,\n });\n\n for (const connection of this.connections.values()) {\n connection.disconnect();\n }\n this.connections.clear();\n\n this.log.debug(\"All connections disconnected\");\n }\n}\n","/**\n * Base WebSocket error class\n */\nexport class WebSocketError extends Error {\n constructor(\n message: string,\n public readonly code?: number,\n ) {\n super(message);\n this.name = \"WebSocketError\";\n }\n}\n\n/**\n * Error thrown when WebSocket connection fails\n */\nexport class WebSocketConnectionError extends WebSocketError {\n constructor(message: string, code?: number) {\n super(message, code);\n this.name = \"WebSocketConnectionError\";\n }\n}\n\n/**\n * Error thrown when WebSocket message validation fails\n */\nexport class WebSocketValidationError extends WebSocketError {\n constructor(message: string) {\n super(message);\n this.name = \"WebSocketValidationError\";\n }\n}\n","import type { Static } from \"alepha\";\nimport type { ChannelPrimitive, TWSObject } from \"../primitives/$channel.ts\";\n\n/**\n * WebSocket connection interface\n */\nexport interface WebSocketConnection {\n /**\n * Unique connection ID\n */\n id: string;\n\n /**\n * User ID (if authenticated and security module is used)\n */\n userId?: string;\n\n /**\n * Room IDs that this connection is currently in\n */\n roomIds: string[];\n\n /**\n * Send a message to this connection\n */\n send(message: any): Promise<void>;\n\n /**\n * Close this connection\n */\n close(code?: number, reason?: string): Promise<void>;\n\n /**\n * Connection metadata (custom data)\n */\n metadata?: Record<string, any>;\n\n /**\n * Connection state\n */\n readyState: WebSocketState;\n}\n\n/**\n * WebSocket state enum\n */\nexport enum WebSocketState {\n CONNECTING = 0,\n OPEN = 1,\n CLOSING = 2,\n CLOSED = 3,\n}\n\n/**\n * WebSocket endpoint configuration (server-side)\n */\nexport interface WebSocketPrimitiveOptions<\n TClient extends TWSObject,\n TServer extends TWSObject,\n> {\n /**\n * Channel definition with schema and path\n */\n channel: ChannelPrimitive<TClient, TServer>;\n\n /**\n * Handler for incoming messages from clients\n */\n handler: WebSocketHandler<TClient, TServer>;\n\n /**\n * Optional connection handler (called when a client connects)\n */\n onConnect?: (params: {\n /**\n * Unique connection ID of the client\n */\n connectionId: string;\n\n /**\n * User ID of the connected client (if authenticated and security module is used)\n */\n userId?: string;\n\n /**\n * Room IDs that the client is connecting to\n */\n roomIds: string[];\n }) => Promise<void> | void;\n\n /**\n * Optional disconnection handler (called when a client disconnects)\n */\n onDisconnect?: (params: {\n /**\n * Unique connection ID of the client\n */\n connectionId: string;\n\n /**\n * User ID of the connected client (if authenticated and security module is used)\n */\n userId?: string;\n\n /**\n * Room IDs that the client was connected to\n */\n roomIds: string[];\n }) => Promise<void> | void;\n\n /**\n * Change WebSocket server provider (for custom implementations)\n */\n provider?: any;\n\n /**\n * Whether to enforce security (authentication, authorization) on this WebSocket endpoint\n * Requires alepha/security integration\n */\n secure?: boolean;\n\n /**\n * Limit number of connections per user (if authenticated)\n */\n maxConnectionsPerUser?: number;\n}\n\n/**\n * WebSocket message handler\n */\nexport type WebSocketHandler<\n TClient extends TWSObject,\n TServer extends TWSObject,\n> = (\n context: WebSocketHandlerContext<TClient, TServer>,\n) => Promise<void> | void;\n\n/**\n * WebSocket handler context\n */\nexport interface WebSocketHandlerContext<\n TClient extends TWSObject,\n TServer extends TWSObject,\n> {\n /**\n * Unique connection ID of the client\n */\n connectionId: string;\n\n /**\n * User ID of the connected client (if authenticated and security module is used)\n */\n userId?: string;\n\n /**\n * Room ID that the client sent the message from\n */\n roomId: string;\n\n /**\n * The parsed and validated message from the client\n */\n message: Static<TServer>;\n\n /**\n * Reply function tailored to current context (connectionId, roomId)\n */\n reply: (options: {\n /**\n * Message to send\n */\n message: Static<TClient>;\n\n /**\n * Optional: specify room ID to send to (defaults to sender's room ID)\n */\n roomId?: string;\n\n /**\n * Optional: exclude the sender connection from receiving the message\n * Will populate exceptConnectionIds with sender connection ID behind the scenes\n */\n exceptSelf?: boolean;\n\n /**\n * Optional: exclude specific connection IDs from receiving the message\n */\n exceptConnectionIds?: string[];\n\n /**\n * Optional: exclude specific user IDs from receiving the message\n * Requires alepha/security integration\n */\n exceptUserIds?: string[];\n }) => Promise<void>;\n}\n\n/**\n * Emit options for sending messages from the server\n */\nexport interface EmitOptions<TClient extends TWSObject> {\n /**\n * Message to send to clients\n */\n message: Static<TClient>;\n\n /**\n * Room ID to send the message to\n */\n roomId?: string;\n\n /**\n * Room IDs to send the message to\n */\n roomIds?: string[];\n\n /**\n * User ID to send the message to (if authenticated)\n */\n userId?: string;\n\n /**\n * User IDs to send the message to (if authenticated)\n */\n userIds?: string[];\n\n /**\n * Connection ID to send the message to\n */\n connectionId?: string;\n\n /**\n * Connection IDs to send the message to\n */\n connectionIds?: string[];\n\n /**\n * Optional: exclude specific connection IDs from receiving the message\n */\n exceptConnectionIds?: string[];\n\n /**\n * Optional: exclude specific user IDs from receiving the message\n * Requires alepha/security integration\n */\n exceptUserIds?: string[];\n}\n","import { $logger } from \"alepha/logger\";\n\n/**\n * Manages WebSocket room memberships\n *\n * Rooms are logical groupings of connections. A connection can be in multiple rooms,\n * and messages can be targeted to specific rooms.\n */\nexport class RoomManager {\n protected readonly log = $logger();\n\n /**\n * Maps roomId → Set<connectionId>\n */\n protected readonly rooms = new Map<string, Set<string>>();\n\n /**\n * Maps connectionId → Set<roomId>\n * Inverse index for fast lookup of connection's rooms\n */\n protected readonly connectionRooms = new Map<string, Set<string>>();\n\n /**\n * Join a connection to one or more rooms\n */\n public joinRooms(connectionId: string, roomIds: string[]): void {\n for (const roomId of roomIds) {\n this.joinRoom(connectionId, roomId);\n }\n }\n\n /**\n * Join a connection to a room\n */\n public joinRoom(connectionId: string, roomId: string): void {\n // Add to room\n let room = this.rooms.get(roomId);\n if (!room) {\n room = new Set();\n this.rooms.set(roomId, room);\n }\n room.add(connectionId);\n\n // Update inverse index\n let connRooms = this.connectionRooms.get(connectionId);\n if (!connRooms) {\n connRooms = new Set();\n this.connectionRooms.set(connectionId, connRooms);\n }\n connRooms.add(roomId);\n\n this.log.debug(`Connection ${connectionId} joined room ${roomId}`);\n }\n\n /**\n * Leave a connection from a room\n */\n public leaveRoom(connectionId: string, roomId: string): void {\n // Remove from room\n const room = this.rooms.get(roomId);\n if (room) {\n room.delete(connectionId);\n if (room.size === 0) {\n this.rooms.delete(roomId);\n }\n }\n\n // Update inverse index\n const connRooms = this.connectionRooms.get(connectionId);\n if (connRooms) {\n connRooms.delete(roomId);\n if (connRooms.size === 0) {\n this.connectionRooms.delete(connectionId);\n }\n }\n\n this.log.debug(`Connection ${connectionId} left room ${roomId}`);\n }\n\n /**\n * Remove a connection from all rooms\n */\n public leaveAllRooms(connectionId: string): void {\n const connRooms = this.connectionRooms.get(connectionId);\n if (!connRooms) {\n return;\n }\n\n for (const roomId of connRooms) {\n const room = this.rooms.get(roomId);\n if (room) {\n room.delete(connectionId);\n if (room.size === 0) {\n this.rooms.delete(roomId);\n }\n }\n }\n\n this.connectionRooms.delete(connectionId);\n this.log.debug(`Connection ${connectionId} left all rooms`);\n }\n\n /**\n * Get all connection IDs in a room\n */\n public getRoomConnections(roomId: string): string[] {\n const room = this.rooms.get(roomId);\n return room ? Array.from(room) : [];\n }\n\n /**\n * Get all room IDs for a connection\n */\n public getConnectionRooms(connectionId: string): string[] {\n const connRooms = this.connectionRooms.get(connectionId);\n return connRooms ? Array.from(connRooms) : [];\n }\n\n /**\n * Check if a connection is in a room\n */\n public isInRoom(connectionId: string, roomId: string): boolean {\n const connRooms = this.connectionRooms.get(connectionId);\n return connRooms ? connRooms.has(roomId) : false;\n }\n\n /**\n * Get all active rooms\n */\n public getAllRooms(): string[] {\n return Array.from(this.rooms.keys());\n }\n\n /**\n * Get total number of connections across all rooms\n */\n public getTotalConnections(): number {\n return this.connectionRooms.size;\n }\n\n /**\n * Get room statistics\n */\n public getStats(): {\n totalRooms: number;\n totalConnections: number;\n roomSizes: Map<string, number>;\n } {\n const roomSizes = new Map<string, number>();\n for (const [roomId, connections] of this.rooms) {\n roomSizes.set(roomId, connections.size);\n }\n\n return {\n totalRooms: this.rooms.size,\n totalConnections: this.connectionRooms.size,\n roomSizes,\n };\n }\n}\n","import { type Static, t } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport { $topic } from \"alepha/topic\";\n\n/**\n * WebSocket message distribution event\n */\nconst webSocketMessageSchema = {\n payload: t.object({\n /**\n * Channel path (e.g., \"/ws/chat\")\n */\n channelPath: t.text(),\n\n /**\n * Target room ID(s)\n */\n roomIds: t.optional(t.array(t.text())),\n\n /**\n * Target user ID(s)\n */\n userIds: t.optional(t.array(t.text())),\n\n /**\n * Target connection ID(s)\n */\n connectionIds: t.optional(t.array(t.text())),\n\n /**\n * Exclude connection ID(s) from receiving the message\n */\n exceptConnectionIds: t.optional(t.array(t.text())),\n\n /**\n * Exclude user ID(s) from receiving the message\n */\n exceptUserIds: t.optional(t.array(t.text())),\n\n /**\n * The message payload to send\n */\n message: t.any(),\n }),\n};\n\n/**\n * WebSocket Topic Service\n *\n * Manages pub/sub messaging for WebSocket connections across multiple server instances.\n * Uses alepha/topic for cross-instance message distribution, enabling horizontal scaling.\n *\n * When a WebSocket message needs to be sent:\n * 1. Server instance A publishes to the topic\n * 2. All server instances (A, B, C, etc.) receive the message\n * 3. Each instance sends to its local connections that match the criteria\n *\n * This enables:\n * - Multiple server instances handling WebSocket connections\n * - Redis-backed message distribution (with alepha/topic/redis)\n * - Horizontal scaling without losing messages\n */\nexport class WebSocketTopicService {\n protected readonly log = $logger();\n\n /**\n * Handler function to be called when a message is received from the topic\n * This is set by the WebSocket provider during initialization\n */\n public messageHandler?: (\n event: Static<(typeof webSocketMessageSchema)[\"payload\"]>,\n ) => Promise<void>;\n\n /**\n * Topic for distributing WebSocket messages across server instances\n */\n public readonly topic = $topic({\n name: \"websocket:broadcast\",\n description:\n \"Distributes WebSocket messages across server instances for horizontal scaling\",\n schema: webSocketMessageSchema,\n handler: async (message) => {\n if (this.messageHandler) {\n await this.messageHandler(message.payload);\n }\n },\n });\n\n /**\n * Publish a message to be distributed across all server instances\n */\n public async publish(\n event: Static<(typeof webSocketMessageSchema)[\"payload\"]>,\n ): Promise<void> {\n await this.topic.publish(event);\n }\n\n /**\n * Set the handler for incoming messages\n */\n public setMessageHandler(\n handler: (\n event: Static<(typeof webSocketMessageSchema)[\"payload\"]>,\n ) => Promise<void>,\n ): void {\n this.messageHandler = handler;\n }\n}\n","import { $module, type Alepha } from \"alepha\";\nimport { AlephaTopic } from \"alepha/topic\";\nimport { $channel } from \"./primitives/$channel.ts\";\nimport { $websocket } from \"./primitives/$websocket.ts\";\nimport { WebSocketClient } from \"./services/WebSocketClient.ts\";\n\n/**\n * alepha/websocket (Browser)\n *\n * Browser-side WebSocket client module. Provides WebSocketClient service\n * for managing WebSocket connections from the browser.\n *\n * For React applications, use alepha/react/websocket with the useRoom hook.\n */\nexport * from \"./index.shared.ts\";\n\nexport const AlephaWebSocket = $module({\n name: \"alepha.websocket\",\n primitives: [$channel, $websocket],\n services: [WebSocketClient],\n register: (alepha: Alepha) => {\n alepha.with(AlephaTopic);\n alepha.with(WebSocketClient);\n },\n});\n"],"mappings":"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"}
|
|
@@ -1,16 +1,37 @@
|
|
|
1
1
|
import * as alepha42 from "alepha";
|
|
2
|
-
import { Alepha,
|
|
2
|
+
import { Alepha, KIND, Primitive, Static, TObject, TString, TUnion } from "alepha";
|
|
3
3
|
import * as alepha_topic0 from "alepha/topic";
|
|
4
4
|
import * as alepha_logger1 from "alepha/logger";
|
|
5
5
|
import { WebSocket as WebSocket$1, WebSocketServer } from "ws";
|
|
6
6
|
import { IncomingMessage } from "node:http";
|
|
7
7
|
|
|
8
|
-
//#region src/websocket/
|
|
8
|
+
//#region src/websocket/errors/WebSocketError.d.ts
|
|
9
|
+
/**
|
|
10
|
+
* Base WebSocket error class
|
|
11
|
+
*/
|
|
12
|
+
declare class WebSocketError extends Error {
|
|
13
|
+
readonly code?: number | undefined;
|
|
14
|
+
constructor(message: string, code?: number | undefined);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Error thrown when WebSocket connection fails
|
|
18
|
+
*/
|
|
19
|
+
declare class WebSocketConnectionError extends WebSocketError {
|
|
20
|
+
constructor(message: string, code?: number);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Error thrown when WebSocket message validation fails
|
|
24
|
+
*/
|
|
25
|
+
declare class WebSocketValidationError extends WebSocketError {
|
|
26
|
+
constructor(message: string);
|
|
27
|
+
}
|
|
28
|
+
//#endregion
|
|
29
|
+
//#region src/websocket/primitives/$channel.d.ts
|
|
9
30
|
type TWSObject = TObject | TUnion;
|
|
10
31
|
/**
|
|
11
|
-
* Channel
|
|
32
|
+
* Channel primitive options
|
|
12
33
|
*/
|
|
13
|
-
interface
|
|
34
|
+
interface ChannelPrimitiveOptions<TClient extends TWSObject, TServer extends TWSObject> {
|
|
14
35
|
/**
|
|
15
36
|
* WebSocket endpoint path (e.g., "/ws/chat")
|
|
16
37
|
*/
|
|
@@ -104,10 +125,10 @@ interface ChannelDescriptorOptions<TClient extends TWSObject, TServer extends TW
|
|
|
104
125
|
* ```
|
|
105
126
|
*/
|
|
106
127
|
declare const $channel: {
|
|
107
|
-
<TClient extends TWSObject, TServer extends TWSObject>(options:
|
|
108
|
-
[KIND]: typeof
|
|
128
|
+
<TClient extends TWSObject, TServer extends TWSObject>(options: ChannelPrimitiveOptions<TClient, TServer>): ChannelPrimitive<TClient, TServer>;
|
|
129
|
+
[KIND]: typeof ChannelPrimitive;
|
|
109
130
|
};
|
|
110
|
-
declare class
|
|
131
|
+
declare class ChannelPrimitive<TClient extends TWSObject, TServer extends TWSObject> extends Primitive<ChannelPrimitiveOptions<TClient, TServer>> {}
|
|
111
132
|
//#endregion
|
|
112
133
|
//#region src/websocket/interfaces/WebSocketInterfaces.d.ts
|
|
113
134
|
/**
|
|
@@ -155,11 +176,11 @@ declare enum WebSocketState {
|
|
|
155
176
|
/**
|
|
156
177
|
* WebSocket endpoint configuration (server-side)
|
|
157
178
|
*/
|
|
158
|
-
interface
|
|
179
|
+
interface WebSocketPrimitiveOptions<TClient extends TWSObject, TServer extends TWSObject> {
|
|
159
180
|
/**
|
|
160
181
|
* Channel definition with schema and path
|
|
161
182
|
*/
|
|
162
|
-
channel:
|
|
183
|
+
channel: ChannelPrimitive<TClient, TServer>;
|
|
163
184
|
/**
|
|
164
185
|
* Handler for incoming messages from clients
|
|
165
186
|
*/
|
|
@@ -318,7 +339,7 @@ declare abstract class WebSocketServerProvider {
|
|
|
318
339
|
/**
|
|
319
340
|
* Register a WebSocket endpoint with its channel configuration
|
|
320
341
|
*/
|
|
321
|
-
abstract registerEndpoint<TClient extends TWSObject, TServer extends TWSObject>(config:
|
|
342
|
+
abstract registerEndpoint<TClient extends TWSObject, TServer extends TWSObject>(config: WebSocketPrimitiveOptions<TClient, TServer>): void;
|
|
322
343
|
/**
|
|
323
344
|
* Emit a message to clients based on targeting criteria
|
|
324
345
|
*
|
|
@@ -343,7 +364,7 @@ declare abstract class WebSocketServerProvider {
|
|
|
343
364
|
abstract closeConnection(connectionId: string, code?: number, reason?: string): Promise<void>;
|
|
344
365
|
}
|
|
345
366
|
//#endregion
|
|
346
|
-
//#region src/websocket/
|
|
367
|
+
//#region src/websocket/primitives/$websocket.d.ts
|
|
347
368
|
/**
|
|
348
369
|
* Defines a WebSocket server endpoint for a specific channel.
|
|
349
370
|
*
|
|
@@ -386,10 +407,10 @@ declare abstract class WebSocketServerProvider {
|
|
|
386
407
|
* ```
|
|
387
408
|
*/
|
|
388
409
|
declare const $websocket: {
|
|
389
|
-
<TClient extends TWSObject, TServer extends TWSObject>(options:
|
|
390
|
-
[KIND]: typeof
|
|
410
|
+
<TClient extends TWSObject, TServer extends TWSObject>(options: WebSocketPrimitiveOptions<TClient, TServer>): WebSocketPrimitive<TClient, TServer>;
|
|
411
|
+
[KIND]: typeof WebSocketPrimitive;
|
|
391
412
|
};
|
|
392
|
-
declare class
|
|
413
|
+
declare class WebSocketPrimitive<TClient extends TWSObject, TServer extends TWSObject> extends Primitive<WebSocketPrimitiveOptions<TClient, TServer>> {
|
|
393
414
|
protected readonly webSocketServerProvider: WebSocketServerProvider;
|
|
394
415
|
protected onInit(): void;
|
|
395
416
|
/**
|
|
@@ -423,27 +444,6 @@ declare class WebSocketDescriptor<TClient extends TWSObject, TServer extends TWS
|
|
|
423
444
|
emit(options: EmitOptions<TClient>): Promise<void>;
|
|
424
445
|
}
|
|
425
446
|
//#endregion
|
|
426
|
-
//#region src/websocket/errors/WebSocketError.d.ts
|
|
427
|
-
/**
|
|
428
|
-
* Base WebSocket error class
|
|
429
|
-
*/
|
|
430
|
-
declare class WebSocketError extends Error {
|
|
431
|
-
readonly code?: number | undefined;
|
|
432
|
-
constructor(message: string, code?: number | undefined);
|
|
433
|
-
}
|
|
434
|
-
/**
|
|
435
|
-
* Error thrown when WebSocket connection fails
|
|
436
|
-
*/
|
|
437
|
-
declare class WebSocketConnectionError extends WebSocketError {
|
|
438
|
-
constructor(message: string, code?: number);
|
|
439
|
-
}
|
|
440
|
-
/**
|
|
441
|
-
* Error thrown when WebSocket message validation fails
|
|
442
|
-
*/
|
|
443
|
-
declare class WebSocketValidationError extends WebSocketError {
|
|
444
|
-
constructor(message: string);
|
|
445
|
-
}
|
|
446
|
-
//#endregion
|
|
447
447
|
//#region src/websocket/services/RoomManager.d.ts
|
|
448
448
|
/**
|
|
449
449
|
* Manages WebSocket room memberships
|
|
@@ -524,7 +524,7 @@ declare module "alepha" {
|
|
|
524
524
|
* One connection can handle multiple rooms on the same channel.
|
|
525
525
|
*/
|
|
526
526
|
declare class WebSocketChannelConnection<TClient extends TWSObject, TServer extends TWSObject> {
|
|
527
|
-
protected readonly channel:
|
|
527
|
+
protected readonly channel: ChannelPrimitive<TClient, TServer>;
|
|
528
528
|
protected readonly options: {
|
|
529
529
|
url?: string;
|
|
530
530
|
autoReconnect?: boolean;
|
|
@@ -549,7 +549,7 @@ declare class WebSocketChannelConnection<TClient extends TWSObject, TServer exte
|
|
|
549
549
|
protected onConnectCallbacks: Set<() => void>;
|
|
550
550
|
protected onDisconnectCallbacks: Set<() => void>;
|
|
551
551
|
protected onErrorCallbacks: Set<(error: Error) => void>;
|
|
552
|
-
constructor(channel:
|
|
552
|
+
constructor(channel: ChannelPrimitive<TClient, TServer>, options: {
|
|
553
553
|
url?: string;
|
|
554
554
|
autoReconnect?: boolean;
|
|
555
555
|
reconnectInterval?: number;
|
|
@@ -618,7 +618,7 @@ declare class WebSocketClient {
|
|
|
618
618
|
/**
|
|
619
619
|
* Subscribe to a room on a channel
|
|
620
620
|
*/
|
|
621
|
-
subscribe<TClient extends TWSObject, TServer extends TWSObject>(roomId: string, channel:
|
|
621
|
+
subscribe<TClient extends TWSObject, TServer extends TWSObject>(roomId: string, channel: ChannelPrimitive<TClient, TServer>, handler: (message: Static<TClient>) => void, options?: {
|
|
622
622
|
url?: string;
|
|
623
623
|
autoReconnect?: boolean;
|
|
624
624
|
reconnectInterval?: number;
|
|
@@ -630,11 +630,11 @@ declare class WebSocketClient {
|
|
|
630
630
|
/**
|
|
631
631
|
* Send message to a room on a channel
|
|
632
632
|
*/
|
|
633
|
-
send<TClient extends TWSObject, TServer extends TWSObject>(roomId: string, channel:
|
|
633
|
+
send<TClient extends TWSObject, TServer extends TWSObject>(roomId: string, channel: ChannelPrimitive<TClient, TServer>, message: Static<TServer>): Promise<void>;
|
|
634
634
|
/**
|
|
635
635
|
* Get connection for a channel
|
|
636
636
|
*/
|
|
637
|
-
getConnection<TClient extends TWSObject, TServer extends TWSObject>(channel:
|
|
637
|
+
getConnection<TClient extends TWSObject, TServer extends TWSObject>(channel: ChannelPrimitive<TClient, TServer>): WebSocketChannelConnection<TClient, TServer> | undefined;
|
|
638
638
|
/**
|
|
639
639
|
* Disconnect all connections
|
|
640
640
|
*/
|
|
@@ -703,7 +703,7 @@ declare class WebSocketTopicService {
|
|
|
703
703
|
/**
|
|
704
704
|
* Topic for distributing WebSocket messages across server instances
|
|
705
705
|
*/
|
|
706
|
-
readonly topic: alepha_topic0.
|
|
706
|
+
readonly topic: alepha_topic0.TopicPrimitive<{
|
|
707
707
|
payload: alepha42.TObject<{
|
|
708
708
|
/**
|
|
709
709
|
* Channel path (e.g., "/ws/chat")
|
|
@@ -761,18 +761,18 @@ declare class NodeWebSocketServerProvider extends WebSocketServerProvider {
|
|
|
761
761
|
WEBSOCKET_PATH: string;
|
|
762
762
|
};
|
|
763
763
|
protected wss?: WebSocketServer;
|
|
764
|
-
protected endpoints: Map<string,
|
|
764
|
+
protected endpoints: Map<string, WebSocketPrimitiveOptions<any, any>>;
|
|
765
765
|
protected connections: Map<string, WebSocketConnection>;
|
|
766
766
|
protected userConnections: Map<string, Set<string>>;
|
|
767
767
|
protected nextConnectionId: number;
|
|
768
|
-
registerEndpoint<TClient extends TWSObject, TServer extends TWSObject>(config:
|
|
768
|
+
registerEndpoint<TClient extends TWSObject, TServer extends TWSObject>(config: WebSocketPrimitiveOptions<TClient, TServer>): void;
|
|
769
769
|
emit<TClient extends TWSObject>(channelPath: string, options: EmitOptions<TClient>): Promise<void>;
|
|
770
770
|
getConnections(): WebSocketConnection[];
|
|
771
771
|
getRoomConnections(roomId: string): WebSocketConnection[];
|
|
772
772
|
getUserConnections(userId: string): WebSocketConnection[];
|
|
773
773
|
closeConnection(connectionId: string, code?: number, reason?: string): Promise<void>;
|
|
774
774
|
protected handleUpgrade(request: IncomingMessage, socket: any, head: Buffer): boolean;
|
|
775
|
-
protected handleConnection<TClient extends TWSObject, TServer extends TWSObject>(ws: WebSocket$1, endpoint:
|
|
775
|
+
protected handleConnection<TClient extends TWSObject, TServer extends TWSObject>(ws: WebSocket$1, endpoint: WebSocketPrimitiveOptions<TClient, TServer>, request: IncomingMessage): void;
|
|
776
776
|
protected extractRoomIds(url: URL): string[];
|
|
777
777
|
/**
|
|
778
778
|
* Send message to local connections based on targeting criteria
|
|
@@ -785,9 +785,9 @@ declare class NodeWebSocketServerProvider extends WebSocketServerProvider {
|
|
|
785
785
|
exceptConnectionIds?: string[];
|
|
786
786
|
exceptUserIds?: string[];
|
|
787
787
|
}): Promise<void>;
|
|
788
|
-
protected readonly start: alepha42.
|
|
789
|
-
protected readonly ready: alepha42.
|
|
790
|
-
protected readonly stop: alepha42.
|
|
788
|
+
protected readonly start: alepha42.HookPrimitive<"start">;
|
|
789
|
+
protected readonly ready: alepha42.HookPrimitive<"ready">;
|
|
790
|
+
protected readonly stop: alepha42.HookPrimitive<"stop">;
|
|
791
791
|
}
|
|
792
792
|
declare class NodeWebSocketConnection implements WebSocketConnection {
|
|
793
793
|
readonly id: string;
|
|
@@ -795,10 +795,10 @@ declare class NodeWebSocketConnection implements WebSocketConnection {
|
|
|
795
795
|
readonly roomIds: string[];
|
|
796
796
|
protected readonly ws: WebSocket$1;
|
|
797
797
|
protected readonly provider: NodeWebSocketServerProvider;
|
|
798
|
-
protected readonly endpoint:
|
|
798
|
+
protected readonly endpoint: WebSocketPrimitiveOptions<any, any>;
|
|
799
799
|
protected readonly log: alepha_logger1.Logger;
|
|
800
800
|
metadata?: Record<string, any>;
|
|
801
|
-
constructor(id: string, userId: string | undefined, roomIds: string[], ws: WebSocket$1, provider: NodeWebSocketServerProvider, endpoint:
|
|
801
|
+
constructor(id: string, userId: string | undefined, roomIds: string[], ws: WebSocket$1, provider: NodeWebSocketServerProvider, endpoint: WebSocketPrimitiveOptions<any, any>);
|
|
802
802
|
get readyState(): WebSocketState;
|
|
803
803
|
send(message: any): Promise<void>;
|
|
804
804
|
close(code?: number, reason?: string): Promise<void>;
|
|
@@ -845,7 +845,7 @@ declare module "alepha" {
|
|
|
845
845
|
/**
|
|
846
846
|
* Provides real-time bidirectional communication using WebSockets.
|
|
847
847
|
*
|
|
848
|
-
* The WebSockets module enables building real-time applications using the `$websocket`
|
|
848
|
+
* The WebSockets module enables building real-time applications using the `$websocket` primitive
|
|
849
849
|
* on class properties. It provides automatic connection management, message routing, type-safe
|
|
850
850
|
* message handling, and seamless integration with other Alepha modules.
|
|
851
851
|
*
|
|
@@ -857,5 +857,5 @@ declare module "alepha" {
|
|
|
857
857
|
*/
|
|
858
858
|
declare const AlephaWebSocket: alepha42.Service<alepha42.Module>;
|
|
859
859
|
//#endregion
|
|
860
|
-
export { $channel, $websocket, AlephaWebSocket,
|
|
860
|
+
export { $channel, $websocket, AlephaWebSocket, ChannelPrimitive, ChannelPrimitiveOptions, EmitOptions, NodeWebSocketConnection, NodeWebSocketServerProvider, RoomManager, TWSObject, WebSocketChannelConnection, WebSocketClient, WebSocketConnection, WebSocketConnectionError, WebSocketError, WebSocketHandler, WebSocketHandlerContext, WebSocketPrimitive, WebSocketPrimitiveOptions, WebSocketServerProvider, WebSocketState, WebSocketTopicService, WebSocketValidationError };
|
|
861
861
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/websocket/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { $env, $hook, $inject, $module, Alepha, AlephaError,
|
|
1
|
+
import { $env, $hook, $inject, $module, Alepha, AlephaError, KIND, Primitive, TypeBoxValue, createPrimitive, t } from "alepha";
|
|
2
2
|
import { AlephaServer } from "alepha/server";
|
|
3
3
|
import { $topic, AlephaTopic } from "alepha/topic";
|
|
4
4
|
import { $logger } from "alepha/logger";
|
|
5
5
|
import { WebSocket as WebSocket$1, WebSocketServer } from "ws";
|
|
6
6
|
|
|
7
|
-
//#region src/websocket/
|
|
7
|
+
//#region src/websocket/primitives/$channel.ts
|
|
8
8
|
/**
|
|
9
9
|
* Defines a WebSocket channel with specified client and server message schemas.
|
|
10
10
|
*
|
|
@@ -68,10 +68,10 @@ import { WebSocket as WebSocket$1, WebSocketServer } from "ws";
|
|
|
68
68
|
* ```
|
|
69
69
|
*/
|
|
70
70
|
const $channel = (options) => {
|
|
71
|
-
return
|
|
71
|
+
return createPrimitive(ChannelPrimitive, options);
|
|
72
72
|
};
|
|
73
|
-
var
|
|
74
|
-
$channel[KIND] =
|
|
73
|
+
var ChannelPrimitive = class extends Primitive {};
|
|
74
|
+
$channel[KIND] = ChannelPrimitive;
|
|
75
75
|
|
|
76
76
|
//#endregion
|
|
77
77
|
//#region src/websocket/providers/WebSocketServerProvider.ts
|
|
@@ -84,7 +84,7 @@ $channel[KIND] = ChannelDescriptor;
|
|
|
84
84
|
var WebSocketServerProvider = class {};
|
|
85
85
|
|
|
86
86
|
//#endregion
|
|
87
|
-
//#region src/websocket/
|
|
87
|
+
//#region src/websocket/primitives/$websocket.ts
|
|
88
88
|
/**
|
|
89
89
|
* Defines a WebSocket server endpoint for a specific channel.
|
|
90
90
|
*
|
|
@@ -127,9 +127,9 @@ var WebSocketServerProvider = class {};
|
|
|
127
127
|
* ```
|
|
128
128
|
*/
|
|
129
129
|
const $websocket = (options) => {
|
|
130
|
-
return
|
|
130
|
+
return createPrimitive(WebSocketPrimitive, options);
|
|
131
131
|
};
|
|
132
|
-
var
|
|
132
|
+
var WebSocketPrimitive = class extends Primitive {
|
|
133
133
|
webSocketServerProvider = $inject(WebSocketServerProvider);
|
|
134
134
|
onInit() {
|
|
135
135
|
this.webSocketServerProvider.registerEndpoint(this.options);
|
|
@@ -166,7 +166,7 @@ var WebSocketDescriptor = class extends Descriptor {
|
|
|
166
166
|
await this.webSocketServerProvider.emit(this.options.channel.options.path, options);
|
|
167
167
|
}
|
|
168
168
|
};
|
|
169
|
-
$websocket[KIND] =
|
|
169
|
+
$websocket[KIND] = WebSocketPrimitive;
|
|
170
170
|
|
|
171
171
|
//#endregion
|
|
172
172
|
//#region src/websocket/errors/WebSocketError.ts
|
|
@@ -568,7 +568,7 @@ var NodeWebSocketServerProvider = class extends WebSocketServerProvider {
|
|
|
568
568
|
on: "ready",
|
|
569
569
|
handler: async () => {
|
|
570
570
|
if (this.alepha.isServerless() || !this.wss) return;
|
|
571
|
-
const httpServer = this.alepha.
|
|
571
|
+
const httpServer = this.alepha.store.get("alepha.node.server");
|
|
572
572
|
if (httpServer) {
|
|
573
573
|
httpServer.on("upgrade", (request, socket, head) => {
|
|
574
574
|
this.handleUpgrade(request, socket, head);
|
|
@@ -1068,7 +1068,7 @@ var WebSocketClient = class {
|
|
|
1068
1068
|
/**
|
|
1069
1069
|
* Provides real-time bidirectional communication using WebSockets.
|
|
1070
1070
|
*
|
|
1071
|
-
* The WebSockets module enables building real-time applications using the `$websocket`
|
|
1071
|
+
* The WebSockets module enables building real-time applications using the `$websocket` primitive
|
|
1072
1072
|
* on class properties. It provides automatic connection management, message routing, type-safe
|
|
1073
1073
|
* message handling, and seamless integration with other Alepha modules.
|
|
1074
1074
|
*
|
|
@@ -1080,7 +1080,7 @@ var WebSocketClient = class {
|
|
|
1080
1080
|
*/
|
|
1081
1081
|
const AlephaWebSocket = $module({
|
|
1082
1082
|
name: "alepha.websocket",
|
|
1083
|
-
|
|
1083
|
+
primitives: [$channel, $websocket],
|
|
1084
1084
|
services: [
|
|
1085
1085
|
WebSocketServerProvider,
|
|
1086
1086
|
NodeWebSocketServerProvider,
|
|
@@ -1098,5 +1098,5 @@ const AlephaWebSocket = $module({
|
|
|
1098
1098
|
});
|
|
1099
1099
|
|
|
1100
1100
|
//#endregion
|
|
1101
|
-
export { $channel, $websocket, AlephaWebSocket,
|
|
1101
|
+
export { $channel, $websocket, AlephaWebSocket, ChannelPrimitive, NodeWebSocketConnection, NodeWebSocketServerProvider, RoomManager, WebSocketChannelConnection, WebSocketClient, WebSocketConnectionError, WebSocketError, WebSocketPrimitive, WebSocketServerProvider, WebSocketState, WebSocketTopicService, WebSocketValidationError };
|
|
1102
1102
|
//# sourceMappingURL=index.js.map
|