@webqit/webflo 0.11.61 → 1.0.1

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.
Files changed (66) hide show
  1. package/package.json +1 -1
  2. package/src/{Context.js → AbstractContext.js} +1 -9
  3. package/src/deployment-pi/origins/index.js +1 -1
  4. package/src/index.js +1 -9
  5. package/src/runtime-pi/HttpEvent.js +101 -81
  6. package/src/runtime-pi/HttpUser.js +126 -0
  7. package/src/runtime-pi/MessagingOverBroadcast.js +9 -0
  8. package/src/runtime-pi/MessagingOverChannel.js +85 -0
  9. package/src/runtime-pi/MessagingOverSocket.js +106 -0
  10. package/src/runtime-pi/MultiportMessagingAPI.js +81 -0
  11. package/src/runtime-pi/WebfloCookieStorage.js +27 -0
  12. package/src/runtime-pi/WebfloEventTarget.js +39 -0
  13. package/src/runtime-pi/WebfloMessageEvent.js +58 -0
  14. package/src/runtime-pi/WebfloMessagingAPI.js +69 -0
  15. package/src/runtime-pi/{Router.js → WebfloRouter.js} +3 -34
  16. package/src/runtime-pi/WebfloRuntime.js +52 -0
  17. package/src/runtime-pi/WebfloStorage.js +109 -0
  18. package/src/runtime-pi/client/ClientMessaging.js +5 -0
  19. package/src/runtime-pi/client/Context.js +2 -6
  20. package/src/runtime-pi/client/CookieStorage.js +17 -0
  21. package/src/runtime-pi/client/Router.js +3 -13
  22. package/src/runtime-pi/client/SessionStorage.js +33 -0
  23. package/src/runtime-pi/client/Url.js +24 -72
  24. package/src/runtime-pi/client/WebfloClient.js +544 -0
  25. package/src/runtime-pi/client/WebfloRootClient1.js +179 -0
  26. package/src/runtime-pi/client/WebfloRootClient2.js +109 -0
  27. package/src/runtime-pi/client/WebfloSubClient.js +165 -0
  28. package/src/runtime-pi/client/Workport.js +89 -161
  29. package/src/runtime-pi/client/generate.js +3 -3
  30. package/src/runtime-pi/client/index.js +13 -18
  31. package/src/runtime-pi/client/worker/ClientMessaging.js +5 -0
  32. package/src/runtime-pi/client/worker/Context.js +2 -6
  33. package/src/runtime-pi/client/worker/CookieStorage.js +17 -0
  34. package/src/runtime-pi/client/worker/SessionStorage.js +13 -0
  35. package/src/runtime-pi/client/worker/WebfloWorker.js +294 -0
  36. package/src/runtime-pi/client/worker/Workport.js +13 -73
  37. package/src/runtime-pi/client/worker/index.js +7 -18
  38. package/src/runtime-pi/index.js +1 -8
  39. package/src/runtime-pi/server/ClientMessaging.js +18 -0
  40. package/src/runtime-pi/server/ClientMessagingRegistry.js +57 -0
  41. package/src/runtime-pi/server/Context.js +2 -6
  42. package/src/runtime-pi/server/CookieStorage.js +17 -0
  43. package/src/runtime-pi/server/Router.js +2 -68
  44. package/src/runtime-pi/server/SessionStorage.js +53 -0
  45. package/src/runtime-pi/server/WebfloServer.js +755 -0
  46. package/src/runtime-pi/server/index.js +7 -18
  47. package/src/runtime-pi/util-http.js +268 -32
  48. package/src/runtime-pi/xURL.js +25 -22
  49. package/src/runtime-pi/xfetch.js +2 -2
  50. package/src/runtime-pi/Application.js +0 -29
  51. package/src/runtime-pi/Cookies.js +0 -82
  52. package/src/runtime-pi/Runtime.js +0 -21
  53. package/src/runtime-pi/client/Application.js +0 -76
  54. package/src/runtime-pi/client/Runtime.js +0 -525
  55. package/src/runtime-pi/client/createStorage.js +0 -58
  56. package/src/runtime-pi/client/worker/Application.js +0 -44
  57. package/src/runtime-pi/client/worker/Runtime.js +0 -275
  58. package/src/runtime-pi/server/Application.js +0 -101
  59. package/src/runtime-pi/server/Runtime.js +0 -558
  60. package/src/runtime-pi/xFormData.js +0 -24
  61. package/src/runtime-pi/xHeaders.js +0 -146
  62. package/src/runtime-pi/xRequest.js +0 -46
  63. package/src/runtime-pi/xRequestHeaders.js +0 -109
  64. package/src/runtime-pi/xResponse.js +0 -33
  65. package/src/runtime-pi/xResponseHeaders.js +0 -117
  66. package/src/runtime-pi/xxHttpMessage.js +0 -102
