@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.
- package/README.md +54 -53
- 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 +43 -28
- 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 +3 -3
- 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 +405 -0
- package/lib/webrtc/cli.js +182 -0
- package/lib/webrtc/cluster.js +338 -0
- package/lib/webrtc/e2ee.js +274 -0
- package/lib/webrtc/ice.js +363 -0
- package/lib/webrtc/index.js +212 -0
- package/lib/webrtc/joinToken.js +171 -0
- package/lib/webrtc/observe.js +260 -0
- package/lib/webrtc/peer.js +143 -0
- package/lib/webrtc/room.js +184 -0
- package/lib/webrtc/sdp.js +503 -0
- package/lib/webrtc/sfu/index.js +251 -0
- package/lib/webrtc/sfu/livekit.js +304 -0
- package/lib/webrtc/sfu/mediasoup.js +357 -0
- package/lib/webrtc/sfu/memory.js +221 -0
- package/lib/webrtc/signaling.js +590 -0
- package/lib/webrtc/stun.js +484 -0
- package/lib/webrtc/turn/codec.js +370 -0
- package/lib/webrtc/turn/credentials.js +156 -0
- package/lib/webrtc/turn/server.js +648 -0
- package/package.json +2 -2
- package/types/body.d.ts +82 -14
- package/types/cli.d.ts +40 -2
- package/types/index.d.ts +19 -6
- package/types/middleware.d.ts +18 -72
- package/types/orm.d.ts +4 -13
- package/types/request.d.ts +3 -3
- package/types/webrtc.d.ts +501 -0
package/lib/grpc/proto.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @module grpc/proto
|
|
3
|
-
* @description Zero-dependency proto3 parser
|
|
3
|
+
* @description Zero-dependency proto3 parser - reads `.proto` file text and produces
|
|
4
4
|
* message descriptors, enum definitions, and service/RPC declarations
|
|
5
5
|
* that the codec and server use at runtime.
|
|
6
6
|
*
|
|
@@ -17,9 +17,9 @@
|
|
|
17
17
|
* @example
|
|
18
18
|
* const { parseProto } = require('./proto');
|
|
19
19
|
* const schema = parseProto(fs.readFileSync('chat.proto', 'utf8'));
|
|
20
|
-
* // schema.messages
|
|
21
|
-
* // schema.enums
|
|
22
|
-
* // schema.services
|
|
20
|
+
* // schema.messages - { MessageName: { fields: [...] } }
|
|
21
|
+
* // schema.enums - { EnumName: { values: { ... } } }
|
|
22
|
+
* // schema.services - { ServiceName: { methods: { ... } } }
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
25
|
const fs = require('fs');
|
|
@@ -254,7 +254,7 @@ function parseProto(source, opts = {})
|
|
|
254
254
|
{
|
|
255
255
|
next(); expect('='); schema.syntax = next().value; expect(';');
|
|
256
256
|
if (schema.syntax !== 'proto3')
|
|
257
|
-
log.warn('proto file uses syntax "%s"
|
|
257
|
+
log.warn('proto file uses syntax "%s" - only proto3 is fully supported', schema.syntax);
|
|
258
258
|
}
|
|
259
259
|
else if (tok.value === 'package')
|
|
260
260
|
{
|
package/lib/grpc/reflection.js
CHANGED
|
@@ -54,7 +54,7 @@ function buildFileDescriptorProto(schema, filename)
|
|
|
54
54
|
// field 2: package (string)
|
|
55
55
|
if (schema.package) w.string(2, schema.package);
|
|
56
56
|
|
|
57
|
-
// field 3: dependency (repeated string)
|
|
57
|
+
// field 3: dependency (repeated string) - imported file names
|
|
58
58
|
if (schema.imports)
|
|
59
59
|
{
|
|
60
60
|
for (const imp of schema.imports)
|
|
@@ -191,7 +191,7 @@ function _buildFieldDescriptorProto(field, schema)
|
|
|
191
191
|
// field 9: oneof_index (if in a oneof)
|
|
192
192
|
if (field.oneofName !== undefined)
|
|
193
193
|
{
|
|
194
|
-
// Will use the index from the parent
|
|
194
|
+
// Will use the index from the parent - simplified to 0 for now
|
|
195
195
|
w.int32(9, 0);
|
|
196
196
|
}
|
|
197
197
|
|
package/lib/grpc/server.js
CHANGED
|
@@ -130,7 +130,7 @@ class GrpcServiceRegistry
|
|
|
130
130
|
{
|
|
131
131
|
if (!handlers[methodName])
|
|
132
132
|
{
|
|
133
|
-
log.warn('no handler for %s/%s
|
|
133
|
+
log.warn('no handler for %s/%s - will return UNIMPLEMENTED', serviceName, methodName);
|
|
134
134
|
}
|
|
135
135
|
|
|
136
136
|
const routePath = pathPrefix + '/' + methodName;
|
|
@@ -178,7 +178,7 @@ class GrpcServiceRegistry
|
|
|
178
178
|
const contentType = headers['content-type'] || '';
|
|
179
179
|
if (!contentType.startsWith('application/grpc'))
|
|
180
180
|
{
|
|
181
|
-
return false; // Not a gRPC request
|
|
181
|
+
return false; // Not a gRPC request - let the normal HTTP pipeline handle it
|
|
182
182
|
}
|
|
183
183
|
|
|
184
184
|
const grpcPath = headers[':path'];
|
|
@@ -290,7 +290,7 @@ class GrpcServiceRegistry
|
|
|
290
290
|
}
|
|
291
291
|
|
|
292
292
|
/**
|
|
293
|
-
* Begin draining
|
|
293
|
+
* Begin draining - reject new calls and wait for active calls to finish.
|
|
294
294
|
*
|
|
295
295
|
* @param {number} [timeout=30000] - Maximum time to wait in ms.
|
|
296
296
|
* @returns {Promise<void>}
|
package/lib/grpc/status.js
CHANGED
|
@@ -25,7 +25,7 @@ const GrpcStatus = {
|
|
|
25
25
|
OK: 0,
|
|
26
26
|
/** The operation was cancelled (typically by the caller). */
|
|
27
27
|
CANCELLED: 1,
|
|
28
|
-
/** Unknown error
|
|
28
|
+
/** Unknown error - a catch-all for unexpected failures. */
|
|
29
29
|
UNKNOWN: 2,
|
|
30
30
|
/** The client specified an invalid argument. */
|
|
31
31
|
INVALID_ARGUMENT: 3,
|
|
@@ -47,7 +47,7 @@ const GrpcStatus = {
|
|
|
47
47
|
OUT_OF_RANGE: 11,
|
|
48
48
|
/** The operation is not implemented or not supported. */
|
|
49
49
|
UNIMPLEMENTED: 12,
|
|
50
|
-
/** Internal error
|
|
50
|
+
/** Internal error - invariants expected by the server have been broken. */
|
|
51
51
|
INTERNAL: 13,
|
|
52
52
|
/** The service is currently unavailable, usually a transient condition. */
|
|
53
53
|
UNAVAILABLE: 14,
|
package/lib/grpc/watch.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Watches `.proto` files for changes using `fs.watch()` and
|
|
5
5
|
* re-parses/re-registers gRPC services automatically.
|
|
6
6
|
*
|
|
7
|
-
* **Dev-only**
|
|
7
|
+
* **Dev-only** - disabled by default when `NODE_ENV=production`.
|
|
8
8
|
*
|
|
9
9
|
* @example
|
|
10
10
|
* const { createApp, watchProto } = require('@zero-server/sdk');
|
package/lib/http/request.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* to correctly resolve `req.ip`, `req.ips`, `req.protocol`, `req.secure`,
|
|
8
8
|
* and `req.hostname` when behind reverse proxies.
|
|
9
9
|
*
|
|
10
|
-
* HTTP/2 compatible
|
|
10
|
+
* HTTP/2 compatible - detects pseudo-headers (`:method`, `:path`,
|
|
11
11
|
* `:authority`) from HTTP/2 requests automatically.
|
|
12
12
|
*/
|
|
13
13
|
const net = require('net');
|
|
@@ -147,12 +147,12 @@ function _inCIDR(addr, cidr)
|
|
|
147
147
|
* Compile a `trust proxy` setting into a function `(addr, index) => boolean`.
|
|
148
148
|
*
|
|
149
149
|
* Supported values:
|
|
150
|
-
* - `true` / `'loopback'`
|
|
151
|
-
* - `false`
|
|
152
|
-
* - `number`
|
|
153
|
-
* - `string`
|
|
154
|
-
* - `string[]`
|
|
155
|
-
* - `function`
|
|
150
|
+
* - `true` / `'loopback'` - trust loopback IPs (127.0.0.0/8, ::1)
|
|
151
|
+
* - `false` - trust nothing
|
|
152
|
+
* - `number` - trust the first N hops (proxies)
|
|
153
|
+
* - `string` - comma-separated IPs or CIDR ranges
|
|
154
|
+
* - `string[]` - array of IPs or CIDR ranges
|
|
155
|
+
* - `function` - custom `(addr, index) => boolean`
|
|
156
156
|
*
|
|
157
157
|
* @param {*} val - The `trust proxy` setting value.
|
|
158
158
|
* @returns {Function} `(addr: string, hopIndex: number) => boolean`
|
|
@@ -177,7 +177,7 @@ function compileTrust(val)
|
|
|
177
177
|
if (val === false || val === undefined || val === null || val === 0) return () => false;
|
|
178
178
|
if (typeof val === 'number' && val > 0)
|
|
179
179
|
{
|
|
180
|
-
// Trust all addresses
|
|
180
|
+
// Trust all addresses - the hop count is handled by _resolveProxyChain
|
|
181
181
|
return () => true;
|
|
182
182
|
}
|
|
183
183
|
|
|
@@ -270,12 +270,12 @@ function _resolveProxyChain(socketAddr, xffHeader, trustFn, trustSetting)
|
|
|
270
270
|
{
|
|
271
271
|
if (!trustFn(addrs[i], addrs.length - i))
|
|
272
272
|
{
|
|
273
|
-
// This address is not trusted
|
|
273
|
+
// This address is not trusted - it's the client
|
|
274
274
|
return { ip: addrs[i], ips: chain };
|
|
275
275
|
}
|
|
276
276
|
}
|
|
277
277
|
|
|
278
|
-
// All addresses are trusted
|
|
278
|
+
// All addresses are trusted - leftmost is the client
|
|
279
279
|
return { ip: addrs[0] || socketAddr, ips: chain };
|
|
280
280
|
}
|
|
281
281
|
|
|
@@ -316,7 +316,7 @@ class Request
|
|
|
316
316
|
/** HTTP version (e.g. '2.0' for HTTP/2). @type {string} */
|
|
317
317
|
this.httpVersion = req.httpVersion;
|
|
318
318
|
|
|
319
|
-
/** `true` when the connection is over TLS (HTTPS)
|
|
319
|
+
/** `true` when the connection is over TLS (HTTPS) - raw socket check. @private */
|
|
320
320
|
this._socketSecure = !!(req.socket && req.socket.encrypted);
|
|
321
321
|
|
|
322
322
|
/** ALPN protocol negotiated (e.g. 'h2', 'http/1.1'). @type {string|null} */
|
|
@@ -335,7 +335,7 @@ class Request
|
|
|
335
335
|
this.locals = {};
|
|
336
336
|
|
|
337
337
|
/**
|
|
338
|
-
* The original URL as received
|
|
338
|
+
* The original URL as received - never rewritten by middleware.
|
|
339
339
|
* Set by `app.handle()`.
|
|
340
340
|
* @type {string}
|
|
341
341
|
*/
|
|
@@ -580,7 +580,7 @@ class Request
|
|
|
580
580
|
}
|
|
581
581
|
|
|
582
582
|
/**
|
|
583
|
-
* Content negotiation
|
|
583
|
+
* Content negotiation - check if the client accepts the given type(s).
|
|
584
584
|
* Returns the best match, or `false` if none match.
|
|
585
585
|
*
|
|
586
586
|
* @param {...string} types - MIME types to check (e.g. 'json', 'html', 'text/plain').
|
package/lib/http/response.js
CHANGED
|
@@ -167,7 +167,7 @@ class Response
|
|
|
167
167
|
if (!hasContentType)
|
|
168
168
|
{
|
|
169
169
|
// Heuristic: if it looks like HTML, set text/html
|
|
170
|
-
// Avoid trimStart() allocation
|
|
170
|
+
// Avoid trimStart() allocation - scan for first non-whitespace char
|
|
171
171
|
let isHTML = false;
|
|
172
172
|
for (let i = 0; i < body.length; i++)
|
|
173
173
|
{
|
|
@@ -655,7 +655,7 @@ class Response
|
|
|
655
655
|
// Handle RST_STREAM from client (browser doesn't want the push)
|
|
656
656
|
stream.on('error', (e) =>
|
|
657
657
|
{
|
|
658
|
-
if (e.code === 'ERR_HTTP2_STREAM_CANCEL') return; // Normal
|
|
658
|
+
if (e.code === 'ERR_HTTP2_STREAM_CANCEL') return; // Normal - client cancelled
|
|
659
659
|
log.debug('push stream error for %s: %s', path, e.message);
|
|
660
660
|
});
|
|
661
661
|
|
package/lib/lifecycle.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* const app = createApp();
|
|
10
10
|
* app.listen(3000);
|
|
11
11
|
*
|
|
12
|
-
* // Automatic
|
|
12
|
+
* // Automatic - SIGTERM/SIGINT handlers registered by listen()
|
|
13
13
|
* // Manual trigger:
|
|
14
14
|
* await app.shutdown();
|
|
15
15
|
*/
|
|
@@ -248,7 +248,7 @@ class LifecycleManager
|
|
|
248
248
|
/**
|
|
249
249
|
* Install `SIGTERM` and `SIGINT` process signal handlers that trigger
|
|
250
250
|
* graceful shutdown. Called automatically by `app.listen()`.
|
|
251
|
-
* Safe to call multiple times
|
|
251
|
+
* Safe to call multiple times - handlers are only installed once.
|
|
252
252
|
*/
|
|
253
253
|
installSignalHandlers()
|
|
254
254
|
{
|
|
@@ -300,14 +300,14 @@ class LifecycleManager
|
|
|
300
300
|
* Perform a full graceful shutdown.
|
|
301
301
|
*
|
|
302
302
|
* Shutdown sequence:
|
|
303
|
-
* 1. Emit `'beforeShutdown'`
|
|
303
|
+
* 1. Emit `'beforeShutdown'` - run pre-shutdown hooks (flush metrics, etc.)
|
|
304
304
|
* 2. Stop accepting new connections (server.close)
|
|
305
305
|
* 3. Close all WebSocket connections with code `1001` (Going Away)
|
|
306
306
|
* 4. Close all SSE streams
|
|
307
307
|
* 5. Drain active gRPC calls
|
|
308
308
|
* 6. Wait for in-flight HTTP requests to complete (with timeout)
|
|
309
309
|
* 7. Close all registered ORM database connections
|
|
310
|
-
* 8. Emit `'shutdown'`
|
|
310
|
+
* 8. Emit `'shutdown'` - final cleanup complete
|
|
311
311
|
*
|
|
312
312
|
* If in-flight requests do not complete within the configured
|
|
313
313
|
* timeout (default 30s), they are forcefully terminated.
|
|
@@ -345,7 +345,7 @@ class LifecycleManager
|
|
|
345
345
|
this.state = LIFECYCLE_STATE.DRAINING;
|
|
346
346
|
await this._emit('beforeShutdown');
|
|
347
347
|
|
|
348
|
-
// 2. Stop accepting new connections (non-blocking
|
|
348
|
+
// 2. Stop accepting new connections (non-blocking - server.close
|
|
349
349
|
// only resolves after all existing connections end, so we don't
|
|
350
350
|
// await it here; it will resolve after the drain step finishes).
|
|
351
351
|
const serverClosePromise = this._closeServer();
|
|
@@ -28,7 +28,7 @@ const COMPRESSIBLE = /^text\/|^application\/(json|javascript|xml|x-www-form-urle
|
|
|
28
28
|
* @param {number} [opts.threshold=1024] - Minimum body size in bytes to compress.
|
|
29
29
|
* @param {number} [opts.level] - Compression level (zlib.constants.Z_DEFAULT_COMPRESSION).
|
|
30
30
|
* @param {string|string[]} [opts.encoding] - Force specific encoding(s). Default: auto-negotiate.
|
|
31
|
-
* @param {Function} [opts.filter] - `(req, res) => boolean`
|
|
31
|
+
* @param {Function} [opts.filter] - `(req, res) => boolean` - return false to skip compression.
|
|
32
32
|
* @returns {Function} Middleware `(req, res, next) => void`.
|
|
33
33
|
*
|
|
34
34
|
* @example
|
|
@@ -139,14 +139,14 @@ function compress(opts = {})
|
|
|
139
139
|
// writeHead before write), we can no longer modify them.
|
|
140
140
|
if (raw.headersSent) return false;
|
|
141
141
|
|
|
142
|
-
// Check Content-Type
|
|
142
|
+
// Check Content-Type - skip non-compressible types
|
|
143
143
|
const ct = raw.getHeader('content-type') || '';
|
|
144
144
|
if (ct && !COMPRESSIBLE.test(ct))
|
|
145
145
|
{
|
|
146
146
|
return false;
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
-
// Never compress SSE streams
|
|
149
|
+
// Never compress SSE streams - compression buffers
|
|
150
150
|
// the small frames and prevents real-time delivery.
|
|
151
151
|
if (ct.includes('text/event-stream'))
|
|
152
152
|
{
|
|
@@ -200,7 +200,7 @@ function compress(opts = {})
|
|
|
200
200
|
{
|
|
201
201
|
headersWritten = true;
|
|
202
202
|
|
|
203
|
-
// Check threshold
|
|
203
|
+
// Check threshold - if the total body is small, skip compression
|
|
204
204
|
const totalChunk = chunk ? (Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk))) : null;
|
|
205
205
|
if (totalChunk && totalChunk.length < threshold)
|
|
206
206
|
{
|
package/lib/observe/health.js
CHANGED
|
@@ -157,7 +157,7 @@ function diskSpaceCheck(opts = {})
|
|
|
157
157
|
* @param {Object<string, Function>} [opts.checks] - Named check functions. Each returns `{ healthy, details }` or a boolean/Promise.
|
|
158
158
|
* @param {number} [opts.timeout=5000] - Max time to wait for all checks in ms.
|
|
159
159
|
* @param {boolean} [opts.verbose=true] - Include check details in response.
|
|
160
|
-
* @param {Function} [opts.onFailure] - `(results) => void`
|
|
160
|
+
* @param {Function} [opts.onFailure] - `(results) => void` - called when any check fails.
|
|
161
161
|
* @returns {Function} Route handler `(req, res) => void`.
|
|
162
162
|
*
|
|
163
163
|
* @example
|
package/lib/observe/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @module observe
|
|
3
|
-
* @description Observability suite
|
|
3
|
+
* @description Observability suite - structured logging, metrics, tracing,
|
|
4
4
|
* and health checks. Zero external dependencies.
|
|
5
5
|
*/
|
|
6
6
|
const { Logger, structuredLogger, LEVELS, LEVEL_NAMES } = require('./logger');
|
package/lib/observe/logger.js
CHANGED
|
@@ -256,7 +256,7 @@ class Logger
|
|
|
256
256
|
* `requestId`, `method`, `url`, `status`, `duration`, `ip`, `userAgent`,
|
|
257
257
|
* and `contentLength`.
|
|
258
258
|
*
|
|
259
|
-
* Also attaches `req.log`
|
|
259
|
+
* Also attaches `req.log` - a child logger with bound request context
|
|
260
260
|
* so handlers can log with full correlation.
|
|
261
261
|
*
|
|
262
262
|
* @param {object} [opts] - Configuration options.
|
|
@@ -266,8 +266,8 @@ class Logger
|
|
|
266
266
|
* @param {boolean} [opts.colors] - ANSI colors (default: TTY detection). Only applies to pretty format.
|
|
267
267
|
* @param {boolean} [opts.timestamps=true] - Include timestamps.
|
|
268
268
|
* @param {WritableStream} [opts.stream] - Output stream override.
|
|
269
|
-
* @param {Function} [opts.skip] - `(req, res) => boolean`
|
|
270
|
-
* @param {Function} [opts.customFields] - `(req, res) => object`
|
|
269
|
+
* @param {Function} [opts.skip] - `(req, res) => boolean` - skip logging for certain requests.
|
|
270
|
+
* @param {Function} [opts.customFields] - `(req, res) => object` - extra fields to merge into each request log entry.
|
|
271
271
|
* @param {string} [opts.msg] - Custom message template. Supports placeholders: `:method`, `:url`, `:status`, `:duration`.
|
|
272
272
|
* @returns {Function} Middleware `(req, res, next) => void`.
|
|
273
273
|
*
|
package/lib/observe/metrics.js
CHANGED
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
|
|
36
36
|
/**
|
|
37
37
|
* Prometheus-compatible Counter. Monotonically increasing.
|
|
38
|
-
* Thread-safe for single-threaded Node
|
|
38
|
+
* Thread-safe for single-threaded Node - no locking needed.
|
|
39
39
|
*/
|
|
40
40
|
class Counter
|
|
41
41
|
{
|
|
@@ -271,7 +271,7 @@ class Histogram
|
|
|
271
271
|
* Start a timer that, when stopped, observes the elapsed duration in seconds.
|
|
272
272
|
*
|
|
273
273
|
* @param {object} [labels] - Label values.
|
|
274
|
-
* @returns {Function} Stop function
|
|
274
|
+
* @returns {Function} Stop function - call it to record the duration.
|
|
275
275
|
*
|
|
276
276
|
* @example
|
|
277
277
|
* const end = histogram.startTimer({ method: 'GET' });
|
|
@@ -654,8 +654,8 @@ function createDefaultMetrics(registry)
|
|
|
654
654
|
*
|
|
655
655
|
* @param {object} [opts] - Options.
|
|
656
656
|
* @param {MetricsRegistry} [opts.registry] - Metrics registry. Creates a new one if not provided.
|
|
657
|
-
* @param {Function} [opts.routeLabel] - `(req) => string`
|
|
658
|
-
* @param {Function} [opts.skip] - `(req) => boolean`
|
|
657
|
+
* @param {Function} [opts.routeLabel] - `(req) => string` - extract route label for metrics. Default: `req.route || req.url`.
|
|
658
|
+
* @param {Function} [opts.skip] - `(req) => boolean` - skip metrics for certain requests.
|
|
659
659
|
* @returns {Function} Middleware `(req, res, next) => void`.
|
|
660
660
|
*
|
|
661
661
|
* @example
|
package/lib/observe/tracing.js
CHANGED
|
@@ -288,7 +288,7 @@ class Tracer
|
|
|
288
288
|
* @constructor
|
|
289
289
|
* @param {object} [opts] - Tracer options.
|
|
290
290
|
* @param {string} [opts.serviceName='unknown'] - Service name for all spans.
|
|
291
|
-
* @param {Function} [opts.exporter] - `(spans: object[]) => void | Promise<void>`
|
|
291
|
+
* @param {Function} [opts.exporter] - `(spans: object[]) => void | Promise<void>` - called with batches of serialised spans.
|
|
292
292
|
* @param {number} [opts.batchSize=100] - Max spans per export batch.
|
|
293
293
|
* @param {number} [opts.flushInterval=5000] - Auto-flush interval in ms.
|
|
294
294
|
* @param {number} [opts.sampleRate=1.0] - Sampling rate (0.0 to 1.0). 1.0 = sample everything.
|
|
@@ -414,7 +414,7 @@ class Tracer
|
|
|
414
414
|
}
|
|
415
415
|
catch (_)
|
|
416
416
|
{
|
|
417
|
-
// Silently drop export errors
|
|
417
|
+
// Silently drop export errors - tracing should never break the app
|
|
418
418
|
}
|
|
419
419
|
}
|
|
420
420
|
|
|
@@ -439,8 +439,8 @@ class Tracer
|
|
|
439
439
|
*
|
|
440
440
|
* @param {object} [opts] - Options.
|
|
441
441
|
* @param {Tracer} [opts.tracer] - Tracer instance. Creates a default if not provided.
|
|
442
|
-
* @param {Function} [opts.routeLabel] - `(req) => string`
|
|
443
|
-
* @param {Function} [opts.skip] - `(req) => boolean`
|
|
442
|
+
* @param {Function} [opts.routeLabel] - `(req) => string` - extract route label for span name.
|
|
443
|
+
* @param {Function} [opts.skip] - `(req) => boolean` - skip tracing for certain requests.
|
|
444
444
|
* @param {boolean} [opts.propagate=true] - Propagate W3C trace context via response headers.
|
|
445
445
|
* @returns {Function} Middleware `(req, res, next) => void`.
|
|
446
446
|
*
|
package/lib/orm/adapters/json.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @module orm/adapters/json
|
|
3
3
|
* @description JSON file-backed database adapter.
|
|
4
|
-
* Persists data to JSON files on disk
|
|
4
|
+
* Persists data to JSON files on disk - one file per table.
|
|
5
5
|
* Zero-dependency, suitable for prototyping, small apps, and
|
|
6
6
|
* embedded scenarios. Uses atomic writes for safety.
|
|
7
7
|
*
|
|
@@ -413,7 +413,7 @@ class MemoryAdapter
|
|
|
413
413
|
for (let i = 0; i < where.length; i++)
|
|
414
414
|
{
|
|
415
415
|
const clause = where[i];
|
|
416
|
-
// Skip raw SQL clauses
|
|
416
|
+
// Skip raw SQL clauses - not supported in memory adapter
|
|
417
417
|
if (clause.raw) continue;
|
|
418
418
|
const matches = this._matchClause(row, clause);
|
|
419
419
|
|
|
@@ -695,7 +695,7 @@ class MemoryAdapter
|
|
|
695
695
|
|
|
696
696
|
/**
|
|
697
697
|
* Drop an index.
|
|
698
|
-
* @param {string} _table - Table name (unused
|
|
698
|
+
* @param {string} _table - Table name (unused - searches all tables).
|
|
699
699
|
* @param {string} name - Index name.
|
|
700
700
|
* @returns {Promise<void>}
|
|
701
701
|
*/
|
|
@@ -369,7 +369,7 @@ class MongoAdapter
|
|
|
369
369
|
|
|
370
370
|
let results = await cursor.toArray();
|
|
371
371
|
|
|
372
|
-
// Distinct
|
|
372
|
+
// Distinct - in-memory since MongoDB distinct() only returns values for a single field
|
|
373
373
|
if (distinct && fields && fields.length > 0)
|
|
374
374
|
{
|
|
375
375
|
const seen = new Set();
|
|
@@ -420,7 +420,7 @@ class MongoAdapter
|
|
|
420
420
|
for (let i = 0; i < where.length; i++)
|
|
421
421
|
{
|
|
422
422
|
const w = where[i];
|
|
423
|
-
// Skip raw SQL clauses
|
|
423
|
+
// Skip raw SQL clauses - not applicable to MongoDB
|
|
424
424
|
if (w.raw) continue;
|
|
425
425
|
const { field, op, value, logic } = w;
|
|
426
426
|
const clause = this._opToMongo(field, op, value);
|
|
@@ -685,7 +685,7 @@ class MysqlAdapter extends BaseSqlAdapter
|
|
|
685
685
|
}
|
|
686
686
|
|
|
687
687
|
/**
|
|
688
|
-
* Get full database overview
|
|
688
|
+
* Get full database overview - all tables with size and row counts.
|
|
689
689
|
* Returns a structured database summary.
|
|
690
690
|
* @returns {Promise<{ tables: Array, totalSize: string, totalRows: number }>}
|
|
691
691
|
*/
|
|
@@ -724,7 +724,7 @@ class MysqlAdapter extends BaseSqlAdapter
|
|
|
724
724
|
}
|
|
725
725
|
|
|
726
726
|
/**
|
|
727
|
-
* Get processlist
|
|
727
|
+
* Get processlist - active connections/queries.
|
|
728
728
|
* @returns {Promise<Array<{ id: number, user: string, host: string, db: string, command: string, time: number, state: string, info: string }>>}
|
|
729
729
|
*/
|
|
730
730
|
async processlist()
|
|
@@ -129,7 +129,7 @@ class PostgresAdapter extends BaseSqlAdapter
|
|
|
129
129
|
{
|
|
130
130
|
const w = where[i];
|
|
131
131
|
|
|
132
|
-
// Handle raw WHERE clauses (from whereRaw)
|
|
132
|
+
// Handle raw WHERE clauses (from whereRaw) - convert ? to $N
|
|
133
133
|
if (w.raw)
|
|
134
134
|
{
|
|
135
135
|
let rawExpr = w.raw;
|
|
@@ -847,7 +847,7 @@ class PostgresAdapter extends BaseSqlAdapter
|
|
|
847
847
|
}
|
|
848
848
|
|
|
849
849
|
/**
|
|
850
|
-
* Get full database overview
|
|
850
|
+
* Get full database overview - all tables with size and row counts.
|
|
851
851
|
* @returns {Promise<{ tables: Array, totalSize: string, totalRows: number }>}
|
|
852
852
|
*/
|
|
853
853
|
async overview()
|
|
@@ -84,7 +84,7 @@ class SqliteAdapter extends BaseSqlAdapter
|
|
|
84
84
|
this._db = new Database(filename, dbOpts);
|
|
85
85
|
this._filename = filename;
|
|
86
86
|
|
|
87
|
-
/** @private Prepared statement cache
|
|
87
|
+
/** @private Prepared statement cache - avoids recompilation overhead */
|
|
88
88
|
this._stmtCache = new Map();
|
|
89
89
|
/** @private Maximum cached statements before LRU eviction */
|
|
90
90
|
this._stmtCacheMax = options.stmtCacheSize || 256;
|
|
@@ -685,7 +685,7 @@ class SqliteAdapter extends BaseSqlAdapter
|
|
|
685
685
|
}
|
|
686
686
|
|
|
687
687
|
/**
|
|
688
|
-
* Get counts for all tables
|
|
688
|
+
* Get counts for all tables - structured database overview.
|
|
689
689
|
* @returns {{ tables: Array<{ name: string, rows: number }>, totalRows: number, fileSize: string }}
|
|
690
690
|
*/
|
|
691
691
|
overview()
|
|
@@ -807,7 +807,7 @@ class SqliteAdapter extends BaseSqlAdapter
|
|
|
807
807
|
|
|
808
808
|
/**
|
|
809
809
|
* Drop an index.
|
|
810
|
-
* @param {string} _table - Table name (unused
|
|
810
|
+
* @param {string} _table - Table name (unused - SQLite indexes are schema-scoped).
|
|
811
811
|
* @param {string} name - Index name.
|
|
812
812
|
*/
|
|
813
813
|
dropIndex(_table, name)
|
package/lib/orm/audit.js
CHANGED
package/lib/orm/index.js
CHANGED
|
@@ -5,12 +5,12 @@
|
|
|
5
5
|
* `TYPES` enum, and schema helpers.
|
|
6
6
|
*
|
|
7
7
|
* Supported adapters (all optional "bring your own driver"):
|
|
8
|
-
* - `memory`
|
|
9
|
-
* - `json`
|
|
10
|
-
* - `sqlite`
|
|
11
|
-
* - `mysql`
|
|
12
|
-
* - `postgres`
|
|
13
|
-
* - `mongo`
|
|
8
|
+
* - `memory` - in-process (no driver needed)
|
|
9
|
+
* - `json` - JSON file persistence (no driver needed)
|
|
10
|
+
* - `sqlite` - requires `better-sqlite3`
|
|
11
|
+
* - `mysql` - requires `mysql2`
|
|
12
|
+
* - `postgres` - requires `pg`
|
|
13
|
+
* - `mongo` - requires `mongodb`
|
|
14
14
|
*
|
|
15
15
|
* @example
|
|
16
16
|
* const { Database, Model, TYPES } = require('@zero-server/sdk');
|
|
@@ -231,7 +231,7 @@ class Database
|
|
|
231
231
|
}
|
|
232
232
|
|
|
233
233
|
/**
|
|
234
|
-
* Synchronise all registered models
|
|
234
|
+
* Synchronise all registered models - create tables if they don't exist.
|
|
235
235
|
* Tables are ordered so referenced tables are created first (topological sort).
|
|
236
236
|
* @returns {Promise<void>}
|
|
237
237
|
*/
|
package/lib/orm/migrate.js
CHANGED
|
@@ -322,7 +322,7 @@ class Migrator
|
|
|
322
322
|
|
|
323
323
|
/**
|
|
324
324
|
* Fresh start: drop ALL tables (not just migrated ones) then re-migrate.
|
|
325
|
-
* ⚠️ DESTRUCTIVE
|
|
325
|
+
* ⚠️ DESTRUCTIVE - use with caution.
|
|
326
326
|
*
|
|
327
327
|
* @returns {Promise<{ migrated: string[], batch: number }>}
|
|
328
328
|
*/
|
package/lib/orm/model.js
CHANGED
|
@@ -38,13 +38,13 @@ const { EventEmitter } = require('events');
|
|
|
38
38
|
class Model
|
|
39
39
|
{
|
|
40
40
|
/**
|
|
41
|
-
* Table name
|
|
41
|
+
* Table name - override in subclass.
|
|
42
42
|
* @type {string}
|
|
43
43
|
*/
|
|
44
44
|
static table = '';
|
|
45
45
|
|
|
46
46
|
/**
|
|
47
|
-
* Column schema
|
|
47
|
+
* Column schema - override in subclass.
|
|
48
48
|
* @type {Object<string, object>}
|
|
49
49
|
*/
|
|
50
50
|
static schema = {};
|
|
@@ -74,7 +74,7 @@ class Model
|
|
|
74
74
|
static hidden = [];
|
|
75
75
|
|
|
76
76
|
/**
|
|
77
|
-
* Named query scopes
|
|
77
|
+
* Named query scopes - reusable query conditions.
|
|
78
78
|
* Each scope is a function that receives a Query and returns it.
|
|
79
79
|
* @type {Object<string, Function>}
|
|
80
80
|
*
|
|
@@ -103,7 +103,7 @@ class Model
|
|
|
103
103
|
// -- Computed & Virtual Columns ---------------------
|
|
104
104
|
|
|
105
105
|
/**
|
|
106
|
-
* Computed column definitions
|
|
106
|
+
* Computed column definitions - virtual columns derived from other fields.
|
|
107
107
|
* Not stored in the database; calculated on the fly.
|
|
108
108
|
* Each entry maps a column name to a getter function `(instance) => value`.
|
|
109
109
|
* @type {Object<string, Function>}
|
|
@@ -122,17 +122,17 @@ class Model
|
|
|
122
122
|
static computed = {};
|
|
123
123
|
|
|
124
124
|
/**
|
|
125
|
-
* Attribute casts
|
|
125
|
+
* Attribute casts - automatic type transformations on get/set.
|
|
126
126
|
* Maps column names to cast types or custom cast objects.
|
|
127
127
|
*
|
|
128
128
|
* Built-in cast types:
|
|
129
|
-
* - `'json'`
|
|
130
|
-
* - `'boolean'`
|
|
131
|
-
* - `'integer'`
|
|
132
|
-
* - `'float'`
|
|
133
|
-
* - `'date'`
|
|
134
|
-
* - `'string'`
|
|
135
|
-
* - `'array'`
|
|
129
|
+
* - `'json'` - JSON.parse on get, JSON.stringify on set
|
|
130
|
+
* - `'boolean'` - Cast to true/false
|
|
131
|
+
* - `'integer'` - parseInt
|
|
132
|
+
* - `'float'` - parseFloat
|
|
133
|
+
* - `'date'` - Cast to Date object
|
|
134
|
+
* - `'string'` - Cast to String
|
|
135
|
+
* - `'array'` - JSON parse/stringify for array data
|
|
136
136
|
*
|
|
137
137
|
* Custom casts:
|
|
138
138
|
* - `{ get: (v) => transformed, set: (v) => transformed }`
|
|
@@ -211,7 +211,7 @@ class Model
|
|
|
211
211
|
static _relations = {};
|
|
212
212
|
|
|
213
213
|
/**
|
|
214
|
-
* Database adapter reference
|
|
214
|
+
* Database adapter reference - set by Database.register().
|
|
215
215
|
* @type {object|null}
|
|
216
216
|
* @private
|
|
217
217
|
*/
|
|
@@ -222,7 +222,7 @@ class Model
|
|
|
222
222
|
/**
|
|
223
223
|
* @constructor
|
|
224
224
|
* Create a model instance from a data row.
|
|
225
|
-
* Generally you won't call this directly
|
|
225
|
+
* Generally you won't call this directly - use static methods.
|
|
226
226
|
*
|
|
227
227
|
* @param {object} data - Row data.
|
|
228
228
|
*/
|
|
@@ -825,7 +825,7 @@ class Model
|
|
|
825
825
|
|
|
826
826
|
/**
|
|
827
827
|
* Get all records, optionally filtered.
|
|
828
|
-
* Alias for find()
|
|
828
|
+
* Alias for find() - for LINQ-familiarity.
|
|
829
829
|
*
|
|
830
830
|
* @param {object} [conditions={}] - WHERE conditions.
|
|
831
831
|
* @returns {Promise<Model[]>} All matching records.
|
package/lib/orm/procedures.js
CHANGED
|
@@ -56,7 +56,7 @@ class StoredProcedure
|
|
|
56
56
|
throw new Error('StoredProcedure requires a "body" string');
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
// Sanitize name
|
|
59
|
+
// Sanitize name - only allow identifiers
|
|
60
60
|
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name))
|
|
61
61
|
{
|
|
62
62
|
throw new Error(`Invalid procedure name: "${name}"`);
|
package/lib/orm/profiler.js
CHANGED
|
@@ -117,7 +117,7 @@ class QueryProfiler
|
|
|
117
117
|
|
|
118
118
|
if (!counter || (now - counter.firstTs) >= this._n1Window)
|
|
119
119
|
{
|
|
120
|
-
// Window expired or first query
|
|
120
|
+
// Window expired or first query - start a new window
|
|
121
121
|
counter = { count: 1, firstTs: now };
|
|
122
122
|
this._selectCounters.set(table, counter);
|
|
123
123
|
return;
|