@webqit/webflo 0.20.26 → 0.20.28

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 (57) hide show
  1. package/package.json +8 -5
  2. package/src/build-pi/index.js +6 -4
  3. package/src/init-pi/index.js +0 -1
  4. package/src/runtime-pi/{WebfloRuntime.js → AppRuntime.js} +57 -113
  5. package/src/runtime-pi/webflo-client/DeviceCapabilities.js +1 -1
  6. package/src/runtime-pi/webflo-client/WebfloClient.js +163 -103
  7. package/src/runtime-pi/webflo-client/{WebfloRootClient1.js → WebfloRootClientA.js} +39 -56
  8. package/src/runtime-pi/webflo-client/{WebfloRootClient2.js → WebfloRootClientB.js} +3 -3
  9. package/src/runtime-pi/webflo-client/WebfloSubClient.js +28 -15
  10. package/src/runtime-pi/webflo-client/index.js +3 -3
  11. package/src/runtime-pi/webflo-messaging/ClientPortMixin.js +13 -0
  12. package/src/runtime-pi/{webflo-server/messaging/ClientRequestRealtime.js → webflo-messaging/ClientRequestPort001.js} +13 -9
  13. package/src/runtime-pi/webflo-messaging/ClientRequestPort010.js +4 -0
  14. package/src/runtime-pi/webflo-messaging/ClientRequestPort100.js +17 -0
  15. package/src/runtime-pi/webflo-messaging/WebfloTenancy001.js +27 -0
  16. package/src/runtime-pi/webflo-messaging/WebfloTenant001.js +27 -0
  17. package/src/runtime-pi/webflo-routing/HttpCookies101.js +53 -0
  18. package/src/runtime-pi/webflo-routing/HttpCookies110.js +3 -0
  19. package/src/runtime-pi/webflo-routing/{HttpEvent.js → HttpEvent111.js} +95 -73
  20. package/src/runtime-pi/webflo-routing/HttpKeyvalInterface.js +120 -0
  21. package/src/runtime-pi/webflo-routing/HttpSession001.js +24 -0
  22. package/src/runtime-pi/webflo-routing/HttpSession110.js +3 -0
  23. package/src/runtime-pi/webflo-routing/{HttpThread.js → HttpThread111.js} +54 -13
  24. package/src/runtime-pi/webflo-routing/{HttpUser.js → HttpUser111.js} +10 -23
  25. package/src/runtime-pi/webflo-routing/KeyvalsFactory001.js +53 -0
  26. package/src/runtime-pi/webflo-routing/KeyvalsFactory110.js +48 -0
  27. package/src/runtime-pi/webflo-routing/KeyvalsFactoryInterface.js +56 -0
  28. package/src/runtime-pi/webflo-routing/{WebfloRouter.js → WebfloRouter111.js} +5 -6
  29. package/src/runtime-pi/webflo-server/WebfloServer.js +262 -269
  30. package/src/runtime-pi/webflo-worker/WebfloWorker.js +97 -44
  31. package/src/util.js +3 -2
  32. package/src/runtime-pi/apis.js +0 -9
  33. package/src/runtime-pi/webflo-client/ClientSideCookies.js +0 -18
  34. package/src/runtime-pi/webflo-fetch/LiveResponse.js +0 -476
  35. package/src/runtime-pi/webflo-fetch/index.js +0 -419
  36. package/src/runtime-pi/webflo-fetch/util.js +0 -28
  37. package/src/runtime-pi/webflo-messaging/WQBroadcastChannel.js +0 -10
  38. package/src/runtime-pi/webflo-messaging/WQMessageChannel.js +0 -26
  39. package/src/runtime-pi/webflo-messaging/WQMessageEvent.js +0 -87
  40. package/src/runtime-pi/webflo-messaging/WQMessagePort.js +0 -38
  41. package/src/runtime-pi/webflo-messaging/WQRelayPort.js +0 -47
  42. package/src/runtime-pi/webflo-messaging/WQSockPort.js +0 -111
  43. package/src/runtime-pi/webflo-messaging/WQStarPort.js +0 -112
  44. package/src/runtime-pi/webflo-messaging/wq-message-port.js +0 -413
  45. package/src/runtime-pi/webflo-routing/HttpCookies.js +0 -43
  46. package/src/runtime-pi/webflo-routing/HttpSession.js +0 -11
  47. package/src/runtime-pi/webflo-routing/HttpState.js +0 -182
  48. package/src/runtime-pi/webflo-server/ServerSideCookies.js +0 -22
  49. package/src/runtime-pi/webflo-server/ServerSideSession.js +0 -40
  50. package/src/runtime-pi/webflo-server/messaging/Client.js +0 -27
  51. package/src/runtime-pi/webflo-server/messaging/Clients.js +0 -25
  52. package/src/runtime-pi/webflo-url/Url.js +0 -156
  53. package/src/runtime-pi/webflo-url/index.js +0 -1
  54. package/src/runtime-pi/webflo-url/urlpattern.js +0 -38
  55. package/src/runtime-pi/webflo-url/util.js +0 -109
  56. package/src/runtime-pi/webflo-url/xURL.js +0 -94
  57. package/src/runtime-pi/webflo-worker/WorkerSideCookies.js +0 -21
