@zero-server/sdk 0.9.6 → 0.9.8

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 (94) hide show
  1. package/README.md +54 -53
  2. package/index.js +116 -4
  3. package/lib/app.js +22 -22
  4. package/lib/auth/authorize.js +11 -11
  5. package/lib/auth/enrollment.js +5 -5
  6. package/lib/auth/jwt.js +9 -9
  7. package/lib/auth/oauth.js +1 -1
  8. package/lib/auth/session.js +5 -5
  9. package/lib/auth/trustedDevice.js +2 -2
  10. package/lib/auth/twoFactor.js +11 -11
  11. package/lib/auth/webauthn.js +6 -6
  12. package/lib/body/json.js +1 -1
  13. package/lib/body/raw.js +1 -1
  14. package/lib/body/rawBuffer.js +1 -1
  15. package/lib/body/text.js +1 -1
  16. package/lib/body/urlencoded.js +3 -3
  17. package/lib/cli.js +43 -28
  18. package/lib/cluster.js +3 -3
  19. package/lib/debug.js +10 -10
  20. package/lib/env/index.js +11 -11
  21. package/lib/errors.js +131 -16
  22. package/lib/fetch/index.js +1 -1
  23. package/lib/grpc/call.js +14 -14
  24. package/lib/grpc/client.js +4 -4
  25. package/lib/grpc/codec.js +7 -7
  26. package/lib/grpc/credentials.js +2 -2
  27. package/lib/grpc/frame.js +2 -2
  28. package/lib/grpc/health.js +3 -3
  29. package/lib/grpc/index.js +3 -3
  30. package/lib/grpc/metadata.js +3 -3
  31. package/lib/grpc/proto.js +5 -5
  32. package/lib/grpc/reflection.js +2 -2
  33. package/lib/grpc/server.js +3 -3
  34. package/lib/grpc/status.js +2 -2
  35. package/lib/grpc/watch.js +1 -1
  36. package/lib/http/request.js +13 -13
  37. package/lib/http/response.js +2 -2
  38. package/lib/lifecycle.js +5 -5
  39. package/lib/middleware/compress.js +4 -4
  40. package/lib/observe/health.js +1 -1
  41. package/lib/observe/index.js +1 -1
  42. package/lib/observe/logger.js +3 -3
  43. package/lib/observe/metrics.js +4 -4
  44. package/lib/observe/tracing.js +4 -4
  45. package/lib/orm/adapters/json.js +1 -1
  46. package/lib/orm/adapters/memory.js +2 -2
  47. package/lib/orm/adapters/mongo.js +2 -2
  48. package/lib/orm/adapters/mysql.js +2 -2
  49. package/lib/orm/adapters/postgres.js +2 -2
  50. package/lib/orm/adapters/sqlite.js +3 -3
  51. package/lib/orm/audit.js +1 -1
  52. package/lib/orm/index.js +7 -7
  53. package/lib/orm/migrate.js +1 -1
  54. package/lib/orm/model.js +15 -15
  55. package/lib/orm/procedures.js +1 -1
  56. package/lib/orm/profiler.js +1 -1
  57. package/lib/orm/query.js +9 -9
  58. package/lib/orm/schema.js +1 -1
  59. package/lib/orm/seed/data/person.js +1 -1
  60. package/lib/orm/seed/fake.js +10 -10
  61. package/lib/orm/seed/index.js +4 -4
  62. package/lib/orm/seed/rng.js +1 -1
  63. package/lib/orm/snapshot.js +3 -3
  64. package/lib/orm/tenancy.js +6 -6
  65. package/lib/orm/views.js +1 -1
  66. package/lib/router/index.js +9 -9
  67. package/lib/webrtc/bot.js +405 -0
  68. package/lib/webrtc/cli.js +182 -0
  69. package/lib/webrtc/cluster.js +338 -0
  70. package/lib/webrtc/e2ee.js +274 -0
  71. package/lib/webrtc/ice.js +363 -0
  72. package/lib/webrtc/index.js +212 -0
  73. package/lib/webrtc/joinToken.js +171 -0
  74. package/lib/webrtc/observe.js +260 -0
  75. package/lib/webrtc/peer.js +143 -0
  76. package/lib/webrtc/room.js +184 -0
  77. package/lib/webrtc/sdp.js +503 -0
  78. package/lib/webrtc/sfu/index.js +251 -0
  79. package/lib/webrtc/sfu/livekit.js +304 -0
  80. package/lib/webrtc/sfu/mediasoup.js +357 -0
  81. package/lib/webrtc/sfu/memory.js +221 -0
  82. package/lib/webrtc/signaling.js +590 -0
  83. package/lib/webrtc/stun.js +484 -0
  84. package/lib/webrtc/turn/codec.js +370 -0
  85. package/lib/webrtc/turn/credentials.js +156 -0
  86. package/lib/webrtc/turn/server.js +648 -0
  87. package/package.json +2 -2
  88. package/types/body.d.ts +82 -14
  89. package/types/cli.d.ts +40 -2
  90. package/types/index.d.ts +19 -6
  91. package/types/middleware.d.ts +18 -72
  92. package/types/orm.d.ts +4 -13
  93. package/types/request.d.ts +3 -3
  94. package/types/webrtc.d.ts +501 -0
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 issue tokens
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 everything under /api requires a valid token
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 never throws.
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` resolves the signing key for a JWT header.
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 return the first RSA key
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` decoded payload
351
- * - `req.auth` `{ header, payload, token }` full decode info
352
- * - `req.token` raw JWT string
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` check token revocation.
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
  *
package/lib/auth/oauth.js CHANGED
@@ -224,7 +224,7 @@ function oauth(opts = {})
224
224
  // Validate state (CSRF prevention)
225
225
  if (verify.state && query.state !== verify.state)
226
226
  {
227
- throw _oauthError('State mismatch possible CSRF attack', 'OAUTH_STATE_MISMATCH');
227
+ throw _oauthError('State mismatch - possible CSRF attack', 'OAUTH_STATE_MISMATCH');
228
228
  }
229
229
 
230
230
  const body = {
@@ -263,7 +263,7 @@ class Session
263
263
  return { ...this._flash };
264
264
  }
265
265
 
266
- /** @private serialize to JSON for cookie/store */
266
+ /** @private - serialize to JSON for cookie/store */
267
267
  _serialize()
268
268
  {
269
269
  const obj = { d: this._data };
@@ -271,7 +271,7 @@ class Session
271
271
  return JSON.stringify(obj);
272
272
  }
273
273
 
274
- /** @private deserialize from JSON */
274
+ /** @private - deserialize from JSON */
275
275
  static _deserialize(json, id)
276
276
  {
277
277
  try
@@ -472,7 +472,7 @@ function session(opts = {})
472
472
  req.session = sess;
473
473
 
474
474
  // Intercept response to persist session
475
- // Hook into res.raw.end (Node ServerResponse) the Response wrapper
475
+ // Hook into res.raw.end (Node ServerResponse) - the Response wrapper
476
476
  // has no .end() method; its .send()/.json() helpers call raw.end().
477
477
  const raw = res.raw;
478
478
  const origEnd = raw.end.bind(raw);
@@ -481,7 +481,7 @@ function session(opts = {})
481
481
  try
482
482
  {
483
483
  // _saveSession calls res.cookie() / res.clearCookie() which set
484
- // Set-Cookie headers on raw via raw.setHeader safe because
484
+ // Set-Cookie headers on raw via raw.setHeader - safe because
485
485
  // headers aren't flushed until the original end() runs.
486
486
  // NOTE: store-based sessions are sync-compatible because
487
487
  // MemoryStore.set/get return resolved promises synchronously
@@ -574,7 +574,7 @@ function _saveSession(req, res, sess, ctx)
574
574
  const encrypted = _encrypt(payload, ctx.keys[0]);
575
575
  if (encrypted.length > MAX_COOKIE_SIZE)
576
576
  {
577
- log.warn('session cookie exceeds %d bytes consider using a store', MAX_COOKIE_SIZE);
577
+ log.warn('session cookie exceeds %d bytes - consider using a store', MAX_COOKIE_SIZE);
578
578
  }
579
579
  res.cookie(ctx.cookieName, encrypted, cOpts);
580
580
  log.debug('cookie session saved');
@@ -7,7 +7,7 @@
7
7
  * Subsequent requests skip the 2FA prompt if the trust token is valid.
8
8
  * Supports secret rotation, IP binding, and revocation.
9
9
  *
10
- * Uses AES-256-GCM encryption tokens are encrypted, not just signed,
10
+ * Uses AES-256-GCM encryption - tokens are encrypted, not just signed,
11
11
  * preventing information leakage.
12
12
  *
13
13
  * @example
@@ -370,7 +370,7 @@ function _defaultFingerprint(req)
370
370
  */
371
371
  function _defaultGetUserId(req)
372
372
  {
373
- if (!req.user) throw new Error('No user on request authentication middleware required');
373
+ if (!req.user) throw new Error('No user on request - authentication middleware required');
374
374
  return req.user.id || req.user.sub || req.user._id;
375
375
  }
376
376
 
@@ -4,7 +4,7 @@
4
4
  * Implements TOTP (RFC 6238 / RFC 4226), backup codes,
5
5
  * and composable middleware for step-up verification.
6
6
  *
7
- * Uses only Node.js built-in `crypto` no external packages.
7
+ * Uses only Node.js built-in `crypto` - no external packages.
8
8
  *
9
9
  * @example | Setup 2FA for a user
10
10
  * const { twoFactor } = require('@zero-server/sdk');
@@ -228,7 +228,7 @@ function _base32Decode(str)
228
228
 
229
229
  /**
230
230
  * Generate an HOTP code for a given counter value.
231
- * Implements RFC 4226 §5 HMAC-based One-Time Password.
231
+ * Implements RFC 4226 §5 - HMAC-based One-Time Password.
232
232
  *
233
233
  * @param {Buffer} secret - Shared secret key.
234
234
  * @param {number} counter - 8-byte counter value.
@@ -498,13 +498,13 @@ function verifyBackupCode(code, hashes)
498
498
 
499
499
  /**
500
500
  * Middleware that requires completed 2FA verification on the session.
501
- * Checks `req.session.get('twoFactorVerified')` returns 403 if not set.
501
+ * Checks `req.session.get('twoFactorVerified')` - returns 403 if not set.
502
502
  *
503
503
  * Designed to compose with `jwt()` or `session()` middleware:
504
504
  *
505
505
  * app.use(jwt({ secret }));
506
506
  * app.use(require2FA());
507
- * // or
507
+ * // - or -
508
508
  * app.use(session({ secret }));
509
509
  * app.use(require2FA());
510
510
  *
@@ -517,7 +517,7 @@ function verifyBackupCode(code, hashes)
517
517
  * Defaults to always requiring 2FA.
518
518
  * @returns {Function} Middleware `(req, res, next) => void`.
519
519
  *
520
- * @example | Basic all authenticated users must complete 2FA
520
+ * @example | Basic - all authenticated users must complete 2FA
521
521
  * app.use(require2FA());
522
522
  *
523
523
  * @example | Only enforce for users who have enrolled
@@ -548,7 +548,7 @@ function require2FA(opts = {})
548
548
  const enabled = await isEnabled(req);
549
549
  if (!enabled)
550
550
  {
551
- log('2FA not enabled for user skipping');
551
+ log('2FA not enabled for user - skipping');
552
552
  return next();
553
553
  }
554
554
  }
@@ -568,7 +568,7 @@ function require2FA(opts = {})
568
568
  const session = req.session;
569
569
  if (!session || typeof session.get !== 'function')
570
570
  {
571
- log.warn('require2FA: no session found is session() middleware active?');
571
+ log.warn('require2FA: no session found - is session() middleware active?');
572
572
  const raw = res.raw || res;
573
573
  if (raw.headersSent) return;
574
574
  raw.statusCode = 500;
@@ -583,7 +583,7 @@ function require2FA(opts = {})
583
583
  return next();
584
584
  }
585
585
 
586
- log.warn('2FA not completed blocking request');
586
+ log.warn('2FA not completed - blocking request');
587
587
  const raw = res.raw || res;
588
588
  if (raw.headersSent) return;
589
589
  raw.statusCode = statusCode;
@@ -690,7 +690,7 @@ function verifyTOTPMiddleware(opts = {})
690
690
  raw.end(JSON.stringify({ error: 'Too many attempts. Try again later.', retryAfter }));
691
691
  return;
692
692
  }
693
- // Lockout expired reset
693
+ // Lockout expired - reset
694
694
  attempts.delete(ip);
695
695
  }
696
696
 
@@ -735,7 +735,7 @@ function verifyTOTPMiddleware(opts = {})
735
735
 
736
736
  if (result.valid)
737
737
  {
738
- // Replay prevention (RFC 6238 §5.2) check AFTER signature
738
+ // Replay prevention (RFC 6238 §5.2) - check AFTER signature
739
739
  // verification to avoid leaking timing information about whether
740
740
  // a code was previously used.
741
741
  if (opts.replayStore)
@@ -775,7 +775,7 @@ function verifyTOTPMiddleware(opts = {})
775
775
  catch (err)
776
776
  {
777
777
  log.error('replay store error: %s', err.message);
778
- // Fail open for store errors don't block legitimate users
778
+ // Fail open for store errors - don't block legitimate users
779
779
  }
780
780
  }
781
781
 
@@ -145,7 +145,7 @@ const cbor = {
145
145
  if (additionalInfo === 23) return undefined;
146
146
  if (additionalInfo === 25)
147
147
  {
148
- // float16 not commonly used in WebAuthn but handle it
148
+ // float16 - not commonly used in WebAuthn but handle it
149
149
  offset -= 0; // already read
150
150
  return readArgument(additionalInfo);
151
151
  }
@@ -593,7 +593,7 @@ function verifyRegistration(opts)
593
593
  if (clientData.type !== 'webauthn.create')
594
594
  return { verified: false, credential: null, error: `Invalid type: ${clientData.type}` };
595
595
 
596
- // Strict origin validation exact match, no regex
596
+ // Strict origin validation - exact match, no regex
597
597
  if (clientData.origin !== expectedOrigin)
598
598
  return { verified: false, credential: null, error: `Origin mismatch: ${clientData.origin}` };
599
599
 
@@ -627,7 +627,7 @@ function verifyRegistration(opts)
627
627
 
628
628
  if (fmt === 'none')
629
629
  {
630
- // No attestation acceptable for 'none' conveyance
630
+ // No attestation - acceptable for 'none' conveyance
631
631
  log.debug('registration with "none" attestation format');
632
632
  }
633
633
  else if (fmt === 'packed')
@@ -644,7 +644,7 @@ function verifyRegistration(opts)
644
644
  }
645
645
  else
646
646
  {
647
- log.warn('unknown attestation format: %s treating as none', fmt);
647
+ log.warn('unknown attestation format: %s - treating as none', fmt);
648
648
  }
649
649
 
650
650
  // 8. Extract public key from COSE format
@@ -703,7 +703,7 @@ function _verifyPackedAttestation(attStmt, parsedAuthData, rawAuthData, clientDa
703
703
  }
704
704
  else
705
705
  {
706
- // Self-attestation verify with the credential public key
706
+ // Self-attestation - verify with the credential public key
707
707
  const { key } = _coseToPublicKey(parsedAuthData.credentialPublicKey);
708
708
  const algName = _coseAlgToNodeAlg(alg);
709
709
  return crypto.verify(algName, signedData, key, sig);
@@ -881,7 +881,7 @@ function verifyAuthentication(opts)
881
881
  if (!authData.userPresent)
882
882
  return { verified: false, newCounter: null, error: 'User not present' };
883
883
 
884
- // 5. Counter validation detect cloned authenticators
884
+ // 5. Counter validation - detect cloned authenticators
885
885
  if (authData.signCount > 0 || credential.counter > 0)
886
886
  {
887
887
  if (authData.signCount <= credential.counter)
package/lib/body/json.js CHANGED
@@ -37,7 +37,7 @@ function _sanitize(obj)
37
37
  * @param {boolean} [options.strict=true] - When true, reject non-object/array roots.
38
38
  * @param {string|string[]|Function} [options.type='application/json'] - Content-Type(s) to match.
39
39
  * @param {boolean} [options.requireSecure=false] - When true, reject non-HTTPS requests with 403.
40
- * @param {Function} [options.verify] - `verify(req, res, buf, encoding)` called before parsing. Throw to reject with 403.
40
+ * @param {Function} [options.verify] - `verify(req, res, buf, encoding)` - called before parsing. Throw to reject with 403.
41
41
  * @param {boolean} [options.inflate=true] - Decompress gzip/deflate/br bodies. When false, compressed bodies return 415.
42
42
  * @returns {Function} Async middleware `(req, res, next) => void`.
43
43
  *
package/lib/body/raw.js CHANGED
@@ -15,7 +15,7 @@ const sendError = require('./sendError');
15
15
  * @param {string|number} [options.limit] - Max body size. Default `'1mb'`.
16
16
  * @param {string|string[]|Function} [options.type='application/octet-stream'] - Content-Type(s) to match.
17
17
  * @param {boolean} [options.requireSecure=false] - When true, reject non-HTTPS requests with 403.
18
- * @param {Function} [options.verify] - `verify(req, res, buf)` called before setting body. Throw to reject with 403.
18
+ * @param {Function} [options.verify] - `verify(req, res, buf)` - called before setting body. Throw to reject with 403.
19
19
  * @param {boolean} [options.inflate=true] - Decompress gzip/deflate/br bodies. When false, compressed bodies return 415.
20
20
  * @returns {Function} Async middleware `(req, res, next) => void`.
21
21
  *
@@ -78,7 +78,7 @@ function rawBuffer(req, opts = {})
78
78
  const encoding = (headers['content-encoding'] || '').toLowerCase().trim();
79
79
  const isCompressed = encoding && encoding !== 'identity';
80
80
 
81
- // Content-Length pre-check (skip for compressed bodies CL is the compressed size)
81
+ // Content-Length pre-check (skip for compressed bodies - CL is the compressed size)
82
82
  if (!isCompressed)
83
83
  {
84
84
  const cl = parseInt(headers['content-length'], 10);
package/lib/body/text.js CHANGED
@@ -17,7 +17,7 @@ const sendError = require('./sendError');
17
17
  * @param {string} [options.encoding='utf8'] - Fallback character encoding when Content-Type has no charset.
18
18
  * @param {string|string[]|Function} [options.type='text/*'] - Content-Type(s) to match.
19
19
  * @param {boolean} [options.requireSecure=false] - When true, reject non-HTTPS requests with 403.
20
- * @param {Function} [options.verify] - `verify(req, res, buf, encoding)` called before decoding. Throw to reject with 403.
20
+ * @param {Function} [options.verify] - `verify(req, res, buf, encoding)` - called before decoding. Throw to reject with 403.
21
21
  * @param {boolean} [options.inflate=true] - Decompress gzip/deflate/br bodies. When false, compressed bodies return 415.
22
22
  * @returns {Function} Async middleware `(req, res, next) => void`.
23
23
  *
@@ -35,7 +35,7 @@ function appendValue(prev, val)
35
35
  * @param {boolean} [options.requireSecure=false] - When true, reject non-HTTPS requests with 403.
36
36
  * @param {number} [options.parameterLimit=1000] - Max number of parameters. Prevents DoS via huge payloads.
37
37
  * @param {number} [options.depth=32] - Max nesting depth for bracket syntax. Prevents deep-nesting DoS.
38
- * @param {Function} [options.verify] - `verify(req, res, buf, encoding)` called before parsing. Throw to reject with 403.
38
+ * @param {Function} [options.verify] - `verify(req, res, buf, encoding)` - called before parsing. Throw to reject with 403.
39
39
  * @param {boolean} [options.inflate=true] - Decompress gzip/deflate/br bodies.
40
40
  * @returns {Function} Async middleware `(req, res, next) => void`.
41
41
  *
@@ -158,7 +158,7 @@ function urlencoded(options = {})
158
158
  cur.push(v);
159
159
  break;
160
160
  }
161
- // Intermediate empty bracket navigate into next element of the array
161
+ // Intermediate empty bracket - navigate into next element of the array
162
162
  if (!Array.isArray(cur))
163
163
  {
164
164
  const arr = [];
@@ -200,7 +200,7 @@ function urlencoded(options = {})
200
200
  cur = cur[idx];
201
201
  } else
202
202
  {
203
- // Non-numeric key on array navigate into last pushed object
203
+ // Non-numeric key on array - navigate into last pushed object
204
204
  if (cur.length === 0) cur.push({});
205
205
  if (typeof cur[cur.length - 1] !== 'object') cur.push({});
206
206
  const obj = cur[cur.length - 1];
package/lib/cli.js CHANGED
@@ -9,7 +9,7 @@
9
9
  * that exports your database adapter and connection settings.
10
10
  *
11
11
  * @example
12
- * // zero.config.js required by all CLI commands except make:* and help
12
+ * // zero.config.js - required by all CLI commands except make:* and help
13
13
  * module.exports = {
14
14
  * adapter: 'sqlite',
15
15
  * connection: { filename: './app.db' },
@@ -18,13 +18,13 @@
18
18
  * };
19
19
  *
20
20
  * // Run via npx (no global install needed):
21
- * // npx zh migrate
22
- * // npx zh migrate:rollback
23
- * // npx zh migrate:status
24
- * // npx zh seed
25
- * // npx zh make:model User
26
- * // npx zh make:migration create_posts
27
- * // npx zh make:seeder Users
21
+ * // npx zs migrate
22
+ * // npx zs migrate:rollback
23
+ * // npx zs migrate:status
24
+ * // npx zs seed
25
+ * // npx zs make:model User
26
+ * // npx zs make:migration create_posts
27
+ * // npx zs make:seeder Users
28
28
  *
29
29
  * // Or programmatically:
30
30
  * const { runCLI } = require('@zero-server/sdk');
@@ -150,6 +150,15 @@ class CLI
150
150
  */
151
151
  async run()
152
152
  {
153
+ // WebRTC subcommands - delegate the whole `webrtc:*` namespace to the
154
+ // webrtc CLI module so the top-level dispatcher stays small.
155
+ if (typeof this.command === 'string' && this.command.startsWith('webrtc:'))
156
+ {
157
+ const { runWebRTCCommand } = require('./webrtc/cli');
158
+ await runWebRTCCommand(this.command.slice('webrtc:'.length), this.flags);
159
+ return;
160
+ }
161
+
153
162
  const commands = {
154
163
  'migrate': () => this._migrate(),
155
164
  'migrate:rollback': () => this._rollback(),
@@ -206,7 +215,7 @@ class CLI
206
215
  throw new Error(
207
216
  'No configuration file found.\n' +
208
217
  'Create a zero.config.js with database and migration settings.\n' +
209
- 'See "zh help" for examples.'
218
+ 'See "zs help" for examples.'
210
219
  );
211
220
  }
212
221
 
@@ -421,8 +430,8 @@ class CLI
421
430
  const status = await migrator.status();
422
431
  if (status.executed.includes(lastMigration.name))
423
432
  {
424
- console.error(red(`Cannot remove "${lastMigration.name}" it has already been applied.`));
425
- console.error(dim('Run "zh migrate:rollback" first, then try again.'));
433
+ console.error(red(`Cannot remove "${lastMigration.name}" - it has already been applied.`));
434
+ console.error(dim('Run "zs migrate:rollback" first, then try again.'));
426
435
  process.exitCode = 1;
427
436
  await db.close();
428
437
  return;
@@ -510,7 +519,7 @@ class CLI
510
519
  const name = this.args.find(a => !a.startsWith('-'));
511
520
  if (!name)
512
521
  {
513
- console.error(red('Usage: zh make:model <Name>'));
522
+ console.error(red('Usage: zs make:model <Name>'));
514
523
  process.exitCode = 1;
515
524
  return;
516
525
  }
@@ -566,7 +575,7 @@ module.exports = ${className};
566
575
  const name = this.args.find(a => !a.startsWith('-'));
567
576
  if (!name)
568
577
  {
569
- console.error(red('Usage: zh make:migration <name>'));
578
+ console.error(red('Usage: zs make:migration <name>'));
570
579
  process.exitCode = 1;
571
580
  return;
572
581
  }
@@ -645,7 +654,7 @@ module.exports = {
645
654
 
646
655
  if (hasNoChanges(changes))
647
656
  {
648
- console.log(dim('No schema changes detected nothing to migrate.'));
657
+ console.log(dim('No schema changes detected - nothing to migrate.'));
649
658
  return;
650
659
  }
651
660
 
@@ -692,7 +701,7 @@ module.exports = {
692
701
  const name = this.args.find(a => !a.startsWith('-'));
693
702
  if (!name)
694
703
  {
695
- console.error(red('Usage: zh make:seeder <name>'));
704
+ console.error(red('Usage: zs make:seeder <name>'));
696
705
  process.exitCode = 1;
697
706
  return;
698
707
  }
@@ -742,9 +751,9 @@ module.exports = ${className};
742
751
  _help()
743
752
  {
744
753
  console.log(`
