@stonyx/sockets 0.1.0 → 0.1.1-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,174 @@
1
+ # Handlers
2
+
3
+ ## Overview
4
+
5
+ Handlers are the primary extension point for consumers. Each handler is a class that extends `Handler` and defines a `server()` method, a `client()` method, or both. Handler files live in the handler directory (default: `./socket-handlers`).
6
+
7
+ ## Base Class
8
+
9
+ ```javascript
10
+ // src/handler.js
11
+ export default class Handler {
12
+ static skipAuth = false;
13
+ }
14
+ ```
15
+
16
+ That's it — 3 lines. The base class exists to provide the `skipAuth` default and a common prototype for `instanceof` checks.
17
+
18
+ ## Defining a Handler
19
+
20
+ ### Server-only handler
21
+
22
+ ```javascript
23
+ import { Handler } from '@stonyx/sockets';
24
+
25
+ export default class ValidateGameHandler extends Handler {
26
+ server(data, client) {
27
+ // data = whatever the client sent in the 'data' field
28
+ // client = the WebSocket client object (with .id, .ip, .meta, .send())
29
+
30
+ // Return a value to send it back as the response
31
+ return { valid: true };
32
+
33
+ // Return undefined/null to send no response
34
+ }
35
+ }
36
+ ```
37
+
38
+ ### Client-only handler
39
+
40
+ ```javascript
41
+ import { Handler } from '@stonyx/sockets';
42
+
43
+ export default class ScanGamesHandler extends Handler {
44
+ client(response) {
45
+ // response = whatever the server sent back
46
+ // this.client = reference to the SocketClient instance
47
+
48
+ this.client.app.scanGames(response);
49
+ }
50
+ }
51
+ ```
52
+
53
+ ### Dual handler (both sides)
54
+
55
+ ```javascript
56
+ import { Handler } from '@stonyx/sockets';
57
+
58
+ export default class EchoHandler extends Handler {
59
+ server(data) {
60
+ return data; // echo back
61
+ }
62
+
63
+ client(response) {
64
+ console.log('Got echo:', response);
65
+ }
66
+ }
67
+ ```
68
+
69
+ ## Auth Handler (Required)
70
+
71
+ The `auth` handler is special — `SocketServer.init()` throws if it doesn't find a handler named `auth` with a `server()` method.
72
+
73
+ ```javascript
74
+ import { Handler } from '@stonyx/sockets';
75
+ import config from 'stonyx/config';
76
+
77
+ export default class AuthHandler extends Handler {
78
+ static skipAuth = true; // MUST be true — auth runs before authentication
79
+
80
+ server(data, client) {
81
+ if (data.authKey !== config.sockets.authKey) return client.close();
82
+
83
+ // Register client in the server's client map
84
+ this._serverRef.clientMap.set(client.id, client);
85
+
86
+ // Optionally set app-level metadata
87
+ client.meta = { role: 'worker' };
88
+
89
+ // Returning a truthy value triggers the framework to:
90
+ // 1. Set client.__authenticated = true
91
+ // 2. Generate and send a per-session encryption key (if encryption enabled)
92
+ // 3. Send the response back to the client
93
+ return 'success';
94
+ }
95
+
96
+ client(response) {
97
+ // this.client = the SocketClient instance
98
+ if (response !== 'success') this.client.promise.reject(response);
99
+ this.client.promise.resolve();
100
+ }
101
+ }
102
+ ```
103
+
104
+ ### Auth enforcement rules
105
+
106
+ - If a message arrives from an unauthenticated client:
107
+ - And the handler is the `auth` handler → allowed
108
+ - And the handler has `static skipAuth = true` → allowed
109
+ - Otherwise → rejected, connection closed
110
+ - The framework sets `client.__authenticated = true` when the auth handler returns a truthy value
111
+ - If the auth handler returns `undefined`/`null`/falsy, auth fails (no response sent)
112
+
113
+ ## Handler Discovery
114
+
115
+ Both `SocketServer` and `SocketClient` call `forEachFileImport` on the handler directory:
116
+
117
+ ```javascript
118
+ await forEachFileImport(handlerDir, (HandlerClass, { name }) => {
119
+ const instance = new HandlerClass();
120
+ if (typeof instance.server === 'function') {
121
+ instance._serverRef = this;
122
+ this.handlers[name] = instance;
123
+ }
124
+ }, { ignoreAccessFailure: true });
125
+ ```
126
+
127
+ - **Filename → handler name:** kebab-case to camelCase (`validate-game.js` → `validateGame`)
128
+ - **Exception:** `auth.js` stays as `auth`
129
+ - **ignoreAccessFailure:** If the handler directory doesn't exist, no error is thrown
130
+
131
+ ## Handler Context
132
+
133
+ ### Inside `server()` hooks
134
+
135
+ - `this._serverRef` — the `SocketServer` instance
136
+ - First argument `data` — the parsed `data` field from the message
137
+ - Second argument `client` — the WebSocket client object
138
+
139
+ ### Inside `client()` hooks
140
+
141
+ - `this.client` — the `SocketClient` instance (set via `.call()` binding)
142
+ - First argument `response` — the parsed `response` field from the message
143
+
144
+ ## Wire Protocol
145
+
146
+ All messages are JSON objects with a `request` field:
147
+
148
+ ```javascript
149
+ // Client → Server (outgoing request)
150
+ { request: 'handlerName', data: { ... } }
151
+
152
+ // Server → Client (response from handler return value)
153
+ { request: 'handlerName', response: { ... } }
154
+
155
+ // Server → Client (explicit send via sendTo/broadcast)
156
+ { request: 'handlerName', data: { ... } }
157
+ ```
158
+
159
+ ## Built-in Handlers
160
+
161
+ These are handled by the framework — consumers do NOT define handlers for them:
162
+
163
+ ### heartBeat
164
+
165
+ - **Server:** Receives `heartBeat` request → responds with `{ request: 'heartBeat', response: true }`
166
+ - **Client:** Receives `heartBeat` response → schedules next heartbeat via `setTimeout`
167
+ - **Lifecycle:** Automatically started after successful auth; interval configured via `heartBeatInterval`
168
+
169
+ ### auth (framework-level handling)
170
+
171
+ While consumers define the auth handler logic, the framework wraps it with:
172
+ - Auto-setting `client.__authenticated = true` on truthy return
173
+ - Auto-generating and transmitting per-session encryption keys
174
+ - Auto-starting the heartbeat cycle on the client side
@@ -0,0 +1,36 @@
1
+ # @stonyx/sockets — Agent Documentation Index
2
+
3
+ Comprehensive reference for AI agents working on the `@stonyx/sockets` package. Start here, then drill into specific docs as needed.
4
+
5
+ ## Quick Orientation
6
+
7
+ `@stonyx/sockets` is a Stonyx framework module providing WebSocket server/client with handler auto-discovery, auth enforcement, AES-256-GCM encryption, and built-in heartbeat. It follows the same conventions as `@stonyx/rest-server` and `stonyx-orm`.
8
+
9
+ ## Documentation
10
+
11
+ - [architecture.md](./architecture.md) — Module structure, singleton pattern, Stonyx integration, handler discovery lifecycle
12
+ - [handlers.md](./handlers.md) — Handler class API, server/client hooks, auth flow, skipAuth, wire protocol
13
+ - [encryption.md](./encryption.md) — AES-256-GCM encryption, key derivation, handshake flow, session keys
14
+ - [configuration.md](./configuration.md) — All config options, env vars, defaults, how config loads via Stonyx
15
+ - [testing.md](./testing.md) — Test structure, running tests, sample handlers, writing new tests
16
+ - [api-reference.md](./api-reference.md) — Complete method/property reference for SocketServer, SocketClient, Handler
17
+
18
+ ## Key Files
19
+
20
+ | File | Purpose |
21
+ |------|---------|
22
+ | `src/main.js` | Entry point — `Sockets` default class (Stonyx auto-init) + barrel exports |
23
+ | `src/server.js` | `SocketServer` — singleton, handler discovery, auth gate, message dispatch |
24
+ | `src/client.js` | `SocketClient` — singleton, handler discovery, connect/auth/heartbeat |
25
+ | `src/handler.js` | `Handler` base class (3 lines — just `skipAuth` flag) |
26
+ | `src/encryption.js` | AES-256-GCM encrypt/decrypt, key derivation, session key generation |
27
+ | `config/environment.js` | Default config with env var overrides |
28
+
29
+ ## Conventions
30
+
31
+ - **Singleton pattern:** `if (Class.instance) return Class.instance;` in constructor
32
+ - **Stonyx module keywords:** `stonyx-async` + `stonyx-module` in package.json
33
+ - **Config namespace:** `config.sockets` (camelCase of package name minus `@stonyx/`)
34
+ - **Logging:** `log.socket()` via `logColor: 'white'` + `logMethod: 'socket'` in config
35
+ - **Handler discovery:** `forEachFileImport` from `@stonyx/utils/file`, kebab-to-camelCase naming
36
+ - **Test runner:** `stonyx test` (not plain `qunit`) — bootstraps Stonyx before running tests
@@ -0,0 +1,135 @@
1
+ # Testing
2
+
3
+ ## Running Tests
4
+
5
+ ```bash
6
+ # From the stonyx-sockets directory
7
+ npx stonyx test
8
+
9
+ # Or via pnpm
10
+ pnpm test
11
+ ```
12
+
13
+ **Important:** Use `stonyx test`, not plain `qunit`. The Stonyx test runner bootstraps the framework (config, logging, module init) before running QUnit. Without it, `stonyx/config` and `log.socket()` won't be available.
14
+
15
+ ## Test Structure
16
+
17
+ ```
18
+ test/
19
+ ├── config/
20
+ │ └── environment.js # Test-specific config overrides
21
+ ├── sample/
22
+ │ └── socket-handlers/
23
+ │ ├── auth.js # Sample auth handler (server + client hooks)
24
+ │ └── echo.js # Simple echo handler (both hooks)
25
+ ├── unit/
26
+ │ ├── handler-test.js # Base Handler class tests
27
+ │ ├── encryption-test.js # AES-256-GCM encrypt/decrypt tests
28
+ │ ├── server-test.js # SocketServer unit tests (no network)
29
+ │ └── client-test.js # SocketClient unit tests (no network)
30
+ └── integration/
31
+ └── socket-test.js # Full server+client round-trip tests
32
+ ```
33
+
34
+ ## Test Config
35
+
36
+ ```javascript
37
+ // test/config/environment.js
38
+ export default {
39
+ sockets: {
40
+ handlerDir: './test/sample/socket-handlers',
41
+ heartBeatInterval: 60000, // Long interval so timers don't fire during tests
42
+ encryption: 'false', // Disabled for test simplicity
43
+ }
44
+ }
45
+ ```
46
+
47
+ ## Sample Handlers
48
+
49
+ ### auth.js
50
+
51
+ Validates `authKey` against config, registers client in `clientMap`, resolves the connection promise. Has `static skipAuth = true`.
52
+
53
+ ### echo.js
54
+
55
+ Server returns whatever data it receives. Client stores the response on `client._lastEchoResponse` for test assertions.
56
+
57
+ ## Writing Unit Tests
58
+
59
+ Unit tests do NOT start a WebSocket server. They test class behavior directly:
60
+
61
+ ```javascript
62
+ import QUnit from 'qunit';
63
+ import SocketServer from '../../src/server.js';
64
+
65
+ const { module, test } = QUnit;
66
+
67
+ module('[Unit] SocketServer', function (hooks) {
68
+ hooks.afterEach(function () {
69
+ const server = SocketServer.instance;
70
+ if (server) server.reset();
71
+ });
72
+
73
+ test('Singleton pattern', function (assert) {
74
+ const s1 = new SocketServer();
75
+ const s2 = new SocketServer();
76
+ assert.strictEqual(s1, s2);
77
+ s1.reset();
78
+ });
79
+ });
80
+ ```
81
+
82
+ Key patterns:
83
+ - Always call `reset()` in `afterEach` to clear the singleton
84
+ - Use `sinon` for stubs/spies when needed
85
+ - Restore sinon in `afterEach` with `sinon.restore()`
86
+
87
+ ## Writing Integration Tests
88
+
89
+ Integration tests start a real server and client:
90
+
91
+ ```javascript
92
+ import QUnit from 'qunit';
93
+ import SocketServer from '../../src/server.js';
94
+ import SocketClient from '../../src/client.js';
95
+ import { setupIntegrationTests } from 'stonyx/test-helpers';
96
+
97
+ const { module, test } = QUnit;
98
+
99
+ module('[Integration] Sockets', function (hooks) {
100
+ setupIntegrationTests(hooks); // Waits for Stonyx.ready
101
+
102
+ hooks.afterEach(function () {
103
+ const client = SocketClient.instance;
104
+ const server = SocketServer.instance;
105
+ if (client) client.reset();
106
+ if (server) server.reset();
107
+ });
108
+
109
+ test('Round-trip', async function (assert) {
110
+ const server = new SocketServer();
111
+ await server.init();
112
+
113
+ const client = new SocketClient();
114
+ await client.init();
115
+
116
+ client.send({ request: 'echo', data: { msg: 'hello' } });
117
+ await new Promise(resolve => setTimeout(resolve, 200));
118
+
119
+ assert.deepEqual(client._lastEchoResponse, { msg: 'hello' });
120
+ });
121
+ });
122
+ ```
123
+
124
+ Key patterns:
125
+ - `setupIntegrationTests(hooks)` — adds a `hooks.before` that `await Stonyx.ready`
126
+ - Always clean up in `afterEach` — `reset()` terminates connections and clears state
127
+ - Use `setTimeout` + `await` for async message assertions (messages are async)
128
+ - For multiple clients: null out `SocketClient.instance` between creations, track extras for cleanup
129
+
130
+ ## Common Gotchas
131
+
132
+ - **Process hangs after tests:** Usually caused by un-cleared heartbeat timers or unclosed WebSocket servers. Ensure `reset()` is called for all instances.
133
+ - **`log.socket is not a function`:** Running `qunit` directly instead of `stonyx test`. The Stonyx bootstrap is required.
134
+ - **`moduleClass is not a constructor`:** The `src/main.js` default export must be a class (not just named exports). The `Sockets` class serves as the Stonyx auto-init entry point.
135
+ - **Port conflicts:** Integration tests use port 2667 by default. If tests run in parallel with other services, override `SOCKET_PORT`.
@@ -0,0 +1,16 @@
1
+ name: CI
2
+
3
+ on:
4
+ pull_request:
5
+ branches: [dev, main]
6
+
7
+ concurrency:
8
+ group: ci-${{ github.head_ref || github.ref }}
9
+ cancel-in-progress: true
10
+
11
+ permissions:
12
+ contents: read
13
+
14
+ jobs:
15
+ test:
16
+ uses: abofs/stonyx-workflows/.github/workflows/ci.yml@main
@@ -0,0 +1,51 @@
1
+ name: Publish to NPM
2
+
3
+ on:
4
+ repository_dispatch:
5
+ types: [cascade-publish]
6
+ workflow_dispatch:
7
+ inputs:
8
+ version-type:
9
+ description: 'Version type'
10
+ required: true
11
+ type: choice
12
+ options:
13
+ - patch
14
+ - minor
15
+ - major
16
+ custom-version:
17
+ description: 'Custom version (optional, overrides version-type)'
18
+ required: false
19
+ type: string
20
+ pull_request:
21
+ types: [opened, synchronize, reopened]
22
+ branches: [main]
23
+ push:
24
+ branches: [main]
25
+
26
+ concurrency:
27
+ group: ${{ github.event_name == 'repository_dispatch' && 'cascade-update' || format('publish-{0}', github.ref) }}
28
+ cancel-in-progress: false
29
+
30
+ permissions:
31
+ contents: write
32
+ id-token: write
33
+ pull-requests: write
34
+
35
+ jobs:
36
+ publish:
37
+ if: "!contains(github.event.head_commit.message, '[skip ci]')"
38
+ uses: abofs/stonyx-workflows/.github/workflows/npm-publish.yml@main
39
+ with:
40
+ version-type: ${{ github.event.inputs.version-type }}
41
+ custom-version: ${{ github.event.inputs.custom-version }}
42
+ cascade-source: ${{ github.event.client_payload.source_package || '' }}
43
+ secrets: inherit
44
+
45
+ cascade:
46
+ needs: publish
47
+ uses: abofs/stonyx-workflows/.github/workflows/cascade.yml@main
48
+ with:
49
+ package-name: ${{ needs.publish.outputs.package-name }}
50
+ published-version: ${{ needs.publish.outputs.published-version }}
51
+ secrets: inherit
package/README.md CHANGED
@@ -1,6 +1,15 @@
1
1
  # @stonyx/sockets
