@mono-labs/dev 0.1.256 → 0.1.257

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +294 -0
  2. package/package.json +1 -1
package/README.md ADDED
@@ -0,0 +1,294 @@
1
+ # @mono-labs/dev
2
+
3
+ Local development server and WebSocket adapter for mono-labs. Maps Lambda handlers to Express routes, emulates API Gateway WebSocket connections, and provides a Redis caching abstraction.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ yarn add @mono-labs/dev
9
+ ```
10
+
11
+ ### Optional Peer Dependencies
12
+
13
+ ```bash
14
+ # For Redis-backed channel store and CacheRelay
15
+ yarn add ioredis
16
+
17
+ # For API Gateway WebSocket mode (SocketGatewayClient)
18
+ yarn add @aws-sdk/client-apigatewaymanagementapi
19
+ ```
20
+
21
+ ## API Reference
22
+
23
+ ### LocalServer
24
+
25
+ Express-based HTTP server that maps Lambda handlers to local routes.
26
+
27
+ ```typescript
28
+ import { LocalServer } from '@mono-labs/dev'
29
+
30
+ const server = new LocalServer({ debug: true })
31
+ ```
32
+
33
+ - **`constructor(config?)`** -- Creates a new server. `LocalServerConfig` options: `debug?: boolean`, `useRedis?: boolean`, `redis?: RedisConfig`.
34
+ - **`.app`** -- The underlying `express.Express` instance for custom middleware.
35
+
36
+ #### `.lambda(path, handler, options?)`
37
+
38
+ Registers a Lambda handler at the given HTTP path. Supports two event types:
39
+
40
+ ```typescript
41
+ // API Gateway V2 (default)
42
+ server.lambda('/api/*', myApiGatewayHandler)
43
+ server.lambda('/api/*', myApiGatewayHandler, { eventType: 'api-gateway' })
44
+
45
+ // Application Load Balancer
46
+ server.lambda('/alb/*', myALBHandler, { eventType: 'alb' })
47
+ ```
48
+
49
+ #### `.attachSocket(adapterFn, config?)`
50
+
51
+ Attaches the WebSocket adapter to the server's HTTP instance:
52
+
53
+ ```typescript
54
+ import { attachSocketAdapter } from '@mono-labs/dev'
55
+
56
+ const socket = server.attachSocket(attachSocketAdapter, {
57
+ debug: true,
58
+ connectHandler: async (connectionId, { token }) => ({
59
+ response: { statusCode: 200 },
60
+ userContext: { userId: '123', organizationId: 'org-1' }
61
+ }),
62
+ routes: {
63
+ ping: async (body, ctx) => ({ statusCode: 200, body: 'pong' })
64
+ }
65
+ })
66
+ ```
67
+
68
+ Returns the same object as `attachSocketAdapter()` (see below).
69
+
70
+ #### `.listen(port, hostname?)`
71
+
72
+ Starts the HTTP server:
73
+
74
+ ```typescript
75
+ server.listen(3000)
76
+ ```
77
+
78
+ ---
79
+
80
+ ### WebSocket System
81
+
82
+ #### `attachSocketAdapter(wss, config?)`
83
+
84
+ Wires a `WebSocketServer` instance with connection tracking, action routing, and event synthesis:
85
+
86
+ ```typescript
87
+ import { WebSocketServer } from 'ws'
88
+ import { attachSocketAdapter } from '@mono-labs/dev'
89
+
90
+ const wss = new WebSocketServer({ noServer: true })
91
+ const {
92
+ postToConnection,
93
+ connectionRegistry,
94
+ actionRouter,
95
+ channelStore,
96
+ socketEmitter,
97
+ getConnectionId,
98
+ } = attachSocketAdapter(wss, { debug: true })
99
+ ```
100
+
101
+ **`SocketAdapterConfig`** options:
102
+ - `domainName?: string` -- Domain for synthesized events (default: `'localhost'`)
103
+ - `stage?: string` -- Stage for synthesized events (default: `'local'`)
104
+ - `debug?: boolean` -- Enable debug logging
105
+ - `connectHandler?: ConnectHandlerFn` -- Custom $connect handler
106
+ - `disconnectHandler?: DisconnectHandlerFn` -- Custom $disconnect handler
107
+ - `routes?: Record<string, ActionHandler>` -- Action routes
108
+ - `defaultHandler?: ActionHandler` -- Fallback for unmatched actions
109
+ - `channelStore?: ChannelStore` -- Custom channel store implementation
110
+ - `useRedis?: boolean` -- Use Redis-backed channel store
111
+ - `redis?: RedisConfig` -- Redis connection options
112
+
113
+ #### `ConnectionRegistry`
114
+
115
+ Tracks WebSocket connections and their user contexts:
116
+
117
+ ```typescript
118
+ registry.register(ws): ConnectionId
119
+ registry.unregister(connectionId): void
120
+ registry.get(connectionId): WebSocket | undefined
121
+ registry.getAll(): ConnectionId[]
122
+ registry.setUserContext(connectionId, ctx): void
123
+ registry.getUserContext(connectionId): WebSocketUserContext | undefined
124
+ registry.getConnectionsByUserId(userId): ConnectionId[]
125
+ registry.getConnectionsByOrgId(orgId): ConnectionId[]
126
+ ```
127
+
128
+ #### `ActionRouter`
129
+
130
+ Routes incoming WebSocket messages to handlers based on the `action` field:
131
+
132
+ ```typescript
133
+ router.addRoute('chat:send', async (body, ctx) => {
134
+ return { statusCode: 200, body: 'ok' }
135
+ })
136
+ router.setDefaultHandler(async (body, ctx) => {
137
+ return { statusCode: 400, body: 'Unknown action' }
138
+ })
139
+ ```
140
+
141
+ **`ActionHandlerContext`**: `{ connectionId, requestContext, postToConnection, userContext }`
142
+
143
+ #### `SocketGatewayClient`
144
+
145
+ Dual-mode client for posting messages to WebSocket connections. Accepts either a `ConnectionRegistry` for local mode or a URL string for AWS API Gateway mode:
146
+
147
+ ```typescript
148
+ import { SocketGatewayClient } from '@mono-labs/dev'
149
+
150
+ // Local mode (uses ConnectionRegistry to find WebSocket instances)
151
+ const localClient = new SocketGatewayClient(connectionRegistry)
152
+
153
+ // API Gateway mode (uses @aws-sdk/client-apigatewaymanagementapi)
154
+ const awsClient = new SocketGatewayClient('https://abc123.execute-api.us-east-1.amazonaws.com/prod')
155
+
156
+ await client.postToConnection(connectionId, { message: 'hello' })
157
+
158
+ // Get a bound function for dependency injection
159
+ const postFn = client.asFunction()
160
+ ```
161
+
162
+ #### `SocketEmitter`
163
+
164
+ High-level emit API targeting users, orgs, connections, channels, or broadcast:
165
+
166
+ ```typescript
167
+ import type { EmitTarget } from '@mono-labs/dev'
168
+
169
+ await socketEmitter.emit({ userId: 'user-1' }, { event: 'update' })
170
+ await socketEmitter.emit({ orgId: 'org-1' }, { event: 'notify' })
171
+ await socketEmitter.emit({ connectionId: 'conn-1' }, data)
172
+ await socketEmitter.emit({ channel: 'room-1' }, data)
173
+ await socketEmitter.emit('broadcast', data)
174
+ ```
175
+
176
+ #### `ChannelStore`
177
+
178
+ Manages pub/sub channel subscriptions. Two implementations:
179
+
180
+ - **`InMemoryChannelStore`** -- Default. In-memory Maps, no dependencies.
181
+ - **`RedisChannelStore`** -- Redis-backed. Requires `ioredis` peer dependency.
182
+
183
+ ```typescript
184
+ interface ChannelStore {
185
+ subscribe(connectionId, channel): Promise<void>
186
+ unsubscribe(connectionId, channel): Promise<void>
187
+ getSubscribers(channel): Promise<ConnectionId[]>
188
+ removeAll(connectionId): Promise<void>
189
+ }
190
+ ```
191
+
192
+ ---
193
+
194
+ ### CacheRelay
195
+
196
+ Redis abstraction with namespaced operations. Requires `ioredis` peer dependency.
197
+
198
+ ```typescript
199
+ import { initCacheRelay, getCacheRelay } from '@mono-labs/dev'
200
+
201
+ // Initialize (defaults to localhost:6379)
202
+ initCacheRelay()
203
+
204
+ // Or with a connection string (bare hostnames auto-normalized to redis://)
205
+ initCacheRelay('my-redis-host:6379')
206
+
207
+ // Get the singleton instance
208
+ const cache = getCacheRelay()
209
+ ```
210
+
211
+ **Top-level convenience methods:**
212
+
213
+ ```typescript
214
+ await cache.set('key', { foo: 'bar' }, { ttlSeconds: 300 })
215
+ const value = await cache.get('key') // string | null
216
+ const parsed = await cache.gett<MyType>('key') // T | null
217
+ await cache.del('key1', 'key2')
218
+ ```
219
+
220
+ **Namespaced operations:**
221
+
222
+ | Namespace | Operations |
223
+ |-----------|-----------|
224
+ | `cache.strings` | `get`, `set`, `mget`, `mset`, `incr`, `incrby`, `decr`, `decrby`, `append`, `strlen` |
225
+ | `cache.hashes` | `get`, `set`, `getAll`, `del`, `exists`, `keys`, `vals`, `len`, `mset`, `mget`, `incrby` |
226
+ | `cache.lists` | `push`, `lpush`, `pop`, `lpop`, `range`, `len`, `trim`, `index`, `set`, `rem` |
227
+ | `cache.sets` | `add`, `rem`, `members`, `isMember`, `card`, `union`, `inter`, `diff` |
228
+ | `cache.sortedSets` | `add`, `rem`, `range`, `rangeWithScores`, `rangeByScore`, `revRange`, `score`, `rank`, `card`, `incrby`, `remRangeByRank`, `remRangeByScore` |
229
+ | `cache.keys` | `exists`, `expire`, `ttl`, `pttl`, `persist`, `rename`, `type`, `scan` |
230
+ | `cache.pubsub` | `publish`, `subscribe`, `unsubscribe` |
231
+ | `cache.transactions` | `multi`, `exec` |
232
+ | `cache.scripts` | `eval`, `evalsha` |
233
+ | `cache.geo` | `add`, `pos`, `dist`, `radius` |
234
+ | `cache.hyperloglog` | `add`, `count`, `merge` |
235
+ | `cache.bitmaps` | `setbit`, `getbit`, `count`, `op` |
236
+ | `cache.streams` | `add`, `read`, `len`, `range`, `trim` |
237
+
238
+ Access the raw `ioredis` client via `cache.raw`.
239
+
240
+ ---
241
+
242
+ ## Types
243
+
244
+ All exported types from `@mono-labs/dev`:
245
+
246
+ ```typescript
247
+ // LocalServer
248
+ type ApiGatewayHandler
249
+ type ALBHandler
250
+ interface LocalServerConfig
251
+
252
+ // WebSocket
253
+ type ConnectionId
254
+ type PostToConnectionFn
255
+ interface SocketAdapterConfig
256
+ interface RedisConfig
257
+ type ConnectHandlerFn
258
+ type DisconnectHandlerFn
259
+ type ActionHandler
260
+ interface ActionHandlerContext
261
+ interface ActionHandlerResult
262
+ interface LocalRequestContext
263
+ interface WebSocketUserContext
264
+ interface ChannelStore
265
+ type EmitTarget
266
+
267
+ // CacheRelay
268
+ interface CacheRelay
269
+ interface StringOps
270
+ interface HashOps
271
+ interface ListOps
272
+ interface SetOps
273
+ interface SortedSetOps
274
+ interface KeyOps
275
+ interface PubSubOps
276
+ interface TransactionOps
277
+ interface ScriptOps
278
+ interface GeoOps
279
+ interface HyperLogLogOps
280
+ interface BitmapOps
281
+ interface StreamOps
282
+ ```
283
+
284
+ ## Development
285
+
286
+ Build the dev package:
287
+
288
+ ```bash
289
+ yarn build:dev
290
+ ```
291
+
292
+ This package has no internal `@mono-labs` dependencies -- it is independent in the dependency graph.
293
+
294
+ See the [Contributing guide](../../CONTRIBUTING.md) for full development setup.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mono-labs/dev",
3
- "version": "0.1.256",
3
+ "version": "0.1.257",
4
4
  "type": "commonjs",
5
5
  "description": "Local development server and WebSocket adapter for mono-labs",
6
6
  "main": "dist/index.js",