@zero-server/sdk 0.9.6 → 0.9.7
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 +44 -44
- package/index.js +116 -4
- package/lib/app.js +22 -22
- package/lib/auth/authorize.js +11 -11
- package/lib/auth/enrollment.js +5 -5
- package/lib/auth/jwt.js +9 -9
- package/lib/auth/oauth.js +1 -1
- package/lib/auth/session.js +5 -5
- package/lib/auth/trustedDevice.js +2 -2
- package/lib/auth/twoFactor.js +11 -11
- package/lib/auth/webauthn.js +6 -6
- package/lib/body/json.js +1 -1
- package/lib/body/raw.js +1 -1
- package/lib/body/rawBuffer.js +1 -1
- package/lib/body/text.js +1 -1
- package/lib/body/urlencoded.js +3 -3
- package/lib/cli.js +19 -4
- package/lib/cluster.js +3 -3
- package/lib/debug.js +10 -10
- package/lib/env/index.js +11 -11
- package/lib/errors.js +131 -16
- package/lib/fetch/index.js +1 -1
- package/lib/grpc/call.js +14 -14
- package/lib/grpc/client.js +4 -4
- package/lib/grpc/codec.js +7 -7
- package/lib/grpc/credentials.js +2 -2
- package/lib/grpc/frame.js +2 -2
- package/lib/grpc/health.js +3 -3
- package/lib/grpc/index.js +3 -3
- package/lib/grpc/metadata.js +3 -3
- package/lib/grpc/proto.js +5 -5
- package/lib/grpc/reflection.js +2 -2
- package/lib/grpc/server.js +3 -3
- package/lib/grpc/status.js +2 -2
- package/lib/grpc/watch.js +1 -1
- package/lib/http/request.js +13 -13
- package/lib/http/response.js +2 -2
- package/lib/lifecycle.js +5 -5
- package/lib/middleware/compress.js +4 -4
- package/lib/observe/health.js +1 -1
- package/lib/observe/index.js +1 -1
- package/lib/observe/logger.js +3 -3
- package/lib/observe/metrics.js +4 -4
- package/lib/observe/tracing.js +4 -4
- package/lib/orm/adapters/json.js +1 -1
- package/lib/orm/adapters/memory.js +2 -2
- package/lib/orm/adapters/mongo.js +2 -2
- package/lib/orm/adapters/mysql.js +2 -2
- package/lib/orm/adapters/postgres.js +2 -2
- package/lib/orm/adapters/sqlite.js +3 -3
- package/lib/orm/audit.js +1 -1
- package/lib/orm/index.js +7 -7
- package/lib/orm/migrate.js +1 -1
- package/lib/orm/model.js +15 -15
- package/lib/orm/procedures.js +1 -1
- package/lib/orm/profiler.js +1 -1
- package/lib/orm/query.js +9 -9
- package/lib/orm/schema.js +1 -1
- package/lib/orm/seed/data/person.js +1 -1
- package/lib/orm/seed/fake.js +10 -10
- package/lib/orm/seed/index.js +4 -4
- package/lib/orm/seed/rng.js +1 -1
- package/lib/orm/snapshot.js +2 -2
- package/lib/orm/tenancy.js +6 -6
- package/lib/orm/views.js +1 -1
- package/lib/router/index.js +9 -9
- package/lib/webrtc/bot.js +361 -0
- package/lib/webrtc/cli.js +182 -0
- package/lib/webrtc/cluster.js +350 -0
- package/lib/webrtc/e2ee.js +282 -0
- package/lib/webrtc/ice.js +370 -0
- package/lib/webrtc/index.js +132 -0
- package/lib/webrtc/joinToken.js +116 -0
- package/lib/webrtc/observe.js +229 -0
- package/lib/webrtc/peer.js +116 -0
- package/lib/webrtc/room.js +171 -0
- package/lib/webrtc/sdp.js +508 -0
- package/lib/webrtc/sfu/index.js +201 -0
- package/lib/webrtc/sfu/livekit.js +301 -0
- package/lib/webrtc/sfu/mediasoup.js +317 -0
- package/lib/webrtc/sfu/memory.js +204 -0
- package/lib/webrtc/signaling.js +546 -0
- package/lib/webrtc/stun.js +492 -0
- package/lib/webrtc/turn/codec.js +370 -0
- package/lib/webrtc/turn/credentials.js +141 -0
- package/lib/webrtc/turn/server.js +633 -0
- package/package.json +2 -2
- package/types/body.d.ts +1 -1
- package/types/cli.d.ts +1 -1
- package/types/index.d.ts +16 -4
- package/types/middleware.d.ts +1 -1
- package/types/orm.d.ts +3 -3
- package/types/request.d.ts +3 -3
- package/types/webrtc.d.ts +501 -0
package/README.md
CHANGED
|
@@ -12,14 +12,14 @@
|
|
|
12
12
|
|
|
13
13
|
<p align="center">
|
|
14
14
|
<a href="https://github.com/tonywied17/zero-server/actions"><img src="https://img.shields.io/github/actions/workflow/status/tonywied17/zero-server/ci.yml?branch=main&style=flat-square&logo=githubactions&logoColor=white&label=CI" alt="CI"></a>
|
|
15
|
-
<a href="https://github.com/tonywied17/zero-server/actions"><img src="https://img.shields.io/badge/tests-
|
|
16
|
-
<a href="https://github.com/tonywied17/zero-server"><img src="https://img.shields.io/badge/coverage-
|
|
15
|
+
<a href="https://github.com/tonywied17/zero-server/actions"><img src="https://img.shields.io/badge/tests-7767%20passing-brightgreen?style=flat-square&logo=vitest&logoColor=white" alt="tests"></a>
|
|
16
|
+
<a href="https://github.com/tonywied17/zero-server"><img src="https://img.shields.io/badge/coverage-95.85%25-brightgreen?style=flat-square&logo=vitest&logoColor=white" alt="coverage"></a>
|
|
17
17
|
<a href="https://z-server.dev"><img src="https://img.shields.io/badge/docs-z--server.dev-00d8e0?style=flat-square&logo=readthedocs&logoColor=white" alt="docs"></a>
|
|
18
18
|
<a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/license-MIT-00d8e0?style=flat-square&logo=opensourceinitiative&logoColor=white" alt="MIT"></a>
|
|
19
19
|
<a href="https://nodejs.org"><img src="https://img.shields.io/badge/node-%3E%3D18-brightgreen?style=flat-square&logo=nodedotjs&logoColor=white" alt="node >=18"></a>
|
|
20
20
|
</p>
|
|
21
21
|
|
|
22
|
-
> **Zero-dependency backend framework for Node.js
|
|
22
|
+
> **Zero-dependency backend framework for Node.js - routing, ORM, auth, WebSocket, SSE, observability, and 20+ middleware as one SDK or focused scoped packages.**
|
|
23
23
|
|
|
24
24
|
<p align="center">
|
|
25
25
|
<strong>
|
|
@@ -35,9 +35,9 @@
|
|
|
35
35
|
npm install @zero-server/sdk
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
-
Requires Node.js 18+. No external dependencies
|
|
38
|
+
Requires Node.js 18+. No external dependencies - everything is built on Node.js core APIs.
|
|
39
39
|
|
|
40
|
-
`@zero-server/sdk` is the recommended install
|
|
40
|
+
`@zero-server/sdk` is the recommended install - it re-exports the entire framework from a single import. Use it for new projects, demos, and most production apps.
|
|
41
41
|
|
|
42
42
|
### Or install only what you need (scoped packages)
|
|
43
43
|
|
|
@@ -63,7 +63,7 @@ npm install @zero-server/core @zero-server/body @zero-server/middleware
|
|
|
63
63
|
| `@zero-server/errors` | every typed `HttpError` class plus ORM/framework errors |
|
|
64
64
|
| `@zero-server/cli` | programmatic `CLI` / `runCLI` entry points for `zh` / `zs` |
|
|
65
65
|
|
|
66
|
-
> Each scoped package is fully standalone at runtime
|
|
66
|
+
> Each scoped package is fully standalone at runtime - its own `index.js`, its own bundled lib, its own types. Mix and match freely; versions stay aligned across the `@zero-server/*` release set.
|
|
67
67
|
|
|
68
68
|
---
|
|
69
69
|
|
|
@@ -92,7 +92,7 @@ app.listen(3000, () => console.log('Listening on :3000'))
|
|
|
92
92
|
|
|
93
93
|
### Middleware
|
|
94
94
|
|
|
95
|
-
20+ built-in middleware
|
|
95
|
+
20+ built-in middleware - all zero-dependency:
|
|
96
96
|
|
|
97
97
|
| Middleware | Purpose |
|
|
98
98
|
|---|---|
|
|
@@ -113,14 +113,14 @@ app.listen(3000, () => console.log('Listening on :3000'))
|
|
|
113
113
|
|
|
114
114
|
Full auth stack with no external libraries:
|
|
115
115
|
|
|
116
|
-
- **JWT**
|
|
117
|
-
- **Sessions**
|
|
118
|
-
- **OAuth 2.0**
|
|
119
|
-
- **Authorization**
|
|
116
|
+
- **JWT** - `jwt()` middleware, `jwtSign()`, `jwtVerify()`, `jwtDecode()`, JWKS key sets, access/refresh token pairs
|
|
117
|
+
- **Sessions** - `session()` middleware with in-memory store (pluggable)
|
|
118
|
+
- **OAuth 2.0** - `oauth()` middleware with PKCE, pre-configured providers (Google, GitHub, Microsoft, etc.)
|
|
119
|
+
- **Authorization** - `authorize()` policies, `can()` / `canAny()` permission checks, `gate()` middleware
|
|
120
120
|
|
|
121
121
|
### ORM & Database
|
|
122
122
|
|
|
123
|
-
Full-featured ORM with 7 adapters
|
|
123
|
+
Full-featured ORM with 7 adapters - memory, JSON file, SQLite, MySQL, PostgreSQL, MongoDB, and Redis:
|
|
124
124
|
|
|
125
125
|
```js
|
|
126
126
|
const { Database, Model, TYPES } = require('@zero-server/sdk')
|
|
@@ -142,7 +142,7 @@ await User.create({ name: 'Alice', email: 'alice@example.com' })
|
|
|
142
142
|
const users = await User.find({ name: 'Alice' })
|
|
143
143
|
```
|
|
144
144
|
|
|
145
|
-
**Query builder**
|
|
145
|
+
**Query builder** - `where()`, `select()`, `orderBy()`, `limit()`, `offset()`, `join()`, `groupBy()`, `having()`, `paginate()`, `findOrCreate()`
|
|
146
146
|
|
|
147
147
|
**Advanced ORM features:**
|
|
148
148
|
|
|
@@ -163,12 +163,12 @@ const users = await User.find({ name: 'Alice' })
|
|
|
163
163
|
|
|
164
164
|
### Real-Time
|
|
165
165
|
|
|
166
|
-
- **WebSocket**
|
|
167
|
-
- **Server-Sent Events**
|
|
166
|
+
- **WebSocket** - `app.ws(path, handler)` with RFC 6455, `WebSocketPool` for rooms, broadcasting, and sub-protocols
|
|
167
|
+
- **Server-Sent Events** - `res.sse()` with auto-IDs, named events, and keep-alive
|
|
168
168
|
|
|
169
169
|
### Observability
|
|
170
170
|
|
|
171
|
-
Built-in Prometheus metrics, health checks, distributed tracing, and structured logging
|
|
171
|
+
Built-in Prometheus metrics, health checks, distributed tracing, and structured logging - zero dependencies.
|
|
172
172
|
|
|
173
173
|
```js
|
|
174
174
|
const { createApp, metricsMiddleware } = require('@zero-server/sdk')
|
|
@@ -193,7 +193,7 @@ logins.inc({ provider: 'github' })
|
|
|
193
193
|
app.listen(3000)
|
|
194
194
|
```
|
|
195
195
|
|
|
196
|
-
**Scrape with Prometheus**
|
|
196
|
+
**Scrape with Prometheus** - create a `prometheus.yml`:
|
|
197
197
|
|
|
198
198
|
```yaml
|
|
199
199
|
scrape_configs:
|
|
@@ -209,13 +209,13 @@ docker run -d -p 9090:9090 -v ./prometheus.yml:/etc/prometheus/prometheus.yml pr
|
|
|
209
209
|
```
|
|
210
210
|
|
|
211
211
|
**Also includes:**
|
|
212
|
-
- **Distributed tracing**
|
|
213
|
-
- **Structured logging**
|
|
212
|
+
- **Distributed tracing** - `Tracer` and `Span` with W3C Trace Context (`traceparent` propagation), `instrumentFetch()` for outgoing requests
|
|
213
|
+
- **Structured logging** - `Logger` with levels, JSON output, and namespaced `debug()` logger
|
|
214
214
|
|
|
215
215
|
### Lifecycle & Clustering
|
|
216
216
|
|
|
217
|
-
- **Graceful shutdown**
|
|
218
|
-
- **Clustering**
|
|
217
|
+
- **Graceful shutdown** - signal handlers (SIGTERM/SIGINT), in-flight request draining, automatic WebSocket/SSE/database cleanup
|
|
218
|
+
- **Clustering** - `clusterize()` for multi-worker processes with auto-respawn and exponential backoff
|
|
219
219
|
|
|
220
220
|
### CLI
|
|
221
221
|
|
|
@@ -419,35 +419,35 @@ npm run docs
|
|
|
419
419
|
|
|
420
420
|
```
|
|
421
421
|
lib/
|
|
422
|
-
app.js
|
|
423
|
-
auth/
|
|
424
|
-
body/
|
|
425
|
-
cli.js
|
|
426
|
-
cluster.js
|
|
427
|
-
debug.js
|
|
428
|
-
env/
|
|
429
|
-
errors.js
|
|
430
|
-
fetch/
|
|
431
|
-
grpc/
|
|
422
|
+
app.js - App class (middleware, routing, listen, ws upgrade, lifecycle)
|
|
423
|
+
auth/ - JWT, OAuth 2.0, sessions, MFA (TOTP/WebAuthn), authorization
|
|
424
|
+
body/ - body parsers (json, urlencoded, text, raw, multipart)
|
|
425
|
+
cli.js - CLI runner (migrate, seed, scaffold commands)
|
|
426
|
+
cluster.js - multi-worker clustering with auto-respawn
|
|
427
|
+
debug.js - namespaced debug logger
|
|
428
|
+
env/ - typed .env loader with schema validation
|
|
429
|
+
errors.js - 25+ HttpError / framework / ORM error classes
|
|
430
|
+
fetch/ - HTTP/HTTPS client (mTLS, AbortSignal, retries)
|
|
431
|
+
grpc/ - HTTP/2 gRPC stack: server, client, codec, framing,
|
|
432
432
|
status, metadata, health, reflection, balancer, watch
|
|
433
|
-
http/
|
|
434
|
-
lifecycle.js
|
|
435
|
-
middleware/
|
|
433
|
+
http/ - Request & Response wrappers
|
|
434
|
+
lifecycle.js - graceful shutdown and lifecycle management
|
|
435
|
+
middleware/ - cors, helmet, logger, rateLimit, compress, static, timeout,
|
|
436
436
|
requestId, cookieParser, csrf, validate, errorHandler
|
|
437
|
-
observe/
|
|
438
|
-
orm/
|
|
437
|
+
observe/ - Prometheus metrics, W3C tracing, health checks, structured logging
|
|
438
|
+
orm/ - Database, Model, Query, adapters, migrations, seeds, cache,
|
|
439
439
|
replicas, search, geo, tenancy, audit, views, procedures, plugins
|
|
440
|
-
router/
|
|
441
|
-
sse/
|
|
442
|
-
ws/
|
|
443
|
-
packages/
|
|
440
|
+
router/ - Router with sub-app mounting and pattern matching
|
|
441
|
+
sse/ - SSE stream controller
|
|
442
|
+
ws/ - WebSocket connection, handshake, and room management
|
|
443
|
+
packages/ - generated scoped @zero-server/* re-exports (one dir per scope)
|
|
444
444
|
.tools/
|
|
445
|
-
scope-manifest.js
|
|
445
|
+
scope-manifest.js - single source of truth for scoped packages & their surface
|
|
446
446
|
generate-package-stubs.js
|
|
447
447
|
generate-scope-docs.js
|
|
448
|
-
types/
|
|
449
|
-
website-docs/
|
|
450
|
-
test/
|
|
448
|
+
types/ - full TypeScript definitions
|
|
449
|
+
website-docs/ - live demo server, controllers, and playground UI
|
|
450
|
+
test/ - vitest test suite (7000+ tests, 95%+ coverage)
|
|
451
451
|
```
|
|
452
452
|
|
|
453
453
|
## Testing
|
package/index.js
CHANGED
|
@@ -59,6 +59,23 @@ const {
|
|
|
59
59
|
ChannelCredentials, createRotatingCredentials,
|
|
60
60
|
watchProto,
|
|
61
61
|
} = require('./lib/grpc');
|
|
62
|
+
const {
|
|
63
|
+
createWebRTC, SignalingHub, Room, Peer,
|
|
64
|
+
parseSdp, stringifySdp,
|
|
65
|
+
parseCandidate, stringifyCandidate, filterCandidates,
|
|
66
|
+
isPrivateIp, isLoopbackIp, isLinkLocalIp, isMdnsHostname,
|
|
67
|
+
stunBinding, encodeBindingRequest, decodeMessage,
|
|
68
|
+
encodeXorMappedAddress, decodeXorMappedAddress,
|
|
69
|
+
STUN_MAGIC_COOKIE, STUN_METHOD, STUN_CLASS, STUN_ATTR,
|
|
70
|
+
issueTurnCredentials, TurnServer,
|
|
71
|
+
SfuAdapter, MemorySfuAdapter, MediasoupSfuAdapter, LiveKitSfuAdapter, loadSfuAdapter, signJoinToken, verifyJoinToken,
|
|
72
|
+
spawnBotPeer,
|
|
73
|
+
bindObservability,
|
|
74
|
+
E2eeChannel, attachE2ee, generateE2eeKeyPair, sealKey, openSealedKey,
|
|
75
|
+
useCluster, ClusterCoordinator, MemoryClusterAdapter,
|
|
76
|
+
runWebRTCCommand,
|
|
77
|
+
WebRTCError, SignalingError, IceError, TurnError, SdpError,
|
|
78
|
+
} = require('./lib/webrtc');
|
|
62
79
|
const { version } = require('./package.json');
|
|
63
80
|
|
|
64
81
|
module.exports = {
|
|
@@ -247,12 +264,12 @@ module.exports = {
|
|
|
247
264
|
ClusterManager,
|
|
248
265
|
/** @see module:cluster */
|
|
249
266
|
cluster: clusterize,
|
|
250
|
-
// Observability
|
|
267
|
+
// Observability - Structured Logging
|
|
251
268
|
/** @see module:observe/logger */
|
|
252
269
|
Logger,
|
|
253
270
|
/** @see module:observe/logger */
|
|
254
271
|
structuredLogger,
|
|
255
|
-
// Observability
|
|
272
|
+
// Observability - Metrics
|
|
256
273
|
/** @see module:observe/metrics */
|
|
257
274
|
Counter,
|
|
258
275
|
/** @see module:observe/metrics */
|
|
@@ -269,7 +286,7 @@ module.exports = {
|
|
|
269
286
|
metricsMiddleware,
|
|
270
287
|
/** @see module:observe/metrics */
|
|
271
288
|
metricsEndpoint: metricsEndpointHandler,
|
|
272
|
-
// Observability
|
|
289
|
+
// Observability - Tracing
|
|
273
290
|
/** @see module:observe/tracing */
|
|
274
291
|
Span,
|
|
275
292
|
/** @see module:observe/tracing */
|
|
@@ -282,7 +299,7 @@ module.exports = {
|
|
|
282
299
|
tracingMiddleware,
|
|
283
300
|
/** @see module:observe/tracing */
|
|
284
301
|
instrumentFetch,
|
|
285
|
-
// Observability
|
|
302
|
+
// Observability - Health Checks
|
|
286
303
|
/** @see module:observe/health */
|
|
287
304
|
healthCheck,
|
|
288
305
|
/** @see module:observe/health */
|
|
@@ -409,6 +426,101 @@ module.exports = {
|
|
|
409
426
|
// gRPC: Proto hot-reload
|
|
410
427
|
/** @see module:grpc/watch */
|
|
411
428
|
watchProto,
|
|
429
|
+
// WebRTC
|
|
430
|
+
/** @see module:webrtc */
|
|
431
|
+
createWebRTC,
|
|
432
|
+
/** @see module:webrtc/signaling */
|
|
433
|
+
SignalingHub,
|
|
434
|
+
/** @see module:webrtc/room */
|
|
435
|
+
Room,
|
|
436
|
+
/** @see module:webrtc/peer */
|
|
437
|
+
Peer,
|
|
438
|
+
/** @see module:webrtc/sdp */
|
|
439
|
+
parseSdp,
|
|
440
|
+
/** @see module:webrtc/sdp */
|
|
441
|
+
stringifySdp,
|
|
442
|
+
/** @see module:webrtc/ice */
|
|
443
|
+
parseCandidate,
|
|
444
|
+
/** @see module:webrtc/ice */
|
|
445
|
+
stringifyCandidate,
|
|
446
|
+
/** @see module:webrtc/ice */
|
|
447
|
+
filterCandidates,
|
|
448
|
+
/** @see module:webrtc/ice */
|
|
449
|
+
isPrivateIp,
|
|
450
|
+
/** @see module:webrtc/ice */
|
|
451
|
+
isLoopbackIp,
|
|
452
|
+
/** @see module:webrtc/ice */
|
|
453
|
+
isLinkLocalIp,
|
|
454
|
+
/** @see module:webrtc/ice */
|
|
455
|
+
isMdnsHostname,
|
|
456
|
+
/** @see module:webrtc/stun */
|
|
457
|
+
stunBinding,
|
|
458
|
+
/** @see module:webrtc/stun */
|
|
459
|
+
encodeBindingRequest,
|
|
460
|
+
/** @see module:webrtc/stun */
|
|
461
|
+
decodeMessage,
|
|
462
|
+
/** @see module:webrtc/stun */
|
|
463
|
+
encodeXorMappedAddress,
|
|
464
|
+
/** @see module:webrtc/stun */
|
|
465
|
+
decodeXorMappedAddress,
|
|
466
|
+
/** @see module:webrtc/stun */
|
|
467
|
+
STUN_MAGIC_COOKIE,
|
|
468
|
+
/** @see module:webrtc/stun */
|
|
469
|
+
STUN_METHOD,
|
|
470
|
+
/** @see module:webrtc/stun */
|
|
471
|
+
STUN_CLASS,
|
|
472
|
+
/** @see module:webrtc/stun */
|
|
473
|
+
STUN_ATTR,
|
|
474
|
+
/** @see module:webrtc/turn/credentials */
|
|
475
|
+
issueTurnCredentials,
|
|
476
|
+
/** @see module:webrtc/turn/server */
|
|
477
|
+
TurnServer,
|
|
478
|
+
/** @see module:webrtc/sfu */
|
|
479
|
+
SfuAdapter,
|
|
480
|
+
/** @see module:webrtc/sfu/memory */
|
|
481
|
+
MemorySfuAdapter,
|
|
482
|
+
/** @see module:webrtc/sfu/mediasoup */
|
|
483
|
+
MediasoupSfuAdapter,
|
|
484
|
+
/** @see module:webrtc/sfu/livekit */
|
|
485
|
+
LiveKitSfuAdapter,
|
|
486
|
+
/** @see module:webrtc/sfu */
|
|
487
|
+
loadSfuAdapter,
|
|
488
|
+
/** @see module:webrtc/signaling */
|
|
489
|
+
signJoinToken,
|
|
490
|
+
/** @see module:webrtc/signaling */
|
|
491
|
+
verifyJoinToken,
|
|
492
|
+
/** @see module:webrtc/bot */
|
|
493
|
+
spawnBotPeer,
|
|
494
|
+
/** @see module:webrtc/observe */
|
|
495
|
+
bindObservability,
|
|
496
|
+
/** @see module:webrtc/e2ee */
|
|
497
|
+
E2eeChannel,
|
|
498
|
+
/** @see module:webrtc/e2ee */
|
|
499
|
+
attachE2ee,
|
|
500
|
+
/** @see module:webrtc/e2ee */
|
|
501
|
+
generateE2eeKeyPair,
|
|
502
|
+
/** @see module:webrtc/e2ee */
|
|
503
|
+
sealKey,
|
|
504
|
+
/** @see module:webrtc/e2ee */
|
|
505
|
+
openSealedKey,
|
|
506
|
+
/** @see module:webrtc/cluster */
|
|
507
|
+
useCluster,
|
|
508
|
+
/** @see module:webrtc/cluster */
|
|
509
|
+
ClusterCoordinator,
|
|
510
|
+
/** @see module:webrtc/cluster */
|
|
511
|
+
MemoryClusterAdapter,
|
|
512
|
+
/** @see module:webrtc/cli */
|
|
513
|
+
runWebRTCCommand,
|
|
514
|
+
/** @see module:errors */
|
|
515
|
+
WebRTCError,
|
|
516
|
+
/** @see module:errors */
|
|
517
|
+
SignalingError,
|
|
518
|
+
/** @see module:errors */
|
|
519
|
+
IceError,
|
|
520
|
+
/** @see module:errors */
|
|
521
|
+
TurnError,
|
|
522
|
+
/** @see module:errors */
|
|
523
|
+
SdpError,
|
|
412
524
|
/** Package version */
|
|
413
525
|
version,
|
|
414
526
|
};
|
package/lib/app.js
CHANGED
|
@@ -74,7 +74,7 @@ class App
|
|
|
74
74
|
this._settings = {};
|
|
75
75
|
|
|
76
76
|
/**
|
|
77
|
-
* Application-level locals
|
|
77
|
+
* Application-level locals - persistent across the app lifecycle.
|
|
78
78
|
* Merged into every `req.locals` and `res.locals` on every request.
|
|
79
79
|
* @type {Object<string, *>}
|
|
80
80
|
*/
|
|
@@ -167,10 +167,10 @@ class App
|
|
|
167
167
|
|
|
168
168
|
/**
|
|
169
169
|
* Register middleware or mount a sub-router.
|
|
170
|
-
* - `use(fn)`
|
|
171
|
-
* - `use('/prefix', fn)`
|
|
170
|
+
* - `use(fn)` - global middleware applied to every request.
|
|
171
|
+
* - `use('/prefix', fn)` - path-scoped middleware (strips the prefix
|
|
172
172
|
* before calling `fn` so downstream sees relative paths).
|
|
173
|
-
* - `use('/prefix', router)`
|
|
173
|
+
* - `use('/prefix', router)` - mount a Router sub-app at the given prefix.
|
|
174
174
|
*
|
|
175
175
|
* @param {string|Function} pathOrFn - A path prefix string, or middleware function.
|
|
176
176
|
* @param {Function|Router} [fn] - Middleware function or Router when first arg is a path.
|
|
@@ -254,7 +254,7 @@ class App
|
|
|
254
254
|
*/
|
|
255
255
|
handle(req, res)
|
|
256
256
|
{
|
|
257
|
-
// Skip gRPC requests
|
|
257
|
+
// Skip gRPC requests - already handled via server.on('stream')
|
|
258
258
|
if (this._grpcRegistry && req.headers['content-type']?.startsWith('application/grpc'))
|
|
259
259
|
return;
|
|
260
260
|
|
|
@@ -356,7 +356,7 @@ class App
|
|
|
356
356
|
*/
|
|
357
357
|
listen(port = 3000, opts, cb)
|
|
358
358
|
{
|
|
359
|
-
// Normalise arguments
|
|
359
|
+
// Normalise arguments - allow `listen(port, cb)` without opts
|
|
360
360
|
if (typeof opts === 'function') { cb = opts; opts = undefined; }
|
|
361
361
|
|
|
362
362
|
const isH2 = opts && opts.http2;
|
|
@@ -376,7 +376,7 @@ class App
|
|
|
376
376
|
}
|
|
377
377
|
else if (isH2)
|
|
378
378
|
{
|
|
379
|
-
// HTTP/2 cleartext (h2c)
|
|
379
|
+
// HTTP/2 cleartext (h2c) - no TLS, for internal services
|
|
380
380
|
const h2Opts = opts ? { ...opts } : {};
|
|
381
381
|
delete h2Opts.http2;
|
|
382
382
|
server = http2.createServer(h2Opts, this.handler);
|
|
@@ -402,7 +402,7 @@ class App
|
|
|
402
402
|
{
|
|
403
403
|
if (this._grpcRegistry.handleStream(stream, headers))
|
|
404
404
|
return; // handled as gRPC
|
|
405
|
-
// Otherwise fall through
|
|
405
|
+
// Otherwise fall through - the compat handler fires the normal request event
|
|
406
406
|
});
|
|
407
407
|
}
|
|
408
408
|
|
|
@@ -461,8 +461,8 @@ class App
|
|
|
461
461
|
* Register a lifecycle event listener.
|
|
462
462
|
*
|
|
463
463
|
* Supported events:
|
|
464
|
-
* - `'beforeShutdown'`
|
|
465
|
-
* - `'shutdown'`
|
|
464
|
+
* - `'beforeShutdown'` - fires before shutdown begins (flush caches, finish writes)
|
|
465
|
+
* - `'shutdown'` - fires after shutdown is complete
|
|
466
466
|
*
|
|
467
467
|
* @param {'beforeShutdown'|'shutdown'} event - Lifecycle event name.
|
|
468
468
|
* @param {Function} fn - Async or sync callback.
|
|
@@ -570,7 +570,7 @@ class App
|
|
|
570
570
|
}
|
|
571
571
|
|
|
572
572
|
/**
|
|
573
|
-
* Configure the shutdown timeout
|
|
573
|
+
* Configure the shutdown timeout-the maximum time (ms) to wait for
|
|
574
574
|
* in-flight requests to finish before forcefully terminating them.
|
|
575
575
|
*
|
|
576
576
|
* @param {number} ms - Timeout in milliseconds.
|
|
@@ -730,7 +730,7 @@ class App
|
|
|
730
730
|
* @param {object|Function} [opts] - Options object, or the handler function directly.
|
|
731
731
|
* @param {number} [opts.maxPayload=1048576] - Maximum incoming frame size in bytes (default 1 MB).
|
|
732
732
|
* @param {number} [opts.pingInterval=30000] - Auto-ping interval in ms. Set `0` to disable.
|
|
733
|
-
* @param {Function} [opts.verifyClient] - `(req) => boolean`
|
|
733
|
+
* @param {Function} [opts.verifyClient] - `(req) => boolean` - return false to reject the upgrade.
|
|
734
734
|
* @param {Function} handler - `(ws, req) => void`.
|
|
735
735
|
*
|
|
736
736
|
* @example | Simple
|
|
@@ -815,56 +815,56 @@ class App
|
|
|
815
815
|
route(method, path, ...fns) { const o = this._extractOpts(fns); this.router.add(method, path, fns, o); }
|
|
816
816
|
|
|
817
817
|
/**
|
|
818
|
-
* @see App#route
|
|
818
|
+
* @see App#route - shortcut for GET requests.
|
|
819
819
|
* @param {string} path - Route pattern.
|
|
820
820
|
* @param {...Function} fns - Handler functions.
|
|
821
821
|
* @returns {App} `this` for chaining.
|
|
822
822
|
*/
|
|
823
823
|
get(path, ...fns) { if (arguments.length === 1 && typeof path === 'string' && fns.length === 0) return this.set(path); this.route('GET', path, ...fns); return this; }
|
|
824
824
|
/**
|
|
825
|
-
* @see App#route
|
|
825
|
+
* @see App#route - shortcut for POST requests.
|
|
826
826
|
* @param {string} path - Route pattern.
|
|
827
827
|
* @param {...Function} fns - Handler functions.
|
|
828
828
|
* @returns {App} `this` for chaining.
|
|
829
829
|
*/
|
|
830
830
|
post(path, ...fns) { this.route('POST', path, ...fns); return this; }
|
|
831
831
|
/**
|
|
832
|
-
* @see App#route
|
|
832
|
+
* @see App#route - shortcut for PUT requests.
|
|
833
833
|
* @param {string} path - Route pattern.
|
|
834
834
|
* @param {...Function} fns - Handler functions.
|
|
835
835
|
* @returns {App} `this` for chaining.
|
|
836
836
|
*/
|
|
837
837
|
put(path, ...fns) { this.route('PUT', path, ...fns); return this; }
|
|
838
838
|
/**
|
|
839
|
-
* @see App#route
|
|
839
|
+
* @see App#route - shortcut for DELETE requests.
|
|
840
840
|
* @param {string} path - Route pattern.
|
|
841
841
|
* @param {...Function} fns - Handler functions.
|
|
842
842
|
* @returns {App} `this` for chaining.
|
|
843
843
|
*/
|
|
844
844
|
delete(path, ...fns) { this.route('DELETE', path, ...fns); return this; }
|
|
845
845
|
/**
|
|
846
|
-
* @see App#route
|
|
846
|
+
* @see App#route - shortcut for PATCH requests.
|
|
847
847
|
* @param {string} path - Route pattern.
|
|
848
848
|
* @param {...Function} fns - Handler functions.
|
|
849
849
|
* @returns {App} `this` for chaining.
|
|
850
850
|
*/
|
|
851
851
|
patch(path, ...fns) { this.route('PATCH', path, ...fns); return this; }
|
|
852
852
|
/**
|
|
853
|
-
* @see App#route
|
|
853
|
+
* @see App#route - shortcut for OPTIONS requests.
|
|
854
854
|
* @param {string} path - Route pattern.
|
|
855
855
|
* @param {...Function} fns - Handler functions.
|
|
856
856
|
* @returns {App} `this` for chaining.
|
|
857
857
|
*/
|
|
858
858
|
options(path, ...fns) { this.route('OPTIONS', path, ...fns); return this; }
|
|
859
859
|
/**
|
|
860
|
-
* @see App#route
|
|
860
|
+
* @see App#route - shortcut for HEAD requests.
|
|
861
861
|
* @param {string} path - Route pattern.
|
|
862
862
|
* @param {...Function} fns - Handler functions.
|
|
863
863
|
* @returns {App} `this` for chaining.
|
|
864
864
|
*/
|
|
865
865
|
head(path, ...fns) { this.route('HEAD', path, ...fns); return this; }
|
|
866
866
|
/**
|
|
867
|
-
* @see App#route
|
|
867
|
+
* @see App#route - matches every HTTP method.
|
|
868
868
|
* @param {string} path - Route pattern.
|
|
869
869
|
* @param {...Function} fns - Handler functions.
|
|
870
870
|
* @returns {App} `this` for chaining.
|
|
@@ -872,7 +872,7 @@ class App
|
|
|
872
872
|
all(path, ...fns) { this.route('ALL', path, ...fns); return this; }
|
|
873
873
|
|
|
874
874
|
/**
|
|
875
|
-
* Chainable route builder
|
|
875
|
+
* Chainable route builder - register multiple methods on the same path.
|
|
876
876
|
*
|
|
877
877
|
* @param {string} path - Route pattern.
|
|
878
878
|
* @returns {object} Chain object with HTTP verb methods.
|
|
@@ -1149,7 +1149,7 @@ class App
|
|
|
1149
1149
|
|
|
1150
1150
|
/**
|
|
1151
1151
|
* Create an OAuth2 client bound to this app.
|
|
1152
|
-
* Returns the client
|
|
1152
|
+
* Returns the client - does NOT mount any middleware automatically.
|
|
1153
1153
|
*
|
|
1154
1154
|
* @param {object} opts - OAuth options (see `oauth()` for full documentation).
|
|
1155
1155
|
* @returns {{ authorize: Function, callback: Function, refresh: Function, userInfo: Function }}
|
package/lib/auth/authorize.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @module auth/authorize
|
|
3
|
-
* @description Authorization helpers
|
|
3
|
+
* @description Authorization helpers - role-based access control (RBAC),
|
|
4
4
|
* permission-based access, and policy classes.
|
|
5
5
|
*
|
|
6
6
|
* Works with any authentication middleware that sets `req.user`.
|
|
@@ -13,22 +13,22 @@
|
|
|
13
13
|
* app.use(jwt({ secret: process.env.JWT_SECRET }));
|
|
14
14
|
* app.use(attachUserHelpers());
|
|
15
15
|
*
|
|
16
|
-
* // Role-based
|
|
16
|
+
* // Role-based - only admins and editors can modify posts
|
|
17
17
|
* app.put('/posts/:id', authorize('admin', 'editor'), (req, res) => {
|
|
18
18
|
* res.json({ updated: true });
|
|
19
19
|
* });
|
|
20
20
|
*
|
|
21
|
-
* // Permission-based
|
|
21
|
+
* // Permission-based - require ALL listed permissions
|
|
22
22
|
* app.delete('/users/:id', can('users:read', 'users:delete'), (req, res) => {
|
|
23
23
|
* res.json({ deleted: true });
|
|
24
24
|
* });
|
|
25
25
|
*
|
|
26
|
-
* // ANY permission
|
|
26
|
+
* // ANY permission - useful for overlapping access
|
|
27
27
|
* app.get('/reports', canAny('reports:read', 'admin:read'), (req, res) => {
|
|
28
28
|
* res.json({ reports: [] });
|
|
29
29
|
* });
|
|
30
30
|
*
|
|
31
|
-
* // Policy class
|
|
31
|
+
* // Policy class - resource-level authorization
|
|
32
32
|
* class PostPolicy extends Policy {
|
|
33
33
|
* before(user) { if (user.role === 'superadmin') return true; }
|
|
34
34
|
* update(user, post) { return user.id === post.authorId || user.role === 'admin'; }
|
|
@@ -114,9 +114,9 @@ function authorize(...roles)
|
|
|
114
114
|
* Checks `req.user.permissions` (array or Set) for the required permission(s).
|
|
115
115
|
*
|
|
116
116
|
* Permission strings follow a `resource:action` convention:
|
|
117
|
-
* - `'posts:write'`
|
|
118
|
-
* - `'users:delete'`
|
|
119
|
-
* - `'*'`
|
|
117
|
+
* - `'posts:write'` - write access to posts
|
|
118
|
+
* - `'users:delete'` - delete users
|
|
119
|
+
* - `'*'` - superuser wildcard
|
|
120
120
|
*
|
|
121
121
|
* @param {...string} permissions - Required permissions (ALL must be present unless `opts.any` is true).
|
|
122
122
|
* @returns {Function} Middleware `(req, res, next) => void`.
|
|
@@ -293,7 +293,7 @@ function gate(policy, action, getResource)
|
|
|
293
293
|
});
|
|
294
294
|
}
|
|
295
295
|
|
|
296
|
-
// Attach resource if loaded
|
|
296
|
+
// Attach resource if loaded - saves a redundant DB query in the handler
|
|
297
297
|
if (resource && !req.resource) req.resource = resource;
|
|
298
298
|
next();
|
|
299
299
|
};
|
|
@@ -306,8 +306,8 @@ function gate(policy, action, getResource)
|
|
|
306
306
|
* Call this middleware after JWT/session middleware.
|
|
307
307
|
*
|
|
308
308
|
* Adds:
|
|
309
|
-
* - `req.user.is(...roles)`
|
|
310
|
-
* - `req.user.can(...perms)`
|
|
309
|
+
* - `req.user.is(...roles)` - check roles
|
|
310
|
+
* - `req.user.can(...perms)` - check permissions
|
|
311
311
|
*
|
|
312
312
|
* @returns {Function} Middleware.
|
|
313
313
|
*
|
package/lib/auth/enrollment.js
CHANGED
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
* for TOTP-based two-factor authentication.
|
|
6
6
|
*
|
|
7
7
|
* Steps:
|
|
8
|
-
* 1. `start()`
|
|
9
|
-
* 2. `verify()`
|
|
10
|
-
* 3. `complete()`
|
|
11
|
-
* 4. `disable()`
|
|
8
|
+
* 1. `start()` - Generate secret + backup codes, store in session
|
|
9
|
+
* 2. `verify()` - Confirm user can produce a valid TOTP code
|
|
10
|
+
* 3. `complete()` - Persist the verified secret to the database
|
|
11
|
+
* 4. `disable()` - Remove 2FA from the account
|
|
12
12
|
*
|
|
13
13
|
* @example | Full enrollment flow
|
|
14
14
|
* const { enrollment } = require('@zero-server/sdk');
|
|
@@ -356,7 +356,7 @@ function enrollment(opts = {})
|
|
|
356
356
|
|
|
357
357
|
function _defaultGetAccount(req)
|
|
358
358
|
{
|
|
359
|
-
if (!req.user) throw new Error('No user on request
|
|
359
|
+
if (!req.user) throw new Error('No user on request - authentication middleware required');
|
|
360
360
|
return req.user.email || req.user.id || req.user.sub;
|
|
361
361
|
}
|
|
362
362
|
|
package/lib/auth/jwt.js
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* const app = createApp();
|
|
14
14
|
* const SECRET = process.env.JWT_SECRET;
|
|
15
15
|
*
|
|
16
|
-
* // Public
|
|
16
|
+
* // Public - issue tokens
|
|
17
17
|
* app.post('/login', json(), async (req, res) => {
|
|
18
18
|
* const { email, password } = req.body;
|
|
19
19
|
* const user = await db.users.findOne({ email });
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
* res.json({ token });
|
|
25
25
|
* });
|
|
26
26
|
*
|
|
27
|
-
* // Protected
|
|
27
|
+
* // Protected - everything under /api requires a valid token
|
|
28
28
|
* const api = Router();
|
|
29
29
|
* api.use(jwt({ secret: SECRET }));
|
|
30
30
|
* api.get('/me', (req, res) => res.json({ id: req.user.sub, role: req.user.role }));
|
|
@@ -79,7 +79,7 @@ function _base64urlDecode(str)
|
|
|
79
79
|
|
|
80
80
|
/**
|
|
81
81
|
* Decode a JWT without verifying the signature.
|
|
82
|
-
* Returns `null` for malformed tokens
|
|
82
|
+
* Returns `null` for malformed tokens - never throws.
|
|
83
83
|
*
|
|
84
84
|
* @param {string} token - Raw JWT string.
|
|
85
85
|
* @returns {{ header: object, payload: object, signature: string }|null}
|
|
@@ -268,7 +268,7 @@ function verify(token, secretOrKey, opts = {})
|
|
|
268
268
|
* @param {Function} [opts.fetcher] - Custom fetch function (default: built-in fetch).
|
|
269
269
|
* @param {number} [opts.cacheTtl=600000] - Cache TTL in ms (default 10 minutes).
|
|
270
270
|
* @param {number} [opts.requestTimeout=5000] - Request timeout in ms.
|
|
271
|
-
* @returns {Function} `async (header) => publicKey`
|
|
271
|
+
* @returns {Function} `async (header) => publicKey` - resolves the signing key for a JWT header.
|
|
272
272
|
*
|
|
273
273
|
* @example
|
|
274
274
|
* const getKey = jwks('https://auth.example.com/.well-known/jwks.json');
|
|
@@ -330,7 +330,7 @@ function jwks(jwksUri, opts = {})
|
|
|
330
330
|
return pem;
|
|
331
331
|
}
|
|
332
332
|
|
|
333
|
-
// No kid
|
|
333
|
+
// No kid - return the first RSA key
|
|
334
334
|
const first = keys.values().next().value;
|
|
335
335
|
if (!first) throw _jwtError('No suitable key in JWKS', 'JWKS_NO_KEY');
|
|
336
336
|
return first;
|
|
@@ -347,9 +347,9 @@ function jwks(jwksUri, opts = {})
|
|
|
347
347
|
* Create JWT authentication middleware.
|
|
348
348
|
*
|
|
349
349
|
* On success, populates:
|
|
350
|
-
* - `req.user`
|
|
351
|
-
* - `req.auth`
|
|
352
|
-
* - `req.token`
|
|
350
|
+
* - `req.user` - decoded payload
|
|
351
|
+
* - `req.auth` - `{ header, payload, token }` full decode info
|
|
352
|
+
* - `req.token` - raw JWT string
|
|
353
353
|
*
|
|
354
354
|
* @param {object} opts - Configuration.
|
|
355
355
|
* @param {string|Buffer} [opts.secret] - HMAC secret for HS* algorithms.
|
|
@@ -367,7 +367,7 @@ function jwks(jwksUri, opts = {})
|
|
|
367
367
|
* @param {number} [opts.clockTolerance=0] - Clock skew tolerance in seconds.
|
|
368
368
|
* @param {number} [opts.maxAge] - Maximum token age in seconds.
|
|
369
369
|
* @param {boolean} [opts.credentialsRequired=true] - Return 401 if no token found (false = optional auth).
|
|
370
|
-
* @param {Function} [opts.isRevoked] - `async (payload) => boolean`
|
|
370
|
+
* @param {Function} [opts.isRevoked] - `async (payload) => boolean` - check token revocation.
|
|
371
371
|
* @param {Function} [opts.onError] - Custom error handler `(err, req, res) => void`.
|
|
372
372
|
* @returns {Function} Middleware `(req, res, next) => void`.
|
|
373
373
|
*
|