@zero-server/orm 0.9.5 → 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 +1 -1
- package/index.d.ts +1 -1
- package/index.js +1 -1
- package/lib/debug.js +10 -10
- 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/package.json +3 -3
- 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
|
@@ -10,7 +10,7 @@ Full-featured ORM with seven adapters (memory, JSON file, SQLite, MySQL, Postgre
|
|
|
10
10
|
npm install @zero-server/orm
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
Or
|
|
13
|
+
Or install the full SDK to get everything at once:
|
|
14
14
|
|
|
15
15
|
```bash
|
|
16
16
|
npm install @zero-server/sdk
|
package/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
// AUTO-GENERATED by .tools/generate-package-stubs.js
|
|
1
|
+
// AUTO-GENERATED by .tools/generate-package-stubs.js - edit .tools/scope-manifest.js and re-run `npm run packages:generate`.
|
|
2
2
|
export * from './types/orm';
|
package/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// AUTO-GENERATED by .tools/generate-package-stubs.js
|
|
1
|
+
// AUTO-GENERATED by .tools/generate-package-stubs.js - edit .tools/scope-manifest.js and re-run `npm run packages:generate`.
|
|
2
2
|
'use strict';
|
|
3
3
|
const lib = require("./lib/orm");
|
|
4
4
|
|
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
|
|
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
|
|
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
|
|
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)
|
|
282
|
-
* `'debug'` (1)
|
|
283
|
-
* `'info'` (2)
|
|
284
|
-
* `'warn'` (3)
|
|
285
|
-
* `'error'` (4)
|
|
286
|
-
* `'fatal'` (5)
|
|
287
|
-
* `'silent'` (6)
|
|
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
|
{
|
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;
|
package/lib/orm/query.js
CHANGED
|
@@ -388,7 +388,7 @@ class Query
|
|
|
388
388
|
}
|
|
389
389
|
|
|
390
390
|
/**
|
|
391
|
-
* Alias for with()
|
|
391
|
+
* Alias for with() - mirrors Entity Framework include syntax.
|
|
392
392
|
* @param {...string|object} relations - Relation names or config objects to eager-load.
|
|
393
393
|
* @returns {Query} This query for chaining.
|
|
394
394
|
*/
|
|
@@ -998,7 +998,7 @@ class Query
|
|
|
998
998
|
}
|
|
999
999
|
|
|
1000
1000
|
/**
|
|
1001
|
-
* Make Query thenable
|
|
1001
|
+
* Make Query thenable - allows `await query`.
|
|
1002
1002
|
* @param {Function} resolve - Fulfillment handler.
|
|
1003
1003
|
* @param {Function} reject - Rejection handler.
|
|
1004
1004
|
* @returns {Promise} Result of exec().
|
|
@@ -1043,7 +1043,7 @@ class Query
|
|
|
1043
1043
|
}
|
|
1044
1044
|
|
|
1045
1045
|
/**
|
|
1046
|
-
* Alias for exec
|
|
1046
|
+
* Alias for exec - explicitly convert to array.
|
|
1047
1047
|
* @returns {Promise<Array>} Matching rows as an array.
|
|
1048
1048
|
*/
|
|
1049
1049
|
toArray()
|
|
@@ -1072,7 +1072,7 @@ class Query
|
|
|
1072
1072
|
}
|
|
1073
1073
|
|
|
1074
1074
|
/**
|
|
1075
|
-
* Alias for first()
|
|
1075
|
+
* Alias for first() - C# FirstOrDefault returns null on empty.
|
|
1076
1076
|
* @returns {Promise<object|null>} Matching row or null.
|
|
1077
1077
|
*/
|
|
1078
1078
|
firstOrDefault()
|
|
@@ -1081,7 +1081,7 @@ class Query
|
|
|
1081
1081
|
}
|
|
1082
1082
|
|
|
1083
1083
|
/**
|
|
1084
|
-
* Alias for avg()
|
|
1084
|
+
* Alias for avg() - C# naming.
|
|
1085
1085
|
* @param {string} field - Column name.
|
|
1086
1086
|
* @returns {Promise<number>} Average of the column values.
|
|
1087
1087
|
*/
|
|
@@ -1091,7 +1091,7 @@ class Query
|
|
|
1091
1091
|
}
|
|
1092
1092
|
|
|
1093
1093
|
/**
|
|
1094
|
-
* Alias for reduce()
|
|
1094
|
+
* Alias for reduce() - C# Aggregate naming.
|
|
1095
1095
|
* @param {Function} fn - Callback function.
|
|
1096
1096
|
* @param {*} seed - Initial accumulator value.
|
|
1097
1097
|
* @returns {Promise<*>} Accumulated value.
|
|
@@ -1126,7 +1126,7 @@ class Query
|
|
|
1126
1126
|
}
|
|
1127
1127
|
|
|
1128
1128
|
/**
|
|
1129
|
-
* Alias for last()
|
|
1129
|
+
* Alias for last() - C# naming.
|
|
1130
1130
|
* @returns {Promise<object|null>} Matching row or null.
|
|
1131
1131
|
*/
|
|
1132
1132
|
lastOrDefault()
|
|
@@ -1346,7 +1346,7 @@ class Query
|
|
|
1346
1346
|
// -- Projection --
|
|
1347
1347
|
|
|
1348
1348
|
/**
|
|
1349
|
-
* FlatMap
|
|
1349
|
+
* FlatMap - project each element to an array and flatten.
|
|
1350
1350
|
* @param {Function} fn - (item, index) => Array
|
|
1351
1351
|
* @returns {Promise<Array>} Flattened projected results.
|
|
1352
1352
|
*/
|
|
@@ -1599,7 +1599,7 @@ class Query
|
|
|
1599
1599
|
}
|
|
1600
1600
|
|
|
1601
1601
|
/**
|
|
1602
|
-
* Inverse of when
|
|
1602
|
+
* Inverse of when - apply query logic when condition is falsy.
|
|
1603
1603
|
*
|
|
1604
1604
|
* @param {*} condition - Condition to evaluate.
|
|
1605
1605
|
* @param {Function} fn - Callback function.
|
package/lib/orm/schema.js
CHANGED
|
@@ -180,7 +180,7 @@ function validateValue(value, colDef, colName)
|
|
|
180
180
|
try { return JSON.parse(value); }
|
|
181
181
|
catch (e) { throw new Error(`"${colName}" must be valid JSON`); }
|
|
182
182
|
}
|
|
183
|
-
// Already an object/array
|
|
183
|
+
// Already an object/array - return as-is for storage
|
|
184
184
|
return value;
|
|
185
185
|
}
|
|
186
186
|
case 'uuid':
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* job titles, prefixes/suffixes, gender, bio phrases, zodiac signs.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
/** Title prefixes
|
|
9
|
+
/** Title prefixes - separate lists per target sex for contextual use. */
|
|
10
10
|
const NAME_PREFIXES = {
|
|
11
11
|
male: ['Mr.', 'Dr.', 'Prof.'],
|
|
12
12
|
female: ['Ms.', 'Mrs.', 'Dr.', 'Prof.', 'Miss'],
|
package/lib/orm/seed/fake.js
CHANGED
|
@@ -5,13 +5,13 @@
|
|
|
5
5
|
* @description Extensible fake data generator.
|
|
6
6
|
*
|
|
7
7
|
* Key capabilities:
|
|
8
|
-
* • Seeded / reproducible output
|
|
9
|
-
* • Guaranteed-unique values
|
|
10
|
-
* • Multi-locale names
|
|
11
|
-
* • Rich phone formats
|
|
12
|
-
* • Configurable emails
|
|
13
|
-
* • Flexible usernames
|
|
14
|
-
* • Fixed-length numeric strings
|
|
8
|
+
* • Seeded / reproducible output - Fake.seed(42)
|
|
9
|
+
* • Guaranteed-unique values - Fake.unique(() => Fake.email())
|
|
10
|
+
* • Multi-locale names - Fake.firstName({ locale: 'ja', sex: 'female' })
|
|
11
|
+
* • Rich phone formats - Fake.phone({ countryCode: 'DE', format: 'international' })
|
|
12
|
+
* • Configurable emails - Fake.email({ provider: 'company.io' })
|
|
13
|
+
* • Flexible usernames - Fake.username({ style: 'underscore' })
|
|
14
|
+
* • Fixed-length numeric strings - Fake.numericString(8)
|
|
15
15
|
* • Person: job titles, bio, zodiac, gender, prefix/suffix
|
|
16
16
|
* • Location: city, country, state, address, lat/lng, timezone
|
|
17
17
|
* • Commerce: product, company, category, price, industry
|
|
@@ -928,7 +928,7 @@ class Fake
|
|
|
928
928
|
// -- Internet & Network -------------------------------------------------
|
|
929
929
|
|
|
930
930
|
/**
|
|
931
|
-
* Random email address (backward-compatible shorthand
|
|
931
|
+
* Random email address (backward-compatible shorthand - same as email()).
|
|
932
932
|
* Accepts no arguments for historical use.
|
|
933
933
|
*/
|
|
934
934
|
|
|
@@ -1051,7 +1051,7 @@ class Fake
|
|
|
1051
1051
|
static userAgent() { return _pick(USER_AGENTS); }
|
|
1052
1052
|
|
|
1053
1053
|
/**
|
|
1054
|
-
* Random password-like string. NOT suitable for real passwords
|
|
1054
|
+
* Random password-like string. NOT suitable for real passwords - uses a
|
|
1055
1055
|
* PRNG seeded from Math.random, not a CSPRNG.
|
|
1056
1056
|
*
|
|
1057
1057
|
* @param {object} [options] - Configuration options.
|
|
@@ -1180,7 +1180,7 @@ class Fake
|
|
|
1180
1180
|
}
|
|
1181
1181
|
}
|
|
1182
1182
|
|
|
1183
|
-
// Static uniqueness tracker
|
|
1183
|
+
// Static uniqueness tracker - shared across all calls in the process lifetime
|
|
1184
1184
|
Fake._tracker = new UniqueTracker();
|
|
1185
1185
|
|
|
1186
1186
|
module.exports = { Fake };
|
package/lib/orm/seed/index.js
CHANGED
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
* @description Public API for the seed subsystem.
|
|
6
6
|
*
|
|
7
7
|
* Re-exports:
|
|
8
|
-
* - `Fake`
|
|
9
|
-
* - `Factory`
|
|
10
|
-
* - `Seeder`
|
|
11
|
-
* - `SeederRunner`
|
|
8
|
+
* - `Fake` - static fake-data generator
|
|
9
|
+
* - `Factory` - model factory for defining / creating test fixtures
|
|
10
|
+
* - `Seeder` - base class for database seeders
|
|
11
|
+
* - `SeederRunner` - orchestrates running multiple seeders
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
const { Fake } = require('./fake');
|
package/lib/orm/seed/rng.js
CHANGED
package/lib/orm/snapshot.js
CHANGED
|
@@ -234,7 +234,7 @@ function generateMigrationCode(migrationName, changes, currentSnap)
|
|
|
234
234
|
for (const table of changes.tables.dropped)
|
|
235
235
|
{
|
|
236
236
|
upLines.push(` await db.adapter.dropTable('${table}');`);
|
|
237
|
-
// down recreates
|
|
237
|
+
// down recreates - but we need the previous snapshot's schema for that
|
|
238
238
|
// This is handled via the `prev` reference embedded in the dropped table
|
|
239
239
|
}
|
|
240
240
|
|
|
@@ -268,7 +268,7 @@ function generateMigrationCode(migrationName, changes, currentSnap)
|
|
|
268
268
|
return `'use strict';
|
|
269
269
|
|
|
270
270
|
/**
|
|
271
|
-
* Auto-generated migration
|
|
271
|
+
* Auto-generated migration - ${migrationName}
|
|
272
272
|
* Generated by: npx zh make:migration
|
|
273
273
|
*/
|
|
274
274
|
module.exports = {
|
package/lib/orm/tenancy.js
CHANGED
|
@@ -43,8 +43,8 @@ class TenantContext
|
|
|
43
43
|
/**
|
|
44
44
|
* Multi-tenancy manager.
|
|
45
45
|
* Supports two strategies:
|
|
46
|
-
* - `'row'`
|
|
47
|
-
* - `'schema'`
|
|
46
|
+
* - `'row'` - adds a tenant column to every query (row-level isolation)
|
|
47
|
+
* - `'schema'` - uses separate database schemas per tenant (PostgreSQL)
|
|
48
48
|
*/
|
|
49
49
|
class TenantManager
|
|
50
50
|
{
|
|
@@ -242,7 +242,7 @@ class TenantManager
|
|
|
242
242
|
const origCount = ModelClass.count.bind(ModelClass);
|
|
243
243
|
const origExists = ModelClass.exists.bind(ModelClass);
|
|
244
244
|
|
|
245
|
-
// Patch query()
|
|
245
|
+
// Patch query() - all query builder paths
|
|
246
246
|
ModelClass.query = function ()
|
|
247
247
|
{
|
|
248
248
|
const q = origQuery();
|
|
@@ -251,7 +251,7 @@ class TenantManager
|
|
|
251
251
|
return q;
|
|
252
252
|
};
|
|
253
253
|
|
|
254
|
-
// Patch create()
|
|
254
|
+
// Patch create() - inject tenant column
|
|
255
255
|
ModelClass.create = async function (data)
|
|
256
256
|
{
|
|
257
257
|
const tid = manager.getCurrentTenant();
|
|
@@ -283,7 +283,7 @@ class TenantManager
|
|
|
283
283
|
return origFindOne(conditions);
|
|
284
284
|
};
|
|
285
285
|
|
|
286
|
-
// Patch findById()
|
|
286
|
+
// Patch findById() - still applies tenant scope
|
|
287
287
|
ModelClass.findById = async function (id)
|
|
288
288
|
{
|
|
289
289
|
const tid = manager.getCurrentTenant();
|
|
@@ -344,7 +344,7 @@ class TenantManager
|
|
|
344
344
|
throw new Error('Schema-based tenancy requires a SQL adapter');
|
|
345
345
|
}
|
|
346
346
|
|
|
347
|
-
// Sanitize schema name
|
|
347
|
+
// Sanitize schema name - only allow alphanumerics and underscores
|
|
348
348
|
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(schema))
|
|
349
349
|
{
|
|
350
350
|
throw new Error(`Invalid schema name: "${schema}"`);
|
package/lib/orm/views.js
CHANGED
|
@@ -293,7 +293,7 @@ class DatabaseView
|
|
|
293
293
|
const descriptor = this._query.build();
|
|
294
294
|
const table = descriptor.table;
|
|
295
295
|
|
|
296
|
-
// Validate field names and table
|
|
296
|
+
// Validate field names and table - identifier-safe only
|
|
297
297
|
const idRe = /^[a-zA-Z_][a-zA-Z0-9_.*]*$/;
|
|
298
298
|
const fields = descriptor.fields
|
|
299
299
|
? descriptor.fields.filter(f => idRe.test(f)).join(', ') || '*'
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zero-server/orm",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.7",
|
|
4
4
|
"description": "Database, Model, Query, migrations, seeds, search, geo, tenancy, audit.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"zero-server",
|
|
@@ -45,10 +45,10 @@
|
|
|
45
45
|
},
|
|
46
46
|
"sideEffects": false,
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@zero-server/errors": "0.9.
|
|
48
|
+
"@zero-server/errors": "0.9.7"
|
|
49
49
|
},
|
|
50
50
|
"peerDependencies": {
|
|
51
|
-
"@zero-server/sdk": ">=0.9.
|
|
51
|
+
"@zero-server/sdk": ">=0.9.7"
|
|
52
52
|
},
|
|
53
53
|
"peerDependenciesMeta": {
|
|
54
54
|
"@zero-server/sdk": {
|
package/types/body.d.ts
CHANGED
package/types/cli.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
// Re-exports for @zero-server/cli
|
|
1
|
+
// Re-exports for @zero-server/cli - CLI runner types
|
|
2
2
|
export { CLI, runCLI } from './orm';
|
package/types/index.d.ts
CHANGED
|
@@ -11,6 +11,18 @@ export { RouterInstance, RouteChain, RouteEntry, RouteInfo, RouteOptions, RouteH
|
|
|
11
11
|
export { Request, RangeResult } from './request';
|
|
12
12
|
export { Response, SendFileOptions, CookieOptions, PushOptions } from './response';
|
|
13
13
|
export { SSEOptions, SSEStream } from './sse';
|
|
14
|
+
export {
|
|
15
|
+
createWebRTC, SignalingHub, Room as WebRTCRoom, Peer as WebRTCPeer,
|
|
16
|
+
parseSdp, stringifySdp, parseCandidate, stringifyCandidate,
|
|
17
|
+
stunBinding, encodeBindingRequest, decodeMessage,
|
|
18
|
+
encodeXorMappedAddress, decodeXorMappedAddress,
|
|
19
|
+
STUN_MAGIC_COOKIE, STUN_METHOD, STUN_CLASS, STUN_ATTR,
|
|
20
|
+
issueTurnCredentials, TurnServer, SfuAdapter,
|
|
21
|
+
signJoinToken, verifyJoinToken,
|
|
22
|
+
WebRTCOptions, RoomOptions as WebRTCRoomOptions, PeerInfo, SignalingMessage,
|
|
23
|
+
IceServerConfig, TurnCredentials, IssueTurnCredentialsOptions, ClusterAdapter as WebRTCClusterAdapter,
|
|
24
|
+
WebRTCError, SignalingError, IceError, TurnError, SdpError,
|
|
25
|
+
} from './webrtc';
|
|
14
26
|
export { LifecycleManager, LifecycleState, LIFECYCLE_STATE } from './lifecycle';
|
|
15
27
|
export { ClusterManager, ClusterOptions, cluster } from './cluster';
|
|
16
28
|
export {
|
|
@@ -294,10 +306,10 @@ declare const zeroServer: {
|
|
|
294
306
|
LIFECYCLE_STATE: typeof LIFECYCLE_STATE;
|
|
295
307
|
ClusterManager: typeof ClusterManager;
|
|
296
308
|
cluster: typeof clusterize;
|
|
297
|
-
// Observability
|
|
309
|
+
// Observability - Structured Logging
|
|
298
310
|
Logger: typeof Logger;
|
|
299
311
|
structuredLogger: typeof structuredLogger;
|
|
300
|
-
// Observability
|
|
312
|
+
// Observability - Metrics
|
|
301
313
|
Counter: typeof Counter;
|
|
302
314
|
Gauge: typeof Gauge;
|
|
303
315
|
Histogram: typeof Histogram;
|
|
@@ -306,14 +318,14 @@ declare const zeroServer: {
|
|
|
306
318
|
createDefaultMetrics: typeof createDefaultMetrics;
|
|
307
319
|
metricsMiddleware: typeof metricsMiddleware;
|
|
308
320
|
metricsEndpoint: typeof metricsEndpointHandler;
|
|
309
|
-
// Observability
|
|
321
|
+
// Observability - Tracing
|
|
310
322
|
Span: typeof Span;
|
|
311
323
|
Tracer: typeof Tracer;
|
|
312
324
|
parseTraceparent: typeof parseTraceparent;
|
|
313
325
|
formatTraceparent: typeof formatTraceparent;
|
|
314
326
|
tracingMiddleware: typeof tracingMiddleware;
|
|
315
327
|
instrumentFetch: typeof instrumentFetch;
|
|
316
|
-
// Observability
|
|
328
|
+
// Observability - Health Checks
|
|
317
329
|
healthCheck: typeof healthCheck;
|
|
318
330
|
createHealthHandlers: typeof createHealthHandlers;
|
|
319
331
|
memoryCheck: typeof memoryCheck;
|
package/types/middleware.d.ts
CHANGED
|
@@ -136,7 +136,7 @@ export interface CompressOptions {
|
|
|
136
136
|
level?: number;
|
|
137
137
|
/** Force specific encoding(s). */
|
|
138
138
|
encoding?: string | string[];
|
|
139
|
-
/** Filter function
|
|
139
|
+
/** Filter function - return false to skip compression. */
|
|
140
140
|
filter?: (req: Request, res: Response) => boolean;
|
|
141
141
|
}
|
|
142
142
|
|
package/types/orm.d.ts
CHANGED
|
@@ -29,7 +29,7 @@ export interface SchemaColumnDef {
|
|
|
29
29
|
enum?: string[];
|
|
30
30
|
/** Allowed values (set type). */
|
|
31
31
|
values?: string[];
|
|
32
|
-
/** Mass-assignment protection
|
|
32
|
+
/** Mass-assignment protection - exclude from bulk writes. */
|
|
33
33
|
guarded?: boolean;
|
|
34
34
|
/** Precision for decimal types. */
|
|
35
35
|
precision?: number;
|
|
@@ -229,7 +229,7 @@ export class Query {
|
|
|
229
229
|
take(n: number): Query;
|
|
230
230
|
/** Alias for offset (LINQ naming). */
|
|
231
231
|
skip(n: number): Query;
|
|
232
|
-
/** Alias for exec
|
|
232
|
+
/** Alias for exec - explicitly convert to array. */
|
|
233
233
|
toArray(): Promise<Model[]>;
|
|
234
234
|
/** Shorthand for orderBy(field, 'desc'). */
|
|
235
235
|
orderByDesc(field: string): Query;
|
|
@@ -260,7 +260,7 @@ export class Query {
|
|
|
260
260
|
/** Inject a raw WHERE clause for SQL adapters (ignored by memory/mongo). */
|
|
261
261
|
whereRaw(sql: string, ...params: any[]): Query;
|
|
262
262
|
|
|
263
|
-
/** Thenable support
|
|
263
|
+
/** Thenable support - `await query`. */
|
|
264
264
|
then<TResult1 = Model[], TResult2 = never>(
|
|
265
265
|
onfulfilled?: ((value: Model[]) => TResult1 | PromiseLike<TResult1>) | null,
|
|
266
266
|
onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null
|
package/types/request.d.ts
CHANGED
|
@@ -31,7 +31,7 @@ export interface Request {
|
|
|
31
31
|
readonly ips: string[];
|
|
32
32
|
/** `true` when the connection is over TLS (trust-proxy-aware). */
|
|
33
33
|
readonly secure: boolean;
|
|
34
|
-
/** Protocol string
|
|
34
|
+
/** Protocol string - `'https'` or `'http'` (trust-proxy-aware). */
|
|
35
35
|
readonly protocol: 'http' | 'https';
|
|
36
36
|
/** HTTP version string (e.g. '1.1', '2.0'). */
|
|
37
37
|
httpVersion: string;
|
|
@@ -49,7 +49,7 @@ export interface Request {
|
|
|
49
49
|
id?: string;
|
|
50
50
|
/** Whether the request timed out (populated by timeout middleware). */
|
|
51
51
|
timedOut?: boolean;
|
|
52
|
-
/** The original URL as received
|
|
52
|
+
/** The original URL as received - never rewritten by middleware. */
|
|
53
53
|
originalUrl: string;
|
|
54
54
|
/** The URL path on which the current router was mounted. */
|
|
55
55
|
baseUrl: string;
|
|
@@ -83,7 +83,7 @@ export interface Request {
|
|
|
83
83
|
subdomains(offset?: number): string[];
|
|
84
84
|
|
|
85
85
|
/**
|
|
86
|
-
* Content negotiation
|
|
86
|
+
* Content negotiation - check which types the client accepts.
|
|
87
87
|
*/
|
|
88
88
|
accepts(...types: string[]): string | false;
|
|
89
89
|
|
|
@@ -0,0 +1,501 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TypeScript surface for @zero-server/webrtc.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors the runtime surface exported from `lib/webrtc/index.js` and
|
|
5
|
+
* re-exported from the top-level SDK in `index.js`. Every entry listed
|
|
6
|
+
* in `.tools/scope-manifest.js` under the `webrtc` scope MUST have a
|
|
7
|
+
* matching declaration here - this is enforced by
|
|
8
|
+
* `test/packages/webrtc-types.test.js`.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { EventEmitter } from 'node:events';
|
|
12
|
+
import type { KeyObject } from 'node:crypto';
|
|
13
|
+
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
// Shared option / config types
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
|
|
18
|
+
export interface IceServerConfig {
|
|
19
|
+
urls: string | string[];
|
|
20
|
+
username?: string;
|
|
21
|
+
credential?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface TurnCredentials {
|
|
25
|
+
urls: string[];
|
|
26
|
+
username: string;
|
|
27
|
+
credential: string;
|
|
28
|
+
ttl: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface IssueTurnCredentialsOptions {
|
|
32
|
+
secret: string;
|
|
33
|
+
userId: string;
|
|
34
|
+
ttl?: string | number;
|
|
35
|
+
servers: string[];
|
|
36
|
+
realm?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface SignalingHubOptions {
|
|
40
|
+
maxSdpSize?: number;
|
|
41
|
+
maxCandidatesPerOffer?: number;
|
|
42
|
+
peerMessageRate?: number;
|
|
43
|
+
maxProtocolErrors?: number;
|
|
44
|
+
ipAttachRate?: number;
|
|
45
|
+
originAllowlist?: string[];
|
|
46
|
+
joinTokenSecret?: string | Buffer;
|
|
47
|
+
autoCreateRooms?: boolean;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface WebRTCOptions extends SignalingHubOptions {
|
|
51
|
+
path?: string;
|
|
52
|
+
iceServers?: IceServerConfig[] | 'auto';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface PeerAttachInfo {
|
|
56
|
+
user?: unknown;
|
|
57
|
+
ip?: string;
|
|
58
|
+
origin?: string;
|
|
59
|
+
[extra: string]: unknown;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface PeerTransport {
|
|
63
|
+
send(data: string): void;
|
|
64
|
+
on(event: 'message' | 'close', cb: (...args: unknown[]) => void): void;
|
|
65
|
+
close(code?: number, reason?: string): void;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
69
|
+
// SDP / ICE helpers
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
|
|
72
|
+
export interface ParsedSdpMedia {
|
|
73
|
+
type: string;
|
|
74
|
+
port: number;
|
|
75
|
+
proto: string;
|
|
76
|
+
formats: string[];
|
|
77
|
+
iceUfrag?: string;
|
|
78
|
+
icePwd?: string;
|
|
79
|
+
fingerprint?: { algorithm: string; hash: string };
|
|
80
|
+
candidates?: ParsedIceCandidate[];
|
|
81
|
+
[key: string]: unknown;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export interface ParsedSdp {
|
|
85
|
+
version: number;
|
|
86
|
+
origin: Record<string, unknown>;
|
|
87
|
+
sessionName: string;
|
|
88
|
+
media: ParsedSdpMedia[];
|
|
89
|
+
[key: string]: unknown;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export interface ParsedIceCandidate {
|
|
93
|
+
foundation: string;
|
|
94
|
+
component: number;
|
|
95
|
+
transport: string;
|
|
96
|
+
priority: number;
|
|
97
|
+
address: string;
|
|
98
|
+
port: number;
|
|
99
|
+
type: string;
|
|
100
|
+
relatedAddress?: string;
|
|
101
|
+
relatedPort?: number;
|
|
102
|
+
tcpType?: string;
|
|
103
|
+
[key: string]: unknown;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export declare function parseSdp(sdp: string, opts?: { maxBytes?: number }): ParsedSdp;
|
|
107
|
+
export declare function stringifySdp(parsed: ParsedSdp): string;
|
|
108
|
+
|
|
109
|
+
export declare function parseCandidate(line: string): ParsedIceCandidate;
|
|
110
|
+
export declare function stringifyCandidate(parsed: ParsedIceCandidate): string;
|
|
111
|
+
export declare function filterCandidates(
|
|
112
|
+
candidates: ParsedIceCandidate[],
|
|
113
|
+
opts?: { allowPrivate?: boolean; allowLoopback?: boolean; allowLinkLocal?: boolean; allowMdns?: boolean }
|
|
114
|
+
): ParsedIceCandidate[];
|
|
115
|
+
|
|
116
|
+
export declare function isPrivateIp(addr: string): boolean;
|
|
117
|
+
export declare function isLoopbackIp(addr: string): boolean;
|
|
118
|
+
export declare function isLinkLocalIp(addr: string): boolean;
|
|
119
|
+
export declare function isMdnsHostname(addr: string): boolean;
|
|
120
|
+
|
|
121
|
+
export declare const CANDIDATE_TYPES: Readonly<{ HOST: string; SRFLX: string; PRFLX: string; RELAY: string }>;
|
|
122
|
+
export declare const TCP_TYPES: Readonly<{ ACTIVE: string; PASSIVE: string; SO: string }>;
|
|
123
|
+
|
|
124
|
+
// ---------------------------------------------------------------------------
|
|
125
|
+
// STUN / TURN
|
|
126
|
+
// ---------------------------------------------------------------------------
|
|
127
|
+
|
|
128
|
+
export declare function stunBinding(opts: {
|
|
129
|
+
host: string;
|
|
130
|
+
port?: number;
|
|
131
|
+
timeoutMs?: number;
|
|
132
|
+
retries?: number;
|
|
133
|
+
socketType?: 'udp4' | 'udp6';
|
|
134
|
+
}): Promise<{ family: 4 | 6; address: string; port: number }>;
|
|
135
|
+
|
|
136
|
+
export declare function encodeBindingRequest(transactionId?: Buffer): { buffer: Buffer; transactionId: Buffer };
|
|
137
|
+
export declare function decodeMessage(buf: Buffer): {
|
|
138
|
+
method: number;
|
|
139
|
+
class: number;
|
|
140
|
+
transactionId: Buffer;
|
|
141
|
+
attributes: Array<{ type: number; value: Buffer }>;
|
|
142
|
+
};
|
|
143
|
+
export declare function encodeXorMappedAddress(address: string, port: number, transactionId: Buffer): Buffer;
|
|
144
|
+
export declare function decodeXorMappedAddress(value: Buffer, transactionId: Buffer): { family: 4 | 6; address: string; port: number };
|
|
145
|
+
|
|
146
|
+
export declare const STUN_MAGIC_COOKIE: number;
|
|
147
|
+
export declare const STUN_METHOD: Readonly<{ BINDING: number }>;
|
|
148
|
+
export declare const STUN_CLASS: Readonly<{ REQUEST: number; INDICATION: number; SUCCESS: number; ERROR: number }>;
|
|
149
|
+
export declare const STUN_ATTR: Readonly<{ MAPPED_ADDRESS: number; XOR_MAPPED_ADDRESS: number; ERROR_CODE: number; SOFTWARE: number }>;
|
|
150
|
+
|
|
151
|
+
export declare function issueTurnCredentials(opts: IssueTurnCredentialsOptions): TurnCredentials;
|
|
152
|
+
|
|
153
|
+
export declare class TurnServer {
|
|
154
|
+
constructor(opts: {
|
|
155
|
+
secret: string;
|
|
156
|
+
realm?: string;
|
|
157
|
+
listeners: Array<{ proto: 'udp' | 'tcp' | 'tls'; port: number; host?: string; tls?: { cert: Buffer; key: Buffer } }>;
|
|
158
|
+
quotas?: { maxAllocationsPerUser?: number; maxBytesPerMinute?: number };
|
|
159
|
+
defaultLifetime?: number;
|
|
160
|
+
maxLifetime?: number;
|
|
161
|
+
relayHost?: string;
|
|
162
|
+
});
|
|
163
|
+
readonly realm: string;
|
|
164
|
+
start(): Promise<void>;
|
|
165
|
+
stop(): Promise<void>;
|
|
166
|
+
address(): { address: string; port: number } | null;
|
|
167
|
+
on(event: 'allocation', listener: (ev: { userId: string; relay: { address: string; port: number }; client: { address: string; port: number } }) => void): this;
|
|
168
|
+
on(event: 'deallocation', listener: (ev: { userId: string; client: { address: string; port: number }; reason?: string }) => void): this;
|
|
169
|
+
on(event: 'error', listener: (err: Error) => void): this;
|
|
170
|
+
on(event: string, listener: (...args: unknown[]) => void): this;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// ---------------------------------------------------------------------------
|
|
174
|
+
// Signaling core
|
|
175
|
+
// ---------------------------------------------------------------------------
|
|
176
|
+
|
|
177
|
+
export type PeerState = 'stable' | 'have-local-offer' | 'have-remote-offer';
|
|
178
|
+
|
|
179
|
+
export declare const PEER_STATE: Readonly<{
|
|
180
|
+
STABLE: 'stable';
|
|
181
|
+
HAVE_LOCAL_OFFER: 'have-local-offer';
|
|
182
|
+
HAVE_REMOTE_OFFER: 'have-remote-offer';
|
|
183
|
+
}>;
|
|
184
|
+
|
|
185
|
+
export declare class Peer {
|
|
186
|
+
readonly id: string;
|
|
187
|
+
readonly user: unknown;
|
|
188
|
+
readonly ip: string | null;
|
|
189
|
+
readonly transport: PeerTransport;
|
|
190
|
+
state: PeerState;
|
|
191
|
+
room: Room | null;
|
|
192
|
+
errors: number;
|
|
193
|
+
readonly connectedAt: number;
|
|
194
|
+
closed: boolean;
|
|
195
|
+
e2ee?: E2eeChannel;
|
|
196
|
+
constructor(transport: PeerTransport, info?: PeerAttachInfo);
|
|
197
|
+
send(type: string, payload?: object): void;
|
|
198
|
+
sendError(code: string, message: string): void;
|
|
199
|
+
close(code?: number, reason?: string): void;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export declare class Room {
|
|
203
|
+
readonly name: string;
|
|
204
|
+
readonly hub: SignalingHub | null;
|
|
205
|
+
isOpen: boolean;
|
|
206
|
+
constructor(name: string, opts?: { hub?: SignalingHub });
|
|
207
|
+
open(): this;
|
|
208
|
+
require(fn: (peer: Peer) => boolean | Promise<boolean>): this;
|
|
209
|
+
canPublish(fn: (peer: Peer) => boolean): this;
|
|
210
|
+
canSubscribe(fn: (peer: Peer) => boolean): this;
|
|
211
|
+
readonly size: number;
|
|
212
|
+
peers(): Peer[];
|
|
213
|
+
canJoin(peer: Peer): boolean | Promise<boolean>;
|
|
214
|
+
broadcast(type: string, payload?: object, exceptPeerId?: string): void;
|
|
215
|
+
close(reason?: string): void;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export interface SignalingHubEvents {
|
|
219
|
+
join: (ev: { peer: Peer; room: Room }) => void;
|
|
220
|
+
leave: (ev: { peer: Peer; room: Room }) => void;
|
|
221
|
+
offer: (ev: { peer: Peer; target: Peer | null; room: Room; sdp: string }) => void;
|
|
222
|
+
answer: (ev: { peer: Peer; target: Peer | null; room: Room; sdp: string }) => void;
|
|
223
|
+
signal: (ev: { peer: Peer; type: string }) => void;
|
|
224
|
+
joinFailed: (ev: { peer: Peer; reason: string; room?: string }) => void;
|
|
225
|
+
publishFailed: (ev: { peer: Peer; reason: string; room: string }) => void;
|
|
226
|
+
subscribeFailed: (ev: { peer: Peer; reason: string; room: string }) => void;
|
|
227
|
+
wireError: (ev: { peer: Peer; code: string }) => void;
|
|
228
|
+
e2eeKey: (ev: { peer: Peer; room: Room; epoch: number; key: string }) => void;
|
|
229
|
+
clusterError: (err: Error) => void;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export declare class SignalingHub extends EventEmitter {
|
|
233
|
+
constructor(opts?: SignalingHubOptions);
|
|
234
|
+
readonly size: number;
|
|
235
|
+
room(name: string): Room;
|
|
236
|
+
rooms(): Room[];
|
|
237
|
+
attach(transport: PeerTransport, info?: PeerAttachInfo): Peer;
|
|
238
|
+
close(): void;
|
|
239
|
+
on<E extends keyof SignalingHubEvents>(event: E, listener: SignalingHubEvents[E]): this;
|
|
240
|
+
on(event: string, listener: (...args: unknown[]) => void): this;
|
|
241
|
+
off<E extends keyof SignalingHubEvents>(event: E, listener: SignalingHubEvents[E]): this;
|
|
242
|
+
off(event: string, listener: (...args: unknown[]) => void): this;
|
|
243
|
+
emit<E extends keyof SignalingHubEvents>(event: E, ...args: Parameters<SignalingHubEvents[E]>): boolean;
|
|
244
|
+
emit(event: string, ...args: unknown[]): boolean;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
export declare function createWebRTC(app: unknown, opts?: WebRTCOptions): SignalingHub;
|
|
248
|
+
|
|
249
|
+
// ---------------------------------------------------------------------------
|
|
250
|
+
// Join tokens
|
|
251
|
+
// ---------------------------------------------------------------------------
|
|
252
|
+
|
|
253
|
+
export interface SignJoinTokenOptions {
|
|
254
|
+
secret: string | Buffer;
|
|
255
|
+
user: string | { id?: string; userId?: string; sub?: string; [k: string]: unknown };
|
|
256
|
+
room: string;
|
|
257
|
+
ttl?: number;
|
|
258
|
+
claims?: Record<string, unknown>;
|
|
259
|
+
algorithm?: string;
|
|
260
|
+
audience?: string;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
export interface VerifyJoinTokenOptions {
|
|
264
|
+
secret: string | Buffer;
|
|
265
|
+
room?: string;
|
|
266
|
+
audience?: string | string[];
|
|
267
|
+
algorithms?: string | string[];
|
|
268
|
+
clockTolerance?: number;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export declare function signJoinToken(opts: SignJoinTokenOptions): string;
|
|
272
|
+
|
|
273
|
+
export declare function verifyJoinToken(
|
|
274
|
+
token: string,
|
|
275
|
+
opts: VerifyJoinTokenOptions
|
|
276
|
+
): { room: string; user: unknown; sub?: string; aud?: string; [k: string]: unknown };
|
|
277
|
+
|
|
278
|
+
// ---------------------------------------------------------------------------
|
|
279
|
+
// Observability
|
|
280
|
+
// ---------------------------------------------------------------------------
|
|
281
|
+
|
|
282
|
+
export interface ObservabilityBindOptions {
|
|
283
|
+
metrics?: unknown;
|
|
284
|
+
tracer?: unknown;
|
|
285
|
+
prefix?: string;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
export declare function bindObservability(
|
|
289
|
+
hub: SignalingHub,
|
|
290
|
+
opts?: ObservabilityBindOptions
|
|
291
|
+
): () => void;
|
|
292
|
+
|
|
293
|
+
// ---------------------------------------------------------------------------
|
|
294
|
+
// E2EE key relay
|
|
295
|
+
// ---------------------------------------------------------------------------
|
|
296
|
+
|
|
297
|
+
export interface E2eeKeyEvent {
|
|
298
|
+
from: string;
|
|
299
|
+
epoch: number;
|
|
300
|
+
key: Buffer;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
export declare class E2eeChannel {
|
|
304
|
+
readonly peer: Peer;
|
|
305
|
+
readonly hub: SignalingHub;
|
|
306
|
+
epoch: number;
|
|
307
|
+
constructor(peer: Peer, hub: SignalingHub);
|
|
308
|
+
publish(epoch: number | null, key: Buffer | Uint8Array | string): number;
|
|
309
|
+
subscribe(fn: (ev: E2eeKeyEvent) => void): () => void;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
export declare function attachE2ee(peer: Peer, hub: SignalingHub): E2eeChannel;
|
|
313
|
+
export declare function generateE2eeKeyPair(): { publicKey: KeyObject; privateKey: KeyObject };
|
|
314
|
+
export declare function sealKey(plaintext: Buffer | Uint8Array, recipientPubKey: KeyObject | Buffer): Buffer;
|
|
315
|
+
export declare function openSealedKey(sealed: Buffer | Uint8Array, recipientPrivKey: KeyObject | Buffer): Buffer;
|
|
316
|
+
|
|
317
|
+
// ---------------------------------------------------------------------------
|
|
318
|
+
// Cluster adapter
|
|
319
|
+
// ---------------------------------------------------------------------------
|
|
320
|
+
|
|
321
|
+
export interface ClusterAdapter {
|
|
322
|
+
publish(channel: string, message: unknown): void | Promise<void>;
|
|
323
|
+
subscribe(channel: string, handler: (message: unknown) => void): (() => void) | void;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
export interface UseClusterOptions {
|
|
327
|
+
nodeId?: string;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
export declare class ClusterCoordinator {
|
|
331
|
+
readonly hub: SignalingHub;
|
|
332
|
+
readonly adapter: ClusterAdapter;
|
|
333
|
+
readonly nodeId: string;
|
|
334
|
+
constructor(hub: SignalingHub, adapter: ClusterAdapter, opts?: UseClusterOptions);
|
|
335
|
+
locate(peerId: string): { nodeId: string; room: string } | null;
|
|
336
|
+
routeDirect(toPeerId: string, type: string, payload: object): boolean;
|
|
337
|
+
fanoutRoom(roomName: string, type: string, payload: object, excludeId?: string): void;
|
|
338
|
+
close(): void;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
export declare function useCluster(
|
|
342
|
+
hub: SignalingHub,
|
|
343
|
+
adapter: ClusterAdapter,
|
|
344
|
+
opts?: UseClusterOptions
|
|
345
|
+
): ClusterCoordinator;
|
|
346
|
+
|
|
347
|
+
export declare class MemoryClusterAdapter implements ClusterAdapter {
|
|
348
|
+
publish(channel: string, message: unknown): void;
|
|
349
|
+
subscribe(channel: string, handler: (message: unknown) => void): () => void;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// ---------------------------------------------------------------------------
|
|
353
|
+
// CLI
|
|
354
|
+
// ---------------------------------------------------------------------------
|
|
355
|
+
|
|
356
|
+
export interface WebRTCCommandDeps {
|
|
357
|
+
out?: (line: string) => void;
|
|
358
|
+
err?: (line: string) => void;
|
|
359
|
+
setExit?: (code: number) => void;
|
|
360
|
+
stunBinding?: typeof stunBinding;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
export declare function runWebRTCCommand(
|
|
364
|
+
subcmd: 'stun' | 'turn-creds' | 'join-token' | 'verify-token' | 'help' | string,
|
|
365
|
+
flags?: Map<string, string>,
|
|
366
|
+
deps?: WebRTCCommandDeps
|
|
367
|
+
): Promise<number>;
|
|
368
|
+
|
|
369
|
+
// ---------------------------------------------------------------------------
|
|
370
|
+
// SFU adapter (interface only - real implementations land in later PRs)
|
|
371
|
+
// ---------------------------------------------------------------------------
|
|
372
|
+
|
|
373
|
+
export interface SfuPeerInfo {
|
|
374
|
+
id: string;
|
|
375
|
+
user?: unknown;
|
|
376
|
+
room: string;
|
|
377
|
+
joinedAt: number;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
export interface SfuRouter { id: string; routerId: string; }
|
|
381
|
+
export interface SfuTransport {
|
|
382
|
+
id: string;
|
|
383
|
+
transportId: string;
|
|
384
|
+
routerId: string;
|
|
385
|
+
peer: SfuPeerInfo | null;
|
|
386
|
+
iceParameters: unknown;
|
|
387
|
+
dtlsParameters: unknown;
|
|
388
|
+
}
|
|
389
|
+
export interface SfuProducer {
|
|
390
|
+
id: string;
|
|
391
|
+
producerId: string;
|
|
392
|
+
transportId: string;
|
|
393
|
+
kind: 'audio' | 'video';
|
|
394
|
+
rtpParams: unknown;
|
|
395
|
+
paused: boolean;
|
|
396
|
+
}
|
|
397
|
+
export interface SfuConsumer {
|
|
398
|
+
id: string;
|
|
399
|
+
consumerId: string;
|
|
400
|
+
transportId: string;
|
|
401
|
+
producerId: string;
|
|
402
|
+
kind: 'audio' | 'video';
|
|
403
|
+
rtpParams: unknown;
|
|
404
|
+
rtpCaps: unknown;
|
|
405
|
+
}
|
|
406
|
+
export interface SfuStats {
|
|
407
|
+
kind: 'global' | 'router' | 'transport';
|
|
408
|
+
[key: string]: unknown;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
export type SfuEventHandler = (event: string, payload: unknown) => void;
|
|
412
|
+
|
|
413
|
+
export declare class SfuAdapter {
|
|
414
|
+
constructor();
|
|
415
|
+
createRouter(opts?: unknown): Promise<SfuRouter>;
|
|
416
|
+
createTransport(router: SfuRouter, peer: SfuPeerInfo): Promise<SfuTransport>;
|
|
417
|
+
produce(transport: SfuTransport, kind: 'audio' | 'video', rtpParams: unknown): Promise<SfuProducer>;
|
|
418
|
+
consume(transport: SfuTransport, producerId: string, rtpCaps: unknown): Promise<SfuConsumer>;
|
|
419
|
+
pauseProducer(producerId: string): Promise<void>;
|
|
420
|
+
resumeProducer(producerId: string): Promise<void>;
|
|
421
|
+
closeRouter(routerId: string): Promise<void>;
|
|
422
|
+
stats(scope?: string): Promise<SfuStats>;
|
|
423
|
+
onEvent(handler: SfuEventHandler): () => void;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
export declare class MemorySfuAdapter extends SfuAdapter {
|
|
427
|
+
constructor(opts?: Record<string, unknown>);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
export interface MediasoupAdapterOptions {
|
|
431
|
+
mediasoup?: unknown;
|
|
432
|
+
worker?: unknown;
|
|
433
|
+
workerSettings?: Record<string, unknown>;
|
|
434
|
+
mediaCodecs?: Array<Record<string, unknown>>;
|
|
435
|
+
webRtcTransportOptions?: Record<string, unknown>;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
export declare class MediasoupSfuAdapter extends SfuAdapter {
|
|
439
|
+
constructor(opts?: MediasoupAdapterOptions);
|
|
440
|
+
close(): Promise<void>;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
export interface LiveKitAdapterOptions {
|
|
444
|
+
url: string;
|
|
445
|
+
apiKey: string;
|
|
446
|
+
apiSecret: string;
|
|
447
|
+
livekit?: unknown;
|
|
448
|
+
client?: unknown;
|
|
449
|
+
defaultRoomOpts?: Record<string, unknown>;
|
|
450
|
+
defaultGrants?: Record<string, unknown>;
|
|
451
|
+
tokenTtl?: string | number;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
export declare class LiveKitSfuAdapter extends SfuAdapter {
|
|
455
|
+
constructor(opts: LiveKitAdapterOptions);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
export declare function loadSfuAdapter(
|
|
459
|
+
spec: SfuAdapter | 'memory' | 'mediasoup' | 'livekit' | string,
|
|
460
|
+
opts?: Record<string, unknown>,
|
|
461
|
+
): SfuAdapter;
|
|
462
|
+
|
|
463
|
+
// ---------------------------------------------------------------------------
|
|
464
|
+
// Server-side bot peer (wrtc)
|
|
465
|
+
// ---------------------------------------------------------------------------
|
|
466
|
+
|
|
467
|
+
export interface BotPeerOptions {
|
|
468
|
+
hub: SignalingHub;
|
|
469
|
+
room: string;
|
|
470
|
+
user?: unknown;
|
|
471
|
+
ip?: string;
|
|
472
|
+
joinToken?: string;
|
|
473
|
+
iceServers?: Array<Record<string, unknown>>;
|
|
474
|
+
rtcConfig?: Record<string, unknown>;
|
|
475
|
+
wrtc?: unknown;
|
|
476
|
+
onTrack?: (track: unknown, streams: unknown[], fromPeerId: string) => void;
|
|
477
|
+
onDataChannel?: (channel: unknown, fromPeerId: string) => void;
|
|
478
|
+
onPeerJoin?: (remotePeerId: string) => void;
|
|
479
|
+
onPeerLeave?: (remotePeerId: string) => void;
|
|
480
|
+
onError?: (err: Error) => void;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
export interface BotPeerHandle {
|
|
484
|
+
peer: Peer;
|
|
485
|
+
peerConnections: Map<string, unknown>;
|
|
486
|
+
getPeerConnection: (remotePeerId: string) => unknown | undefined;
|
|
487
|
+
ready: Promise<{ peerId: string }>;
|
|
488
|
+
close: () => void;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
export declare function spawnBotPeer(opts: BotPeerOptions): BotPeerHandle;
|
|
492
|
+
|
|
493
|
+
// ---------------------------------------------------------------------------
|
|
494
|
+
// Errors
|
|
495
|
+
// ---------------------------------------------------------------------------
|
|
496
|
+
|
|
497
|
+
export declare class WebRTCError extends Error { readonly code: string; }
|
|
498
|
+
export declare class SignalingError extends WebRTCError {}
|
|
499
|
+
export declare class IceError extends WebRTCError {}
|
|
500
|
+
export declare class TurnError extends WebRTCError {}
|
|
501
|
+
export declare class SdpError extends WebRTCError {}
|