@venturekit/runtime 0.0.0-dev.20260312012510 → 0.0.0-dev.20260329102450
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/config/env.d.ts +62 -0
- package/dist/config/env.d.ts.map +1 -0
- package/dist/config/env.js +98 -0
- package/dist/config/env.js.map +1 -0
- package/dist/config/secrets.d.ts +67 -0
- package/dist/config/secrets.d.ts.map +1 -0
- package/dist/config/secrets.js +132 -0
- package/dist/config/secrets.js.map +1 -0
- package/dist/handler/adapter.d.ts.map +1 -0
- package/dist/{adapter.js → handler/adapter.js} +5 -44
- package/dist/handler/adapter.js.map +1 -0
- package/dist/{context.d.ts → handler/context.d.ts} +44 -3
- package/dist/handler/context.d.ts.map +1 -0
- package/dist/{context.js → handler/context.js} +9 -9
- package/dist/handler/context.js.map +1 -0
- package/dist/handler/errors.d.ts.map +1 -0
- package/dist/{errors.js → handler/errors.js} +11 -25
- package/dist/handler/errors.js.map +1 -0
- package/dist/{handler.d.ts → handler/handler.d.ts} +36 -3
- package/dist/handler/handler.d.ts.map +1 -0
- package/dist/{handler.js → handler/handler.js} +59 -23
- package/dist/handler/handler.js.map +1 -0
- package/dist/handler/response.d.ts.map +1 -0
- package/dist/{response.js → handler/response.js} +9 -17
- package/dist/handler/response.js.map +1 -0
- package/dist/handler/task-handler.d.ts +192 -0
- package/dist/handler/task-handler.d.ts.map +1 -0
- package/dist/handler/task-handler.js +262 -0
- package/dist/handler/task-handler.js.map +1 -0
- package/dist/index.d.ts +33 -14
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +30 -48
- package/dist/index.js.map +1 -1
- package/dist/{logger.d.ts → logging/logger.d.ts} +1 -1
- package/dist/logging/logger.d.ts.map +1 -0
- package/dist/{logger.js → logging/logger.js} +8 -16
- package/dist/logging/logger.js.map +1 -0
- package/dist/logging/tracing.d.ts +51 -0
- package/dist/logging/tracing.d.ts.map +1 -0
- package/dist/logging/tracing.js +77 -0
- package/dist/logging/tracing.js.map +1 -0
- package/dist/middleware/middleware.d.ts +120 -0
- package/dist/middleware/middleware.d.ts.map +1 -0
- package/dist/middleware/middleware.js +270 -0
- package/dist/middleware/middleware.js.map +1 -0
- package/dist/middleware/rate-limit-store.d.ts +153 -0
- package/dist/middleware/rate-limit-store.d.ts.map +1 -0
- package/dist/middleware/rate-limit-store.js +182 -0
- package/dist/middleware/rate-limit-store.js.map +1 -0
- package/dist/openapi/openapi.d.ts +147 -0
- package/dist/openapi/openapi.d.ts.map +1 -0
- package/dist/openapi/openapi.js +235 -0
- package/dist/openapi/openapi.js.map +1 -0
- package/dist/openapi.d.ts +10 -0
- package/dist/openapi.d.ts.map +1 -0
- package/dist/openapi.js +9 -0
- package/dist/openapi.js.map +1 -0
- package/dist/patterns/circuit-breaker.d.ts +78 -0
- package/dist/patterns/circuit-breaker.d.ts.map +1 -0
- package/dist/patterns/circuit-breaker.js +113 -0
- package/dist/patterns/circuit-breaker.js.map +1 -0
- package/dist/patterns/idempotency.d.ts +175 -0
- package/dist/patterns/idempotency.d.ts.map +1 -0
- package/dist/patterns/idempotency.js +305 -0
- package/dist/patterns/idempotency.js.map +1 -0
- package/dist/patterns/invoke.d.ts +279 -0
- package/dist/patterns/invoke.d.ts.map +1 -0
- package/dist/patterns/invoke.js +581 -0
- package/dist/patterns/invoke.js.map +1 -0
- package/dist/patterns/saga.d.ts +391 -0
- package/dist/patterns/saga.d.ts.map +1 -0
- package/dist/patterns/saga.js +556 -0
- package/dist/patterns/saga.js.map +1 -0
- package/dist/patterns.d.ts +17 -0
- package/dist/patterns.d.ts.map +1 -0
- package/dist/patterns.js +17 -0
- package/dist/patterns.js.map +1 -0
- package/dist/rate-limit.d.ts +16 -0
- package/dist/rate-limit.d.ts.map +1 -0
- package/dist/rate-limit.js +15 -0
- package/dist/rate-limit.js.map +1 -0
- package/dist/secrets.d.ts +11 -0
- package/dist/secrets.d.ts.map +1 -0
- package/dist/secrets.js +10 -0
- package/dist/secrets.js.map +1 -0
- package/dist/testing/test-utils.d.ts +99 -0
- package/dist/testing/test-utils.d.ts.map +1 -0
- package/dist/testing/test-utils.js +153 -0
- package/dist/testing/test-utils.js.map +1 -0
- package/dist/testing.d.ts +10 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +9 -0
- package/dist/testing.js.map +1 -0
- package/dist/ws/ws.d.ts +139 -0
- package/dist/ws/ws.d.ts.map +1 -0
- package/dist/ws/ws.js +294 -0
- package/dist/ws/ws.js.map +1 -0
- package/dist/ws.d.ts +5 -133
- package/dist/ws.d.ts.map +1 -1
- package/dist/ws.js +4 -291
- package/dist/ws.js.map +1 -1
- package/package.json +47 -5
- package/dist/adapter.d.ts.map +0 -1
- package/dist/adapter.js.map +0 -1
- package/dist/context.d.ts.map +0 -1
- package/dist/context.js.map +0 -1
- package/dist/errors.d.ts.map +0 -1
- package/dist/errors.js.map +0 -1
- package/dist/handler.d.ts.map +0 -1
- package/dist/handler.js.map +0 -1
- package/dist/logger.d.ts.map +0 -1
- package/dist/logger.js.map +0 -1
- package/dist/middleware.d.ts +0 -47
- package/dist/middleware.d.ts.map +0 -1
- package/dist/middleware.js +0 -147
- package/dist/middleware.js.map +0 -1
- package/dist/response.d.ts.map +0 -1
- package/dist/response.js.map +0 -1
- /package/dist/{adapter.d.ts → handler/adapter.d.ts} +0 -0
- /package/dist/{errors.d.ts → handler/errors.d.ts} +0 -0
- /package/dist/{response.d.ts → handler/response.d.ts} +0 -0
package/dist/ws/ws.js
ADDED
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WebSocket Connection Store
|
|
3
|
+
*
|
|
4
|
+
* DynamoDB-backed connection management for API Gateway WebSocket APIs.
|
|
5
|
+
*
|
|
6
|
+
* Authentication:
|
|
7
|
+
* Two-phase auth — the client connects without credentials, then sends
|
|
8
|
+
* an { action: "auth", token: "<jwt>" } message over the encrypted channel.
|
|
9
|
+
* The connection is stored as unauthenticated on $connect and upgraded
|
|
10
|
+
* to authenticated via connectionStore.authenticate().
|
|
11
|
+
* This avoids leaking JWTs in query strings, server logs, and access logs.
|
|
12
|
+
*
|
|
13
|
+
* Multi-tenancy:
|
|
14
|
+
* If enabled, each connection stores a tenantId alongside userId.
|
|
15
|
+
* Use sendToTenant() to broadcast within a tenant boundary.
|
|
16
|
+
*
|
|
17
|
+
* API:
|
|
18
|
+
* connectionStore.save(connectionId) — $connect (unauthenticated)
|
|
19
|
+
* connectionStore.authenticate(connectionId, metadata) — after JWT verification
|
|
20
|
+
* connectionStore.remove(connectionId) — $disconnect
|
|
21
|
+
* connectionStore.get(connectionId) — single connection
|
|
22
|
+
* connectionStore.getAll() — all connections
|
|
23
|
+
* connectionStore.getByUser(userId) — by user (GSI)
|
|
24
|
+
* connectionStore.getByTenant(tenantId) — by tenant (GSI)
|
|
25
|
+
* connectionStore.postToConnection(domain, stage, id, data) — send to one
|
|
26
|
+
* connectionStore.sendToUser(domain, stage, userId, data) — send to all user sessions
|
|
27
|
+
* connectionStore.sendToTenant(domain, stage, tenantId, data) — broadcast within tenant
|
|
28
|
+
* connectionStore.broadcast(domain, stage, data) — broadcast to all
|
|
29
|
+
*
|
|
30
|
+
* Environment variables:
|
|
31
|
+
* CONNECTIONS_TABLE — DynamoDB table name (required)
|
|
32
|
+
*
|
|
33
|
+
* DynamoDB table schema:
|
|
34
|
+
* Partition key: connectionId (String)
|
|
35
|
+
* TTL attribute: ttl
|
|
36
|
+
* GSI: userId-index (partition key: userId)
|
|
37
|
+
* GSI: tenantId-index (partition key: tenantId) — only if multi-tenancy enabled
|
|
38
|
+
*/
|
|
39
|
+
const DEFAULT_TTL_SECONDS = 7200; // 2 hours
|
|
40
|
+
const AUTH_GRACE_PERIOD_SECONDS = 30; // unauthenticated connections expire quickly
|
|
41
|
+
function getTableName() {
|
|
42
|
+
const table = process.env.CONNECTIONS_TABLE;
|
|
43
|
+
if (!table) {
|
|
44
|
+
throw new Error('CONNECTIONS_TABLE environment variable is not set');
|
|
45
|
+
}
|
|
46
|
+
return table;
|
|
47
|
+
}
|
|
48
|
+
let _ddb = null;
|
|
49
|
+
async function ddb() {
|
|
50
|
+
if (!_ddb) {
|
|
51
|
+
const { DynamoDBClient } = await import('@aws-sdk/client-dynamodb');
|
|
52
|
+
const { DynamoDBDocumentClient } = await import('@aws-sdk/lib-dynamodb');
|
|
53
|
+
_ddb = DynamoDBDocumentClient.from(new DynamoDBClient({}));
|
|
54
|
+
}
|
|
55
|
+
return _ddb;
|
|
56
|
+
}
|
|
57
|
+
async function getDocCommands() {
|
|
58
|
+
return await import('@aws-sdk/lib-dynamodb');
|
|
59
|
+
}
|
|
60
|
+
async function getApigwClient() {
|
|
61
|
+
return await import('@aws-sdk/client-apigatewaymanagementapi');
|
|
62
|
+
}
|
|
63
|
+
function encode(data) {
|
|
64
|
+
return new TextEncoder().encode(JSON.stringify(data));
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Deliver data to a list of connections in parallel.
|
|
68
|
+
* Removes stale connections automatically.
|
|
69
|
+
* Returns count of successful deliveries.
|
|
70
|
+
*/
|
|
71
|
+
async function deliverToMany(store, domainName, stage, connections, data) {
|
|
72
|
+
let delivered = 0;
|
|
73
|
+
const results = await Promise.allSettled(connections
|
|
74
|
+
.filter((c) => c.authenticated)
|
|
75
|
+
.map((c) => store.postToConnection(domainName, stage, c.connectionId, data)));
|
|
76
|
+
for (const result of results) {
|
|
77
|
+
if (result.status === 'fulfilled' && result.value)
|
|
78
|
+
delivered++;
|
|
79
|
+
}
|
|
80
|
+
return delivered;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* WebSocket connection store backed by DynamoDB.
|
|
84
|
+
*
|
|
85
|
+
* Two-phase authentication flow:
|
|
86
|
+
*
|
|
87
|
+
* 1. $connect handler:
|
|
88
|
+
* ```typescript
|
|
89
|
+
* await connectionStore.save(connectionId);
|
|
90
|
+
* // Connection is unauthenticated, TTL = 30s
|
|
91
|
+
* ```
|
|
92
|
+
*
|
|
93
|
+
* 2. Client sends auth message over the encrypted WebSocket:
|
|
94
|
+
* ```json
|
|
95
|
+
* { "action": "auth", "token": "<jwt>" }
|
|
96
|
+
* ```
|
|
97
|
+
*
|
|
98
|
+
* 3. $default handler verifies JWT, then:
|
|
99
|
+
* ```typescript
|
|
100
|
+
* await connectionStore.authenticate(connectionId, { userId, tenantId, email });
|
|
101
|
+
* // Connection is now authenticated, TTL extended to 2h
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
export const connectionStore = {
|
|
105
|
+
/**
|
|
106
|
+
* Save a new connection on $connect.
|
|
107
|
+
* The connection is unauthenticated with a short TTL.
|
|
108
|
+
* The client must send an auth message to upgrade.
|
|
109
|
+
*/
|
|
110
|
+
async save(connectionId) {
|
|
111
|
+
const now = Date.now();
|
|
112
|
+
const client = await ddb();
|
|
113
|
+
const { PutCommand } = await getDocCommands();
|
|
114
|
+
await client.send(new PutCommand({
|
|
115
|
+
TableName: getTableName(),
|
|
116
|
+
Item: {
|
|
117
|
+
connectionId,
|
|
118
|
+
authenticated: false,
|
|
119
|
+
connectedAt: now,
|
|
120
|
+
ttl: Math.floor(now / 1000) + AUTH_GRACE_PERIOD_SECONDS,
|
|
121
|
+
},
|
|
122
|
+
}));
|
|
123
|
+
},
|
|
124
|
+
/**
|
|
125
|
+
* Authenticate a connection after JWT verification.
|
|
126
|
+
* Updates the record with user metadata and extends the TTL.
|
|
127
|
+
*
|
|
128
|
+
* @param connectionId — the connection to authenticate
|
|
129
|
+
* @param metadata — must include userId, optionally tenantId, email, etc.
|
|
130
|
+
*/
|
|
131
|
+
async authenticate(connectionId, metadata) {
|
|
132
|
+
const now = Date.now();
|
|
133
|
+
const { userId, tenantId, email, ...extra } = metadata;
|
|
134
|
+
const updateParts = [
|
|
135
|
+
'authenticated = :auth',
|
|
136
|
+
'userId = :uid',
|
|
137
|
+
'authenticatedAt = :authAt',
|
|
138
|
+
'ttl = :ttl',
|
|
139
|
+
];
|
|
140
|
+
const values = {
|
|
141
|
+
':auth': true,
|
|
142
|
+
':uid': userId,
|
|
143
|
+
':authAt': now,
|
|
144
|
+
':ttl': Math.floor(now / 1000) + DEFAULT_TTL_SECONDS,
|
|
145
|
+
};
|
|
146
|
+
if (tenantId) {
|
|
147
|
+
updateParts.push('tenantId = :tid');
|
|
148
|
+
values[':tid'] = tenantId;
|
|
149
|
+
}
|
|
150
|
+
if (email) {
|
|
151
|
+
updateParts.push('email = :email');
|
|
152
|
+
values[':email'] = email;
|
|
153
|
+
}
|
|
154
|
+
// Store any extra metadata fields
|
|
155
|
+
let extraIdx = 0;
|
|
156
|
+
for (const [key, val] of Object.entries(extra)) {
|
|
157
|
+
const placeholder = `:extra${extraIdx}`;
|
|
158
|
+
updateParts.push(`#extra${extraIdx} = ${placeholder}`);
|
|
159
|
+
values[placeholder] = val;
|
|
160
|
+
extraIdx++;
|
|
161
|
+
}
|
|
162
|
+
const names = {};
|
|
163
|
+
for (let i = 0; i < extraIdx; i++) {
|
|
164
|
+
names[`#extra${i}`] = Object.keys(extra)[i];
|
|
165
|
+
}
|
|
166
|
+
const client = await ddb();
|
|
167
|
+
const { UpdateCommand } = await getDocCommands();
|
|
168
|
+
await client.send(new UpdateCommand({
|
|
169
|
+
TableName: getTableName(),
|
|
170
|
+
Key: { connectionId },
|
|
171
|
+
UpdateExpression: `SET ${updateParts.join(', ')}`,
|
|
172
|
+
ExpressionAttributeValues: values,
|
|
173
|
+
...(extraIdx > 0 ? { ExpressionAttributeNames: names } : {}),
|
|
174
|
+
}));
|
|
175
|
+
},
|
|
176
|
+
/**
|
|
177
|
+
* Remove a connection on $disconnect.
|
|
178
|
+
*/
|
|
179
|
+
async remove(connectionId) {
|
|
180
|
+
const client = await ddb();
|
|
181
|
+
const { DeleteCommand } = await getDocCommands();
|
|
182
|
+
await client.send(new DeleteCommand({
|
|
183
|
+
TableName: getTableName(),
|
|
184
|
+
Key: { connectionId },
|
|
185
|
+
}));
|
|
186
|
+
},
|
|
187
|
+
/**
|
|
188
|
+
* Get a single connection record.
|
|
189
|
+
*/
|
|
190
|
+
async get(connectionId) {
|
|
191
|
+
const client = await ddb();
|
|
192
|
+
const { GetCommand } = await getDocCommands();
|
|
193
|
+
const result = await client.send(new GetCommand({
|
|
194
|
+
TableName: getTableName(),
|
|
195
|
+
Key: { connectionId },
|
|
196
|
+
}));
|
|
197
|
+
return result.Item ?? null;
|
|
198
|
+
},
|
|
199
|
+
/**
|
|
200
|
+
* Get all authenticated connections.
|
|
201
|
+
* For large-scale apps, prefer getByUser() or getByTenant().
|
|
202
|
+
*/
|
|
203
|
+
async getAll() {
|
|
204
|
+
const client = await ddb();
|
|
205
|
+
const { ScanCommand } = await getDocCommands();
|
|
206
|
+
const result = await client.send(new ScanCommand({
|
|
207
|
+
TableName: getTableName(),
|
|
208
|
+
FilterExpression: 'authenticated = :auth',
|
|
209
|
+
ExpressionAttributeValues: { ':auth': true },
|
|
210
|
+
}));
|
|
211
|
+
return (result.Items ?? []);
|
|
212
|
+
},
|
|
213
|
+
/**
|
|
214
|
+
* Get all connections for a specific user.
|
|
215
|
+
* A user can have multiple active connections (e.g. multiple tabs/devices).
|
|
216
|
+
* Requires GSI: userId-index (partition key: userId).
|
|
217
|
+
*/
|
|
218
|
+
async getByUser(userId) {
|
|
219
|
+
const client = await ddb();
|
|
220
|
+
const { QueryCommand } = await getDocCommands();
|
|
221
|
+
const result = await client.send(new QueryCommand({
|
|
222
|
+
TableName: getTableName(),
|
|
223
|
+
IndexName: 'userId-index',
|
|
224
|
+
KeyConditionExpression: 'userId = :uid',
|
|
225
|
+
ExpressionAttributeValues: { ':uid': userId },
|
|
226
|
+
}));
|
|
227
|
+
return (result.Items ?? []);
|
|
228
|
+
},
|
|
229
|
+
/**
|
|
230
|
+
* Get all connections for a specific tenant.
|
|
231
|
+
* Requires GSI: tenantId-index (partition key: tenantId).
|
|
232
|
+
*/
|
|
233
|
+
async getByTenant(tenantId) {
|
|
234
|
+
const client = await ddb();
|
|
235
|
+
const { QueryCommand } = await getDocCommands();
|
|
236
|
+
const result = await client.send(new QueryCommand({
|
|
237
|
+
TableName: getTableName(),
|
|
238
|
+
IndexName: 'tenantId-index',
|
|
239
|
+
KeyConditionExpression: 'tenantId = :tid',
|
|
240
|
+
ExpressionAttributeValues: { ':tid': tenantId },
|
|
241
|
+
}));
|
|
242
|
+
return (result.Items ?? []);
|
|
243
|
+
},
|
|
244
|
+
/**
|
|
245
|
+
* Send data to a specific connection via API Gateway Management API.
|
|
246
|
+
* Automatically cleans up stale connections (GoneException).
|
|
247
|
+
* Returns true if sent, false if the connection was stale and removed.
|
|
248
|
+
*/
|
|
249
|
+
async postToConnection(domainName, stage, connectionId, data) {
|
|
250
|
+
const { ApiGatewayManagementApiClient, PostToConnectionCommand, GoneException } = await getApigwClient();
|
|
251
|
+
const apigw = new ApiGatewayManagementApiClient({
|
|
252
|
+
endpoint: `https://${domainName}/${stage}`,
|
|
253
|
+
});
|
|
254
|
+
try {
|
|
255
|
+
await apigw.send(new PostToConnectionCommand({
|
|
256
|
+
ConnectionId: connectionId,
|
|
257
|
+
Data: encode(data),
|
|
258
|
+
}));
|
|
259
|
+
return true;
|
|
260
|
+
}
|
|
261
|
+
catch (err) {
|
|
262
|
+
if (err instanceof GoneException) {
|
|
263
|
+
await this.remove(connectionId);
|
|
264
|
+
return false;
|
|
265
|
+
}
|
|
266
|
+
throw err;
|
|
267
|
+
}
|
|
268
|
+
},
|
|
269
|
+
/**
|
|
270
|
+
* Send data to all connections belonging to a specific user.
|
|
271
|
+
* Returns the number of connections that received the message.
|
|
272
|
+
*/
|
|
273
|
+
async sendToUser(domainName, stage, userId, data) {
|
|
274
|
+
const connections = await this.getByUser(userId);
|
|
275
|
+
return deliverToMany(this, domainName, stage, connections, data);
|
|
276
|
+
},
|
|
277
|
+
/**
|
|
278
|
+
* Send data to all connections within a specific tenant.
|
|
279
|
+
* Returns the number of connections that received the message.
|
|
280
|
+
*/
|
|
281
|
+
async sendToTenant(domainName, stage, tenantId, data) {
|
|
282
|
+
const connections = await this.getByTenant(tenantId);
|
|
283
|
+
return deliverToMany(this, domainName, stage, connections, data);
|
|
284
|
+
},
|
|
285
|
+
/**
|
|
286
|
+
* Broadcast data to all authenticated connections.
|
|
287
|
+
* Returns the number of clients that received the message.
|
|
288
|
+
*/
|
|
289
|
+
async broadcast(domainName, stage, data) {
|
|
290
|
+
const connections = await this.getAll();
|
|
291
|
+
return deliverToMany(this, domainName, stage, connections, data);
|
|
292
|
+
},
|
|
293
|
+
};
|
|
294
|
+
//# sourceMappingURL=ws.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ws.js","sourceRoot":"","sources":["../../src/ws/ws.ts"],"names":[],"mappings":"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"}
|
package/dist/ws.d.ts
CHANGED
|
@@ -1,139 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* @venturekit/runtime/ws
|
|
3
3
|
*
|
|
4
|
-
* DynamoDB-backed connection management for API Gateway WebSocket APIs.
|
|
5
|
-
*
|
|
6
|
-
* Authentication:
|
|
7
|
-
* Two-phase auth — the client connects without credentials, then sends
|
|
8
|
-
* an { action: "auth", token: "<jwt>" } message over the encrypted channel.
|
|
9
|
-
* The connection is stored as unauthenticated on $connect and upgraded
|
|
10
|
-
* to authenticated via connectionStore.authenticate().
|
|
11
|
-
* This avoids leaking JWTs in query strings, server logs, and access logs.
|
|
12
|
-
*
|
|
13
|
-
* Multi-tenancy:
|
|
14
|
-
* If enabled, each connection stores a tenantId alongside userId.
|
|
15
|
-
* Use sendToTenant() to broadcast within a tenant boundary.
|
|
16
|
-
*
|
|
17
|
-
* API:
|
|
18
|
-
* connectionStore.save(connectionId) — $connect (unauthenticated)
|
|
19
|
-
* connectionStore.authenticate(connectionId, metadata) — after JWT verification
|
|
20
|
-
* connectionStore.remove(connectionId) — $disconnect
|
|
21
|
-
* connectionStore.get(connectionId) — single connection
|
|
22
|
-
* connectionStore.getAll() — all connections
|
|
23
|
-
* connectionStore.getByUser(userId) — by user (GSI)
|
|
24
|
-
* connectionStore.getByTenant(tenantId) — by tenant (GSI)
|
|
25
|
-
* connectionStore.postToConnection(domain, stage, id, data) — send to one
|
|
26
|
-
* connectionStore.sendToUser(domain, stage, userId, data) — send to all user sessions
|
|
27
|
-
* connectionStore.sendToTenant(domain, stage, tenantId, data) — broadcast within tenant
|
|
28
|
-
* connectionStore.broadcast(domain, stage, data) — broadcast to all
|
|
29
|
-
*
|
|
30
|
-
* Environment variables:
|
|
31
|
-
* CONNECTIONS_TABLE — DynamoDB table name (required)
|
|
32
|
-
*
|
|
33
|
-
* DynamoDB table schema:
|
|
34
|
-
* Partition key: connectionId (String)
|
|
35
|
-
* TTL attribute: ttl
|
|
36
|
-
* GSI: userId-index (partition key: userId)
|
|
37
|
-
* GSI: tenantId-index (partition key: tenantId) — only if multi-tenancy enabled
|
|
38
|
-
*/
|
|
39
|
-
export interface ConnectionRecord {
|
|
40
|
-
connectionId: string;
|
|
41
|
-
authenticated: boolean;
|
|
42
|
-
userId?: string;
|
|
43
|
-
tenantId?: string;
|
|
44
|
-
email?: string;
|
|
45
|
-
connectedAt: number;
|
|
46
|
-
authenticatedAt?: number;
|
|
47
|
-
ttl: number;
|
|
48
|
-
[key: string]: unknown;
|
|
49
|
-
}
|
|
50
|
-
export interface ConnectionMetadata {
|
|
51
|
-
userId: string;
|
|
52
|
-
tenantId?: string;
|
|
53
|
-
email?: string;
|
|
54
|
-
[key: string]: unknown;
|
|
55
|
-
}
|
|
56
|
-
/**
|
|
57
4
|
* WebSocket connection store backed by DynamoDB.
|
|
5
|
+
* Requires @aws-sdk/client-dynamodb, @aws-sdk/lib-dynamodb, @aws-sdk/client-apigatewaymanagementapi.
|
|
58
6
|
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
* 1. $connect handler:
|
|
62
|
-
* ```typescript
|
|
63
|
-
* await connectionStore.save(connectionId);
|
|
64
|
-
* // Connection is unauthenticated, TTL = 30s
|
|
65
|
-
* ```
|
|
66
|
-
*
|
|
67
|
-
* 2. Client sends auth message over the encrypted WebSocket:
|
|
68
|
-
* ```json
|
|
69
|
-
* { "action": "auth", "token": "<jwt>" }
|
|
70
|
-
* ```
|
|
71
|
-
*
|
|
72
|
-
* 3. $default handler verifies JWT, then:
|
|
73
|
-
* ```typescript
|
|
74
|
-
* await connectionStore.authenticate(connectionId, { userId, tenantId, email });
|
|
75
|
-
* // Connection is now authenticated, TTL extended to 2h
|
|
76
|
-
* ```
|
|
7
|
+
* Import via: `import { connectionStore } from '@venturekit/runtime/ws'`
|
|
77
8
|
*/
|
|
78
|
-
export
|
|
79
|
-
|
|
80
|
-
* Save a new connection on $connect.
|
|
81
|
-
* The connection is unauthenticated with a short TTL.
|
|
82
|
-
* The client must send an auth message to upgrade.
|
|
83
|
-
*/
|
|
84
|
-
save(connectionId: string): Promise<void>;
|
|
85
|
-
/**
|
|
86
|
-
* Authenticate a connection after JWT verification.
|
|
87
|
-
* Updates the record with user metadata and extends the TTL.
|
|
88
|
-
*
|
|
89
|
-
* @param connectionId — the connection to authenticate
|
|
90
|
-
* @param metadata — must include userId, optionally tenantId, email, etc.
|
|
91
|
-
*/
|
|
92
|
-
authenticate(connectionId: string, metadata: ConnectionMetadata): Promise<void>;
|
|
93
|
-
/**
|
|
94
|
-
* Remove a connection on $disconnect.
|
|
95
|
-
*/
|
|
96
|
-
remove(connectionId: string): Promise<void>;
|
|
97
|
-
/**
|
|
98
|
-
* Get a single connection record.
|
|
99
|
-
*/
|
|
100
|
-
get(connectionId: string): Promise<ConnectionRecord | null>;
|
|
101
|
-
/**
|
|
102
|
-
* Get all authenticated connections.
|
|
103
|
-
* For large-scale apps, prefer getByUser() or getByTenant().
|
|
104
|
-
*/
|
|
105
|
-
getAll(): Promise<ConnectionRecord[]>;
|
|
106
|
-
/**
|
|
107
|
-
* Get all connections for a specific user.
|
|
108
|
-
* A user can have multiple active connections (e.g. multiple tabs/devices).
|
|
109
|
-
* Requires GSI: userId-index (partition key: userId).
|
|
110
|
-
*/
|
|
111
|
-
getByUser(userId: string): Promise<ConnectionRecord[]>;
|
|
112
|
-
/**
|
|
113
|
-
* Get all connections for a specific tenant.
|
|
114
|
-
* Requires GSI: tenantId-index (partition key: tenantId).
|
|
115
|
-
*/
|
|
116
|
-
getByTenant(tenantId: string): Promise<ConnectionRecord[]>;
|
|
117
|
-
/**
|
|
118
|
-
* Send data to a specific connection via API Gateway Management API.
|
|
119
|
-
* Automatically cleans up stale connections (GoneException).
|
|
120
|
-
* Returns true if sent, false if the connection was stale and removed.
|
|
121
|
-
*/
|
|
122
|
-
postToConnection(domainName: string, stage: string, connectionId: string, data: unknown): Promise<boolean>;
|
|
123
|
-
/**
|
|
124
|
-
* Send data to all connections belonging to a specific user.
|
|
125
|
-
* Returns the number of connections that received the message.
|
|
126
|
-
*/
|
|
127
|
-
sendToUser(domainName: string, stage: string, userId: string, data: unknown): Promise<number>;
|
|
128
|
-
/**
|
|
129
|
-
* Send data to all connections within a specific tenant.
|
|
130
|
-
* Returns the number of connections that received the message.
|
|
131
|
-
*/
|
|
132
|
-
sendToTenant(domainName: string, stage: string, tenantId: string, data: unknown): Promise<number>;
|
|
133
|
-
/**
|
|
134
|
-
* Broadcast data to all authenticated connections.
|
|
135
|
-
* Returns the number of clients that received the message.
|
|
136
|
-
*/
|
|
137
|
-
broadcast(domainName: string, stage: string, data: unknown): Promise<number>;
|
|
138
|
-
};
|
|
9
|
+
export { connectionStore } from './ws/ws.js';
|
|
10
|
+
export type { ConnectionRecord, ConnectionMetadata } from './ws/ws.js';
|
|
139
11
|
//# sourceMappingURL=ws.d.ts.map
|
package/dist/ws.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ws.d.ts","sourceRoot":"","sources":["../src/ws.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"ws.d.ts","sourceRoot":"","sources":["../src/ws.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7C,YAAY,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC"}
|