redweb 0.16.2 → 0.16.4
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 +40 -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 +2290 -2286
- package/docs/guides/chatroom.md +1 -1
- package/docs/guides/jsx-without-react.md +14 -14
- package/docs/reference.json +1333 -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/releases/0.16.4.json +2290 -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 +96 -58
- package/index.js +14 -8
- package/package.json +12 -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/CodeHighlight.js +98 -0
- package/src/htmx/Html.js +4 -3
- 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 +4 -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/styles/code-highlight.css +16 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/** Serializes all browser work for one live page, regardless of transport. */
|
|
4
|
+
class PageTaskLane {
|
|
5
|
+
constructor(maxPending = 64) {
|
|
6
|
+
this.maxPending = maxPending;
|
|
7
|
+
this.tasks = [];
|
|
8
|
+
this.running = false;
|
|
9
|
+
this.closed = false;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
enqueue(task) {
|
|
13
|
+
if (typeof task !== 'function') throw new TypeError('Page tasks must be functions.');
|
|
14
|
+
if (this.closed || this.tasks.length + (this.running ? 1 : 0) >= this.maxPending) return null;
|
|
15
|
+
let resolve, reject;
|
|
16
|
+
const result = new Promise((accept, deny) => { resolve = accept; reject = deny; });
|
|
17
|
+
this.tasks.push({ task, resolve, reject });
|
|
18
|
+
if (!this.running) void this.drain();
|
|
19
|
+
return result;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async drain() {
|
|
23
|
+
this.running = true;
|
|
24
|
+
while (!this.closed && this.tasks.length) {
|
|
25
|
+
const current = this.tasks.shift();
|
|
26
|
+
try { current.resolve(await current.task()); }
|
|
27
|
+
catch (error) { current.reject(error); }
|
|
28
|
+
}
|
|
29
|
+
this.running = false;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
close(error = new Error('Page task lane closed.')) {
|
|
33
|
+
if (this.closed) return;
|
|
34
|
+
this.closed = true;
|
|
35
|
+
this.tasks.splice(0).forEach(task => task.reject(error));
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
module.exports = PageTaskLane;
|
|
@@ -222,14 +222,14 @@ class ReactiveRenderer {
|
|
|
222
222
|
const node = this.nodes.get(ownerId(payload.component));
|
|
223
223
|
return node && [...node.html.matchAll(/\b(?:data-rw-state|rw-bind)\s*=\s*["']([^"']+)["']/g)].some(match => match[1] === payload.name);
|
|
224
224
|
});
|
|
225
|
-
if (!this.disposed && generation === this.generation && socket && (patches.length || explicit.length)) {
|
|
226
|
-
try { if (this.authorize) await this.authorize(); }
|
|
227
|
-
catch (error) {
|
|
228
|
-
if (this.disposed || generation !== this.generation) return;
|
|
229
|
-
throw error;
|
|
230
|
-
}
|
|
231
|
-
if (this.disposed || generation !== this.generation) return;
|
|
232
|
-
socket.sendEvent('redweb:patch', { patches, states: explicit });
|
|
225
|
+
if (!this.disposed && generation === this.generation && socket && (patches.length || explicit.length)) {
|
|
226
|
+
try { if (this.authorize) await this.authorize(); }
|
|
227
|
+
catch (error) {
|
|
228
|
+
if (this.disposed || generation !== this.generation) return;
|
|
229
|
+
throw error;
|
|
230
|
+
}
|
|
231
|
+
if (this.disposed || generation !== this.generation) return;
|
|
232
|
+
socket.sendEvent('redweb:patch', { patches, states: explicit });
|
|
233
233
|
}
|
|
234
234
|
}
|
|
235
235
|
|
package/src/htmx/SocketAction.js
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const { AsyncLocalStorage } = require('async_hooks');
|
|
4
|
-
const { bindings } = require('../ws/SocketAction');
|
|
5
|
-
const routes = new AsyncLocalStorage();
|
|
6
|
-
|
|
7
|
-
function attributes(properties) {
|
|
8
|
-
let result = properties;
|
|
9
|
-
for (const name of ['rw-click', 'rw-submit']) {
|
|
10
|
-
const binding = bindings.get(properties[name]);
|
|
11
|
-
if (!binding) continue;
|
|
12
|
-
if (!routes.getStore()?.has(binding.Handler)) throw new TypeError('Socket action handler must belong to this page socket route.');
|
|
13
|
-
if (Object.keys(result).some(key => key.toLowerCase() === 'data-rw-command')) throw new TypeError('Only one socket action may bind a control.');
|
|
14
|
-
result = { ...result, [name]: binding.type, 'data-rw-command': JSON.stringify({ type: binding.type, payload: binding.payload }) };
|
|
15
|
-
}
|
|
16
|
-
return result;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
module.exports = { attributes, withRoute: (handlers, render) => routes.run(handlers, render) };
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { AsyncLocalStorage } = require('async_hooks');
|
|
4
|
+
const { bindings } = require('../ws/SocketAction');
|
|
5
|
+
const routes = new AsyncLocalStorage();
|
|
6
|
+
|
|
7
|
+
function attributes(properties) {
|
|
8
|
+
let result = properties;
|
|
9
|
+
for (const name of ['rw-click', 'rw-submit']) {
|
|
10
|
+
const binding = bindings.get(properties[name]);
|
|
11
|
+
if (!binding) continue;
|
|
12
|
+
if (!routes.getStore()?.has(binding.Handler)) throw new TypeError('Socket action handler must belong to this page socket route.');
|
|
13
|
+
if (Object.keys(result).some(key => key.toLowerCase() === 'data-rw-command')) throw new TypeError('Only one socket action may bind a control.');
|
|
14
|
+
result = { ...result, [name]: binding.type, 'data-rw-command': JSON.stringify({ type: binding.type, payload: binding.payload }) };
|
|
15
|
+
}
|
|
16
|
+
return result;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
module.exports = { attributes, withRoute: (handlers, render) => routes.run(handlers, render) };
|
|
@@ -13,7 +13,7 @@ const BINDING = /{{\s*([A-Za-z_$][\w$]*)\s*}}/g;
|
|
|
13
13
|
const ATTRIBUTE_BINDING = /{{\s*[A-Za-z_$][\w$]*\s*}}/;
|
|
14
14
|
const NAME = /^[A-Za-z_$][\w$]*$/;
|
|
15
15
|
const DIRECTIVES = new Set(['data-rw-state', 'data-rw-html', 'rw-each']);
|
|
16
|
-
const COMPONENT_DIRECTIVES = new Set(['data-rw-component', 'data-rw-state', 'rw-bind', 'rw-click', 'rw-submit', 'rw-status']);
|
|
16
|
+
const COMPONENT_DIRECTIVES = new Set(['data-rw-component', 'data-rw-state', 'rw-bind', 'rw-click', 'rw-submit', 'rw-upload', 'rw-paste', 'rw-status']);
|
|
17
17
|
|
|
18
18
|
function attributes(tag, nameEnd, tracked = DIRECTIVES) {
|
|
19
19
|
const found = new Map();
|
package/src/htmx/index.js
CHANGED
|
@@ -2,9 +2,11 @@ const HtmlRenderer = require('./HtmlRenderer');
|
|
|
2
2
|
const LiveHtmlServer = require('./LiveHtmlServer');
|
|
3
3
|
const LivePage = require('./LivePage');
|
|
4
4
|
const { attribute, codeBlock, each, html, safeUrl: url } = require('./Html');
|
|
5
|
-
const {
|
|
5
|
+
const { highlightCode } = require('./CodeHighlight');
|
|
6
|
+
const { action, component, inject, page, resource, state, upload, view } = require('./metadata');
|
|
7
|
+
const { LiveResource, liveResource } = require('./LiveResource');
|
|
6
8
|
const { start } = require('./start');
|
|
7
9
|
const { exportStatic } = require('./StaticExporter');
|
|
8
10
|
const { defineSite } = require('./StaticSite');
|
|
9
11
|
|
|
10
|
-
module.exports = { action, attribute, codeBlock, component, defineSite, each, exportStatic, html, HtmlRenderer, LiveHtmlServer, LivePage, page, start, state, url, view };
|
|
12
|
+
module.exports = { action, attribute, codeBlock, component, defineSite, each, exportStatic, highlightCode, html, HtmlRenderer, inject, LiveHtmlServer, LivePage, LiveResource, liveResource, page, resource, start, state, upload, url, view };
|
package/src/htmx/metadata.js
CHANGED
|
@@ -7,6 +7,12 @@ const STANDARD_ACTIONS = new WeakMap();
|
|
|
7
7
|
const VIEW_METADATA = new WeakMap();
|
|
8
8
|
const RESOLVED_VIEW = new WeakMap();
|
|
9
9
|
const STANDARD_VIEWS = new WeakMap();
|
|
10
|
+
const RESOURCE_METADATA = new WeakMap();
|
|
11
|
+
const RESOLVED_RESOURCE = new WeakMap();
|
|
12
|
+
const INJECT_METADATA = new WeakMap();
|
|
13
|
+
const RESOLVED_INJECT = new WeakMap();
|
|
14
|
+
const UPLOAD_METADATA = new WeakMap();
|
|
15
|
+
const RESOLVED_UPLOAD = new WeakMap();
|
|
10
16
|
const PAGE_ROOTS = new WeakMap();
|
|
11
17
|
const PAGE_STYLESHEET_ROOTS = new WeakMap();
|
|
12
18
|
const COMPONENT_CLASSES = new WeakSet();
|
|
@@ -65,6 +71,27 @@ function registerView(store, PageClass, stateName, method, implementation) {
|
|
|
65
71
|
metadataVersion += 1;
|
|
66
72
|
}
|
|
67
73
|
|
|
74
|
+
function registerResource(PageClass, property, config) {
|
|
75
|
+
const properties = new Map(RESOURCE_METADATA.get(PageClass) || []);
|
|
76
|
+
properties.set(property, config);
|
|
77
|
+
RESOURCE_METADATA.set(PageClass, properties);
|
|
78
|
+
metadataVersion += 1;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function registerInject(PageClass, property, provider) {
|
|
82
|
+
const properties = new Map(INJECT_METADATA.get(PageClass) || []);
|
|
83
|
+
properties.set(property, provider);
|
|
84
|
+
INJECT_METADATA.set(PageClass, properties);
|
|
85
|
+
metadataVersion += 1;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function registerUpload(PageClass, method, config) {
|
|
89
|
+
const methods = new Map(UPLOAD_METADATA.get(PageClass) || []);
|
|
90
|
+
methods.set(method, config);
|
|
91
|
+
UPLOAD_METADATA.set(PageClass, methods);
|
|
92
|
+
metadataVersion += 1;
|
|
93
|
+
}
|
|
94
|
+
|
|
68
95
|
function resolvedState(PageClass) {
|
|
69
96
|
const cached = RESOLVED_STATE.get(PageClass);
|
|
70
97
|
if (cached?.version === metadataVersion) return cached.value;
|
|
@@ -112,6 +139,15 @@ function resolvedView(PageClass) {
|
|
|
112
139
|
return value;
|
|
113
140
|
}
|
|
114
141
|
|
|
142
|
+
function resolved(store, cache, PageClass) {
|
|
143
|
+
const cached = cache.get(PageClass);
|
|
144
|
+
if (cached?.version === metadataVersion) return cached.value;
|
|
145
|
+
const value = new Map();
|
|
146
|
+
hierarchy(PageClass).forEach(CurrentClass => store.get(CurrentClass)?.forEach((config, property) => value.set(property, config)));
|
|
147
|
+
cache.set(PageClass, { version: metadataVersion, value });
|
|
148
|
+
return value;
|
|
149
|
+
}
|
|
150
|
+
|
|
115
151
|
function pageHead(value) {
|
|
116
152
|
if (value === undefined) return undefined;
|
|
117
153
|
if (!value || typeof value !== 'object' || Array.isArray(value)) throw new TypeError('Page head must be an object.');
|
|
@@ -178,10 +214,10 @@ function page(routePath, options = {}) {
|
|
|
178
214
|
if (!['connection', 'shared'].includes(scope)) {
|
|
179
215
|
throw new TypeError('Page scope must be "connection" or "shared".');
|
|
180
216
|
}
|
|
181
|
-
if (typeof live !== 'boolean') throw new TypeError('Page live must be a boolean.');
|
|
182
|
-
if (options.socket !== undefined && (typeof options.socket !== 'function' || !live || scope !== 'connection')) {
|
|
183
|
-
throw new TypeError('Page socket requires a route class and a live connection-scoped page.');
|
|
184
|
-
}
|
|
217
|
+
if (typeof live !== 'boolean') throw new TypeError('Page live must be a boolean.');
|
|
218
|
+
if (options.socket !== undefined && (typeof options.socket !== 'function' || !live || scope !== 'connection')) {
|
|
219
|
+
throw new TypeError('Page socket requires a route class and a live connection-scoped page.');
|
|
220
|
+
}
|
|
185
221
|
if (layout !== undefined && typeof layout !== 'function') throw new TypeError('Page layout must be a function.');
|
|
186
222
|
const head = pageHead(options.head);
|
|
187
223
|
const cache = pageCache(options.cache, live);
|
|
@@ -192,8 +228,8 @@ function page(routePath, options = {}) {
|
|
|
192
228
|
PAGE_METADATA.set(PageClass, Object.freeze({
|
|
193
229
|
path: routePath,
|
|
194
230
|
template,
|
|
195
|
-
scope,
|
|
196
|
-
...(options.socket && { socket: options.socket }),
|
|
231
|
+
scope,
|
|
232
|
+
...(options.socket && { socket: options.socket }),
|
|
197
233
|
...(live === false && { live: false }),
|
|
198
234
|
...(head && { head }),
|
|
199
235
|
...(cache && { cache }),
|
|
@@ -250,6 +286,71 @@ function state(options = {}) {
|
|
|
250
286
|
};
|
|
251
287
|
}
|
|
252
288
|
|
|
289
|
+
function resource(liveResource, select) {
|
|
290
|
+
const { LiveResource } = require('./LiveResource');
|
|
291
|
+
if (!(liveResource instanceof LiveResource)) throw new TypeError('resource() requires a value created by liveResource().');
|
|
292
|
+
if (typeof select !== 'function') throw new TypeError('resource() requires a key selector.');
|
|
293
|
+
const config = Object.freeze({ resource: liveResource, select });
|
|
294
|
+
return (target, property) => {
|
|
295
|
+
if (property?.kind === 'field') {
|
|
296
|
+
if (property.static || property.private || typeof property.name !== 'string' || !property.name) {
|
|
297
|
+
throw new TypeError('resource() requires a public instance field with a string name.');
|
|
298
|
+
}
|
|
299
|
+
property.addInitializer(function registerStandardResource() {
|
|
300
|
+
registerState(this.constructor, property.name, Object.freeze({ writable: false }));
|
|
301
|
+
registerResource(this.constructor, property.name, config);
|
|
302
|
+
});
|
|
303
|
+
return initialValue => initialValue;
|
|
304
|
+
}
|
|
305
|
+
const PageClass = assertDecoratorTarget(target, 'resource()');
|
|
306
|
+
if (typeof property !== 'string' || !property) throw new TypeError('Resource property must be a non-empty string.');
|
|
307
|
+
registerState(PageClass, property, Object.freeze({ writable: false }));
|
|
308
|
+
registerResource(PageClass, property, config);
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function inject(provider) {
|
|
313
|
+
if (typeof provider !== 'string' || !provider || provider.length > 128 || ['__proto__', 'prototype', 'constructor'].includes(provider)) {
|
|
314
|
+
throw new TypeError('inject() requires a safe non-empty provider name of at most 128 characters.');
|
|
315
|
+
}
|
|
316
|
+
return (target, property) => {
|
|
317
|
+
if (property?.kind === 'field') {
|
|
318
|
+
if (property.static || property.private || typeof property.name !== 'string' || !property.name) {
|
|
319
|
+
throw new TypeError('inject() requires a public instance field with a string name.');
|
|
320
|
+
}
|
|
321
|
+
property.addInitializer(function registerStandardInject() { registerInject(this.constructor, property.name, provider); });
|
|
322
|
+
return initialValue => initialValue;
|
|
323
|
+
}
|
|
324
|
+
const PageClass = assertDecoratorTarget(target, 'inject()');
|
|
325
|
+
if (typeof property !== 'string' || !property) throw new TypeError('Injected property must be a non-empty string.');
|
|
326
|
+
registerInject(PageClass, property, provider);
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function upload(options = {}) {
|
|
331
|
+
if (!options || typeof options !== 'object' || Array.isArray(options)) throw new TypeError('upload() options must be an object.');
|
|
332
|
+
const { maxBytes = 10 * 1024 * 1024, accept = [] } = options;
|
|
333
|
+
if (!Number.isInteger(maxBytes) || maxBytes < 1 || maxBytes > 1024 * 1024 * 1024) throw new RangeError('upload() maxBytes must be an integer between 1 and 1 GiB.');
|
|
334
|
+
const types = Array.isArray(accept) ? accept : [accept];
|
|
335
|
+
if (types.some(type => typeof type !== 'string' || !/^[a-z]+\/(?:[a-z0-9.+-]+|\*)$/.test(type))) throw new TypeError('upload() accept must contain MIME types or type wildcards.');
|
|
336
|
+
const config = Object.freeze({ maxBytes, accept: Object.freeze([...new Set(types)]) });
|
|
337
|
+
return (target, method, descriptor) => {
|
|
338
|
+
if (method?.kind === 'method') {
|
|
339
|
+
if (method.static || method.private || typeof method.name !== 'string' || !method.name || typeof target !== 'function') throw new TypeError('upload() requires a public instance method with a string name.');
|
|
340
|
+
const entry = Object.freeze({ implementation: target, definition: new ActionDefinition() });
|
|
341
|
+
method.addInitializer(function registerStandardUpload() {
|
|
342
|
+
if (this[method.name] === target) { registerStandardAction(this.constructor, method.name, entry); registerUpload(this.constructor, method.name, config); }
|
|
343
|
+
});
|
|
344
|
+
return target;
|
|
345
|
+
}
|
|
346
|
+
const PageClass = assertDecoratorTarget(target, 'upload()');
|
|
347
|
+
if (typeof method !== 'string' || !method || typeof descriptor?.value !== 'function') throw new TypeError('upload() must decorate a method.');
|
|
348
|
+
registerAction(PageClass, method, Object.freeze({ implementation: descriptor.value, definition: new ActionDefinition() }));
|
|
349
|
+
registerUpload(PageClass, method, config);
|
|
350
|
+
return descriptor;
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
|
|
253
354
|
function action(options) {
|
|
254
355
|
const definition = new ActionDefinition(options);
|
|
255
356
|
return (target, method, descriptor) => {
|
|
@@ -321,6 +422,10 @@ function getStateConfig(PageClass, property) {
|
|
|
321
422
|
return resolvedState(PageClass).get(property);
|
|
322
423
|
}
|
|
323
424
|
|
|
425
|
+
function getResourceMetadata(PageClass) { return new Map(resolved(RESOURCE_METADATA, RESOLVED_RESOURCE, PageClass)); }
|
|
426
|
+
function getInjectMetadata(PageClass) { return new Map(resolved(INJECT_METADATA, RESOLVED_INJECT, PageClass)); }
|
|
427
|
+
function getUploadMetadata(PageClass) { return new Map(resolved(UPLOAD_METADATA, RESOLVED_UPLOAD, PageClass)); }
|
|
428
|
+
|
|
324
429
|
function forEachState(PageClass, callback) {
|
|
325
430
|
resolvedState(PageClass).forEach(callback);
|
|
326
431
|
}
|
|
@@ -349,6 +454,9 @@ module.exports = {
|
|
|
349
454
|
getActionDefinition,
|
|
350
455
|
getActionMetadata,
|
|
351
456
|
getPageMetadata,
|
|
457
|
+
getInjectMetadata,
|
|
458
|
+
getUploadMetadata,
|
|
459
|
+
getResourceMetadata,
|
|
352
460
|
getPageStylesheetRoots,
|
|
353
461
|
getPageTemplateRoot,
|
|
354
462
|
getStateConfig,
|
|
@@ -356,10 +464,13 @@ module.exports = {
|
|
|
356
464
|
getViewImplementation,
|
|
357
465
|
getViewMetadata,
|
|
358
466
|
isComponentClass,
|
|
467
|
+
inject,
|
|
359
468
|
page,
|
|
360
469
|
pageCache,
|
|
361
470
|
pageHead,
|
|
362
471
|
setPageStylesheetRoots,
|
|
363
472
|
state,
|
|
473
|
+
resource,
|
|
474
|
+
upload,
|
|
364
475
|
view,
|
|
365
476
|
};
|
package/src/ws/BaseHandler.js
CHANGED
|
@@ -20,12 +20,12 @@ class BaseHandler {
|
|
|
20
20
|
*/
|
|
21
21
|
async handleMessage(socket, message) {
|
|
22
22
|
const validationResult = await this.validateMessage(message, socket);
|
|
23
|
-
if (validationResult === false) {
|
|
24
|
-
throw new Error('Invalid message');
|
|
25
|
-
}
|
|
26
|
-
const guard = require('./HandlerGuard').guards.get(socket);
|
|
27
|
-
if (guard) await guard();
|
|
28
|
-
return this.onMessage(socket, message);
|
|
23
|
+
if (validationResult === false) {
|
|
24
|
+
throw new Error('Invalid message');
|
|
25
|
+
}
|
|
26
|
+
const guard = require('./HandlerGuard').guards.get(socket);
|
|
27
|
+
if (guard) await guard();
|
|
28
|
+
return this.onMessage(socket, message);
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
validateMessage() {
|