@@ -1,78 +1,18 @@
1
+ export class Workport {
1
2
 
2
- export default class Workport {
3
-
4
- constructor() {
5
- // --------
6
- // Post messaging
7
- // --------
8
- this.messaging = {
9
- post: (message, client = this.client) => {
10
- if (!client) throw new Error(`No client for this operation.`);
11
- client.postMessage(message);
12
- return this;
13
- },
14
- listen: (callback, client = this.client) => {
15
- (client || self).addEventListener('message', evt => {
16
- this.client = evt.source;
17
- const response = callback(evt);
18
- let responsePort = evt.ports[0];
19
- if (responsePort) {
20
- if (response instanceof Promise) {
21
- response.then(data => {
22
- responsePort.postMessage(data);
23
- });
24
- } else {
25
- responsePort.postMessage(response);
26
- }
27
- }
28
- });
29
- return this;
30
- },
31
- request: (message, client = this.client) => {
32
- if (!client) throw new Error(`No client for this operation.`);
33
- return new Promise(res => {
34
- let messageChannel = new MessageChannel();
35
- client.postMessage(message, [ messageChannel.port2 ]);
36
- messageChannel.port1.onmessage = e => res(e.data);
37
- });
38
- },
39
- channel(channelId) {
40
- if (!this.channels.has(channelId)) { this.channels.set(channelId, new BroadcastChannel(channel)); }
41
- let channel = this.channels.get(channelId);
42
- return {
43
- broadcast: message => channel.postMessage(message),
44
- listen: callback => channel.addEventListener('message', callback),
45
- };
46
- },
47
- channels: new Map,
48
- };
49
-
50
- // --------
51
- // Notifications
52
- // --------
53
- this.notifications = {
54
- handle: callback => {
55
- self.addEventListener('notificationclick', e => e.waitUntil(callback(e)));
56
- return this;
57
- },
58
- fire: (title, params = {}) => {
59
- return self.registration.showNotification(title, params);
60
- },
61
- };
62
-
63
- // --------
64
- // Push Notifications
65
- // --------
66
- this.push = {
67
- listen: callback => {
68
- self.addEventListener('push', e => e.waitUntil(callback(e)));
69
- return this;
70
- },
71
- };
3
+ showNotification(title, params = {}) {
4
+ return self.registration.showNotification(title, params);
72
5
  }
73
6
 
74
- setCurrentClient(client) {
75
- this.client = client;
7
+ handleNotificationClick(callback) {
8
+ const handler = (e) => e.waitUntil(callback(e));
9
+ self.addEventListener('notificationclick', handler);
10
+ return () => self.removeEventListener('notificationclick', handler);
11
+ }
12
+
13
+ handlePush(callback) {
14
+ const handler = (e) => e.waitUntil(callback(e));
15
+ self.addEventListener('push', handler);
16
+ return () => self.removeEventListener('notificationclick', handler);
76
17
  }
77
-
78
18
  }
@@ -1,21 +1,10 @@
1
+ import { WebfloWorker } from './WebfloWorker.js';
1
2
 
2
- /**
3
- * @imports
4
- */
5
- import Context from './Context.js';
6
- import Application from './Application.js';
7
- import Runtime from './Runtime.js';
8
-
9
- /**
10
- * @start
11
- */
12
- export async function start(applicationInstance = null) {
13
- const cx = this || {};
14
- const defaultApplicationInstance = _cx => new Application(_cx);
15
- return new Runtime(Context.create(cx), applicationInstance || defaultApplicationInstance);
3
+ export function start() {
4
+ const instance = WebfloWorker.create(this || {});
5
+ instance.initialize();
16
6
  }
17
7
 
18
- /**
19
- * @APIS
20
- */
21
- export * as APIS from './Runtime.js';
8
+ export {
9
+ WebfloWorker
10
+ }
@@ -1,14 +1,7 @@
1
-
2
- /**
3
- * @imports
4
- */
5
1
  import * as server from './server/index.js';
6
2
  import * as client from './client/generate.js';
7
3
 
8
- /**
9
- * @exports
10
- */
11
4
  export {
12
5
  server,
13
- client,
6
+ client
14
7
  }
@@ -0,0 +1,18 @@
1
+ import { MultiportMessagingAPI } from '../MultiportMessagingAPI.js';
2
+
3
+ export class ClientMessaging extends MultiportMessagingAPI {
4
+
5
+ get runtime() { return this.parentNode.parentNode; }
6
+
7
+ #portID;
8
+ get portID() { return this.#portID; }
9
+
10
+ constructor(parentNode/*ClientMessagingRegistry*/, portID, params = {}) {
11
+ super(parentNode, params);
12
+ this.#portID = portID;
13
+ }
14
+
15
+ createBroadcastChannel(name) {
16
+ return this.parentNode.createBroadcastChannel(name);
17
+ }
18
+ }
@@ -0,0 +1,57 @@
1
+ import { ClientMessaging } from './ClientMessaging.js';
2
+ import crypto from 'crypto';
3
+
4
+ export class ClientMessagingRegistry extends Map {
5
+
6
+ #parentNode;
7
+ get parentNode() { return this.#parentNode; }
8
+
9
+ #sessionID;
10
+ get sessionID() { return this.#sessionID; }
11
+
12
+ #params;
13
+ get params() { return this.#params; }
14
+
15
+ #channels = new Map;
16
+
17
+ constructor(parentNode/*WebfloServer*/, sessionID, params = {}) {
18
+ super();
19
+ this.#parentNode = parentNode;
20
+ this.#sessionID = sessionID;
21
+ this.#params = params;
22
+ }
23
+
24
+ createPort() {
25
+ const portID = crypto.randomUUID();
26
+ const portInstance = new ClientMessaging(this, portID, this.#params);
27
+ this.set(portID, portInstance);
28
+ portInstance.on('empty', () => {
29
+ this.delete(portID);
30
+ });
31
+ setTimeout(() => {
32
+ if (portInstance.ports.size || !this.has(portID)) return;
33
+ this.delete(portID);
34
+ }, 30000/*30sec*/);
35
+ return portInstance;
36
+ }
37
+
38
+ createBroadcastChannel(name) {
39
+ if (!this.#channels.has(name)) {
40
+ this.#channels.set(name, new BroadcastChannel(this, name));
41
+ }
42
+ return this.#channels.get(name);
43
+ }
44
+ }
45
+
46
+ export class BroadcastChannel extends EventTarget {
47
+ #manager;
48
+ #name;
49
+ constructor(manager, name) {
50
+ super();
51
+ this.#manager = manager;
52
+ this.#name = name;
53
+ }
54
+
55
+ postMessage() {
56
+ }
57
+ }
@@ -1,10 +1,6 @@
1
+ import { AbstractContext } from '../../AbstractContext.js';
1
2
 
2
- /**
3
- * @imports
4
- */
5
- import _Context from '../../Context.js';
6
-
7
- export default class Context extends _Context {
3
+ export class Context extends AbstractContext {
8
4
  // env
9
5
  get env() {
10
6
  return this.dict.env || {};
@@ -0,0 +1,17 @@
1
+ import { WebfloCookieStorage } from '../WebfloCookieStorage.js';
2
+
3
+ export class CookieStorage extends WebfloCookieStorage {
4
+ static create(request) {
5
+ return new this(
6
+ request,
7
+ request.headers.get('Cookie', true).map((c) => [c.name, c])
8
+ );
9
+ }
10
+
11
+ commit(response) {
12
+ for (const cookieStr of this.render()) {
13
+ response.headers.append('Set-Cookie', cookieStr);
14
+ }
15
+ super.commit();
16
+ }
17
+ }
@@ -1,20 +1,9 @@
1
-
2
- /**
3
- * @imports
4
- */
5
1
  import Fs from 'fs';
6
2
  import Url from 'url';
7
3
  import Path from 'path';
8
- import Mime from 'mime-types';
9
- import _Router from '../Router.js';
10
-
11
- /**
12
- * ---------------------------
13
- * The Router class
14
- * ---------------------------
15
- */
4
+ import { WebfloRouter } from '../WebfloRouter.js';
16
5
 
17
- export default class Router extends _Router {
6
+ export class Router extends WebfloRouter {
18
7
 
19
8
  async readTick(thisTick) {
20
9
  thisTick = { ...thisTick };
@@ -52,61 +41,6 @@ export default class Router extends _Router {
52
41
  return Path.join(...args);
53
42
  }
54
43
 
55
- /**
56
- * Reads a static file from the public directory.
57
- *
58
- * @param ServerNavigationEvent httpEvent
59
- *
60
- * @return Promise
61
- */
62
- file(httpEvent) {
63
- let filename = Path.join(this.cx.CWD, this.cx.layout.PUBLIC_DIR, decodeURIComponent(httpEvent.url.pathname));
64
- let index, ext = Path.parse(httpEvent.url.pathname).ext;
65
- // if is a directory search for index file matching the extention
66
- if (!ext && Fs.existsSync(filename) && Fs.lstatSync(filename).isDirectory()) {
67
- ext = '.html';
68
- index = `index${ext}`;
69
- filename = Path.join(filename, index);
70
- }
71
- let enc, acceptEncs = [], supportedEncs = { gzip: '.gz', br: '.br' };
72
- // based on the URL path, extract the file extention. e.g. .js, .doc, ...
73
- // and process encoding
74
- if ((acceptEncs = (httpEvent.request.headers.get('Accept-Encoding') || '').split(',').map(e => e.trim())).length
75
- && (enc = acceptEncs.reduce((prev, _enc) => prev || (Fs.existsSync(filename + supportedEncs[_enc]) && _enc), null))) {
76
- filename = filename + supportedEncs[enc];
77
- } else {
78
- if (!Fs.existsSync(filename)) return;
79
- if (Object.values(supportedEncs).includes(ext)) {
80
- enc = Object.keys(supportedEncs).reduce((prev, _enc) => prev || (supportedEncs[_enc] === ext && _enc), null);
81
- ext = Path.parse(filename.substring(0, filename.length - ext.length)).ext;
82
- }
83
- }
84
-
85
- // read file from file system
86
- return new Promise(resolve => {
87
- Fs.readFile(filename, function(err, data) {
88
- let response;
89
- if (err) {
90
- response = new httpEvent.Response(null, { status: 500, statusText: `Error reading static file: ${filename}` } );
91
- } else {
92
- // if the file is found, set Content-type and send data
93
- let mime = Mime.lookup(ext);
94
- response = new httpEvent.Response(data, { headers: {
95
- contentType: mime === 'application/javascript' ? 'text/javascript' : mime,
96
- contentLength: Buffer.byteLength(data),
97
- } });
98
- if (enc) {
99
- response.headers.set('Content-Encoding', enc);
100
- }
101
- }
102
- response.attrs.filename = filename;
103
- response.attrs.static = true;
104
- response.attrs.index = index;
105
- resolve(response);
106
- });
107
- });
108
- }
109
-
110
44
  /**
111
45
  * Writes a file to the public directory.
112
46
  *
@@ -0,0 +1,53 @@
1
+ import { WebfloStorage } from '../WebfloStorage.js';
2
+ import crypto from 'crypto';
3
+
4
+ const sessionStorage = new Map;
5
+ export class SessionStorage extends WebfloStorage {
6
+
7
+ static create(request, params = {}) {
8
+ let sessionID = request.headers.get('Cookie', true).find((c) => c.name === '__sessid')?.value;
9
+ if (sessionID?.includes('.')) {
10
+ const [rand, signature] = sessionID.split('.');
11
+ const expectedSignature = crypto.createHmac('sha256', params.secret)
12
+ .update(rand)
13
+ .digest('hex');
14
+ if (signature !== expectedSignature) {
15
+ sessionID = undefined;
16
+ }
17
+ }
18
+ if (!sessionID) {
19
+ if (params.secret) {
20
+ const rand = `${(0 | Math.random() * 9e6).toString(36)}`;
21
+ const signature = crypto.createHmac('sha256', params.secret)
22
+ .update(rand)
23
+ .digest('hex');
24
+ sessionID = `${rand}.${signature}`
25
+ } else {
26
+ sessionID = crypto.randomUUID();
27
+ }
28
+ }
29
+ if (sessionStorage.has(sessionID)) {
30
+ return sessionStorage.get(sessionID);
31
+ }
32
+ const instance = new this(request, sessionID);
33
+ sessionStorage.set(sessionID, instance);
34
+ return instance;
35
+ }
36
+
37
+ constructor(request, sessionID) {
38
+ super(request, true);
39
+ this.#sessionID = sessionID;
40
+ }
41
+
42
+ #sessionID;
43
+ get sessionID() {
44
+ return this.#sessionID;
45
+ }
46
+
47
+ commit(response, force = false) {
48
+ if (response.headers.get('Set-Cookie', true).find((c) => c.name === '__sessid')) return;
49
+ if (!force && !this.getAdded().length && !this.getDeleted().length) return;
50
+ response.headers.append('Set-Cookie', `__sessid=${this.#sessionID}; Path=/; Secure; HttpOnly; SameSite=Lax; Max-Age=31536000`);
51
+ super.commit();
52
+ }
53
+ }