2
2
 
3
- WebSocket server and client module for the Stonyx framework.
3
+ WebSocket server and client module for the [Stonyx framework](https://github.com/abofs/stonyx), providing plug-and-play handler discovery, built-in authentication enforcement, AES-256-GCM encryption, and automatic heartbeat keep-alive.
4
+
5
+ ## Highlights
6
+
7
+ * **Handler auto-discovery:** Drop handler files into a directory and the framework registers them automatically.
8
+ * **Unified handler files:** A single file can define both `server()` and `client()` hooks.
9
+ * **Auth enforcement:** An `auth` handler is required. Unauthenticated requests are rejected by default.
10
+ * **Built-in heartbeat:** Keep-alive is managed by the framework — no handler needed.
11
+ * **Encryption by default:** AES-256-GCM with per-session keys, zero external dependencies.
12
+ * **Singleton pattern:** Matches the conventions of `@stonyx/rest-server` and `stonyx-orm`.
4
13
 
5
14
  ## Installation
6
15
 
@@ -8,8 +17,245 @@ WebSocket server and client module for the Stonyx framework.
8
17
  npm install @stonyx/sockets
9
18
  ```
10
19
 
11
- ## Usage
20
+ ## Quick Start
21
+
22
+ ### 1. Create handler files
23
+
24
+ ```
25
+ socket-handlers/ # default directory (configurable)
26
+ auth.js # REQUIRED — must have a server() hook
27
+ scan-games.js # app-specific handlers
28
+ validate-game.js
29
+ ```
30
+
31
+ ### 2. Write a handler
32
+
33
+ Each handler extends `Handler` and defines a `server()` method, a `client()` method, or both:
34
+
35
+ ```javascript
36
+ // socket-handlers/auth.js
37
+ import { Handler } from '@stonyx/sockets';
38
+ import config from 'stonyx/config';
39
+
40
+ export default class AuthHandler extends Handler {
41
+ static skipAuth = true; // auth handler must work before authentication
42
+
43
+ server(data, client) {
44
+ if (data.authKey !== config.sockets.authKey) return client.close();
45
+
46
+ this._serverRef.clientMap.set(client.id, client);
47
+ return 'success';
48
+ }
49
+
50
+ client(response) {
51
+ if (response !== 'success') this.client.promise.reject(response);
52
+ this.client.promise.resolve();
53
+ }
54
+ }
55
+ ```
12
56
 
13
57
  ```javascript
14
- import { SocketServer, SocketClient, Handler } from '@stonyx/sockets';
58
+ // socket-handlers/scan-games.js client-only handler
59
+ import { Handler } from '@stonyx/sockets';
60
+
61
+ export default class ScanGamesHandler extends Handler {
62
+ client(validGames) {
63
+ this.client.app.scanGames(validGames);
64
+ }
65
+ }
66
+ ```
67
+
68
+ ### 3. Start the server / client
69
+
70
+ With Stonyx auto-initialization (recommended):
71
+
72
+ ```bash
73
+ stonyx serve
74
+ ```
75
+
76
+ Or manually:
77
+
78
+ ```javascript
79
+ import { SocketServer, SocketClient } from '@stonyx/sockets';
80
+
81
+ // Server side
82
+ const server = new SocketServer();
83
+ await server.init();
84
+
85
+ // Client side
86
+ const client = new SocketClient();
87
+ await client.init();
88
+ ```
89
+
90
+ ## Handler Architecture
91
+
92
+ ### How handlers are discovered
93
+
94
+ On `init()`, both `SocketServer` and `SocketClient` scan the handler directory using `forEachFileImport`. Each file's default export is inspected:
95
+
96
+ - Has a `server()` method → registered on `SocketServer`
97
+ - Has a `client()` method → registered on `SocketClient`
98
+ - Has both → registered on both sides
99
+
100
+ Handler filenames are converted from kebab-case to camelCase: `validate-game.js` → `validateGame`.
101
+
102
+ ### Handler hooks
103
+
104
+ **Server hook:** `server(data, client)` — receives the request data and the client object. Return a value to send it back as a response.
105
+
106
+ **Client hook:** `client(response)` — receives the server's response. Inside the hook, `this.client` references the `SocketClient` instance.
107
+
108
+ ### skipAuth
109
+
110
+ Set `static skipAuth = true` on a handler class to allow it to execute before the client is authenticated. The `auth` handler must always set this.
111
+
112
+ ## Sending Messages
113
+
114
+ ### Client → Server
115
+
116
+ ```javascript
117
+ // From app code
118
+ client.send({ request: 'handlerName', data: { ... } });
119
+
120
+ // From within a client handler
121
+ this.client.send({ request: 'handlerName', data: { ... } });
15
122
  ```
123
+
124
+ ### Server → Client
125
+
126
+ ```javascript
127
+ // Auto-reply (return value from server() hook becomes the response)
128
+ server(data, client) {
129
+ return 'success'; // sends { request, response: 'success' } back
130
+ }
131
+
132
+ // Send to a specific client by ID
133
+ server.sendTo(clientId, 'scanGames', gamesList);
134
+
135
+ // Broadcast to all authenticated clients
136
+ server.broadcast('announcement', { msg: 'shutdown in 5m' });
137
+
138
+ // Filter by metadata using clientMap directly
139
+ for (const [id, client] of server.clientMap) {
140
+ if (client.meta?.role === 'worker') {
141
+ client.send({ request: 'scanGames', data: games });
142
+ }
143
+ }
144
+ ```
145
+
146
+ ### Wire Protocol
147
+
148
+ All messages are JSON:
149
+
150
+ ```javascript
151
+ { request: 'handlerName', data: { ... } } // outgoing request
152
+ { request: 'handlerName', response: { ... } } // reply
153
+ ```
154
+
155
+ ## Client Object (Server-Side)
156
+
157
+ Each connected WebSocket client is augmented with:
158
+
159
+ | Property | Description |
160
+ |----------|-------------|
161
+ | `client.id` | Auto-assigned numeric ID (incrementing) |
162
+ | `client.ip` | Remote IP address |
163
+ | `client.meta` | App-defined metadata (set in your auth handler) |
164
+ | `client.__authenticated` | Framework-managed auth flag |
165
+ | `client.send(payload)` | Wrapped send that handles JSON + encryption |
166
+
167
+ ## Built-in Mechanisms
168
+
169
+ ### Heartbeat
170
+
171
+ The framework automatically manages keep-alive. After successful authentication, the client begins sending periodic `heartBeat` requests at the configured interval. The server responds automatically. No handler needed.
172
+
173
+ ### Authentication Flow
174
+
175
+ 1. Client connects and sends `{ request: 'auth', data: { authKey } }`
176
+ 2. Server routes to the `auth` handler's `server()` method
177
+ 3. If the handler returns a truthy value, `client.__authenticated` is set to `true`
178
+ 4. Response is sent back; if encryption is enabled, a per-session key is included
179
+ 5. Client's `auth` handler `client()` method processes the response
180
+ 6. Heartbeat cycle begins automatically
181
+
182
+ **Auth enforcement:** Any message from an unauthenticated client is rejected and the connection is closed, unless the handler has `static skipAuth = true`.
183
+
184
+ **Missing auth handler:** `SocketServer.init()` throws if no `auth` handler with a `server()` method exists.
185
+
186
+ ## Encryption
187
+
188
+ Enabled by default (`SOCKET_ENCRYPTION=true`). Uses Node.js native `crypto` — zero external dependencies.
189
+
190
+ - **Algorithm:** AES-256-GCM
191
+ - **Key derivation:** `crypto.scryptSync` from the `authKey` string
192
+ - **Handshake:** Auth request/response use the global key; server generates a per-session key for all subsequent messages
193
+ - **Wire format:** `iv (12 bytes) + auth tag (16 bytes) + ciphertext`
194
+
195
+ Disable with `SOCKET_ENCRYPTION=false`.
196
+
197
+ ## Configuration
198
+
199
+ Configuration is read from `stonyx/config` under `sockets`:
200
+
201
+ | Option | Env Var | Default | Description |
202
+ |--------|---------|---------|-------------|
203
+ | `port` | `SOCKET_PORT` | `2667` | WebSocket server port |
204
+ | `address` | `SOCKET_ADDRESS` | `ws://localhost:2667` | Client connection address |
205
+ | `authKey` | `SOCKET_AUTH_KEY` | `'AUTH_KEY'` | Shared authentication key |
206
+ | `heartBeatInterval` | `SOCKET_HEARTBEAT_INTERVAL` | `30000` | Heartbeat interval in ms |
207
+ | `handlerDir` | `SOCKET_HANDLER_DIR` | `'./socket-handlers'` | Handler directory path |
208
+ | `encryption` | `SOCKET_ENCRYPTION` | `'true'` | Enable AES-256-GCM encryption |
209
+
210
+ ## API Reference
211
+
212
+ ### SocketServer
213
+
214
+ | Method | Description |
215
+ |--------|-------------|
216
+ | `new SocketServer()` | Singleton constructor |
217
+ | `async init()` | Discover handlers, validate auth, start WebSocket server |
218
+ | `sendTo(clientId, request, data)` | Send to one client by ID |
219
+ | `broadcast(request, data)` | Send to all authenticated clients |
220
+ | `clientMap` | `Map<id, client>` of connected clients |
221
+ | `close()` | Terminate all connections and stop the server |
222
+ | `reset()` | Close + clear all state (for testing) |
223
+
224
+ ### SocketClient
225
+
226
+ | Method | Description |
227
+ |--------|-------------|
228
+ | `new SocketClient()` | Singleton constructor |
229
+ | `async init()` | Discover handlers, connect, authenticate |
230
+ | `send(payload)` | Send a message to the server |
231
+ | `close()` | Close the connection |
232
+ | `reconnect()` | Reconnect (max 5 retries) |
233
+ | `reset()` | Close + clear all state (for testing) |
234
+
235
+ ### Handler
236
+
237
+ | Property / Method | Description |
238
+ |-------------------|-------------|
239
+ | `static skipAuth = false` | Set `true` to allow pre-auth access |
240
+ | `server(data, client)` | Server-side hook (optional) |
241
+ | `client(response)` | Client-side hook (optional) |
242
+ | `this._serverRef` | Reference to SocketServer (in server hooks) |
243
+ | `this.client` | Reference to SocketClient (in client hooks) |
244
+
245
+ ## Example Project Structure
246
+
247
+ ```
248
+ my-app/
249
+ ├── config/
250
+ │ └── environment.js
251
+ ├── socket-handlers/
252
+ │ ├── auth.js # Required
253
+ │ ├── scan-games.js
254
+ │ └── validate-game.js
255
+ ├── package.json
256
+ └── app.js
257
+ ```
258
+
259
+ ## License
260
+
261
+ Apache — do what you want, just keep attribution.
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "stonyx-async",
5
5
  "stonyx-module"
6
6
  ],
7
- "version": "0.1.0",
7
+ "version": "0.1.1-alpha.1",
8
8
  "description": "WebSocket server and client module for the Stonyx framework",
9
9
  "main": "src/main.js",
10
10
  "type": "module",
@@ -18,7 +18,8 @@
18
18
  "./handler": "./src/handler.js"
19
19
  },
20
20
  "publishConfig": {
21
- "access": "public"
21
+ "access": "public",
22
+ "provenance": true
22
23
  },
23
24
  "repository": {
24
25
  "type": "git",