package/package.json CHANGED
@@ -12,7 +12,7 @@
12
12
  "vanila-javascript"
13
13
  ],
14
14
  "homepage": "https://webqit.io/tooling/webflo",
15
- "version": "0.20.26",
15
+ "version": "0.20.28",
16
16
  "license": "MIT",
17
17
  "repository": {
18
18
  "type": "git",
@@ -33,9 +33,6 @@
33
33
  "site:build": "vitepress build site",
34
34
  "site:preview": "vitepress preview site --port 4173"
35
35
  },
36
- "exports": {
37
- "./apis": "./src/runtime-pi/apis.js"
38
- },
39
36
  "bin": {
40
37
  "webflo": "src/webflo-cli.js",
41
38
  "webflo-certbot-http-auth-hook": "src/services-pi/cert/http-auth-hook.js",
@@ -44,13 +41,19 @@
44
41
  "dependencies": {
45
42
  "@octokit/webhooks": "^7.15.1",
46
43
  "@webqit/backpack": "^0.1.12",
44
+ "@webqit/fetch-plus": "^0.1.21",
45
+ "@webqit/keyval": "^0.2.17",
46
+ "@webqit/observer": "^3.8.14",
47
47
  "@webqit/oohtml-ssr": "^2.2.2",
48
- "@webqit/use-live": "^0.5.44",
48
+ "@webqit/port-plus": "^0.1.19",
49
+ "@webqit/url-plus": "^0.1.4",
50
+ "@webqit/use-live": "^0.5.49",
49
51
  "@webqit/util": "^0.8.11",
50
52
  "chokidar": "^4.0.3",
51
53
  "dotenv": "^16.4.7",
52
54
  "esbuild": "^0.14.38",
53
55
  "fast-glob": "^3.3.3",
56
+ "idb": "^8.0.3",
54
57
  "mime-types": "^2.1.33",
55
58
  "simple-git": "^2.20.1",
56
59
  "urlpattern-polyfill": "^4.0.3"
@@ -7,12 +7,12 @@ import { gzipSync, brotliCompressSync } from 'zlib';
7
7
  import { _afterLast, _beforeLast } from '@webqit/util/str/index.js';
8
8
  import { _isObject, _isArray } from '@webqit/util/js/index.js';
9
9
  import { jsFile } from '@webqit/backpack/src/dotfile/index.js';
10
+ import { URLPatternPlus } from '@webqit/url-plus';
10
11
  import { bootstrap as serverBootstrap } from '../runtime-pi/webflo-server/bootstrap.js';
11
12
  import { bootstrap as clientBootstrap } from '../runtime-pi/webflo-client/bootstrap.js';
12
13
  import { bootstrap as workerBootstrap } from '../runtime-pi/webflo-worker/bootstrap.js';
13
14
  import { UseLiveTransform } from './esbuild-plugin-uselive-transform.js';
14
15
  import { CLIContext } from '../CLIContext.js';
15
- import '../runtime-pi/webflo-url/urlpattern.js';
16
16
 
17
17
  function declareConfig({ $source, configExport, indentation = 0 }) {
18
18
  const varName = 'config';
@@ -370,11 +370,13 @@ async function generateWorkerScript({ $context, bootstrap, ...restParams }) {
370
370
  for (const strategy of ['cache_first_urls', 'cache_only_urls']) {
371
371
  if (configExport.WORKER[strategy].length) {
372
372
  const [urls, patterns] = configExport.WORKER[strategy].reduce(([urls, patterns], url) => {
373
- const patternInstance = new URLPattern(url, 'http://localhost');
373
+ const patternInstance = new URLPatternPlus(url, 'http://localhost');
374
374
  const isPattern = patternInstance.isPattern();
375
- if (isPattern && (patternInstance.pattern.hostname !== 'localhost' || patternInstance.pattern.port)) {
375
+
376
+ if (isPattern && (patternInstance.hostname !== 'localhost' || patternInstance.port)) {
376
377
  throw new Error(`Pattern URLs must have no origin part. Recieved "${url}".`);
377
378
  }
379
+
378
380
  return isPattern ? [urls, patterns.concat(patternInstance)] : [urls.concat(url), patterns];
379
381
  }, [[], []]);
380
382
  if (patterns.length) {
@@ -391,7 +393,7 @@ async function generateWorkerScript({ $context, bootstrap, ...restParams }) {
391
393
  configExport.WORKER[strategy] = patterns.reduce((all, pattern) => {
392
394
  const matchedFiles = files.filter((file) => pattern.test(file, 'http://localhost'));
393
395
  if (matchedFiles.length) return all.concat(matchedFiles);
394
- throw new Error(`The pattern "${pattern.pattern.pathname}" didn't match any files.`);
396
+ throw new Error(`The pattern "${pattern.pathname}" didn't match any files.`);
395
397
  }, urls);
396
398
  }
397
399
  }
@@ -5,7 +5,6 @@ import { exec } from 'child_process';
5
5
  import { _toTitle } from '@webqit/util/str/index.js';
6
6
  import { readInitConfig } from '../deployment-pi/util.js';
7
7
  import { CLIContext } from '../CLIContext.js';
8
- import * as deployment from '../deployment-pi/index.js';
9
8
 
10
9
  export const desc = {
11
10
  init: 'Generate a preset Webflo starter app.',
@@ -1,33 +1,18 @@
1
- import { WebfloRouter } from './webflo-routing/WebfloRouter.js';
2
- import { response as responseShim, headers as headersShim } from './webflo-fetch/index.js';
3
- import { LiveResponse } from './webflo-fetch/LiveResponse.js';
1
+ import { LiveResponse, ResponsePlus } from '@webqit/fetch-plus';
2
+ import { WebfloRouter111 } from './webflo-routing/WebfloRouter111.js';
4
3
  import { AppBootstrap } from './AppBootstrap.js';
5
- import { HttpEvent } from './webflo-routing/HttpEvent.js';
6
- import { HttpThread } from './webflo-routing/HttpThread.js';
7
- import { HttpSession } from './webflo-routing/HttpSession.js';
8
- import { HttpUser } from './webflo-routing/HttpUser.js';
9
- import { _wq } from '../util.js';
4
+ import { _meta } from '../util.js';
10
5
 
11
- export class WebfloRuntime {
6
+ export class AppRuntime {
12
7
 
13
8
  #instanceController = new AbortController;
14
9
  get $instanceController() { return this.#instanceController; }
15
10
 
16
- static get Router() { return WebfloRouter; }
17
-
18
- static get HttpEvent() { return HttpEvent; }
19
-
20
- static get HttpThread() { return HttpThread; }
21
-
22
- static get HttpSession() { return HttpSession; }
23
-
24
- static get HttpUser() { return HttpUser; }
25
-
26
11
  static create(bootstrap) { return new this(bootstrap); }
27
12
 
28
13
  #bootstrap;
29
-
30
14
  get bootstrap() { return this.#bootstrap; }
15
+
31
16
  get cx() { return this.bootstrap.cx; }
32
17
  get config() { return this.bootstrap.config; }
33
18
  get routes() { return this.bootstrap.routes; }
@@ -47,20 +32,6 @@ export class WebfloRuntime {
47
32
  if (this.bootstrap.init.SETUP) {
48
33
  await this.bootstrap.init.SETUP(this);
49
34
  }
50
- await this.initCreateStorage();
51
- return this.#instanceController;
52
- }
53
-
54
- async initCreateStorage() {
55
- if (!this.bootstrap.init.createStorage) {
56
- const inmemSessionRegistry = new Map;
57
- this.bootstrap.init.createStorage = (namespace) => {
58
- if (!inmemSessionRegistry.has(namespace)) {
59
- inmemSessionRegistry.set(namespace, new Map);
60
- }
61
- return inmemSessionRegistry.get(namespace);
62
- };
63
- }
64
35
  return this.#instanceController;
65
36
  }
66
37
 
@@ -76,48 +47,26 @@ export class WebfloRuntime {
76
47
  return this.#instanceController;
77
48
  }
78
49
 
79
- createStorage(namespace, ttl) {
80
- return this.bootstrap.init.createStorage(namespace, ttl);
81
- }
82
-
83
50
  createRequest(href, init = {}) {
84
51
  return new Request(href, init);
85
52
  }
86
53
 
87
- createHttpThread({ store, threadID, ...rest }) {
88
- return this.constructor.HttpThread.create({ store, threadID, ...rest });
89
- }
90
-
91
- createHttpCookies({ request, thread, ...rest }) {
92
- return this.constructor.HttpCookies.create({ request, thread, ...rest });
93
- }
94
-
95
- createHttpSession({ store, request, thread, ...rest }) {
96
- return this.constructor.HttpSession.create({ store, request, thread, ...rest });
97
- }
98
-
99
- createHttpUser({ store, request, thread, client, ...rest }) {
100
- return this.constructor.HttpUser.create({ store, request, thread, client, ...rest });
101
- }
102
-
103
- createHttpEvent({ request, thread, cookies, session, user, client, detail, signal, state, ...rest }) {
104
- return this.constructor.HttpEvent.create(null, { request, thread, cookies, session, user, client, detail, signal, state, ...rest });
105
- }
106
-
107
54
  async dispatchNavigationEvent({ httpEvent, crossLayerFetch, clientPortB }) {
108
55
  const { flags: FLAGS, logger: LOGGER } = this.cx;
109
56
 
110
57
  // Dispatch event
111
- const router = new this.constructor.Router(this, httpEvent.url.pathname);
58
+ const router = new WebfloRouter111(this, httpEvent.url.pathname);
112
59
  await router.route(['SETUP'], httpEvent.spawn());
113
60
 
114
61
  // Do proper routing for respone
115
62
  const response = await new Promise(async (resolve) => {
116
63
  let autoLiveResponse, response;
117
- httpEvent.client.wqLifecycle.messaging.then(() => {
64
+
65
+ httpEvent.client.readyStateChange('messaging').then(() => {
118
66
  autoLiveResponse = new LiveResponse(null, { status: 202, statusText: 'Accepted', done: false });
119
67
  resolve(autoLiveResponse);
120
68
  });
69
+
121
70
  const route = async () => {
122
71
  const routeMethods = [httpEvent.request.method, 'default'];
123
72
  const remoteFetch = (...args) => this.remoteFetch(...args);
@@ -134,53 +83,46 @@ export class WebfloRuntime {
134
83
  response = new Response(null, { status: 500, statusText: e.message });
135
84
  }
136
85
 
137
- if (!/Response/.test(LiveResponse.test(response))) {
138
- const isLifecyleComplete = httpEvent.lifeCycleComplete() ?? true;
86
+ if (autoLiveResponse) {
87
+ await this.handleThreadStatus(httpEvent, response);
88
+ await autoLiveResponse.replaceWith(response, { done: true });
89
+ return;
90
+ }
91
+
92
+ if (response instanceof Response) {
93
+ ResponsePlus.upgradeInPlace(response);
94
+ } else if (!(response instanceof LiveResponse)) {
95
+ const isLifecyleComplete = ['waiting', 'done'].includes(httpEvent.readyState);
139
96
  response = LiveResponse.test(response) !== 'Default' || !isLifecyleComplete
140
- ? await LiveResponse.from(response, { done: isLifecyleComplete })
141
- : responseShim.from.value(response);
97
+ ? LiveResponse.from(response, { done: isLifecyleComplete })
98
+ : ResponsePlus.from(response);
142
99
  }
143
100
 
144
101
  // Any "status" in thread?
145
102
  await this.handleThreadStatus(httpEvent, response);
146
-
147
- // Resolve now...
148
- if (autoLiveResponse) {
149
- await autoLiveResponse.replaceWith(response, { done: true });
150
- } else {
151
- resolve(response);
152
- }
103
+ resolve(response);
153
104
  });
154
105
 
155
- // Commit data in the exact order. Reason: in how they depend on each other
156
- for (const storage of [httpEvent.user, httpEvent.session, httpEvent.cookies]) {
157
- await storage?.commit?.(response, FLAGS['dev']);
158
- }
159
-
160
106
  // End-of-life cleanup
161
107
  const cleanup = async () => {
162
- for (const storage of [httpEvent.user, httpEvent.session, httpEvent.cookies]) {
163
- storage.cleanup();
164
- }
165
- if (!httpEvent.thread.extended) {
166
- await httpEvent.thread.clear();
167
- }
108
+ await Promise.all([httpEvent.thread, httpEvent.user, httpEvent.session, httpEvent.cookies].map((x) => x._cleanup()));
168
109
  };
169
110
 
170
- // Wait for any whileLive promises to resolve
171
- if (LiveResponse.test(response) === 'LiveResponse' && response.whileLive()) {
172
- httpEvent.waitUntil(response.whileLive(true));
111
+ // Wait for any "live" response to resolve
112
+ if (response instanceof LiveResponse && response.readyState !== 'done') {
113
+ httpEvent.waitUntil(response.readyStateChange('done'));
114
+ httpEvent.waitUntil(httpEvent.client.readyStateChange('open').then(async () => {
115
+ await new Promise((r) => setTimeout(r, 100));
116
+ }));
173
117
  }
174
118
 
175
- // Send the X-Background-Messaging-Port header
119
+ // Send the X-Message-Port header
176
120
  // This server's event lifecycle management
177
- if (!(httpEvent.lifeCycleComplete() ?? true)) {
121
+ if (!['waiting', 'done'].includes(httpEvent.readyState)) {
178
122
  if (this.isClientSide) {
179
- const responseMeta = _wq(response, 'meta');
180
- responseMeta.set('background_port', clientPortB);
123
+ LiveResponse.attachPort(response, clientPortB);
181
124
  } else {
182
- const upstreamBackgroundPort = response.headers.get('X-Background-Messaging-Port');
183
- response.headers.set('X-Background-Messaging-Port', clientPortB);
125
+ response.headers.set('X-Message-Port', clientPortB);
184
126
  }
185
127
 
186
128
  // On navigation:
@@ -195,42 +137,44 @@ export class WebfloRuntime {
195
137
  }
196
138
  }, 0);
197
139
  });
140
+
198
141
  // On close:
199
142
  // Abort httpEvent itself
200
- httpEvent.client.wqLifecycle.close.then(() => {
143
+ httpEvent.client.readyStateChange('close').then(() => {
201
144
  httpEvent.abort();
202
145
  });
203
146
 
204
147
  // On ROOT event complete:
205
148
  // Close httpEvent.client
206
- httpEvent.lifeCycleComplete(true).then(async () => {
149
+ httpEvent.readyStateChange('done').then(async () => {
207
150
  httpEvent.client.close();
208
- cleanup();
151
+ await cleanup();
209
152
  });
210
- } else cleanup();
153
+ } else await cleanup();
211
154
 
212
- if (!this.isClientSide && LiveResponse.test(response) === 'LiveResponse') {
155
+ if (!this.isClientSide
156
+ && response instanceof LiveResponse) {
213
157
  // Must convert to Response on the server-side before returning
214
- return await response.toResponse({ client: httpEvent.client });
158
+ const outgoingResponse = response.toResponse({ port: httpEvent.client });
159
+ return outgoingResponse;
215
160
  }
216
161
 
217
162
  return response;
218
163
  }
219
164
 
220
165
  async handleThreadStatus(httpEvent, response) {
221
- if (!response.headers.get('Location')) {
222
- const status = await httpEvent.thread.consume('status', true);
223
- if (!status.length) return;
224
- // Fire redirect message?
225
- httpEvent.waitUntil(new Promise((resolve) => {
226
- httpEvent.client.wqLifecycle.open.then(async () => {
227
- setTimeout(() => {
228
- httpEvent.client.postMessage(status, { wqEventOptions: { type: 'alert' } });
229
- resolve();
230
- }, 500); // half a sec
231
- }, { once: true });
232
- }));
233
- }
166
+ if ((response instanceof Response
167
+ || response instanceof LiveResponse)
168
+ && response.headers.get('Location')) return;
169
+
170
+ const status = await httpEvent.thread.consume('status', true);
171
+ if (!status.length) return;
172
+
173
+ httpEvent.waitUntil(httpEvent.client.readyStateChange('open').then(async () => {
174
+ await new Promise((r) => setTimeout(r, 100));
175
+ httpEvent.client.postMessage(status, { type: 'alert' });
176
+ await new Promise((r) => setTimeout(r, 100));
177
+ }));
234
178
  }
235
179
 
236
180
  streamSlice(stream, { start, end }) {
@@ -328,13 +272,13 @@ export class WebfloRuntime {
328
272
 
329
273
  createStreamingResponse(httpEvent, readStream, stats) {
330
274
  let response;
331
- const requestRange = headersShim.get.value.call(httpEvent.request.headers, 'Range', true); // Parses the Range header
275
+ const requestRange = httpEvent.request.headers.get('Range', true); // Parses the Range header
332
276
  if (requestRange.length) {
333
277
  const streams = requestRange.reduce((streams, range) => {
334
278
  if (!streams) return;
335
- const [start, end] = range.render(stats.size); // Resolve offsets
279
+ const [start, end] = range.resolveAgainst(stats.size); // Resolve offsets
336
280
  const currentStart = (streams[streams.length - 1]?.end || -1) + 1;
337
- if (!range.isValid(currentStart, stats.size)) return; // Only after rendering()
281
+ if (!range.canResolveAgainst(currentStart, stats.size)) return; // Only after rendering()
338
282
  return streams.concat({ start, end, stream: readStream({ start, end }) });
339
283
  }, []);
340
284
  if (!streams) {
@@ -1,4 +1,4 @@
1
- import { Observer } from '@webqit/use-live';
1
+ import { Observer } from '@webqit/observer';
2
2
 
3
3
  export class DeviceCapabilities {
4
4