zero-query 1.2.0 → 1.2.3
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/cli/commands/build-api.js +0 -1
- package/cli/commands/build.js +0 -1
- package/cli/commands/bundle.js +0 -2
- package/cli/commands/create.js +46 -1
- package/cli/scaffold/webrtc/app/components/video-room.js +276 -130
- package/cli/scaffold/webrtc/package.json +16 -0
- package/cli/scaffold/webrtc/server/index.js +198 -0
- package/dist/API.md +1 -1
- package/dist/zquery.dist.zip +0 -0
- package/dist/zquery.js +296 -155
- package/dist/zquery.min.js +7 -7
- package/package.json +3 -2
- package/src/component.js +161 -112
- package/src/core.js +11 -3
- package/src/expression.js +26 -19
- package/src/reactive.js +18 -2
- package/src/router.js +38 -3
- package/src/ssr.js +14 -11
- package/src/store.js +16 -9
- package/src/utils.js +24 -5
- package/tests/audit.test.js +9 -9
- package/tests/bench/render.bench.js +77 -0
- package/tests/cli.test.js +7 -7
- package/tests/component.test.js +107 -0
- package/tests/core.test.js +16 -0
- package/tests/expression.test.js +31 -0
- package/tests/http.test.js +45 -0
- package/tests/reactive.test.js +25 -0
- package/tests/router.test.js +96 -0
- package/tests/ssr.test.js +46 -0
- package/tests/store.test.js +10 -0
- package/tests/utils.test.js +34 -0
- package/cli/scaffold/webrtc/app/lib/room.js +0 -252
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{NAME}}",
|
|
3
|
+
"private": true,
|
|
4
|
+
"type": "commonjs",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"start": "node server/index.js",
|
|
7
|
+
"dev": "node server/index.js"
|
|
8
|
+
},
|
|
9
|
+
"dependencies": {
|
|
10
|
+
"zero-query": "latest"
|
|
11
|
+
},
|
|
12
|
+
"devDependencies": {
|
|
13
|
+
"@zero-server/sdk": "^0.9.8",
|
|
14
|
+
"@zero-server/webrtc": "^0.9.8"
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
// server/index.js - WebRTC signaling + TURN backend
|
|
2
|
+
//
|
|
3
|
+
// Hosts the zQuery webrtc demo over real WebSocket signaling using
|
|
4
|
+
// @zero-server/webrtc's SignalingHub. Browsers connect with
|
|
5
|
+
// `$.webrtc.join('ws://host:PORT/rtc', { room })` and exchange offers /
|
|
6
|
+
// answers / ICE through the hub - so two real machines (not just two
|
|
7
|
+
// tabs in the same browser) can call each other.
|
|
8
|
+
//
|
|
9
|
+
// Usage:
|
|
10
|
+
// node server/index.js # listens on PORT (default 3000)
|
|
11
|
+
// PORT=8080 node server/index.js
|
|
12
|
+
//
|
|
13
|
+
// Optional environment variables:
|
|
14
|
+
// WEBRTC_JWT_SECRET - if set, joins must include a signed join token.
|
|
15
|
+
// GET /rtc/token/:room returns a short-TTL token.
|
|
16
|
+
// TURN_SECRET - if set, GET /rtc/turn returns ephemeral
|
|
17
|
+
// RFC 7635 TURN credentials. Otherwise it returns
|
|
18
|
+
// a plain STUN-only iceServers list.
|
|
19
|
+
// TURN_URLS - comma-separated TURN/STUN URLs to hand out
|
|
20
|
+
// (e.g. 'turn:turn.example.com:3478?transport=udp').
|
|
21
|
+
//
|
|
22
|
+
// @zero-server/sdk and @zero-server/webrtc are devDependencies declared in
|
|
23
|
+
// package.json. If they aren't installed yet, this script prompts to
|
|
24
|
+
// install them once on first run (same pattern as the dev server does
|
|
25
|
+
// for zero-http).
|
|
26
|
+
|
|
27
|
+
'use strict';
|
|
28
|
+
|
|
29
|
+
const path = require('node:path');
|
|
30
|
+
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
// Install prompt
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Prompt the user to install a missing devDependency. Resolves true on yes.
|
|
37
|
+
*/
|
|
38
|
+
function promptInstall(label) {
|
|
39
|
+
const rl = require('node:readline').createInterface({
|
|
40
|
+
input: process.stdin,
|
|
41
|
+
output: process.stdout,
|
|
42
|
+
});
|
|
43
|
+
return new Promise((resolve) => {
|
|
44
|
+
rl.question(
|
|
45
|
+
'\n This server needs ' + label + ', which is not installed.\n' +
|
|
46
|
+
' These packages are only used by the demo server and are not\n' +
|
|
47
|
+
' required for the client bundle, building, or production.\n' +
|
|
48
|
+
' Install them now? (y/n): ',
|
|
49
|
+
(answer) => {
|
|
50
|
+
rl.close();
|
|
51
|
+
resolve(answer.trim().toLowerCase() === 'y');
|
|
52
|
+
}
|
|
53
|
+
);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* `require` a package; on failure, prompt the user to npm-install it as a
|
|
59
|
+
* devDependency, then re-require. Exits the process if the user declines.
|
|
60
|
+
*/
|
|
61
|
+
async function requireOrInstall(pkgs) {
|
|
62
|
+
try {
|
|
63
|
+
return pkgs.map((p) => require(p));
|
|
64
|
+
} catch (err) {
|
|
65
|
+
if (err.code !== 'MODULE_NOT_FOUND') throw err;
|
|
66
|
+
const ok = await promptInstall(pkgs.join(' + '));
|
|
67
|
+
if (!ok) {
|
|
68
|
+
console.error('\n ✖ Cannot start the WebRTC server without these packages.\n');
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
const { execSync } = require('node:child_process');
|
|
72
|
+
const args = pkgs.join(' ');
|
|
73
|
+
console.log('\n Installing ' + args + '...\n');
|
|
74
|
+
execSync('npm install --save-dev ' + args, { stdio: 'inherit' });
|
|
75
|
+
return pkgs.map((p) => require(p));
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ---------------------------------------------------------------------------
|
|
80
|
+
// Bootstrap
|
|
81
|
+
// ---------------------------------------------------------------------------
|
|
82
|
+
|
|
83
|
+
async function main() {
|
|
84
|
+
const [sdk, webrtc] = await requireOrInstall([
|
|
85
|
+
'@zero-server/sdk',
|
|
86
|
+
'@zero-server/webrtc',
|
|
87
|
+
]);
|
|
88
|
+
|
|
89
|
+
const {
|
|
90
|
+
createApp,
|
|
91
|
+
helmet,
|
|
92
|
+
cors,
|
|
93
|
+
compress,
|
|
94
|
+
static: serveStatic,
|
|
95
|
+
} = sdk;
|
|
96
|
+
const {
|
|
97
|
+
SignalingHub,
|
|
98
|
+
signJoinToken,
|
|
99
|
+
issueTurnCredentials,
|
|
100
|
+
} = webrtc;
|
|
101
|
+
|
|
102
|
+
const PORT = parseInt(process.env.PORT || '3000', 10);
|
|
103
|
+
const ROOT = path.resolve(__dirname, '..');
|
|
104
|
+
const JWT = process.env.WEBRTC_JWT_SECRET || null;
|
|
105
|
+
const TURN_SEC = process.env.TURN_SECRET || null;
|
|
106
|
+
const TURN_URLS = (process.env.TURN_URLS || '').split(',').map((s) => s.trim()).filter(Boolean);
|
|
107
|
+
const STUN_FALLBACK = ['stun:stun.l.google.com:19302'];
|
|
108
|
+
|
|
109
|
+
const app = createApp();
|
|
110
|
+
|
|
111
|
+
// ---- Security & static assets ----
|
|
112
|
+
app.use(helmet({
|
|
113
|
+
contentSecurityPolicy: {
|
|
114
|
+
directives: {
|
|
115
|
+
defaultSrc: ["'self'"],
|
|
116
|
+
scriptSrc: ["'self'", "'unsafe-inline'"],
|
|
117
|
+
styleSrc: ["'self'", "'unsafe-inline'"],
|
|
118
|
+
imgSrc: ["'self'", 'data:', 'blob:'],
|
|
119
|
+
mediaSrc: ["'self'", 'blob:'],
|
|
120
|
+
connectSrc: ["'self'", 'ws:', 'wss:'],
|
|
121
|
+
fontSrc: ["'self'", 'data:'],
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
hsts: false,
|
|
125
|
+
}));
|
|
126
|
+
app.use(cors());
|
|
127
|
+
app.use(compress({ threshold: 1024 }));
|
|
128
|
+
app.use(serveStatic(ROOT, { index: 'index.html' }));
|
|
129
|
+
|
|
130
|
+
// ---- Signaling hub ----
|
|
131
|
+
const hubOpts = {};
|
|
132
|
+
if (JWT) hubOpts.joinTokenSecret = JWT;
|
|
133
|
+
const hub = new SignalingHub(hubOpts);
|
|
134
|
+
|
|
135
|
+
app.ws('/rtc', (ws, req) => {
|
|
136
|
+
hub.attach(ws, {
|
|
137
|
+
ip: req.ip,
|
|
138
|
+
origin: req.headers && req.headers.origin,
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
hub.on('join', ({ peer, room }) => console.log(' + peer', peer.id, 'joined', room.name));
|
|
143
|
+
hub.on('leave', ({ peer, room }) => console.log(' - peer', peer.id, 'left', room.name));
|
|
144
|
+
|
|
145
|
+
// ---- Join token endpoint (only when WEBRTC_JWT_SECRET is set) ----
|
|
146
|
+
app.get('/rtc/token/:room', (req, res) => {
|
|
147
|
+
if (!JWT) return res.json({ wsUrl: _wsUrl(req), token: null });
|
|
148
|
+
const token = signJoinToken({
|
|
149
|
+
secret: JWT,
|
|
150
|
+
user: { id: 'anon-' + Math.random().toString(36).slice(2, 10) },
|
|
151
|
+
room: req.params.room,
|
|
152
|
+
ttl: 300,
|
|
153
|
+
});
|
|
154
|
+
res.json({ wsUrl: _wsUrl(req), token });
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
// ---- TURN/STUN credential endpoint ----
|
|
158
|
+
app.get('/rtc/turn', (req, res) => {
|
|
159
|
+
if (TURN_SEC && TURN_URLS.length) {
|
|
160
|
+
const creds = issueTurnCredentials({
|
|
161
|
+
secret: TURN_SEC,
|
|
162
|
+
userId: 'anon-' + Math.random().toString(36).slice(2, 10),
|
|
163
|
+
ttl: '20m',
|
|
164
|
+
servers: TURN_URLS,
|
|
165
|
+
});
|
|
166
|
+
return res.json({ iceServers: [creds] });
|
|
167
|
+
}
|
|
168
|
+
// Dev fallback - public STUN only. Works for two machines on the
|
|
169
|
+
// same network but cannot traverse symmetric NATs without TURN.
|
|
170
|
+
const urls = TURN_URLS.length ? TURN_URLS : STUN_FALLBACK;
|
|
171
|
+
res.json({ iceServers: [{ urls }] });
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
// ---- Listen ----
|
|
175
|
+
app.listen(PORT, () => {
|
|
176
|
+
console.log('\n ⚡ WebRTC server → http://localhost:' + PORT);
|
|
177
|
+
console.log(' • signaling ws://localhost:' + PORT + '/rtc');
|
|
178
|
+
console.log(' • turn creds http://localhost:' + PORT + '/rtc/turn');
|
|
179
|
+
if (JWT) {
|
|
180
|
+
console.log(' • join tokens http://localhost:' + PORT + '/rtc/token/<room>');
|
|
181
|
+
} else {
|
|
182
|
+
console.log(' • join tokens disabled (set WEBRTC_JWT_SECRET to enable)');
|
|
183
|
+
}
|
|
184
|
+
console.log('');
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function _wsUrl(req) {
|
|
189
|
+
const host = (req.headers && req.headers.host) || 'localhost:3000';
|
|
190
|
+
const proto = (req.headers && req.headers['x-forwarded-proto'] === 'https') ? 'wss' : 'ws';
|
|
191
|
+
return proto + '://' + host + '/rtc';
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
main().catch((err) => {
|
|
195
|
+
console.error('\n ✖ WebRTC server failed to start:\n');
|
|
196
|
+
console.error(err);
|
|
197
|
+
process.exit(1);
|
|
198
|
+
});
|
package/dist/API.md
CHANGED
|
@@ -6555,7 +6555,7 @@ The WebRTC surface is layered — pick the level you need:
|
|
|
6555
6555
|
| **Hardening** | `SFrameContext`, `attachE2ee`, TURN refresher | You need E2EE and rotating TURN credentials. |
|
|
6556
6556
|
|
|
6557
6557
|
|
|
6558
|
-
> **Tip:** Want a working starting point? Run `npx zero-query create my-app --webrtc-demo` (alias `-w`) to scaffold a one-page video room with
|
|
6558
|
+
> **Tip:** Want a working starting point? Run `npx zero-query create my-app --webrtc-demo` (alias `-w`) to scaffold a one-page video room with mic / camera / screen-share toggles, a reactive roster, and a chat data channel. The scaffold installs [zero-server](https://github.com/tonywied17/zero-server), launches the signaling + static server on `http://localhost:3000`, and opens your browser - one command, no extra setup. Camera and microphone stay off until the user opts in.
|
|
6559
6559
|
|
|
6560
6560
|
|
|
6561
6561
|
### Surface Status
|
package/dist/zquery.dist.zip
CHANGED
|
Binary file
|