@syncar/server 1.0.0-alpha.1 → 1.0.0-alpha.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 +157 -90
- package/dist/index.d.ts +66 -66
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +55 -55
package/dist/index.d.ts
CHANGED
|
@@ -215,7 +215,7 @@ interface IClientConnection {
|
|
|
215
215
|
* Message types for protocol communication
|
|
216
216
|
*
|
|
217
217
|
* @remarks
|
|
218
|
-
* Enum defining all message types supported by the
|
|
218
|
+
* Enum defining all message types supported by the Syncar protocol.
|
|
219
219
|
*
|
|
220
220
|
* @example
|
|
221
221
|
* ```ts
|
|
@@ -309,7 +309,7 @@ declare enum ErrorCode {
|
|
|
309
309
|
* Base message interface
|
|
310
310
|
*
|
|
311
311
|
* @remarks
|
|
312
|
-
* All messages in the
|
|
312
|
+
* All messages in the Syncar protocol extend this interface with
|
|
313
313
|
* common fields like ID, timestamp, and optional channel.
|
|
314
314
|
*
|
|
315
315
|
* @property id - Unique message identifier
|
|
@@ -549,7 +549,7 @@ type Message<T = unknown> = DataMessage<T> | SignalMessage | ErrorMessage | AckM
|
|
|
549
549
|
*
|
|
550
550
|
* @example
|
|
551
551
|
* ```ts
|
|
552
|
-
* import type { IMiddlewareAction } from '@
|
|
552
|
+
* import type { IMiddlewareAction } from '@syncar/server'
|
|
553
553
|
*
|
|
554
554
|
* function handleAction(action: IMiddlewareAction) {
|
|
555
555
|
* switch (action) {
|
|
@@ -701,7 +701,7 @@ interface Context<S = Record<string, unknown>> {
|
|
|
701
701
|
*
|
|
702
702
|
* @example
|
|
703
703
|
* ```ts
|
|
704
|
-
* import type { Middleware } from '@
|
|
704
|
+
* import type { Middleware } from '@syncar/server'
|
|
705
705
|
*
|
|
706
706
|
* const authMiddleware: Middleware = async (context, next) => {
|
|
707
707
|
* const token = context.req.message?.data?.token
|
|
@@ -1628,7 +1628,7 @@ type ServerInstance = WebSocketServer;
|
|
|
1628
1628
|
*
|
|
1629
1629
|
* @remarks
|
|
1630
1630
|
* Configuration options for the WebSocket transport layer. Extends the
|
|
1631
|
-
* standard `ws` library options with
|
|
1631
|
+
* standard `ws` library options with Syncar-specific settings.
|
|
1632
1632
|
*
|
|
1633
1633
|
* @example
|
|
1634
1634
|
* ```ts
|
|
@@ -1733,7 +1733,7 @@ interface WebSocketServerTransportConfig extends ServerOptions {
|
|
|
1733
1733
|
* @example
|
|
1734
1734
|
* ### Basic usage
|
|
1735
1735
|
* ```ts
|
|
1736
|
-
* import { WebSocketServerTransport } from '@
|
|
1736
|
+
* import { WebSocketServerTransport } from '@syncar/server'
|
|
1737
1737
|
*
|
|
1738
1738
|
* const transport = new WebSocketServerTransport({
|
|
1739
1739
|
* server: httpServer,
|
|
@@ -1875,7 +1875,7 @@ declare class WebSocketServerTransport extends EventEmitter {
|
|
|
1875
1875
|
*
|
|
1876
1876
|
* @example
|
|
1877
1877
|
* ```ts
|
|
1878
|
-
* const server =
|
|
1878
|
+
* const server = createSyncarServer({ port: 3000 })
|
|
1879
1879
|
* await server.start()
|
|
1880
1880
|
*
|
|
1881
1881
|
* const stats = server.getStats()
|
|
@@ -1899,15 +1899,15 @@ interface IServerStats {
|
|
|
1899
1899
|
* Server configuration options
|
|
1900
1900
|
*
|
|
1901
1901
|
* @remarks
|
|
1902
|
-
* Complete configuration interface for the
|
|
1902
|
+
* Complete configuration interface for the Syncar server. These options
|
|
1903
1903
|
* control the WebSocket transport layer, connection handling, middleware,
|
|
1904
1904
|
* and performance tuning parameters.
|
|
1905
1905
|
*
|
|
1906
1906
|
* @example
|
|
1907
1907
|
* ```ts
|
|
1908
|
-
* import {
|
|
1908
|
+
* import { createSyncarServer } from '@syncar/server'
|
|
1909
1909
|
*
|
|
1910
|
-
* const server =
|
|
1910
|
+
* const server = createSyncarServer({
|
|
1911
1911
|
* port: 3000,
|
|
1912
1912
|
* host: '0.0.0.0',
|
|
1913
1913
|
* path: '/ws',
|
|
@@ -1970,7 +1970,7 @@ interface IServerOptions {
|
|
|
1970
1970
|
*/
|
|
1971
1971
|
host: string;
|
|
1972
1972
|
/**
|
|
1973
|
-
* WebSocket path (default: '/
|
|
1973
|
+
* WebSocket path (default: '/syncar')
|
|
1974
1974
|
*
|
|
1975
1975
|
* @remarks
|
|
1976
1976
|
* The URL path for WebSocket connections. Clients must connect to
|
|
@@ -2045,7 +2045,7 @@ interface IServerOptions {
|
|
|
2045
2045
|
broadcastChunkSize: number;
|
|
2046
2046
|
}
|
|
2047
2047
|
/**
|
|
2048
|
-
*
|
|
2048
|
+
* Syncar Server - Real-time WebSocket server with pub/sub channels
|
|
2049
2049
|
*
|
|
2050
2050
|
* @remarks
|
|
2051
2051
|
* The main server class providing WebSocket communication with broadcast
|
|
@@ -2053,9 +2053,9 @@ interface IServerOptions {
|
|
|
2053
2053
|
*
|
|
2054
2054
|
* @example
|
|
2055
2055
|
* ```ts
|
|
2056
|
-
* import {
|
|
2056
|
+
* import { createSyncarServer } from '@syncar/server'
|
|
2057
2057
|
*
|
|
2058
|
-
* const server =
|
|
2058
|
+
* const server = createSyncarServer({ port: 3000 })
|
|
2059
2059
|
* await server.start()
|
|
2060
2060
|
*
|
|
2061
2061
|
* // Create channels
|
|
@@ -2072,9 +2072,9 @@ interface IServerOptions {
|
|
|
2072
2072
|
* chat.publish({ text: 'Welcome!' })
|
|
2073
2073
|
* ```
|
|
2074
2074
|
*
|
|
2075
|
-
* @see {@link
|
|
2075
|
+
* @see {@link createSyncarServer} for factory function
|
|
2076
2076
|
*/
|
|
2077
|
-
declare class
|
|
2077
|
+
declare class SyncarServer {
|
|
2078
2078
|
private readonly config;
|
|
2079
2079
|
private transport;
|
|
2080
2080
|
readonly registry: ClientRegistry;
|
|
@@ -2096,7 +2096,7 @@ declare class SyncaServer {
|
|
|
2096
2096
|
*
|
|
2097
2097
|
* @example
|
|
2098
2098
|
* ```ts
|
|
2099
|
-
* const server =
|
|
2099
|
+
* const server = createSyncarServer({ port: 3000 })
|
|
2100
2100
|
* await server.start()
|
|
2101
2101
|
* console.log('Server is running')
|
|
2102
2102
|
* ```
|
|
@@ -2232,7 +2232,7 @@ declare class SyncaServer {
|
|
|
2232
2232
|
*
|
|
2233
2233
|
* @example
|
|
2234
2234
|
* ```ts
|
|
2235
|
-
* import { createAuthMiddleware } from '@
|
|
2235
|
+
* import { createAuthMiddleware } from '@syncar/server'
|
|
2236
2236
|
*
|
|
2237
2237
|
* server.use(createAuthMiddleware({
|
|
2238
2238
|
* verifyToken: async (token) => jwt.verify(token, SECRET)
|
|
@@ -2324,31 +2324,31 @@ declare class SyncaServer {
|
|
|
2324
2324
|
private setupTransportHandlers;
|
|
2325
2325
|
}
|
|
2326
2326
|
/**
|
|
2327
|
-
* Create a
|
|
2327
|
+
* Create a Syncar server with automatic WebSocket transport setup
|
|
2328
2328
|
*
|
|
2329
2329
|
* @remarks
|
|
2330
|
-
* Factory function that creates a configured
|
|
2330
|
+
* Factory function that creates a configured SyncarServer instance.
|
|
2331
2331
|
* Automatically sets up the WebSocket transport layer if not provided,
|
|
2332
2332
|
* merges user configuration with defaults, and creates the client registry.
|
|
2333
2333
|
*
|
|
2334
2334
|
* @param config - Optional partial server configuration. All properties are optional
|
|
2335
2335
|
* and will be merged with {@link DEFAULT_SERVER_CONFIG}.
|
|
2336
2336
|
*
|
|
2337
|
-
* @returns Configured
|
|
2337
|
+
* @returns Configured Syncar server instance ready to be started
|
|
2338
2338
|
*
|
|
2339
2339
|
* @example
|
|
2340
2340
|
* ### Basic usage
|
|
2341
2341
|
* ```ts
|
|
2342
|
-
* import {
|
|
2342
|
+
* import { createSyncarServer } from '@syncar/server'
|
|
2343
2343
|
*
|
|
2344
|
-
* const server =
|
|
2344
|
+
* const server = createSyncarServer({ port: 3000 })
|
|
2345
2345
|
* await server.start()
|
|
2346
2346
|
* ```
|
|
2347
2347
|
*
|
|
2348
2348
|
* @example
|
|
2349
2349
|
* ### With custom configuration
|
|
2350
2350
|
* ```ts
|
|
2351
|
-
* const server =
|
|
2351
|
+
* const server = createSyncarServer({
|
|
2352
2352
|
* port: 8080,
|
|
2353
2353
|
* host: 'localhost',
|
|
2354
2354
|
* path: '/ws',
|
|
@@ -2364,14 +2364,14 @@ declare class SyncaServer {
|
|
|
2364
2364
|
* ### With existing HTTP server
|
|
2365
2365
|
* ```ts
|
|
2366
2366
|
* import { createServer } from 'node:http'
|
|
2367
|
-
* import {
|
|
2367
|
+
* import { createSyncarServer } from '@syncar/server'
|
|
2368
2368
|
*
|
|
2369
2369
|
* const httpServer = createServer((req, res) => {
|
|
2370
2370
|
* res.writeHead(200)
|
|
2371
2371
|
* res.end('OK')
|
|
2372
2372
|
* })
|
|
2373
2373
|
*
|
|
2374
|
-
* const server =
|
|
2374
|
+
* const server = createSyncarServer({
|
|
2375
2375
|
* server: httpServer,
|
|
2376
2376
|
* path: '/ws',
|
|
2377
2377
|
* })
|
|
@@ -2383,12 +2383,12 @@ declare class SyncaServer {
|
|
|
2383
2383
|
* ### With Express
|
|
2384
2384
|
* ```ts
|
|
2385
2385
|
* import express from 'express'
|
|
2386
|
-
* import {
|
|
2386
|
+
* import { createSyncarServer } from '@syncar/server'
|
|
2387
2387
|
*
|
|
2388
2388
|
* const app = express()
|
|
2389
2389
|
* const httpServer = app.listen(3000)
|
|
2390
2390
|
*
|
|
2391
|
-
* const server =
|
|
2391
|
+
* const server = createSyncarServer({
|
|
2392
2392
|
* server: httpServer,
|
|
2393
2393
|
* path: '/ws',
|
|
2394
2394
|
* })
|
|
@@ -2399,9 +2399,9 @@ declare class SyncaServer {
|
|
|
2399
2399
|
* @example
|
|
2400
2400
|
* ### With custom logger
|
|
2401
2401
|
* ```ts
|
|
2402
|
-
* import {
|
|
2402
|
+
* import { createSyncarServer } from '@syncar/server'
|
|
2403
2403
|
*
|
|
2404
|
-
* const server =
|
|
2404
|
+
* const server = createSyncarServer({
|
|
2405
2405
|
* port: 3000,
|
|
2406
2406
|
* logger: {
|
|
2407
2407
|
* debug: (msg, ...args) => console.debug('[DEBUG]', msg, ...args),
|
|
@@ -2415,9 +2415,9 @@ declare class SyncaServer {
|
|
|
2415
2415
|
* @example
|
|
2416
2416
|
* ### With middleware
|
|
2417
2417
|
* ```ts
|
|
2418
|
-
* import {
|
|
2418
|
+
* import { createSyncarServer, createLoggingMiddleware } from '@syncar/server'
|
|
2419
2419
|
*
|
|
2420
|
-
* const server =
|
|
2420
|
+
* const server = createSyncarServer({
|
|
2421
2421
|
* port: 3000,
|
|
2422
2422
|
* middleware: [
|
|
2423
2423
|
* createLoggingMiddleware(),
|
|
@@ -2427,10 +2427,10 @@ declare class SyncaServer {
|
|
|
2427
2427
|
* await server.start()
|
|
2428
2428
|
* ```
|
|
2429
2429
|
*
|
|
2430
|
-
* @see {@link
|
|
2430
|
+
* @see {@link SyncarServer} for server class API
|
|
2431
2431
|
* @see {@link DEFAULT_SERVER_CONFIG} for default configuration values
|
|
2432
2432
|
*/
|
|
2433
|
-
declare function
|
|
2433
|
+
declare function createSyncarServer(config?: Partial<IServerOptions>): SyncarServer;
|
|
2434
2434
|
|
|
2435
2435
|
/**
|
|
2436
2436
|
* Middleware Factories
|
|
@@ -2496,7 +2496,7 @@ interface AuthMiddlewareOptions {
|
|
|
2496
2496
|
*
|
|
2497
2497
|
* @example
|
|
2498
2498
|
* ```ts
|
|
2499
|
-
* import { createAuthMiddleware } from '@
|
|
2499
|
+
* import { createAuthMiddleware } from '@syncar/server/middleware'
|
|
2500
2500
|
*
|
|
2501
2501
|
* const authMiddleware = createAuthMiddleware({
|
|
2502
2502
|
* verifyToken: async (token) => {
|
|
@@ -2568,7 +2568,7 @@ interface LoggingMiddlewareOptions {
|
|
|
2568
2568
|
*
|
|
2569
2569
|
* @example
|
|
2570
2570
|
* ```ts
|
|
2571
|
-
* import { createLoggingMiddleware } from '@
|
|
2571
|
+
* import { createLoggingMiddleware } from '@syncar/server/middleware'
|
|
2572
2572
|
*
|
|
2573
2573
|
* const loggingMiddleware = createLoggingMiddleware({
|
|
2574
2574
|
* logger: console,
|
|
@@ -2627,7 +2627,7 @@ interface RateLimitMiddlewareOptions {
|
|
|
2627
2627
|
*
|
|
2628
2628
|
* @example
|
|
2629
2629
|
* ```ts
|
|
2630
|
-
* import { createRateLimitMiddleware } from '@
|
|
2630
|
+
* import { createRateLimitMiddleware } from '@syncar/server/middleware'
|
|
2631
2631
|
*
|
|
2632
2632
|
* const rateLimitMiddleware = createRateLimitMiddleware({
|
|
2633
2633
|
* maxRequests: 100,
|
|
@@ -2688,7 +2688,7 @@ interface ChannelWhitelistMiddlewareOptions {
|
|
|
2688
2688
|
*
|
|
2689
2689
|
* @example
|
|
2690
2690
|
* ```ts
|
|
2691
|
-
* import { createChannelWhitelistMiddleware } from '@
|
|
2691
|
+
* import { createChannelWhitelistMiddleware } from '@syncar/server/middleware'
|
|
2692
2692
|
*
|
|
2693
2693
|
* const whitelistMiddleware = createChannelWhitelistMiddleware({
|
|
2694
2694
|
* allowedChannels: ['chat', 'notifications']
|
|
@@ -2737,7 +2737,7 @@ interface ContextOptions<S = Record<string, unknown>> {
|
|
|
2737
2737
|
initialState?: S;
|
|
2738
2738
|
}
|
|
2739
2739
|
/**
|
|
2740
|
-
* Create
|
|
2740
|
+
* Create onion pattern middleware context
|
|
2741
2741
|
*
|
|
2742
2742
|
* @remarks
|
|
2743
2743
|
* Creates a lightweight middleware context using closures to ensure properties
|
|
@@ -2800,7 +2800,7 @@ declare function createContext<S = Record<string, unknown>>(options: ContextOpti
|
|
|
2800
2800
|
*
|
|
2801
2801
|
* @example
|
|
2802
2802
|
* ```ts
|
|
2803
|
-
* import { ContextManager } from '@
|
|
2803
|
+
* import { ContextManager } from '@syncar/server'
|
|
2804
2804
|
*
|
|
2805
2805
|
* const manager = new ContextManager()
|
|
2806
2806
|
*
|
|
@@ -2814,7 +2814,7 @@ declare function createContext<S = Record<string, unknown>>(options: ContextOpti
|
|
|
2814
2814
|
* const context = await manager.executeConnection(client, 'connect')
|
|
2815
2815
|
* ```
|
|
2816
2816
|
*
|
|
2817
|
-
* @see {@link
|
|
2817
|
+
* @see {@link createSyncarServer} for server-level context management
|
|
2818
2818
|
*/
|
|
2819
2819
|
declare class ContextManager {
|
|
2820
2820
|
/** Registered middleware functions */
|
|
@@ -3120,13 +3120,13 @@ declare class ContextManager {
|
|
|
3120
3120
|
* Errors Module
|
|
3121
3121
|
*
|
|
3122
3122
|
* @description
|
|
3123
|
-
* Custom error classes for the
|
|
3124
|
-
* {@link
|
|
3123
|
+
* Custom error classes for the Syncar server. All errors extend from
|
|
3124
|
+
* {@link SyncarError} and include error codes for programmatic handling.
|
|
3125
3125
|
*
|
|
3126
3126
|
* @remarks
|
|
3127
3127
|
* The error hierarchy:
|
|
3128
3128
|
*
|
|
3129
|
-
* - {@link
|
|
3129
|
+
* - {@link SyncarError} - Base error class
|
|
3130
3130
|
* - {@link ConfigError} - Server configuration issues
|
|
3131
3131
|
* - {@link TransportError} - WebSocket transport issues
|
|
3132
3132
|
* - {@link ChannelError} - Channel operation failures
|
|
@@ -3140,7 +3140,7 @@ declare class ContextManager {
|
|
|
3140
3140
|
* @example
|
|
3141
3141
|
* ### Throwing errors
|
|
3142
3142
|
* ```ts
|
|
3143
|
-
* import { StateError, ValidationError } from '@
|
|
3143
|
+
* import { StateError, ValidationError } from '@syncar/server'
|
|
3144
3144
|
*
|
|
3145
3145
|
* function createChannel(name: string) {
|
|
3146
3146
|
* if (!name) {
|
|
@@ -3156,10 +3156,10 @@ declare class ContextManager {
|
|
|
3156
3156
|
* ### Catching errors
|
|
3157
3157
|
* ```ts
|
|
3158
3158
|
* import {
|
|
3159
|
-
*
|
|
3159
|
+
* SyncarError,
|
|
3160
3160
|
* MiddlewareRejectionError,
|
|
3161
3161
|
* StateError
|
|
3162
|
-
* } from '@
|
|
3162
|
+
* } from '@syncar/server'
|
|
3163
3163
|
*
|
|
3164
3164
|
* try {
|
|
3165
3165
|
* await server.start()
|
|
@@ -3168,7 +3168,7 @@ declare class ContextManager {
|
|
|
3168
3168
|
* console.error('Invalid state:', error.message)
|
|
3169
3169
|
* } else if (error instanceof MiddlewareRejectionError) {
|
|
3170
3170
|
* console.error(`Action rejected: ${error.reason}`)
|
|
3171
|
-
* } else if (error instanceof
|
|
3171
|
+
* } else if (error instanceof SyncarError) {
|
|
3172
3172
|
* console.error(`[${error.code}] ${error.message}`)
|
|
3173
3173
|
* }
|
|
3174
3174
|
* }
|
|
@@ -3178,10 +3178,10 @@ declare class ContextManager {
|
|
|
3178
3178
|
*/
|
|
3179
3179
|
|
|
3180
3180
|
/**
|
|
3181
|
-
* Base
|
|
3181
|
+
* Base Syncar error class
|
|
3182
3182
|
*
|
|
3183
3183
|
* @remarks
|
|
3184
|
-
* All custom errors in the
|
|
3184
|
+
* All custom errors in the Syncar server extend this class. Provides
|
|
3185
3185
|
* consistent error handling with error codes, context, and serialization.
|
|
3186
3186
|
*
|
|
3187
3187
|
* @property code - Error code for programmatic handling
|
|
@@ -3189,7 +3189,7 @@ declare class ContextManager {
|
|
|
3189
3189
|
*
|
|
3190
3190
|
* @example
|
|
3191
3191
|
* ```ts
|
|
3192
|
-
* throw new
|
|
3192
|
+
* throw new SyncarError('Something went wrong', 'CUSTOM_ERROR', { userId: '123' })
|
|
3193
3193
|
* ```
|
|
3194
3194
|
*
|
|
3195
3195
|
* @example
|
|
@@ -3198,7 +3198,7 @@ declare class ContextManager {
|
|
|
3198
3198
|
* try {
|
|
3199
3199
|
* // ...
|
|
3200
3200
|
* } catch (error) {
|
|
3201
|
-
* if (error instanceof
|
|
3201
|
+
* if (error instanceof SyncarError) {
|
|
3202
3202
|
* console.log(error.code) // 'CUSTOM_ERROR'
|
|
3203
3203
|
* console.log(error.message) // 'Something went wrong'
|
|
3204
3204
|
* console.log(error.context) // { userId: '123' }
|
|
@@ -3207,7 +3207,7 @@ declare class ContextManager {
|
|
|
3207
3207
|
* }
|
|
3208
3208
|
* ```
|
|
3209
3209
|
*/
|
|
3210
|
-
declare class
|
|
3210
|
+
declare class SyncarError extends Error {
|
|
3211
3211
|
/**
|
|
3212
3212
|
* Error code for programmatic error handling
|
|
3213
3213
|
*
|
|
@@ -3225,7 +3225,7 @@ declare class SyncaError extends Error {
|
|
|
3225
3225
|
*/
|
|
3226
3226
|
readonly context?: Record<string, unknown>;
|
|
3227
3227
|
/**
|
|
3228
|
-
* Creates a new
|
|
3228
|
+
* Creates a new SyncarError
|
|
3229
3229
|
*
|
|
3230
3230
|
* @param message - Human-readable error message
|
|
3231
3231
|
* @param code - Error code for programmatic handling (default: 'SYNNEL_ERROR')
|
|
@@ -3239,10 +3239,10 @@ declare class SyncaError extends Error {
|
|
|
3239
3239
|
*
|
|
3240
3240
|
* @example
|
|
3241
3241
|
* ```ts
|
|
3242
|
-
* const error = new
|
|
3242
|
+
* const error = new SyncarError('Failed', 'FAIL', { id: 123 })
|
|
3243
3243
|
* console.log(JSON.stringify(error.toJSON(), null, 2))
|
|
3244
3244
|
* // {
|
|
3245
|
-
* // "name": "
|
|
3245
|
+
* // "name": "SyncarError",
|
|
3246
3246
|
* // "message": "Failed",
|
|
3247
3247
|
* // "code": "FAIL",
|
|
3248
3248
|
* // "context": { "id": 123 },
|
|
@@ -3264,9 +3264,9 @@ declare class SyncaError extends Error {
|
|
|
3264
3264
|
*
|
|
3265
3265
|
* @example
|
|
3266
3266
|
* ```ts
|
|
3267
|
-
* const error = new
|
|
3267
|
+
* const error = new SyncarError('Failed', 'FAIL')
|
|
3268
3268
|
* console.log(error.toString())
|
|
3269
|
-
* // "[
|
|
3269
|
+
* // "[SyncarError:FAIL] Failed"
|
|
3270
3270
|
* ```
|
|
3271
3271
|
*/
|
|
3272
3272
|
toString(): string;
|
|
@@ -3284,7 +3284,7 @@ declare class SyncaError extends Error {
|
|
|
3284
3284
|
* }
|
|
3285
3285
|
* ```
|
|
3286
3286
|
*/
|
|
3287
|
-
declare class ConfigError extends
|
|
3287
|
+
declare class ConfigError extends SyncarError {
|
|
3288
3288
|
constructor(message: string, context?: Record<string, unknown>);
|
|
3289
3289
|
}
|
|
3290
3290
|
/**
|
|
@@ -3300,7 +3300,7 @@ declare class ConfigError extends SyncaError {
|
|
|
3300
3300
|
* }
|
|
3301
3301
|
* ```
|
|
3302
3302
|
*/
|
|
3303
|
-
declare class TransportError extends
|
|
3303
|
+
declare class TransportError extends SyncarError {
|
|
3304
3304
|
constructor(message: string, context?: Record<string, unknown>);
|
|
3305
3305
|
}
|
|
3306
3306
|
/**
|
|
@@ -3316,7 +3316,7 @@ declare class TransportError extends SyncaError {
|
|
|
3316
3316
|
* }
|
|
3317
3317
|
* ```
|
|
3318
3318
|
*/
|
|
3319
|
-
declare class ChannelError extends
|
|
3319
|
+
declare class ChannelError extends SyncarError {
|
|
3320
3320
|
constructor(message: string, context?: Record<string, unknown>);
|
|
3321
3321
|
}
|
|
3322
3322
|
/**
|
|
@@ -3332,7 +3332,7 @@ declare class ChannelError extends SyncaError {
|
|
|
3332
3332
|
* }
|
|
3333
3333
|
* ```
|
|
3334
3334
|
*/
|
|
3335
|
-
declare class ClientError extends
|
|
3335
|
+
declare class ClientError extends SyncarError {
|
|
3336
3336
|
constructor(message: string, context?: Record<string, unknown>);
|
|
3337
3337
|
}
|
|
3338
3338
|
/**
|
|
@@ -3348,7 +3348,7 @@ declare class ClientError extends SyncaError {
|
|
|
3348
3348
|
* }
|
|
3349
3349
|
* ```
|
|
3350
3350
|
*/
|
|
3351
|
-
declare class MessageError extends
|
|
3351
|
+
declare class MessageError extends SyncarError {
|
|
3352
3352
|
constructor(message: string, context?: Record<string, unknown>);
|
|
3353
3353
|
}
|
|
3354
3354
|
/**
|
|
@@ -3364,7 +3364,7 @@ declare class MessageError extends SyncaError {
|
|
|
3364
3364
|
* }
|
|
3365
3365
|
* ```
|
|
3366
3366
|
*/
|
|
3367
|
-
declare class ValidationError extends
|
|
3367
|
+
declare class ValidationError extends SyncarError {
|
|
3368
3368
|
constructor(message: string, context?: Record<string, unknown>);
|
|
3369
3369
|
}
|
|
3370
3370
|
/**
|
|
@@ -3384,7 +3384,7 @@ declare class ValidationError extends SyncaError {
|
|
|
3384
3384
|
* }
|
|
3385
3385
|
* ```
|
|
3386
3386
|
*/
|
|
3387
|
-
declare class StateError extends
|
|
3387
|
+
declare class StateError extends SyncarError {
|
|
3388
3388
|
constructor(message: string, context?: Record<string, unknown>);
|
|
3389
3389
|
}
|
|
3390
3390
|
/**
|
|
@@ -3599,4 +3599,4 @@ declare class MiddlewareExecutionError extends Error {
|
|
|
3599
3599
|
toString(): string;
|
|
3600
3600
|
}
|
|
3601
3601
|
|
|
3602
|
-
export { type AckMessage, BROADCAST_CHANNEL, BroadcastChannel, CLOSE_CODES, ChannelError, type ChannelName, ClientError, type ClientId, ConfigError, type Context, ContextManager, type DataMessage, ERROR_CODES, ErrorCode, type ErrorMessage, type IChannelState, type IClientConnection, type IMessageHandler, type IPublishOptions, type IServerOptions, type IServerStats, type Message, MessageError, type MessageId, MessageType, type Middleware, MiddlewareExecutionError, MiddlewareRejectionError, MulticastChannel, type SignalMessage, SignalType, StateError, type SubscriberId,
|
|
3602
|
+
export { type AckMessage, BROADCAST_CHANNEL, BroadcastChannel, CLOSE_CODES, ChannelError, type ChannelName, ClientError, type ClientId, ConfigError, type Context, ContextManager, type DataMessage, ERROR_CODES, ErrorCode, type ErrorMessage, type IChannelState, type IClientConnection, type IMessageHandler, type IPublishOptions, type IServerOptions, type IServerStats, type Message, MessageError, type MessageId, MessageType, type Middleware, MiddlewareExecutionError, MiddlewareRejectionError, MulticastChannel, type SignalMessage, SignalType, StateError, type SubscriberId, SyncarServer as Syncar, SyncarError, SyncarServer, type Timestamp, TransportError, ValidationError, WebSocketServerTransport, createAuthMiddleware, createChannelWhitelistMiddleware, createContext, createLoggingMiddleware, createRateLimitMiddleware, createSyncarServer };
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
function V(){return`${Date.now()}-${Math.random().toString(36).substring(2,11)}`}function z(){return`client-${Date.now()}-${Math.random().toString(36).substring(2,9)}`}var K=1,j=128,J="__";function ie(i){return typeof i=="string"&&i.length>=K&&i.length<=j}function L(i){return i.startsWith(J)}function f(i){if(!ie(i))throw new Error(`Invalid channel name: must be between ${K} and ${j} characters`);if(L(i))throw new Error(`Reserved channel name: channel names starting with '${J}' are reserved for system use`)}function P(i){return i.type==="data"}function Y(i,e,t){return{id:t||V(),type:"data",channel:i,data:e,timestamp:Date.now()}}function O(i,e,t,n){return{id:n||V(),type:"signal",channel:i,signal:e,data:t,timestamp:Date.now()}}var oe={info:"INFO",warn:"WARN",error:"ERROR",debug:"DEBUG"};function ae(){return new Date().toISOString()}function X(i="Synca",e={}){let t={debug:!1,info:!0,warn:!0,error:!0,...e},n=(r,s)=>{let o=ae();return`[${i} ${o}] [${oe[r]}] ${s}`};return{debug:(r,...s)=>{t.debug&&console.log(n("debug",r),...s)},info:(r,...s)=>{t.info&&console.log(n("info",r),...s)},warn:(r,...s)=>{t.warn&&console.warn(n("warn",r),...s)},error:(r,...s)=>{t.error&&console.error(n("error",r),...s)}}}var y="__broadcast__",H={NORMAL:1e3,GOING_AWAY:1001,PROTOCOL_ERROR:1002,UNSUPPORTED_DATA:1003,NO_STATUS:1005,ABNORMAL:1006,INVALID_PAYLOAD:1007,POLICY_VIOLATION:1008,MESSAGE_TOO_BIG:1009,MISSING_EXTENSION:1010,INTERNAL_ERROR:1011,SERVICE_RESTART:1012,TRY_AGAIN_LATER:1013,REJECTED:4001,RATE_LIMITED:4002,CHANNEL_NOT_FOUND:4003,UNAUTHORIZED:4005},ce={REJECTED:"REJECTED",MISSING_CHANNEL:"MISSING_CHANNEL",SUBSCRIBE_REJECTED:"SUBSCRIBE_REJECTED",UNSUBSCRIBE_REJECTED:"UNSUBSCRIBE_REJECTED",RATE_LIMITED:"RATE_LIMITED",AUTH_FAILED:"AUTH_FAILED",NOT_AUTHORIZED:"NOT_AUTHORIZED",CHANNEL_NOT_ALLOWED:"CHANNEL_NOT_ALLOWED",INVALID_MESSAGE:"INVALID_MESSAGE",SERVER_ERROR:"SERVER_ERROR"},Z="/synca",D=1048576,U=3e4,B=5e3,le=3e3,de="0.0.0.0",ge="/synca",he=!0,Q={port:le,host:de,path:ge,enablePing:he,pingInterval:U,pingTimeout:B,broadcastChunkSize:500};var k=class{constructor(e,t,n=500){this.name=e;this.registry=t;this.chunkSize=n}publish(e,t){let n=this.getTargetClients(t);n.length>this.chunkSize?this.publishInChunks(e,n,t):this.publishToClients(e,n,t)}async dispatch(e,t,n){}publishToClients(e,t,n){let r=Y(this.name,e);for(let s of t){if(n?.to&&!n.to.includes(s)||n?.exclude&&n.exclude.includes(s))continue;let o=this.registry.connections.get(s);if(o)try{o.socket.send(JSON.stringify(r))}catch(a){this.registry.logger?.error(`[${this.name}] Failed to send to ${s}:`,a)}}}publishInChunks(e,t,n){let r=0,s=()=>{let o=t.slice(r,r+this.chunkSize);o.length!==0&&(this.publishToClients(e,o,n),r+=this.chunkSize,r<t.length&&setImmediate(s))};s()}},w=class extends k{constructor(e,t=500){super(y,e,t)}getTargetClients(e){return Array.from(this.registry.connections.keys())}get subscriberCount(){return this.registry.connections.size}isEmpty(){return this.registry.connections.size===0}getMiddlewares(){return[]}},E=class extends k{constructor(t){f(t.name);super(t.name,t.registry,t.options?.chunkSize);this.middlewares=[];this.messageHandlers=new Set}getTargetClients(t){return Array.from(this.registry.getChannelSubscribers(this.name))}use(t){this.middlewares.push(t)}getMiddlewares(){return[...this.middlewares]}get subscriberCount(){return this.registry.getChannelSubscribers(this.name).size}onMessage(t){return this.messageHandlers.add(t),()=>this.messageHandlers.delete(t)}subscribe(t){return this.registry.subscribe(t,this.name)}unsubscribe(t){return this.registry.unsubscribe(t,this.name)}async dispatch(t,n,r){if(this.messageHandlers.size>0)for(let s of this.messageHandlers)try{await s(t,n,r)}catch(o){this.registry.logger?.error(`[${this.name}] Error in message handler:`,o)}else this.publish(t,{exclude:[n.id]})}hasSubscriber(t){return this.registry.getChannelSubscribers(this.name).has(t)}getSubscribers(){return new Set(this.registry.getChannelSubscribers(this.name))}isEmpty(){return this.registry.getChannelSubscribers(this.name).size===0}};var g=class i extends Error{constructor(e,t="SYNNEL_ERROR",n){super(e),this.name="SyncaError",this.code=t,this.context=n,Error.captureStackTrace&&Error.captureStackTrace(this,i)}toJSON(){return{name:this.name,message:this.message,code:this.code,context:this.context,stack:this.stack}}toString(){return`[${this.name}:${this.code}] ${this.message}`}},G=class extends g{constructor(e,t){super(e,"CONFIG_ERROR",t),this.name="ConfigError"}},$=class extends g{constructor(e,t){super(e,"TRANSPORT_ERROR",t),this.name="TransportError"}},h=class extends g{constructor(e,t){super(e,"CHANNEL_ERROR",t),this.name="ChannelError"}},q=class extends g{constructor(e,t){super(e,"CLIENT_ERROR",t),this.name="ClientError"}},u=class extends g{constructor(e,t){super(e,"MESSAGE_ERROR",t),this.name="MessageError"}},F=class extends g{constructor(e,t){super(e,"VALIDATION_ERROR",t),this.name="ValidationError"}},p=class extends g{constructor(e,t){super(e,"STATE_ERROR",t),this.name="StateError"}},S=class i extends Error{constructor(t,n,r,s){super(`Action '${n}' rejected: ${t}`);this.name="MiddlewareRejectionError";this.reason=t,this.action=n,this.name="MiddlewareRejectionError",this.code=r,this.context=s,Error.captureStackTrace&&Error.captureStackTrace(this,i)}toJSON(){return{name:this.name,reason:this.reason,action:this.action,code:this.code,context:this.context,message:this.message,stack:this.stack}}toString(){return`[${this.name}:${this.action}] ${this.reason}`}},I=class i extends Error{constructor(e,t,n){super(`Middleware execution error in ${t} during ${e}: ${n.message}`),this.name="MiddlewareExecutionError",this.action=e,this.middleware=t,this.cause=n,Error.captureStackTrace&&Error.captureStackTrace(this,i)}getCause(){return this.cause}toString(){return`[${this.name}] ${this.middleware} failed during ${this.action}: ${this.cause.message}`}};var x=class{constructor(e){this.registry=e.registry,this.context=e.context,this.options={requireChannel:e.options?.requireChannel??!1,allowReservedChannels:e.options?.allowReservedChannels??!1,sendAcknowledgments:e.options?.sendAcknowledgments??!0,autoRespondToPing:e.options?.autoRespondToPing??!0}}async handleSignal(e,t){let n;t.signal==="subscribe"?n=this.context.createSubscribeContext(e,t.channel):t.signal==="unsubscribe"?n=this.context.createUnsubscribeContext(e,t.channel):n=this.context.createMessageContext(e,t);let r=async()=>{switch(t.signal){case"subscribe":await this.handleSubscribe(e,t);break;case"unsubscribe":await this.handleUnsubscribe(e,t);break;case"ping":await this.handlePing(e,t);break;case"pong":await this.handlePong(e,t);break;default:throw new u(`Unknown signal type: ${t.signal}`)}},s;t.channel&&t.channel!==y&&(s=this.registry.getChannel(t.channel));let o=this.context.getPipeline(s);await this.context.execute(n,o,r)}async handleSubscribe(e,t){let{channel:n}=t;if(!this.options.allowReservedChannels&&L(n))throw new h(`Cannot subscribe to reserved channel: ${n}`);if(n===y)throw new h("Cannot subscribe to broadcast channel");if(!this.registry.subscribe(e.id,n))throw new h(`Failed to subscribe client ${e.id} to channel ${n}`);if(this.options.sendAcknowledgments){let s=O(n,"subscribed",void 0,t.id);e.socket.send(JSON.stringify(s),()=>{})}}async handleUnsubscribe(e,t){let{channel:n}=t;if(!this.registry.isSubscribed(e.id,n))throw new h(`Client not subscribed to channel: ${n}`);if(this.registry.unsubscribe(e.id,n),this.options.sendAcknowledgments){let r=O(n,"unsubscribed",void 0,t.id);e.socket.send(JSON.stringify(r),()=>{})}}async handlePing(e,t){if(e.lastPingAt=Date.now(),this.options.autoRespondToPing){let n=O(t.channel,"pong",void 0,t.id);e.socket.send(JSON.stringify(n),()=>{})}}async handlePong(e,t){e.lastPingAt=Date.now()}getOptions(){return this.options}};var M=class{constructor(e){this.registry=e.registry,this.context=e.context,this.options={requireChannel:e.options?.requireChannel??!0}}async handleMessage(e,t){if(!P(t))throw new u("Invalid message type: expected DATA message");let n=this.registry.getChannel(t.channel);if(this.options.requireChannel&&!n)throw new h(`Channel not found: ${t.channel}`);let r=this.context.getPipeline(n),s=this.context.createMessageContext(e,t),o=async()=>{n&&await n.dispatch(t.data,e,t)};await this.context.execute(s,r,o)}canProcessMessage(e){return P(e)?this.options.requireChannel?!!this.registry.getChannel(e.channel):!0:!1}getChannelForMessage(e){return this.registry.getChannel(e.channel)}getOptions(){return this.options}};var T=class{constructor(e){this.registry=e.registry,this.options={rejectionCloseCode:e.options?.rejectionCloseCode??H.REJECTED}}async handleConnection(e){return this.registry.register(e)}async handleDisconnection(e,t){this.registry.get(e)&&this.registry.unregister(e)}getOptions(){return this.options}};var ee=i=>(e,t)=>{let n=-1,r=async s=>{if(s<=n)throw new Error("next() called multiple times");n=s;let o,a=i[s];if(a)o=await a(e,async()=>{await r(s+1)});else if(s===i.length&&t)o=await t();else return e;return o!==void 0&&!e.finalized&&(e.res=o,e.finalized=!0),e};return r(0)};function R(i){let{action:e,client:t,message:n,channel:r,initialState:s={}}=i,o=s;return{req:{action:e,client:t,message:n,channel:r},var:o,finalized:!1,get:a=>o[a],set:(a,c)=>{o[a]=c},reject:a=>{throw new S(a,e)}}}var A=class{constructor(){this.middlewares=[]}use(e){this.middlewares.push(e)}remove(e){let t=this.middlewares.indexOf(e);return t!==-1?(this.middlewares.splice(t,1),!0):!1}clear(){this.middlewares.length=0}getMiddlewares(){return[...this.middlewares]}getPipeline(e){let t=this.getMiddlewares(),n=e?.getMiddlewares?.();return n&&n.length>0&&(t=[...t,...n]),t}async executeConnection(e,t){let n=this.createConnectionContext(e,t);return await this.execute(n)}async executeMessage(e,t){let n=this.createMessageContext(e,t);return await this.execute(n)}async executeSubscribe(e,t,n){let r=this.createSubscribeContext(e,t);return await this.execute(r,this.middlewares,n)}async executeUnsubscribe(e,t,n){let r=this.createUnsubscribeContext(e,t);return await this.execute(r,this.middlewares,n)}async execute(e,t=this.middlewares,n){let r=e.req.action||"unknown",s=t.map((o,a)=>async(c,d)=>{try{await o(c,d)}catch(l){if(l instanceof S||l instanceof I)throw l;let m=o.name||`middleware[${a}]`;throw new I(r,m,l instanceof Error?l:new Error(String(l)))}});return await ee(s)(e,n)}createConnectionContext(e,t){return R({client:e,action:t})}createMessageContext(e,t){return R({client:e,message:t,action:"message"})}createSubscribeContext(e,t){return R({client:e,channel:t,action:"subscribe"})}createUnsubscribeContext(e,t){return R({client:e,channel:t,action:"unsubscribe"})}getCount(){return this.middlewares.length}hasMiddleware(){return this.middlewares.length>0}};var _=class{constructor(e){this.connections=new Map;this.subscriptions=new Map;this.channels=new Map;this.channelInstances=new Map;this.logger=e}register(e){return this.connections.set(e.id,e),e}unregister(e){let t=this.get(e);return t?(this.clearSubscriptions(t),this.connections.delete(e)):!1}clearSubscriptions(e){let t=this.subscriptions.get(e.id);if(t){for(let n of t)this.channels.get(n)?.delete(e.id);this.subscriptions.delete(e.id)}}get(e){return this.connections.get(e)}getAll(){return Array.from(this.connections.values())}getCount(){return this.connections.size}registerChannel(e){this.channels.has(e.name)||this.channels.set(e.name,new Set),this.channelInstances.set(e.name,e)}getChannel(e){return this.channelInstances.get(e)}removeChannel(e){let t=this.channels.get(e);if(!t)return!1;for(let n of t){let r=this.subscriptions.get(n);r&&r.delete(e)}return this.channelInstances.delete(e),this.channels.delete(e)}subscribe(e,t){if(f(t),!this.connections.has(e))return!1;this.channels.has(t)||this.channels.set(t,new Set);let n=this.channels.get(t);return n.has(e)||(n.add(e),this.subscriptions.has(e)||this.subscriptions.set(e,new Set),this.subscriptions.get(e).add(t)),!0}unsubscribe(e,t){let n=this.channels.get(t);if(!n||!n.has(e))return!1;n.delete(e);let r=this.subscriptions.get(e);return r&&(r.delete(t),r.size===0&&this.subscriptions.delete(e)),!0}getSubscribers(e){let t=this.channels.get(e);if(!t)return[];let n=[];for(let r of t){let s=this.connections.get(r);s&&n.push(s)}return n}getSubscriberCount(e){return this.channels.get(e)?.size??0}getChannels(){return Array.from(this.channels.keys())}getTotalSubscriptionCount(){let e=0;for(let t of this.channels.values())e+=t.size;return e}isSubscribed(e,t){return this.channels.get(t)?.has(e)??!1}getClientChannels(e){return this.subscriptions.get(e)??new Set}getChannelSubscribers(e){return this.channels.get(e)??new Set}clear(){this.connections.clear(),this.subscriptions.clear(),this.channels.clear(),this.channelInstances.clear()}};import{EventEmitter as pe}from"events";import{WebSocketServer as Ce}from"ws";var v=class extends pe{constructor(e){super(),this.setMaxListeners(100),this.connections=e.connections??new Map,this.config={...e,path:e.path??Z,maxPayload:e.maxPayload??D,enablePing:e.enablePing??!0,pingInterval:e.pingInterval??U,pingTimeout:e.pingTimeout??B,connections:this.connections};let t=e.ServerConstructor??Ce;this.wsServer=new t({server:this.config.server,path:this.config.path,maxPayload:this.config.maxPayload}),this.setupEventHandlers(),this.config.enablePing&&this.startPingTimer()}setAuthenticator(e){this.authenticator=e}setupEventHandlers(){this.wsServer.on("connection",(e,t)=>{this.handleConnection(e,t)}),this.wsServer.on("error",e=>{this.config.logger?.error("WebSocket Server Error:",e),this.emit("error",e)})}async handleConnection(e,t){let n;try{this.authenticator?n=await this.authenticator(t):this.config.generateId?n=await this.config.generateId(t):n=z()}catch(o){try{e.close(4001,o instanceof Error?o.message:"Unauthorized")}catch{}return}let r=Date.now(),s={socket:e,id:n,connectedAt:r,lastPingAt:r};this.connections.set(n,s),e.on("message",o=>{this.handleMessage(n,o)}),e.on("close",(o,a)=>{this.handleDisconnection(n)}),e.on("error",o=>{this.emit("error",o)}),this.config.enablePing&&this.setupPingPong(n,e),this.emit("connection",s)}handleMessage(e,t){try{let n=JSON.parse(t.toString()),r=this.connections.get(e);r&&n.type==="signal"&&n.signal==="pong"&&(r.lastPingAt=Date.now()),this.emit("message",e,n)}catch(n){this.config.logger?.error(`Failed to parse message from ${e}:`,n),this.emit("error",n)}}handleDisconnection(e){this.emit("disconnection",e),this.connections.delete(e)}setupPingPong(e,t){t.on("pong",()=>{let n=this.connections.get(e);n&&(n.lastPingAt=Date.now())})}startPingTimer(){this.pingTimer&&clearInterval(this.pingTimer),this.pingTimer=setInterval(()=>{this.checkConnections()},this.config.pingInterval)}checkConnections(){let e=Date.now(),t=Array.from(this.connections.values());for(let n of t){let r=n.socket,s=n.lastPingAt??n.connectedAt;if(e-s>this.config.pingInterval+this.config.pingTimeout){r.close(1e3,"Ping timeout");continue}r.readyState===1&&r.ping()}}};var N=class{constructor(e){this.status={started:!1,startedAt:void 0};if(this.config=e,this.registry=this.config.registry,this.context=new A,this.config.middleware)for(let t of this.config.middleware)this.context.use(t)}async start(){if(this.status.started)throw new p("Server is already started");this.transport=this.config.transport,this.connectionHandler=new T({registry:this.registry}),this.messageHandler=new M({registry:this.registry,context:this.context}),this.signalHandler=new x({registry:this.registry,context:this.context}),this.setupTransportHandlers(),this.broadcastChannel=new w(this.registry,this.config.broadcastChunkSize),this.registry.registerChannel(this.broadcastChannel),this.status.started=!0,this.status.startedAt=Date.now()}async stop(){!this.status.started||!this.transport||(this.connectionHandler=void 0,this.messageHandler=void 0,this.signalHandler=void 0,this.registry.clear(),this.broadcastChannel=void 0,this.status.started=!1,this.status.startedAt=void 0)}createBroadcast(){if(!this.status.started||!this.broadcastChannel)throw new p("Server must be started before creating channels");return this.broadcastChannel}createMulticast(e){if(f(e),!this.status.started||!this.transport)throw new p("Server must be started before creating channels");let t=this.registry.getChannel(e);if(t)return t;let n=new E({name:e,registry:this.registry,options:{chunkSize:this.config.broadcastChunkSize}});return this.registry.registerChannel(n),n}hasChannel(e){return!!this.registry.getChannel(e)}getChannels(){return this.registry.getChannels()}use(e){this.context.use(e)}authenticate(e){let t=this.transport||this.config.transport;t&&"setAuthenticator"in t?t.setAuthenticator(e):this.config.logger.warn("Current transport does not support setting an authenticator.")}getStats(){return{startedAt:this.status.startedAt,clientCount:this.registry.getCount(),channelCount:this.registry.getChannels().length,subscriptionCount:this.registry.getTotalSubscriptionCount()}}getConfig(){return this.config}getRegistry(){return this.registry}setupTransportHandlers(){let e=this.transport;e.on("connection",async t=>{try{await this.context.executeConnection(t,"connect"),await this.connectionHandler.handleConnection(t)}catch(n){this.config.logger.error("Error handling connection:",n)}}),e.on("disconnection",async t=>{try{let n=this.registry.get(t);n&&(await this.context.executeConnection(n,"disconnect"),await this.connectionHandler.handleDisconnection(t))}catch(n){this.config.logger.error("Error handling disconnection:",n)}}),e.on("message",async(t,n)=>{try{let r=this.registry.get(t);if(!r)return;n.type==="data"?await this.messageHandler.handleMessage(r,n):n.type==="signal"&&await this.signalHandler.handleSignal(r,n)}catch(r){this.config.logger.error("Error handling message:",r)}}),e.on("error",t=>{this.config.logger.error("Transport error:",t)})}};function me(i={}){let e=i.registry??new _,t=i.logger??X(),n={...Q,middleware:[],...i,registry:e,logger:t};return n.transport||(n.server||import("http").then(r=>{let s=r.createServer();s.listen(n.port,n.host),n.server=s}),n.transport=new v({server:n.server,path:n.path,maxPayload:i.maxPayload??D,enablePing:n.enablePing,pingInterval:n.pingInterval,pingTimeout:n.pingTimeout,connections:e.connections,generateId:n.generateId,logger:n.logger})),new N(n)}function te(i){let{verifyToken:e,getToken:t=s=>s.req.message?.data?.token,attachProperty:n="user",actions:r}=i;return async(s,o)=>{if(r&&!r.includes(s.req.action))return o();let a=t(s);a||s.reject("Authentication token required");try{let c=await e(a);s.req.client&&(s.req.client[n]=c),s.set(n,c),await o()}catch{s.reject("Authentication failed: Invalid token")}}}function ne(i={}){let{logger:e=console,logLevel:t="info",includeMessageData:n=!1,format:r,actions:s}=i;return async(o,a)=>{if(s&&!s.includes(o.req.action))return a();let c=Date.now();await a();let d=Date.now()-c,l={action:o.req.action,clientId:o.req.client?.id,channel:o.req.channel,message:n?o.req.message:void 0,duration:d},m=r?r(l):`[${l.action}] Client: ${l.clientId??"unknown"}${l.channel?` Channel: ${l.channel}`:""} (${d}ms)`;e[t](m)}}var C=new Map;function re(i={}){let{maxRequests:e=100,windowMs:t=6e4,getMessageId:n=a=>a.req.client?.id??"",actions:r=["message"]}=i,s=setInterval(()=>{let a=Date.now();for(let[c,d]of C)d.resetTime<a&&C.delete(c)},t*10),o=async(a,c)=>{if(!r.includes(a.req.action))return c();let d=n(a);if(!d)return c();let l=Date.now(),m=C.get(d);m&&m.resetTime<l&&C.delete(d);let b=C.get(d);b||(b={count:0,resetTime:l+t},C.set(d,b)),b.count>=e&&a.reject(`Rate limit exceeded. Max ${e} requests per ${t}ms`),b.count++,await c()};return o.cleanup=()=>{clearInterval(s),C.clear()},o}function se(i={}){let{allowedChannels:e=[],isDynamic:t,restrictUnsubscribe:n=!1}=i;return async(r,s)=>{if(r.req.action!=="subscribe"&&r.req.action!=="unsubscribe"||r.req.action==="unsubscribe"&&!n||!r.req.channel)return s();if(t)return t(r.req.channel,r.req.client)||r.reject(`Channel '${r.req.channel}' is not allowed`),s();e.includes(r.req.channel)||r.reject(`Channel '${r.req.channel}' is not allowed`),await s()}}export{y as BROADCAST_CHANNEL,w as BroadcastChannel,H as CLOSE_CODES,h as ChannelError,q as ClientError,G as ConfigError,A as ContextManager,ce as ERROR_CODES,u as MessageError,I as MiddlewareExecutionError,S as MiddlewareRejectionError,E as MulticastChannel,p as StateError,N as Synca,g as SyncaError,N as SyncaServer,$ as TransportError,F as ValidationError,v as WebSocketServerTransport,te as createAuthMiddleware,se as createChannelWhitelistMiddleware,R as createContext,ne as createLoggingMiddleware,re as createRateLimitMiddleware,me as createSyncaServer};
|
|
1
|
+
function V(){return`${Date.now()}-${Math.random().toString(36).substring(2,11)}`}function z(){return`client-${Date.now()}-${Math.random().toString(36).substring(2,9)}`}var K=1,j=128,J="__";function ie(i){return typeof i=="string"&&i.length>=K&&i.length<=j}function L(i){return i.startsWith(J)}function f(i){if(!ie(i))throw new Error(`Invalid channel name: must be between ${K} and ${j} characters`);if(L(i))throw new Error(`Reserved channel name: channel names starting with '${J}' are reserved for system use`)}function P(i){return i.type==="data"}function Y(i,e,t){return{id:t||V(),type:"data",channel:i,data:e,timestamp:Date.now()}}function O(i,e,t,n){return{id:n||V(),type:"signal",channel:i,signal:e,data:t,timestamp:Date.now()}}var oe={info:"INFO",warn:"WARN",error:"ERROR",debug:"DEBUG"};function ae(){return new Date().toISOString()}function X(i="Syncar",e={}){let t={debug:!1,info:!0,warn:!0,error:!0,...e},n=(r,s)=>{let o=ae();return`[${i} ${o}] [${oe[r]}] ${s}`};return{debug:(r,...s)=>{t.debug&&console.log(n("debug",r),...s)},info:(r,...s)=>{t.info&&console.log(n("info",r),...s)},warn:(r,...s)=>{t.warn&&console.warn(n("warn",r),...s)},error:(r,...s)=>{t.error&&console.error(n("error",r),...s)}}}var y="__broadcast__",H={NORMAL:1e3,GOING_AWAY:1001,PROTOCOL_ERROR:1002,UNSUPPORTED_DATA:1003,NO_STATUS:1005,ABNORMAL:1006,INVALID_PAYLOAD:1007,POLICY_VIOLATION:1008,MESSAGE_TOO_BIG:1009,MISSING_EXTENSION:1010,INTERNAL_ERROR:1011,SERVICE_RESTART:1012,TRY_AGAIN_LATER:1013,REJECTED:4001,RATE_LIMITED:4002,CHANNEL_NOT_FOUND:4003,UNAUTHORIZED:4005},ce={REJECTED:"REJECTED",MISSING_CHANNEL:"MISSING_CHANNEL",SUBSCRIBE_REJECTED:"SUBSCRIBE_REJECTED",UNSUBSCRIBE_REJECTED:"UNSUBSCRIBE_REJECTED",RATE_LIMITED:"RATE_LIMITED",AUTH_FAILED:"AUTH_FAILED",NOT_AUTHORIZED:"NOT_AUTHORIZED",CHANNEL_NOT_ALLOWED:"CHANNEL_NOT_ALLOWED",INVALID_MESSAGE:"INVALID_MESSAGE",SERVER_ERROR:"SERVER_ERROR"},Z="/syncar",D=1048576,U=3e4,B=5e3,le=3e3,de="0.0.0.0",ge="/syncar",he=!0,Q={port:le,host:de,path:ge,enablePing:he,pingInterval:U,pingTimeout:B,broadcastChunkSize:500};var k=class{constructor(e,t,n=500){this.name=e;this.registry=t;this.chunkSize=n}publish(e,t){let n=this.getTargetClients(t);n.length>this.chunkSize?this.publishInChunks(e,n,t):this.publishToClients(e,n,t)}async dispatch(e,t,n){}publishToClients(e,t,n){let r=Y(this.name,e);for(let s of t){if(n?.to&&!n.to.includes(s)||n?.exclude&&n.exclude.includes(s))continue;let o=this.registry.connections.get(s);if(o)try{o.socket.send(JSON.stringify(r))}catch(a){this.registry.logger?.error(`[${this.name}] Failed to send to ${s}:`,a)}}}publishInChunks(e,t,n){let r=0,s=()=>{let o=t.slice(r,r+this.chunkSize);o.length!==0&&(this.publishToClients(e,o,n),r+=this.chunkSize,r<t.length&&setImmediate(s))};s()}},w=class extends k{constructor(e,t=500){super(y,e,t)}getTargetClients(e){return Array.from(this.registry.connections.keys())}get subscriberCount(){return this.registry.connections.size}isEmpty(){return this.registry.connections.size===0}getMiddlewares(){return[]}},E=class extends k{constructor(t){f(t.name);super(t.name,t.registry,t.options?.chunkSize);this.middlewares=[];this.messageHandlers=new Set}getTargetClients(t){return Array.from(this.registry.getChannelSubscribers(this.name))}use(t){this.middlewares.push(t)}getMiddlewares(){return[...this.middlewares]}get subscriberCount(){return this.registry.getChannelSubscribers(this.name).size}onMessage(t){return this.messageHandlers.add(t),()=>this.messageHandlers.delete(t)}subscribe(t){return this.registry.subscribe(t,this.name)}unsubscribe(t){return this.registry.unsubscribe(t,this.name)}async dispatch(t,n,r){if(this.messageHandlers.size>0)for(let s of this.messageHandlers)try{await s(t,n,r)}catch(o){this.registry.logger?.error(`[${this.name}] Error in message handler:`,o)}else this.publish(t,{exclude:[n.id]})}hasSubscriber(t){return this.registry.getChannelSubscribers(this.name).has(t)}getSubscribers(){return new Set(this.registry.getChannelSubscribers(this.name))}isEmpty(){return this.registry.getChannelSubscribers(this.name).size===0}};var g=class i extends Error{constructor(e,t="SYNNEL_ERROR",n){super(e),this.name="SyncarError",this.code=t,this.context=n,Error.captureStackTrace&&Error.captureStackTrace(this,i)}toJSON(){return{name:this.name,message:this.message,code:this.code,context:this.context,stack:this.stack}}toString(){return`[${this.name}:${this.code}] ${this.message}`}},G=class extends g{constructor(e,t){super(e,"CONFIG_ERROR",t),this.name="ConfigError"}},$=class extends g{constructor(e,t){super(e,"TRANSPORT_ERROR",t),this.name="TransportError"}},h=class extends g{constructor(e,t){super(e,"CHANNEL_ERROR",t),this.name="ChannelError"}},q=class extends g{constructor(e,t){super(e,"CLIENT_ERROR",t),this.name="ClientError"}},u=class extends g{constructor(e,t){super(e,"MESSAGE_ERROR",t),this.name="MessageError"}},F=class extends g{constructor(e,t){super(e,"VALIDATION_ERROR",t),this.name="ValidationError"}},p=class extends g{constructor(e,t){super(e,"STATE_ERROR",t),this.name="StateError"}},S=class i extends Error{constructor(t,n,r,s){super(`Action '${n}' rejected: ${t}`);this.name="MiddlewareRejectionError";this.reason=t,this.action=n,this.name="MiddlewareRejectionError",this.code=r,this.context=s,Error.captureStackTrace&&Error.captureStackTrace(this,i)}toJSON(){return{name:this.name,reason:this.reason,action:this.action,code:this.code,context:this.context,message:this.message,stack:this.stack}}toString(){return`[${this.name}:${this.action}] ${this.reason}`}},I=class i extends Error{constructor(e,t,n){super(`Middleware execution error in ${t} during ${e}: ${n.message}`),this.name="MiddlewareExecutionError",this.action=e,this.middleware=t,this.cause=n,Error.captureStackTrace&&Error.captureStackTrace(this,i)}getCause(){return this.cause}toString(){return`[${this.name}] ${this.middleware} failed during ${this.action}: ${this.cause.message}`}};var x=class{constructor(e){this.registry=e.registry,this.context=e.context,this.options={requireChannel:e.options?.requireChannel??!1,allowReservedChannels:e.options?.allowReservedChannels??!1,sendAcknowledgments:e.options?.sendAcknowledgments??!0,autoRespondToPing:e.options?.autoRespondToPing??!0}}async handleSignal(e,t){let n;t.signal==="subscribe"?n=this.context.createSubscribeContext(e,t.channel):t.signal==="unsubscribe"?n=this.context.createUnsubscribeContext(e,t.channel):n=this.context.createMessageContext(e,t);let r=async()=>{switch(t.signal){case"subscribe":await this.handleSubscribe(e,t);break;case"unsubscribe":await this.handleUnsubscribe(e,t);break;case"ping":await this.handlePing(e,t);break;case"pong":await this.handlePong(e,t);break;default:throw new u(`Unknown signal type: ${t.signal}`)}},s;t.channel&&t.channel!==y&&(s=this.registry.getChannel(t.channel));let o=this.context.getPipeline(s);await this.context.execute(n,o,r)}async handleSubscribe(e,t){let{channel:n}=t;if(!this.options.allowReservedChannels&&L(n))throw new h(`Cannot subscribe to reserved channel: ${n}`);if(n===y)throw new h("Cannot subscribe to broadcast channel");if(!this.registry.subscribe(e.id,n))throw new h(`Failed to subscribe client ${e.id} to channel ${n}`);if(this.options.sendAcknowledgments){let s=O(n,"subscribed",void 0,t.id);e.socket.send(JSON.stringify(s),()=>{})}}async handleUnsubscribe(e,t){let{channel:n}=t;if(!this.registry.isSubscribed(e.id,n))throw new h(`Client not subscribed to channel: ${n}`);if(this.registry.unsubscribe(e.id,n),this.options.sendAcknowledgments){let r=O(n,"unsubscribed",void 0,t.id);e.socket.send(JSON.stringify(r),()=>{})}}async handlePing(e,t){if(e.lastPingAt=Date.now(),this.options.autoRespondToPing){let n=O(t.channel,"pong",void 0,t.id);e.socket.send(JSON.stringify(n),()=>{})}}async handlePong(e,t){e.lastPingAt=Date.now()}getOptions(){return this.options}};var M=class{constructor(e){this.registry=e.registry,this.context=e.context,this.options={requireChannel:e.options?.requireChannel??!0}}async handleMessage(e,t){if(!P(t))throw new u("Invalid message type: expected DATA message");let n=this.registry.getChannel(t.channel);if(this.options.requireChannel&&!n)throw new h(`Channel not found: ${t.channel}`);let r=this.context.getPipeline(n),s=this.context.createMessageContext(e,t),o=async()=>{n&&await n.dispatch(t.data,e,t)};await this.context.execute(s,r,o)}canProcessMessage(e){return P(e)?this.options.requireChannel?!!this.registry.getChannel(e.channel):!0:!1}getChannelForMessage(e){return this.registry.getChannel(e.channel)}getOptions(){return this.options}};var T=class{constructor(e){this.registry=e.registry,this.options={rejectionCloseCode:e.options?.rejectionCloseCode??H.REJECTED}}async handleConnection(e){return this.registry.register(e)}async handleDisconnection(e,t){this.registry.get(e)&&this.registry.unregister(e)}getOptions(){return this.options}};var ee=i=>(e,t)=>{let n=-1,r=async s=>{if(s<=n)throw new Error("next() called multiple times");n=s;let o,a=i[s];if(a)o=await a(e,async()=>{await r(s+1)});else if(s===i.length&&t)o=await t();else return e;return o!==void 0&&!e.finalized&&(e.res=o,e.finalized=!0),e};return r(0)};function R(i){let{action:e,client:t,message:n,channel:r,initialState:s={}}=i,o=s;return{req:{action:e,client:t,message:n,channel:r},var:o,finalized:!1,get:a=>o[a],set:(a,c)=>{o[a]=c},reject:a=>{throw new S(a,e)}}}var A=class{constructor(){this.middlewares=[]}use(e){this.middlewares.push(e)}remove(e){let t=this.middlewares.indexOf(e);return t!==-1?(this.middlewares.splice(t,1),!0):!1}clear(){this.middlewares.length=0}getMiddlewares(){return[...this.middlewares]}getPipeline(e){let t=this.getMiddlewares(),n=e?.getMiddlewares?.();return n&&n.length>0&&(t=[...t,...n]),t}async executeConnection(e,t){let n=this.createConnectionContext(e,t);return await this.execute(n)}async executeMessage(e,t){let n=this.createMessageContext(e,t);return await this.execute(n)}async executeSubscribe(e,t,n){let r=this.createSubscribeContext(e,t);return await this.execute(r,this.middlewares,n)}async executeUnsubscribe(e,t,n){let r=this.createUnsubscribeContext(e,t);return await this.execute(r,this.middlewares,n)}async execute(e,t=this.middlewares,n){let r=e.req.action||"unknown",s=t.map((o,a)=>async(c,d)=>{try{await o(c,d)}catch(l){if(l instanceof S||l instanceof I)throw l;let m=o.name||`middleware[${a}]`;throw new I(r,m,l instanceof Error?l:new Error(String(l)))}});return await ee(s)(e,n)}createConnectionContext(e,t){return R({client:e,action:t})}createMessageContext(e,t){return R({client:e,message:t,action:"message"})}createSubscribeContext(e,t){return R({client:e,channel:t,action:"subscribe"})}createUnsubscribeContext(e,t){return R({client:e,channel:t,action:"unsubscribe"})}getCount(){return this.middlewares.length}hasMiddleware(){return this.middlewares.length>0}};var _=class{constructor(e){this.connections=new Map;this.subscriptions=new Map;this.channels=new Map;this.channelInstances=new Map;this.logger=e}register(e){return this.connections.set(e.id,e),e}unregister(e){let t=this.get(e);return t?(this.clearSubscriptions(t),this.connections.delete(e)):!1}clearSubscriptions(e){let t=this.subscriptions.get(e.id);if(t){for(let n of t)this.channels.get(n)?.delete(e.id);this.subscriptions.delete(e.id)}}get(e){return this.connections.get(e)}getAll(){return Array.from(this.connections.values())}getCount(){return this.connections.size}registerChannel(e){this.channels.has(e.name)||this.channels.set(e.name,new Set),this.channelInstances.set(e.name,e)}getChannel(e){return this.channelInstances.get(e)}removeChannel(e){let t=this.channels.get(e);if(!t)return!1;for(let n of t){let r=this.subscriptions.get(n);r&&r.delete(e)}return this.channelInstances.delete(e),this.channels.delete(e)}subscribe(e,t){if(f(t),!this.connections.has(e))return!1;this.channels.has(t)||this.channels.set(t,new Set);let n=this.channels.get(t);return n.has(e)||(n.add(e),this.subscriptions.has(e)||this.subscriptions.set(e,new Set),this.subscriptions.get(e).add(t)),!0}unsubscribe(e,t){let n=this.channels.get(t);if(!n||!n.has(e))return!1;n.delete(e);let r=this.subscriptions.get(e);return r&&(r.delete(t),r.size===0&&this.subscriptions.delete(e)),!0}getSubscribers(e){let t=this.channels.get(e);if(!t)return[];let n=[];for(let r of t){let s=this.connections.get(r);s&&n.push(s)}return n}getSubscriberCount(e){return this.channels.get(e)?.size??0}getChannels(){return Array.from(this.channels.keys())}getTotalSubscriptionCount(){let e=0;for(let t of this.channels.values())e+=t.size;return e}isSubscribed(e,t){return this.channels.get(t)?.has(e)??!1}getClientChannels(e){return this.subscriptions.get(e)??new Set}getChannelSubscribers(e){return this.channels.get(e)??new Set}clear(){this.connections.clear(),this.subscriptions.clear(),this.channels.clear(),this.channelInstances.clear()}};import{EventEmitter as pe}from"events";import{WebSocketServer as Ce}from"ws";var v=class extends pe{constructor(e){super(),this.setMaxListeners(100),this.connections=e.connections??new Map,this.config={...e,path:e.path??Z,maxPayload:e.maxPayload??D,enablePing:e.enablePing??!0,pingInterval:e.pingInterval??U,pingTimeout:e.pingTimeout??B,connections:this.connections};let t=e.ServerConstructor??Ce;this.wsServer=new t({server:this.config.server,path:this.config.path,maxPayload:this.config.maxPayload}),this.setupEventHandlers(),this.config.enablePing&&this.startPingTimer()}setAuthenticator(e){this.authenticator=e}setupEventHandlers(){this.wsServer.on("connection",(e,t)=>{this.handleConnection(e,t)}),this.wsServer.on("error",e=>{this.config.logger?.error("WebSocket Server Error:",e),this.emit("error",e)})}async handleConnection(e,t){let n;try{this.authenticator?n=await this.authenticator(t):this.config.generateId?n=await this.config.generateId(t):n=z()}catch(o){try{e.close(4001,o instanceof Error?o.message:"Unauthorized")}catch{}return}let r=Date.now(),s={socket:e,id:n,connectedAt:r,lastPingAt:r};this.connections.set(n,s),e.on("message",o=>{this.handleMessage(n,o)}),e.on("close",(o,a)=>{this.handleDisconnection(n)}),e.on("error",o=>{this.emit("error",o)}),this.config.enablePing&&this.setupPingPong(n,e),this.emit("connection",s)}handleMessage(e,t){try{let n=JSON.parse(t.toString()),r=this.connections.get(e);r&&n.type==="signal"&&n.signal==="pong"&&(r.lastPingAt=Date.now()),this.emit("message",e,n)}catch(n){this.config.logger?.error(`Failed to parse message from ${e}:`,n),this.emit("error",n)}}handleDisconnection(e){this.emit("disconnection",e),this.connections.delete(e)}setupPingPong(e,t){t.on("pong",()=>{let n=this.connections.get(e);n&&(n.lastPingAt=Date.now())})}startPingTimer(){this.pingTimer&&clearInterval(this.pingTimer),this.pingTimer=setInterval(()=>{this.checkConnections()},this.config.pingInterval)}checkConnections(){let e=Date.now(),t=Array.from(this.connections.values());for(let n of t){let r=n.socket,s=n.lastPingAt??n.connectedAt;if(e-s>this.config.pingInterval+this.config.pingTimeout){r.close(1e3,"Ping timeout");continue}r.readyState===1&&r.ping()}}};var N=class{constructor(e){this.status={started:!1,startedAt:void 0};if(this.config=e,this.registry=this.config.registry,this.context=new A,this.config.middleware)for(let t of this.config.middleware)this.context.use(t)}async start(){if(this.status.started)throw new p("Server is already started");this.transport=this.config.transport,this.connectionHandler=new T({registry:this.registry}),this.messageHandler=new M({registry:this.registry,context:this.context}),this.signalHandler=new x({registry:this.registry,context:this.context}),this.setupTransportHandlers(),this.broadcastChannel=new w(this.registry,this.config.broadcastChunkSize),this.registry.registerChannel(this.broadcastChannel),this.status.started=!0,this.status.startedAt=Date.now()}async stop(){!this.status.started||!this.transport||(this.connectionHandler=void 0,this.messageHandler=void 0,this.signalHandler=void 0,this.registry.clear(),this.broadcastChannel=void 0,this.status.started=!1,this.status.startedAt=void 0)}createBroadcast(){if(!this.status.started||!this.broadcastChannel)throw new p("Server must be started before creating channels");return this.broadcastChannel}createMulticast(e){if(f(e),!this.status.started||!this.transport)throw new p("Server must be started before creating channels");let t=this.registry.getChannel(e);if(t)return t;let n=new E({name:e,registry:this.registry,options:{chunkSize:this.config.broadcastChunkSize}});return this.registry.registerChannel(n),n}hasChannel(e){return!!this.registry.getChannel(e)}getChannels(){return this.registry.getChannels()}use(e){this.context.use(e)}authenticate(e){let t=this.transport||this.config.transport;t&&"setAuthenticator"in t?t.setAuthenticator(e):this.config.logger.warn("Current transport does not support setting an authenticator.")}getStats(){return{startedAt:this.status.startedAt,clientCount:this.registry.getCount(),channelCount:this.registry.getChannels().length,subscriptionCount:this.registry.getTotalSubscriptionCount()}}getConfig(){return this.config}getRegistry(){return this.registry}setupTransportHandlers(){let e=this.transport;e.on("connection",async t=>{try{await this.context.executeConnection(t,"connect"),await this.connectionHandler.handleConnection(t)}catch(n){this.config.logger.error("Error handling connection:",n)}}),e.on("disconnection",async t=>{try{let n=this.registry.get(t);n&&(await this.context.executeConnection(n,"disconnect"),await this.connectionHandler.handleDisconnection(t))}catch(n){this.config.logger.error("Error handling disconnection:",n)}}),e.on("message",async(t,n)=>{try{let r=this.registry.get(t);if(!r)return;n.type==="data"?await this.messageHandler.handleMessage(r,n):n.type==="signal"&&await this.signalHandler.handleSignal(r,n)}catch(r){this.config.logger.error("Error handling message:",r)}}),e.on("error",t=>{this.config.logger.error("Transport error:",t)})}};function me(i={}){let e=i.registry??new _,t=i.logger??X(),n={...Q,middleware:[],...i,registry:e,logger:t};return n.transport||(n.server||import("http").then(r=>{let s=r.createServer();s.listen(n.port,n.host),n.server=s}),n.transport=new v({server:n.server,path:n.path,maxPayload:i.maxPayload??D,enablePing:n.enablePing,pingInterval:n.pingInterval,pingTimeout:n.pingTimeout,connections:e.connections,generateId:n.generateId,logger:n.logger})),new N(n)}function te(i){let{verifyToken:e,getToken:t=s=>s.req.message?.data?.token,attachProperty:n="user",actions:r}=i;return async(s,o)=>{if(r&&!r.includes(s.req.action))return o();let a=t(s);a||s.reject("Authentication token required");try{let c=await e(a);s.req.client&&(s.req.client[n]=c),s.set(n,c),await o()}catch{s.reject("Authentication failed: Invalid token")}}}function ne(i={}){let{logger:e=console,logLevel:t="info",includeMessageData:n=!1,format:r,actions:s}=i;return async(o,a)=>{if(s&&!s.includes(o.req.action))return a();let c=Date.now();await a();let d=Date.now()-c,l={action:o.req.action,clientId:o.req.client?.id,channel:o.req.channel,message:n?o.req.message:void 0,duration:d},m=r?r(l):`[${l.action}] Client: ${l.clientId??"unknown"}${l.channel?` Channel: ${l.channel}`:""} (${d}ms)`;e[t](m)}}var C=new Map;function re(i={}){let{maxRequests:e=100,windowMs:t=6e4,getMessageId:n=a=>a.req.client?.id??"",actions:r=["message"]}=i,s=setInterval(()=>{let a=Date.now();for(let[c,d]of C)d.resetTime<a&&C.delete(c)},t*10),o=async(a,c)=>{if(!r.includes(a.req.action))return c();let d=n(a);if(!d)return c();let l=Date.now(),m=C.get(d);m&&m.resetTime<l&&C.delete(d);let b=C.get(d);b||(b={count:0,resetTime:l+t},C.set(d,b)),b.count>=e&&a.reject(`Rate limit exceeded. Max ${e} requests per ${t}ms`),b.count++,await c()};return o.cleanup=()=>{clearInterval(s),C.clear()},o}function se(i={}){let{allowedChannels:e=[],isDynamic:t,restrictUnsubscribe:n=!1}=i;return async(r,s)=>{if(r.req.action!=="subscribe"&&r.req.action!=="unsubscribe"||r.req.action==="unsubscribe"&&!n||!r.req.channel)return s();if(t)return t(r.req.channel,r.req.client)||r.reject(`Channel '${r.req.channel}' is not allowed`),s();e.includes(r.req.channel)||r.reject(`Channel '${r.req.channel}' is not allowed`),await s()}}export{y as BROADCAST_CHANNEL,w as BroadcastChannel,H as CLOSE_CODES,h as ChannelError,q as ClientError,G as ConfigError,A as ContextManager,ce as ERROR_CODES,u as MessageError,I as MiddlewareExecutionError,S as MiddlewareRejectionError,E as MulticastChannel,p as StateError,N as Syncar,g as SyncarError,N as SyncarServer,$ as TransportError,F as ValidationError,v as WebSocketServerTransport,te as createAuthMiddleware,se as createChannelWhitelistMiddleware,R as createContext,ne as createLoggingMiddleware,re as createRateLimitMiddleware,me as createSyncarServer};
|
|
2
2
|
//# sourceMappingURL=index.js.map
|