745
- ${bold('zh CLI')} zero-server ORM tooling
754
+ ${bold('zs CLI')} - zero-server ORM tooling
746
755
 
747
- ${bold('Usage:')} npx zh <command> [options]
756
+ ${bold('Usage:')} npx zs <command> [options]
748
757
 
749
758
  ${bold('Commands:')}
750
759
 
@@ -761,6 +770,12 @@ ${bold('Commands:')}
761
770
  ${cyan('make:migration')} <n> Auto-generate migration from model changes
762
771
  ${cyan('make:seeder')} <name> Scaffold a new seeder file
763
772
 
773
+ ${cyan('webrtc:stun')} Run a STUN binding probe, print public addr
774
+ ${cyan('webrtc:turn-creds')} Issue ephemeral TURN credentials
775
+ ${cyan('webrtc:join-token')} Sign a signaling join token
776
+ ${cyan('webrtc:verify-token')} Verify and decode a join token
777
+ ${cyan('webrtc:help')} Show webrtc subcommand help
778
+
764
779
  ${cyan('help')} Show this help message
765
780
  ${cyan('version')} Show version
766
781
 
@@ -788,19 +803,19 @@ ${bold('Config file:')} ${dim('zero.config.js (or .zero-server.js / legacy .zero
788
803
 
789
804
  ${bold('Auto-generated migrations:')}
790
805
 
791
- ${dim('$')} npx zh make:migration create_users ${dim('# detects new User model → generates CREATE TABLE')}
792
- ${dim('$')} npx zh make:migration add_email ${dim('# detects new email column → generates ADD COLUMN')}
793
- ${dim('$')} npx zh make:migration --empty init ${dim('# blank migration (manual mode)')}
794
- ${dim('$')} npx zh migrate ${dim('# apply pending migrations')}
795
- ${dim('$')} npx zh migrate:remove ${dim('# undo last make:migration')}
806
+ ${dim('$')} npx zs make:migration create_users ${dim('# detects new User model → generates CREATE TABLE')}
807
+ ${dim('$')} npx zs make:migration add_email ${dim('# detects new email column → generates ADD COLUMN')}
808
+ ${dim('$')} npx zs make:migration --empty init ${dim('# blank migration (manual mode)')}
809
+ ${dim('$')} npx zs migrate ${dim('# apply pending migrations')}
810
+ ${dim('$')} npx zs migrate:remove ${dim('# undo last make:migration')}
796
811
 
797
812
  ${bold('Examples:')}
798
813
 
799
- ${dim('$')} npx zh make:model User ${dim('# creates models/User.js')}
800
- ${dim('$')} npx zh make:migration create_users ${dim('# auto-generates from models')}
801
- ${dim('$')} npx zh migrate ${dim('# runs all pending migrations')}
802
- ${dim('$')} npx zh migrate --config=db.config.js
803
- ${dim('$')} npx zh seed ${dim('# runs all seeders')}
814
+ ${dim('$')} npx zs make:model User ${dim('# creates models/User.js')}
815
+ ${dim('$')} npx zs make:migration create_users ${dim('# auto-generates from models')}
816
+ ${dim('$')} npx zs migrate ${dim('# runs all pending migrations')}
817
+ ${dim('$')} npx zs migrate --config=db.config.js
818
+ ${dim('$')} npx zs seed ${dim('# runs all seeders')}
804
819
  `);
805
820
  }
806
821
 
@@ -810,7 +825,7 @@ ${bold('Examples:')}
810
825
  _version()
811
826
  {
812
827
  const pkg = require('../package.json');
813
- console.log(`zh v${pkg.version} (zero-server)`);
828
+ console.log(`zs v${pkg.version} (zero-server)`);
814
829
  }
815
830
  }
816
831
 
package/lib/cluster.js CHANGED
@@ -455,7 +455,7 @@ class ClusterManager
455
455
 
456
456
  /**
457
457
  * Perform a rolling restart of all workers (zero-downtime).
458
- * Workers are restarted one at a time a new worker is spawned and
458
+ * Workers are restarted one at a time - a new worker is spawned and
459
459
  * confirmed listening before the old one is disconnected.
460
460
  *
461
461
  * @returns {Promise<void>} Resolves when all workers have been replaced.
@@ -482,7 +482,7 @@ class ClusterManager
482
482
  await new Promise((resolve) =>
483
483
  {
484
484
  replacement.once('listening', resolve);
485
- // Safety timeout don't wait forever
485
+ // Safety timeout - don't wait forever
486
486
  const timer = setTimeout(resolve, 10000);
487
487
  if (timer.unref) timer.unref();
488
488
  });
@@ -630,7 +630,7 @@ function clusterize(workerFn, opts = {})
630
630
  }
631
631
  else
632
632
  {
633
- // Worker process listen for shutdown IPC from primary
633
+ // Worker process - listen for shutdown IPC from primary
634
634
  mgr.onMessage('shutdown', () =>
635
635
  {
636
636
  log.info('worker received shutdown message');
package/lib/debug.js CHANGED
@@ -16,7 +16,7 @@
16
16
  * log.error('failed to connect', err);
17
17
  * log('shorthand for debug level');
18
18
  *
19
- * // Set minimum level anything below is silenced
19
+ * // Set minimum level - anything below is silenced
20
20
  * debug.level('warn'); // only warn, error, fatal
21
21
  * debug.level('silent'); // suppress all output
22
22
  * debug.level('trace'); // show everything
@@ -81,7 +81,7 @@ _enabledPatterns = _parsePatterns();
81
81
  */
82
82
  function _isEnabled(ns)
83
83
  {
84
- if (!_enabledPatterns) return true; // No DEBUG set enable all
84
+ if (!_enabledPatterns) return true; // No DEBUG set - enable all
85
85
  let enabled = false;
86
86
  for (const { neg, re } of _enabledPatterns)
87
87
  {
@@ -116,7 +116,7 @@ function _ts()
116
116
  }
117
117
 
118
118
  /**
119
- * Format arguments (like console.log supports %s, %d, %j, %o).
119
+ * Format arguments (like console.log - supports %s, %d, %j, %o).
120
120
  * @private
121
121
  */
122
122
  function _format(args)
@@ -278,13 +278,13 @@ function debug(namespace)
278
278
  * Messages below this level are silenced.
279
279
  *
280
280
  * @param {string|number} level - Level name or number.
281
- * `'trace'` (0) all output
282
- * `'debug'` (1) debug and above
283
- * `'info'` (2) info and above
284
- * `'warn'` (3) warn and above
285
- * `'error'` (4) error and fatal only
286
- * `'fatal'` (5) fatal only
287
- * `'silent'` (6) nothing
281
+ * `'trace'` (0) - all output
282
+ * `'debug'` (1) - debug and above
283
+ * `'info'` (2) - info and above
284
+ * `'warn'` (3) - warn and above
285
+ * `'error'` (4) - error and fatal only
286
+ * `'fatal'` (5) - fatal only
287
+ * `'silent'` (6) - nothing
288
288
  */
289
289
  debug.level = function(level)
290
290
  {