@lastshotlabs/bunshot 0.0.21 → 0.0.27
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 +3035 -1249
- package/dist/adapters/localStorage.d.ts +6 -0
- package/dist/adapters/localStorage.js +59 -0
- package/dist/adapters/memoryAuth.d.ts +13 -0
- package/dist/adapters/memoryAuth.js +261 -2
- package/dist/adapters/memoryStorage.d.ts +3 -0
- package/dist/adapters/memoryStorage.js +44 -0
- package/dist/adapters/mongoAuth.js +217 -1
- package/dist/adapters/s3Storage.d.ts +14 -0
- package/dist/adapters/s3Storage.js +126 -0
- package/dist/adapters/sqliteAuth.d.ts +30 -0
- package/dist/adapters/sqliteAuth.js +352 -2
- package/dist/app.d.ts +203 -3
- package/dist/app.js +352 -48
- package/dist/cli.js +118 -38
- package/dist/index.d.ts +69 -8
- package/dist/index.js +46 -5
- package/dist/lib/HttpError.d.ts +7 -1
- package/dist/lib/HttpError.js +10 -1
- package/dist/lib/appConfig.d.ts +157 -0
- package/dist/lib/appConfig.js +54 -0
- package/dist/lib/auditLog.d.ts +58 -0
- package/dist/lib/auditLog.js +218 -0
- package/dist/lib/authAdapter.d.ts +140 -1
- package/dist/lib/authRateLimit.js +36 -0
- package/dist/lib/breachedPassword.d.ts +13 -0
- package/dist/lib/breachedPassword.js +48 -0
- package/dist/lib/captcha.d.ts +25 -0
- package/dist/lib/captcha.js +37 -0
- package/dist/lib/constants.d.ts +4 -0
- package/dist/lib/constants.js +4 -0
- package/dist/lib/context.d.ts +24 -1
- package/dist/lib/context.js +17 -3
- package/dist/lib/createRoute.d.ts +28 -2
- package/dist/lib/createRoute.js +54 -3
- package/dist/lib/credentialStuffing.d.ts +31 -0
- package/dist/lib/credentialStuffing.js +77 -0
- package/dist/lib/deletionCancelToken.d.ts +12 -0
- package/dist/lib/deletionCancelToken.js +88 -0
- package/dist/lib/emailVerification.d.ts +6 -0
- package/dist/lib/emailVerification.js +46 -3
- package/dist/lib/groups.d.ts +113 -0
- package/dist/lib/groups.js +133 -0
- package/dist/lib/idempotency.d.ts +22 -0
- package/dist/lib/idempotency.js +182 -0
- package/dist/lib/jwks.d.ts +25 -0
- package/dist/lib/jwks.js +51 -0
- package/dist/lib/jwt.d.ts +15 -2
- package/dist/lib/jwt.js +92 -5
- package/dist/lib/logger.d.ts +2 -0
- package/dist/lib/logger.js +6 -0
- package/dist/lib/m2m.d.ts +29 -0
- package/dist/lib/m2m.js +48 -0
- package/dist/lib/metrics.d.ts +14 -0
- package/dist/lib/metrics.js +158 -0
- package/dist/lib/mfaChallenge.d.ts +14 -1
- package/dist/lib/mfaChallenge.js +111 -6
- package/dist/lib/mongo.js +1 -1
- package/dist/lib/oauthCode.js +23 -18
- package/dist/lib/pagination.d.ts +119 -0
- package/dist/lib/pagination.js +166 -0
- package/dist/lib/resetPassword.js +3 -1
- package/dist/lib/saml.d.ts +25 -0
- package/dist/lib/saml.js +64 -0
- package/dist/lib/scim.d.ts +44 -0
- package/dist/lib/scim.js +54 -0
- package/dist/lib/securityEvents.d.ts +28 -0
- package/dist/lib/securityEvents.js +26 -0
- package/dist/lib/session.d.ts +14 -0
- package/dist/lib/session.js +121 -5
- package/dist/lib/signing.d.ts +52 -0
- package/dist/lib/signing.js +183 -0
- package/dist/lib/storageAdapter.d.ts +30 -0
- package/dist/lib/storageAdapter.js +1 -0
- package/dist/lib/stripUnreferencedSchemas.d.ts +11 -0
- package/dist/lib/stripUnreferencedSchemas.js +79 -0
- package/dist/lib/suspension.d.ts +13 -0
- package/dist/lib/suspension.js +23 -0
- package/dist/lib/tenant.js +2 -2
- package/dist/lib/upload.d.ts +39 -0
- package/dist/lib/upload.js +112 -0
- package/dist/lib/uploadRegistry.d.ts +18 -0
- package/dist/lib/uploadRegistry.js +83 -0
- package/dist/lib/validate.js +2 -2
- package/dist/lib/ws.d.ts +1 -0
- package/dist/lib/ws.js +28 -0
- package/dist/lib/wsHeartbeat.d.ts +12 -0
- package/dist/lib/wsHeartbeat.js +57 -0
- package/dist/lib/wsMessages.d.ts +40 -0
- package/dist/lib/wsMessages.js +330 -0
- package/dist/lib/wsPresence.d.ts +25 -0
- package/dist/lib/wsPresence.js +99 -0
- package/dist/middleware/auditLog.d.ts +22 -0
- package/dist/middleware/auditLog.js +39 -0
- package/dist/middleware/bearerAuth.js +1 -1
- package/dist/middleware/cacheResponse.js +5 -1
- package/dist/middleware/captcha.d.ts +10 -0
- package/dist/middleware/captcha.js +36 -0
- package/dist/middleware/csrf.js +18 -4
- package/dist/middleware/errorHandler.js +4 -1
- package/dist/middleware/identify.js +89 -14
- package/dist/middleware/metrics.d.ts +9 -0
- package/dist/middleware/metrics.js +26 -0
- package/dist/middleware/requestId.d.ts +3 -0
- package/dist/middleware/requestId.js +7 -0
- package/dist/middleware/requestLogger.d.ts +38 -0
- package/dist/middleware/requestLogger.js +68 -0
- package/dist/middleware/requestSigning.d.ts +20 -0
- package/dist/middleware/requestSigning.js +100 -0
- package/dist/middleware/requireMfaSetup.d.ts +16 -0
- package/dist/middleware/requireMfaSetup.js +37 -0
- package/dist/middleware/requireRole.d.ts +9 -3
- package/dist/middleware/requireRole.js +23 -36
- package/dist/middleware/requireScope.d.ts +10 -0
- package/dist/middleware/requireScope.js +25 -0
- package/dist/middleware/requireStepUp.d.ts +18 -0
- package/dist/middleware/requireStepUp.js +29 -0
- package/dist/middleware/scimAuth.d.ts +8 -0
- package/dist/middleware/scimAuth.js +29 -0
- package/dist/middleware/upload.d.ts +5 -0
- package/dist/middleware/upload.js +27 -0
- package/dist/middleware/webhookAuth.d.ts +30 -0
- package/dist/middleware/webhookAuth.js +58 -0
- package/dist/models/AuditLog.d.ts +30 -0
- package/dist/models/AuditLog.js +39 -0
- package/dist/models/AuthUser.d.ts +7 -0
- package/dist/models/AuthUser.js +7 -0
- package/dist/models/Group.d.ts +21 -0
- package/dist/models/Group.js +28 -0
- package/dist/models/GroupMembership.d.ts +21 -0
- package/dist/models/GroupMembership.js +25 -0
- package/dist/models/M2MClient.d.ts +18 -0
- package/dist/models/M2MClient.js +18 -0
- package/dist/routes/auth.d.ts +3 -2
- package/dist/routes/auth.js +238 -21
- package/dist/routes/groups.d.ts +21 -0
- package/dist/routes/groups.js +346 -0
- package/dist/routes/jobs.js +66 -46
- package/dist/routes/m2m.d.ts +2 -0
- package/dist/routes/m2m.js +72 -0
- package/dist/routes/metrics.d.ts +8 -0
- package/dist/routes/metrics.js +55 -0
- package/dist/routes/mfa.js +13 -1
- package/dist/routes/oauth.js +6 -0
- package/dist/routes/oidc.d.ts +2 -0
- package/dist/routes/oidc.js +29 -0
- package/dist/routes/passkey.d.ts +1 -0
- package/dist/routes/passkey.js +157 -0
- package/dist/routes/saml.d.ts +2 -0
- package/dist/routes/saml.js +86 -0
- package/dist/routes/scim.d.ts +2 -0
- package/dist/routes/scim.js +255 -0
- package/dist/routes/uploads.d.ts +14 -0
- package/dist/routes/uploads.js +227 -0
- package/dist/server.d.ts +26 -0
- package/dist/server.js +46 -3
- package/dist/services/auth.d.ts +2 -0
- package/dist/services/auth.js +101 -22
- package/dist/services/mfa.js +2 -2
- package/dist/ws/index.js +5 -1
- package/docs/sections/auth-flow/full.md +203 -47
- package/docs/sections/auth-flow/overview.md +2 -2
- package/docs/sections/auth-security-examples/full.md +388 -0
- package/docs/sections/authentication/full.md +130 -0
- package/docs/sections/authentication/overview.md +5 -0
- package/docs/sections/cli/full.md +13 -1
- package/docs/sections/configuration/full.md +17 -0
- package/docs/sections/configuration/overview.md +1 -0
- package/docs/sections/exports/full.md +34 -3
- package/docs/sections/logging/full.md +83 -0
- package/docs/sections/metrics/full.md +131 -0
- package/docs/sections/oauth/full.md +189 -189
- package/docs/sections/oauth/overview.md +1 -1
- package/docs/sections/pagination/full.md +93 -0
- package/docs/sections/passkey-login/full.md +90 -0
- package/docs/sections/passkey-login/overview.md +1 -0
- package/docs/sections/roles/full.md +224 -135
- package/docs/sections/roles/overview.md +3 -1
- package/docs/sections/signing/full.md +203 -0
- package/docs/sections/uploads/full.md +208 -0
- package/docs/sections/versioning/full.md +85 -0
- package/docs/sections/webhook-auth/full.md +100 -0
- package/docs/sections/websocket/full.md +95 -0
- package/docs/sections/websocket-rooms/full.md +6 -1
- package/package.json +18 -5
|
@@ -81,6 +81,101 @@ await createServer({
|
|
|
81
81
|
|
|
82
82
|
You can supply any subset of `open`, `message`, `close`, `drain` — unset handlers fall back to the defaults.
|
|
83
83
|
|
|
84
|
+
### Heartbeat / Ping-Pong Keepalive
|
|
85
|
+
|
|
86
|
+
Opt-in via `ws.heartbeat`. Detects and closes stale connections via ping/pong:
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
await createServer({
|
|
90
|
+
ws: {
|
|
91
|
+
heartbeat: true, // 30s interval, 10s timeout
|
|
92
|
+
// or fine-tune:
|
|
93
|
+
heartbeat: { intervalMs: 15_000, timeoutMs: 5_000 },
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Every `intervalMs`, the server pings all connected sockets. If a socket hasn't responded within `timeoutMs`, it's closed with code `1001` ("Heartbeat timeout"). `stopHeartbeat()` is called automatically on SIGTERM/SIGINT and is also exported for custom shutdown logic.
|
|
99
|
+
|
|
100
|
+
### Presence Tracking
|
|
101
|
+
|
|
102
|
+
Opt-in via `ws.presence`. Tracks which authenticated users are online in each room (multi-tab aware):
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
await createServer({
|
|
106
|
+
ws: {
|
|
107
|
+
presence: true,
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
When a user's first socket subscribes to a room, all room members receive `{ event: "presence_join", room, userId }`. When the user's last socket leaves (unsubscribe or disconnect), `{ event: "presence_leave", room, userId }` is broadcast.
|
|
113
|
+
|
|
114
|
+
**Query presence:**
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
import { getRoomPresence, getUserPresence } from "@lastshotlabs/bunshot";
|
|
118
|
+
|
|
119
|
+
getRoomPresence("chat:general"); // ["user1", "user2"] — deduplicated userIds
|
|
120
|
+
getUserPresence("user1"); // ["chat:general", "chat:vip"] — rooms where user is present
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Message Persistence
|
|
124
|
+
|
|
125
|
+
Opt-in via `ws.persistence`. Persists messages for late joiners. Rooms must be individually opted in via `configureRoom()`:
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
import { configureRoom, persistMessage, getMessageHistory } from "@lastshotlabs/bunshot";
|
|
129
|
+
|
|
130
|
+
await createServer({
|
|
131
|
+
ws: {
|
|
132
|
+
persistence: {
|
|
133
|
+
store: "memory", // "redis" | "mongo" | "sqlite" | "memory"
|
|
134
|
+
defaults: { maxCount: 100, ttlSeconds: 86_400 }, // 24h
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
// Opt rooms in
|
|
140
|
+
configureRoom("chat:general", { persist: true });
|
|
141
|
+
configureRoom("chat:vip", { persist: true, maxCount: 50, ttlSeconds: 3600 });
|
|
142
|
+
|
|
143
|
+
// Persist from your custom message handler
|
|
144
|
+
await createServer({
|
|
145
|
+
ws: {
|
|
146
|
+
handler: {
|
|
147
|
+
async message(ws, message) {
|
|
148
|
+
const data = JSON.parse(message as string);
|
|
149
|
+
if (data.type === "chat") {
|
|
150
|
+
await persistMessage(data.room, {
|
|
151
|
+
senderId: ws.data.userId,
|
|
152
|
+
payload: data,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// Retrieve history (cursor-based pagination)
|
|
161
|
+
const history = await getMessageHistory("chat:general", { limit: 50 });
|
|
162
|
+
const older = await getMessageHistory("chat:general", { limit: 50, before: history[0].id });
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Messages for non-configured rooms are silently ignored. Store errors are caught and logged (non-blocking) — message delivery is never interrupted by a persistence failure.
|
|
166
|
+
|
|
167
|
+
### Room Name Validation
|
|
168
|
+
|
|
169
|
+
Room names sent by clients via `subscribe` and `unsubscribe` actions are validated before any action is taken. Invalid room names are silently dropped — no error is sent to the client.
|
|
170
|
+
|
|
171
|
+
**Valid room name rules:**
|
|
172
|
+
- Characters: `a–z`, `A–Z`, `0–9`, `_`, `:`, `.`, `/`, `-`
|
|
173
|
+
- Length: 1–128 characters
|
|
174
|
+
|
|
175
|
+
Examples of valid names: `chat:general`, `user:123`, `tenant/room-1`, `notifications`
|
|
176
|
+
|
|
177
|
+
Room names containing characters outside this set (e.g. spaces, `@`, `#`, or Unicode) are silently ignored. This prevents injection of arbitrary pub/sub topic names from untrusted client input.
|
|
178
|
+
|
|
84
179
|
### Overriding the upgrade / auth handler
|
|
85
180
|
|
|
86
181
|
Replace the default cookie-JWT handshake entirely via `ws.upgradeHandler`. You must call `server.upgrade()` yourself and include `rooms: new Set()` in data:
|
|
@@ -13,6 +13,11 @@ Rooms are built on Bun's native pub/sub. `createServer` always intercepts room a
|
|
|
13
13
|
| `getRooms()` | Returns `string[]` of all rooms with at least one active subscriber |
|
|
14
14
|
| `getRoomSubscribers(room)` | Returns `string[]` of socket IDs currently subscribed to `room` |
|
|
15
15
|
| `handleRoomActions(ws, message, onSubscribe?)` | Parses and dispatches subscribe/unsubscribe actions. Returns `true` if the message was a room action (consumed), `false` otherwise. Pass an optional async guard as the third argument. |
|
|
16
|
+
| `getRoomPresence(room)` | Returns deduplicated `string[]` of userIds present in room (requires `ws.presence` enabled) |
|
|
17
|
+
| `getUserPresence(userId)` | Returns `string[]` of rooms where user is present (requires `ws.presence` enabled) |
|
|
18
|
+
| `persistMessage(room, data)` | Store a message for a room (requires `ws.persistence` + `configureRoom`). Returns `StoredMessage \| null` |
|
|
19
|
+
| `getMessageHistory(room, opts?)` | Cursor-based retrieval. `opts`: `{ limit?, before?, after? }` (cursors are message IDs) |
|
|
20
|
+
| `configureRoom(room, opts)` | Opt a room into persistence: `{ persist: true, maxCount?, ttlSeconds? }` |
|
|
16
21
|
|
|
17
22
|
### Client → server: join or leave a room
|
|
18
23
|
|
|
@@ -94,4 +99,4 @@ await createServer({
|
|
|
94
99
|
},
|
|
95
100
|
},
|
|
96
101
|
});
|
|
97
|
-
```
|
|
102
|
+
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lastshotlabs/bunshot",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.27",
|
|
4
4
|
"description": "Batteries-included Bun + Hono API framework — auth, sessions, rate limiting, WebSocket, queues, and OpenAPI docs out of the box",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -52,8 +52,8 @@
|
|
|
52
52
|
"start": "bun src/index.ts",
|
|
53
53
|
"readme": "bun docs/build-readme.ts",
|
|
54
54
|
"readme:npm": "bun docs/build-readme.ts npm",
|
|
55
|
-
"test": "bun test tests/unit tests/integration && bun test tests/isolated",
|
|
56
|
-
"test:isolated": "bun test tests/isolated",
|
|
55
|
+
"test": "bun test tests/unit tests/integration && bun test tests/isolated/optional-deps.test.ts && bun test tests/isolated/jwt-secret.test.ts && bun test tests/isolated/queue.test.ts tests/isolated/jobs-router.test.ts && bun test tests/isolated/queued-deletion.test.ts",
|
|
56
|
+
"test:isolated": "bun test tests/isolated/optional-deps.test.ts && bun test tests/isolated/jwt-secret.test.ts && bun test tests/isolated/queue.test.ts tests/isolated/jobs-router.test.ts && bun test tests/isolated/queued-deletion.test.ts",
|
|
57
57
|
"test:coverage": "bun test --coverage tests/unit tests/integration",
|
|
58
58
|
"test:docker:up": "docker compose -f docker-compose.test.yml up -d --wait",
|
|
59
59
|
"test:docker:down": "docker compose -f docker-compose.test.yml down",
|
|
@@ -66,7 +66,8 @@
|
|
|
66
66
|
"@hono/zod-openapi": "1.2.2",
|
|
67
67
|
"@scalar/hono-api-reference": "0.10.0",
|
|
68
68
|
"arctic": "^3.7.0",
|
|
69
|
-
"jose": "6.2.0"
|
|
69
|
+
"jose": "6.2.0",
|
|
70
|
+
"samlify": "^2.8"
|
|
70
71
|
},
|
|
71
72
|
"peerDependencies": {
|
|
72
73
|
"hono": ">=4.12 <5",
|
|
@@ -75,7 +76,10 @@
|
|
|
75
76
|
"ioredis": ">=5.0 <6",
|
|
76
77
|
"bullmq": ">=5.0 <6",
|
|
77
78
|
"otpauth": ">=9.0 <10",
|
|
78
|
-
"@simplewebauthn/server": ">=10.0.0"
|
|
79
|
+
"@simplewebauthn/server": ">=10.0.0",
|
|
80
|
+
"@aws-sdk/client-s3": ">=3.0",
|
|
81
|
+
"@aws-sdk/s3-request-presigner": ">=3.0",
|
|
82
|
+
"@aws-sdk/lib-storage": ">=3.0"
|
|
79
83
|
},
|
|
80
84
|
"peerDependenciesMeta": {
|
|
81
85
|
"mongoose": {
|
|
@@ -92,6 +96,15 @@
|
|
|
92
96
|
},
|
|
93
97
|
"@simplewebauthn/server": {
|
|
94
98
|
"optional": true
|
|
99
|
+
},
|
|
100
|
+
"@aws-sdk/client-s3": {
|
|
101
|
+
"optional": true
|
|
102
|
+
},
|
|
103
|
+
"@aws-sdk/s3-request-presigner": {
|
|
104
|
+
"optional": true
|
|
105
|
+
},
|
|
106
|
+
"@aws-sdk/lib-storage": {
|
|
107
|
+
"optional": true
|
|
95
108
|
}
|
|
96
109
|
},
|
|
97
110
|
"devDependencies": {
|