redweb 0.16.2 → 0.16.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/CHANGELOG.md +35 -29
- package/README.md +293 -291
- package/contract.d.ts +11 -11
- package/docs/API_EXAMPLES_VERIFICATION.md +22 -22
- package/docs/APPLICATION.md +96 -94
- package/docs/CLI.md +122 -122
- package/docs/CLIENT_DEVELOPMENT.md +9 -9
- package/docs/CONNECTED_CLIENTS_VERIFICATION.md +65 -65
- package/docs/DEVELOPMENT.md +81 -81
- package/docs/GETTING_STARTED.md +78 -78
- package/docs/LIVE_HTML.md +555 -478
- package/docs/MIGRATION.md +28 -28
- package/docs/RELEASE_TRUST.md +88 -88
- package/docs/RUNTIME_DIAGNOSTICS.md +78 -78
- package/docs/SOCKET_CONTRACTS.md +42 -42
- package/docs/SOCKET_PAGES.md +172 -172
- package/docs/SOCKET_PAGE_RELEASE_PREPARATION.md +120 -120
- package/docs/SOCKET_PAGE_VERIFICATION.md +85 -85
- package/docs/generated.json +2286 -2286
- package/docs/guides/chatroom.md +1 -1
- package/docs/guides/jsx-without-react.md +14 -14
- package/docs/reference.json +1329 -1329
- package/docs/releases/0.15.0.json +2217 -2217
- package/docs/releases/0.16.0.json +2217 -2217
- package/docs/releases/0.16.1.json +2286 -2286
- package/docs/releases/0.16.2.json +2286 -2286
- package/docs/releases/0.16.3.json +2286 -0
- package/docs/snippets/components.tsx +24 -24
- package/docs/snippets/counter.tsx +16 -16
- package/docs/snippets/room-access.tsx +11 -11
- package/docs/snippets/site.css +2 -2
- package/docs/snippets/site.tsx +22 -22
- package/docs/topics.json +3 -3
- package/index.d.ts +92 -57
- package/index.js +13 -8
- package/package.json +8 -8
- package/recipes/foundation/README.md +7 -7
- package/recipes/foundation/app.test.cjs +15 -15
- package/recipes/foundation/app.tsx +12 -12
- package/recipes/shared/README.md +7 -7
- package/src/Application.js +4 -4
- package/src/access/failure-codes.json +4 -0
- package/src/cli/ProjectInitializer.js +1 -1
- package/src/cli/arguments.js +21 -21
- package/src/cli/run.js +12 -12
- package/src/cli/templates.js +40 -40
- package/src/docs/Documentation.js +29 -29
- package/src/htmx/Jsx.js +2 -2
- package/src/htmx/LiveHtmlServer.js +5 -1
- package/src/htmx/LivePage.js +5 -1
- package/src/htmx/LiveResource.js +96 -0
- package/src/htmx/PageManager.js +126 -13
- package/src/htmx/PageSocketRoute.js +132 -132
- package/src/htmx/PageTaskLane.js +39 -0
- package/src/htmx/ReactiveRenderer.js +8 -8
- package/src/htmx/SocketAction.js +19 -19
- package/src/htmx/TemplateRenderer.js +1 -1
- package/src/htmx/index.js +3 -2
- package/src/htmx/metadata.js +117 -6
- package/src/ws/BaseHandler.js +6 -6
- package/src/ws/ConnectedClients.js +207 -207
- package/src/ws/HandlerGuard.js +4 -4
- package/src/ws/RoomRegistry.js +4 -4
- package/src/ws/RouteRuntime.js +11 -11
- package/src/ws/SocketAction.js +16 -16
- package/src/ws/SocketContract.js +3 -3
- package/src/ws/SocketRoute.js +7 -7
package/src/ws/RoomRegistry.js
CHANGED
|
@@ -102,12 +102,12 @@ class RoomRegistry {
|
|
|
102
102
|
return roomIds.length;
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
-
members(roomId) {
|
|
105
|
+
members(roomId) {
|
|
106
106
|
this.validateRoomId(roomId);
|
|
107
107
|
return [...(this.rooms.get(roomId) || [])];
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
roomsFor(socket) { return [...(this.memberships.get(socket) || [])]; }
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
roomsFor(socket) { return [...(this.memberships.get(socket) || [])]; }
|
|
111
111
|
|
|
112
112
|
has(roomId, socket) {
|
|
113
113
|
this.validateRoomId(roomId);
|
package/src/ws/RouteRuntime.js
CHANGED
|
@@ -5,8 +5,8 @@ const HeartbeatMonitor = require('./HeartbeatMonitor');
|
|
|
5
5
|
const RoomRegistry = require('./RoomRegistry');
|
|
6
6
|
const SessionRegistry = require('./SessionRegistry');
|
|
7
7
|
const DistributionBridge = require('./DistributionBridge');
|
|
8
|
-
const requestSnapshot = require('../context/RequestSnapshot');
|
|
9
|
-
const { ConnectedClients } = require('./ConnectedClients');
|
|
8
|
+
const requestSnapshot = require('../context/RequestSnapshot');
|
|
9
|
+
const { ConnectedClients } = require('./ConnectedClients');
|
|
10
10
|
|
|
11
11
|
function joinRoom(roomId) { return this.__redwebRuntimeOwner.rooms.join(roomId, this); }
|
|
12
12
|
function enterRoom(roomId) { return this.__redwebRuntimeOwner.rooms.enter(roomId, this); }
|
|
@@ -24,7 +24,7 @@ function publishEvent(type, payload) { return this.__redwebRuntimeOwner.route.pu
|
|
|
24
24
|
function connectionContext() { return this.__redwebRuntimeOwner.ensureContext(this); }
|
|
25
25
|
|
|
26
26
|
class RouteRuntime {
|
|
27
|
-
constructor(route, { heartbeat, rooms, sessions, distribution, drainHandlers, connections }) {
|
|
27
|
+
constructor(route, { heartbeat, rooms, sessions, distribution, drainHandlers, connections }) {
|
|
28
28
|
this.route = route;
|
|
29
29
|
this.inFlight = drainHandlers ? new Set() : null;
|
|
30
30
|
this.abortController = drainHandlers ? new AbortController() : null;
|
|
@@ -32,8 +32,8 @@ class RouteRuntime {
|
|
|
32
32
|
this.contexts = new WeakMap();
|
|
33
33
|
this.requests = new WeakMap();
|
|
34
34
|
this.needsContext = Boolean(route.admissionPolicy || route.protocolPolicy || rooms || sessions || drainHandlers);
|
|
35
|
-
try {
|
|
36
|
-
if (connections !== undefined && !(connections instanceof ConnectedClients)) throw new TypeError('connections must be created with connectedClients().');
|
|
35
|
+
try {
|
|
36
|
+
if (connections !== undefined && !(connections instanceof ConnectedClients)) throw new TypeError('connections must be created with connectedClients().');
|
|
37
37
|
this.heartbeat = heartbeat === undefined ? null : new HeartbeatMonitor(heartbeat, route.logger);
|
|
38
38
|
this.rooms = rooms === undefined || rooms === false
|
|
39
39
|
? null
|
|
@@ -42,10 +42,10 @@ class RouteRuntime {
|
|
|
42
42
|
socket.readyState === 1 && this.contexts.get(socket)?.active !== false,
|
|
43
43
|
contextFor: socket => this.ensureContext(socket),
|
|
44
44
|
policy: route.transportPolicy,
|
|
45
|
-
onChange: (action, roomId) => {
|
|
45
|
+
onChange: (action, roomId) => {
|
|
46
46
|
route.metrics?.increment(`redweb.room.${action}`);
|
|
47
|
-
route.metrics?.gauge('redweb.rooms.active', this.rooms.size);
|
|
48
|
-
connections?.changed(roomId);
|
|
47
|
+
route.metrics?.gauge('redweb.rooms.active', this.rooms.size);
|
|
48
|
+
connections?.changed(roomId);
|
|
49
49
|
},
|
|
50
50
|
});
|
|
51
51
|
this.sessions = sessions === undefined || sessions === false
|
|
@@ -54,10 +54,10 @@ class RouteRuntime {
|
|
|
54
54
|
if (distribution !== undefined && distribution !== false && typeof distribution?.onEvent !== 'function') {
|
|
55
55
|
throw new TypeError('`distribution.onEvent` must be a function.');
|
|
56
56
|
}
|
|
57
|
-
this.distribution = distribution === undefined || distribution === false
|
|
57
|
+
this.distribution = distribution === undefined || distribution === false
|
|
58
58
|
? null
|
|
59
|
-
: new DistributionBridge(distribution, event => distribution.onEvent(event, route), route.logger);
|
|
60
|
-
connections?.attach(route, this.rooms);
|
|
59
|
+
: new DistributionBridge(distribution, event => distribution.onEvent(event, route), route.logger);
|
|
60
|
+
connections?.attach(route, this.rooms);
|
|
61
61
|
} catch (error) {
|
|
62
62
|
this.heartbeat?.stop();
|
|
63
63
|
this.sessions?.stop();
|
package/src/ws/SocketAction.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
// Browser-safe metadata keeps the standalone contract entry free of Node rendering dependencies.
|
|
4
|
-
const bindings = new WeakMap();
|
|
5
|
-
function register(Handler, type) {
|
|
6
|
-
bindings.set(Handler, { Handler, type });
|
|
7
|
-
Object.defineProperty(Handler, 'with', { value(payload) {
|
|
8
|
-
const serialized = JSON.stringify(payload);
|
|
9
|
-
if (serialized === undefined || serialized.length > 65536) throw new TypeError('Socket action payload must be bounded JSON.');
|
|
10
|
-
const binding = Object.freeze({});
|
|
11
|
-
bindings.set(binding, { Handler, type, payload: JSON.parse(serialized) });
|
|
12
|
-
return binding;
|
|
13
|
-
} });
|
|
14
|
-
return Handler;
|
|
15
|
-
}
|
|
16
|
-
module.exports = { register, bindings };
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Browser-safe metadata keeps the standalone contract entry free of Node rendering dependencies.
|
|
4
|
+
const bindings = new WeakMap();
|
|
5
|
+
function register(Handler, type) {
|
|
6
|
+
bindings.set(Handler, { Handler, type });
|
|
7
|
+
Object.defineProperty(Handler, 'with', { value(payload) {
|
|
8
|
+
const serialized = JSON.stringify(payload);
|
|
9
|
+
if (serialized === undefined || serialized.length > 65536) throw new TypeError('Socket action payload must be bounded JSON.');
|
|
10
|
+
const binding = Object.freeze({});
|
|
11
|
+
bindings.set(binding, { Handler, type, payload: JSON.parse(serialized) });
|
|
12
|
+
return binding;
|
|
13
|
+
} });
|
|
14
|
+
return Handler;
|
|
15
|
+
}
|
|
16
|
+
module.exports = { register, bindings };
|
package/src/ws/SocketContract.js
CHANGED
|
@@ -83,7 +83,7 @@ class SocketContract {
|
|
|
83
83
|
if (!this.#validators.has(type)) throw new TypeError('Handler message type is not defined in the contract.');
|
|
84
84
|
if (typeof callback !== 'function') throw new TypeError('A contract handler requires a callback.');
|
|
85
85
|
const contract = this;
|
|
86
|
-
class ContractHandler extends BaseHandler {
|
|
86
|
+
class ContractHandler extends BaseHandler {
|
|
87
87
|
constructor() { super(type); }
|
|
88
88
|
async handleMessage(socket, message) {
|
|
89
89
|
if (socket.context?.protocol?.version !== contract.version) throw new TypeError('The route must negotiate this contract version before handling messages.');
|
|
@@ -93,8 +93,8 @@ class SocketContract {
|
|
|
93
93
|
return super.handleMessage(socket, { ...message, payload });
|
|
94
94
|
}
|
|
95
95
|
onMessage(socket, message) { return callback(socket, message.payload, message); }
|
|
96
|
-
}
|
|
97
|
-
return require('./SocketAction').register(ContractHandler, type);
|
|
96
|
+
}
|
|
97
|
+
return require('./SocketAction').register(ContractHandler, type);
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
client(socket) { return new ContractClient(this, socket); }
|
package/src/ws/SocketRoute.js
CHANGED
|
@@ -93,12 +93,12 @@ class SocketRoute {
|
|
|
93
93
|
limits,
|
|
94
94
|
orderedMessages = false,
|
|
95
95
|
heartbeat,
|
|
96
|
-
connections,
|
|
97
|
-
rooms = connections ? true : undefined,
|
|
96
|
+
connections,
|
|
97
|
+
rooms = connections ? true : undefined,
|
|
98
98
|
sessions,
|
|
99
99
|
metrics,
|
|
100
100
|
distribution,
|
|
101
|
-
drainHandlers = Boolean(connections),
|
|
101
|
+
drainHandlers = Boolean(connections),
|
|
102
102
|
protocol,
|
|
103
103
|
maxPendingUpgrades = 64,
|
|
104
104
|
} = {}) {
|
|
@@ -193,7 +193,7 @@ class SocketRoute {
|
|
|
193
193
|
throw error;
|
|
194
194
|
}
|
|
195
195
|
try {
|
|
196
|
-
this.runtime = new RouteRuntime(this, { heartbeat, rooms, sessions, distribution, drainHandlers, connections });
|
|
196
|
+
this.runtime = new RouteRuntime(this, { heartbeat, rooms, sessions, distribution, drainHandlers, connections });
|
|
197
197
|
Object.assign(this, this.runtime.expose());
|
|
198
198
|
} catch (error) {
|
|
199
199
|
this.disposeServices();
|
|
@@ -453,9 +453,9 @@ class SocketRoute {
|
|
|
453
453
|
return false;
|
|
454
454
|
} else {
|
|
455
455
|
try {
|
|
456
|
-
// A handler may send a recoverable protocol error and explicitly decline
|
|
457
|
-
// success. Undefined remains successful for existing command handlers.
|
|
458
|
-
return await handler.handleMessage(sock, data) !== false;
|
|
456
|
+
// A handler may send a recoverable protocol error and explicitly decline
|
|
457
|
+
// success. Undefined remains successful for existing command handlers.
|
|
458
|
+
return await handler.handleMessage(sock, data) !== false;
|
|
459
459
|
} catch (error) {
|
|
460
460
|
if (this.sendAccessFailure(sock, error, { requestId: data.requestId })) return false;
|
|
461
461
|
if (error instanceof InboundContractValidationError) {
|