@webqit/webflo 0.20.34 → 0.20.36

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 (122) hide show
  1. package/package.json +9 -3
  2. package/src/index.js +7 -7
  3. package/src/{build-pi → webflo-build}/index.js +4 -4
  4. package/src/{config-pi → webflo-config}/deployment/Env.js +0 -4
  5. package/src/{config-pi → webflo-config}/deployment/Layout.js +0 -4
  6. package/src/{config-pi → webflo-config}/deployment/Origins.js +0 -4
  7. package/src/{config-pi → webflo-config}/deployment/Proxy.js +0 -4
  8. package/src/{config-pi → webflo-config}/deployment/index.js +0 -7
  9. package/src/{config-pi → webflo-config}/index.js +0 -7
  10. package/src/{config-pi → webflo-config}/static/Init.js +0 -4
  11. package/src/{config-pi → webflo-config}/static/Manifest.js +0 -4
  12. package/src/{config-pi → webflo-config}/static/Ssg.js +0 -4
  13. package/src/{config-pi → webflo-config}/static/index.js +0 -7
  14. package/src/{deployment-pi → webflo-deployment}/index.js +0 -7
  15. package/src/{deployment-pi → webflo-deployment}/origins/index.js +0 -13
  16. package/src/webflo-exports/ui/index.js +1 -0
  17. package/src/{init-pi → webflo-init}/index.js +1 -1
  18. package/src/webflo-runtime/webflo-client/DeviceViewport.js +173 -0
  19. package/src/{runtime-pi → webflo-runtime}/webflo-client/WebfloClient.js +0 -65
  20. package/src/{runtime-pi → webflo-runtime}/webflo-client/WebfloRootClientA.js +14 -4
  21. package/src/{runtime-pi → webflo-runtime}/webflo-client/bootstrap.js +1 -1
  22. package/src/{runtime-pi → webflo-runtime}/webflo-client/webflo-elements.js +210 -130
  23. package/src/webflo-runtime/webflo-client/webflo-embedded.js +56 -0
  24. package/src/{runtime-pi → webflo-runtime}/webflo-server/WebfloServer.js +1 -1
  25. package/src/{runtime-pi → webflo-runtime}/webflo-server/bootstrap.js +1 -1
  26. package/src/{runtime-pi → webflo-runtime}/webflo-worker/bootstrap.js +1 -1
  27. package/src/{services-pi → webflo-services}/cert/http-auth-hook.js +1 -1
  28. package/test/browser/elements.src.build.js +1239 -0
  29. package/test/browser/elements.src.build.js.map +7 -0
  30. package/test/browser/elements.src.js +3 -0
  31. package/test/browser/index.html +419 -0
  32. package/src/runtime-pi/webflo-client/webflo-embedded.js +0 -54
  33. package/test/index.test.js +0 -26
  34. package/test/site/package.json +0 -9
  35. package/test/site/public/bundle.html +0 -6
  36. package/test/site/public/bundle.html.json +0 -4
  37. package/test/site/public/bundle.js +0 -2
  38. package/test/site/public/bundle.js.gz +0 -0
  39. package/test/site/public/bundle.webflo.js +0 -15
  40. package/test/site/public/bundle.webflo.js.gz +0 -0
  41. package/test/site/public/index.html +0 -30
  42. package/test/site/public/index1.html +0 -35
  43. package/test/site/public/page-2/bundle.html +0 -5
  44. package/test/site/public/page-2/bundle.html.json +0 -1
  45. package/test/site/public/page-2/bundle.js +0 -2
  46. package/test/site/public/page-2/bundle.js.gz +0 -0
  47. package/test/site/public/page-2/index.html +0 -46
  48. package/test/site/public/page-2/logo-130x130.png +0 -0
  49. package/test/site/public/page-2/main.html +0 -3
  50. package/test/site/public/page-3/logo-130x130.png +0 -0
  51. package/test/site/public/page-4/subpage/bundle.html.json +0 -1
  52. package/test/site/public/page-4/subpage/bundle.js +0 -2
  53. package/test/site/public/page-4/subpage/bundle.js.gz +0 -0
  54. package/test/site/public/page-4/subpage/index.html +0 -31
  55. package/test/site/public/sparoots.json +0 -5
  56. package/test/site/public/worker.js +0 -3
  57. package/test/site/public/worker.js.gz +0 -0
  58. package/test/site/server/index.js +0 -16
  59. /package/src/{build-pi → webflo-build}/esbuild-plugin-uselive-transform.js +0 -0
  60. /package/src/{config-pi → webflo-config}/runtime/Client.js +0 -0
  61. /package/src/{config-pi → webflo-config}/runtime/Server.js +0 -0
  62. /package/src/{config-pi → webflo-config}/runtime/client/Worker.js +0 -0
  63. /package/src/{config-pi → webflo-config}/runtime/client/index.js +0 -0
  64. /package/src/{config-pi → webflo-config}/runtime/index.js +0 -0
  65. /package/src/{config-pi → webflo-config}/runtime/server/Headers.js +0 -0
  66. /package/src/{config-pi → webflo-config}/runtime/server/Redirects.js +0 -0
  67. /package/src/{config-pi → webflo-config}/runtime/server/index.js +0 -0
  68. /package/src/{deployment-pi → webflo-deployment}/util.js +0 -0
  69. /package/{test/site/public/page-4/subpage/bundle.html → src/webflo-exports/index.js} +0 -0
  70. /package/src/{init-pi → webflo-init}/templates/pwa/.gitignore +0 -0
  71. /package/src/{init-pi → webflo-init}/templates/pwa/.webqit/webflo/client.json +0 -0
  72. /package/src/{init-pi → webflo-init}/templates/pwa/.webqit/webflo/layout.json +0 -0
  73. /package/src/{init-pi → webflo-init}/templates/pwa/app/handler.server.js +0 -0
  74. /package/src/{init-pi → webflo-init}/templates/pwa/app/page.html +0 -0
  75. /package/src/{init-pi → webflo-init}/templates/pwa/package.json +0 -0
  76. /package/src/{init-pi → webflo-init}/templates/pwa/public/assets/app.css +0 -0
  77. /package/src/{init-pi → webflo-init}/templates/pwa/public/index.html +0 -0
  78. /package/src/{init-pi → webflo-init}/templates/pwa/public/manifest.json +0 -0
  79. /package/src/{init-pi → webflo-init}/templates/web/.gitignore +0 -0
  80. /package/src/{init-pi → webflo-init}/templates/web/.webqit/webflo/client.json +0 -0
  81. /package/src/{init-pi → webflo-init}/templates/web/.webqit/webflo/layout.json +0 -0
  82. /package/src/{init-pi → webflo-init}/templates/web/app/handler.server.js +0 -0
  83. /package/src/{init-pi → webflo-init}/templates/web/app/page.html +0 -0
  84. /package/src/{init-pi → webflo-init}/templates/web/package.json +0 -0
  85. /package/src/{init-pi → webflo-init}/templates/web/public/assets/app.css +0 -0
  86. /package/src/{init-pi → webflo-init}/templates/web/public/index.html +0 -0
  87. /package/src/{runtime-pi → webflo-runtime}/AppBootstrap.js +0 -0
  88. /package/src/{runtime-pi → webflo-runtime}/AppRuntime.js +0 -0
  89. /package/src/{runtime-pi → webflo-runtime}/index.js +0 -0
  90. /package/src/{runtime-pi → webflo-runtime}/webflo-client/ClientSideWorkport.js +0 -0
  91. /package/src/{runtime-pi → webflo-runtime}/webflo-client/DeviceCapabilities.js +0 -0
  92. /package/src/{runtime-pi → webflo-runtime}/webflo-client/WebfloRootClientB.js +0 -0
  93. /package/src/{runtime-pi → webflo-runtime}/webflo-client/WebfloSubClient.js +0 -0
  94. /package/src/{runtime-pi → webflo-runtime}/webflo-client/index.js +0 -0
  95. /package/src/{runtime-pi → webflo-runtime}/webflo-client/webflo-devmode.js +0 -0
  96. /package/src/{runtime-pi → webflo-runtime}/webflo-messaging/ClientPortMixin.js +0 -0
  97. /package/src/{runtime-pi → webflo-runtime}/webflo-messaging/ClientRequestPort001.js +0 -0
  98. /package/src/{runtime-pi → webflo-runtime}/webflo-messaging/ClientRequestPort010.js +0 -0
  99. /package/src/{runtime-pi → webflo-runtime}/webflo-messaging/ClientRequestPort100.js +0 -0
  100. /package/src/{runtime-pi → webflo-runtime}/webflo-messaging/WebfloTenancy001.js +0 -0
  101. /package/src/{runtime-pi → webflo-runtime}/webflo-messaging/WebfloTenant001.js +0 -0
  102. /package/src/{runtime-pi → webflo-runtime}/webflo-routing/HttpCookies101.js +0 -0
  103. /package/src/{runtime-pi → webflo-runtime}/webflo-routing/HttpCookies110.js +0 -0
  104. /package/src/{runtime-pi → webflo-runtime}/webflo-routing/HttpEvent111.js +0 -0
  105. /package/src/{runtime-pi → webflo-runtime}/webflo-routing/HttpKeyvalInterface.js +0 -0
  106. /package/src/{runtime-pi → webflo-runtime}/webflo-routing/HttpSession001.js +0 -0
  107. /package/src/{runtime-pi → webflo-runtime}/webflo-routing/HttpSession110.js +0 -0
  108. /package/src/{runtime-pi → webflo-runtime}/webflo-routing/HttpThread111.js +0 -0
  109. /package/src/{runtime-pi → webflo-runtime}/webflo-routing/HttpUser111.js +0 -0
  110. /package/src/{runtime-pi → webflo-runtime}/webflo-routing/KeyvalsFactory001.js +0 -0
  111. /package/src/{runtime-pi → webflo-runtime}/webflo-routing/KeyvalsFactory110.js +0 -0
  112. /package/src/{runtime-pi → webflo-runtime}/webflo-routing/KeyvalsFactoryInterface.js +0 -0
  113. /package/src/{runtime-pi → webflo-runtime}/webflo-routing/WebfloRouter111.js +0 -0
  114. /package/src/{runtime-pi → webflo-runtime}/webflo-server/index.js +0 -0
  115. /package/src/{runtime-pi → webflo-runtime}/webflo-server/webflo-devmode.js +0 -0
  116. /package/src/{runtime-pi → webflo-runtime}/webflo-worker/WebfloWorker.js +0 -0
  117. /package/src/{runtime-pi → webflo-runtime}/webflo-worker/WorkerSideWorkport.js +0 -0
  118. /package/src/{runtime-pi → webflo-runtime}/webflo-worker/index.js +0 -0
  119. /package/src/{services-pi → webflo-services}/cert/http-cleanup-hook.js +0 -0
  120. /package/src/{services-pi → webflo-services}/cert/index.js +0 -0
  121. /package/src/{services-pi → webflo-services}/index.js +0 -0
  122. /package/src/{services-pi → webflo-services}/push/index.js +0 -0
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.34",
15
+ "version": "0.20.36",
16
16
  "license": "MIT",
17
17
  "repository": {
18
18
  "type": "git",
@@ -22,10 +22,16 @@
22
22
  "url": "https://github.com/webqit/webflo/issues"
23
23
  },
24
24
  "type": "module",
25
- "main": "./src/index.js",
25
+ "main": "./src/webflo-exports/index.js",
26
+ "exports": {
27
+ ".": "./src/webflo-exports/index.js",
28
+ "./*": "./src/webflo-exports/*/index.js",
29
+ "./*.js": "./src/webflo-exports/*.js"
30
+ },
26
31
  "scripts": {
27
32
  "test": "echo \"Nothing to test yet\"",
28
33
  "build": "echo \"Nothing to build yet\"",
34
+ "build:test": "esbuild test/browser/*.src.js --bundle --minify --sourcemap --outdir=test/browser --entry-names=[name].build",
29
35
  "preversion": "npm run test",
30
36
  "postversion": "git push && git push --tags",
31
37
  "version:next": "npm version prerelease --preid=next",
@@ -51,7 +57,7 @@
51
57
  "@webqit/util": "^0.8.11",
52
58
  "chokidar": "^4.0.3",
53
59
  "dotenv": "^16.4.7",
54
- "esbuild": "^0.14.38",
60
+ "esbuild": "^0.14.54",
55
61
  "fast-glob": "^3.3.3",
56
62
  "idb": "^8.0.3",
57
63
  "mime-types": "^2.1.33",
package/src/index.js CHANGED
@@ -1,9 +1,9 @@
1
- import * as config from './config-pi/index.js';
2
- import * as deployment from './deployment-pi/index.js';
3
- import * as runtime from './runtime-pi/index.js';
4
- import * as services from './services-pi/index.js';
5
- import * as starter from './init-pi/index.js';
6
- import * as build from './build-pi/index.js';
1
+ import * as config from './webflo-config/index.js';
2
+ import * as deployment from './webflo-deployment/index.js';
3
+ import * as runtime from './webflo-runtime/index.js';
4
+ import * as services from './webflo-services/index.js';
5
+ import * as init from './webflo-init/index.js';
6
+ import * as build from './webflo-build/index.js';
7
7
 
8
8
  export { CLIContext } from './CLIContext.js';
9
9
  export {
@@ -11,6 +11,6 @@ export {
11
11
  deployment,
12
12
  runtime,
13
13
  services,
14
- starter,
14
+ init,
15
15
  build,
16
16
  }
@@ -8,9 +8,9 @@ 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
10
  import { URLPatternPlus } from '@webqit/url-plus';
11
- import { bootstrap as serverBootstrap } from '../runtime-pi/webflo-server/bootstrap.js';
12
- import { bootstrap as clientBootstrap } from '../runtime-pi/webflo-client/bootstrap.js';
13
- import { bootstrap as workerBootstrap } from '../runtime-pi/webflo-worker/bootstrap.js';
11
+ import { bootstrap as serverBootstrap } from '../webflo-runtime/webflo-server/bootstrap.js';
12
+ import { bootstrap as clientBootstrap } from '../webflo-runtime/webflo-client/bootstrap.js';
13
+ import { bootstrap as workerBootstrap } from '../webflo-runtime/webflo-worker/bootstrap.js';
14
14
  import { UseLiveTransform } from './esbuild-plugin-uselive-transform.js';
15
15
  import { CLIContext } from '../CLIContext.js';
16
16
 
@@ -83,7 +83,7 @@ function declareRoutes({ $context, $source, bootstrap }) {
83
83
 
84
84
  function writeImportWebflo($source, which) {
85
85
  const importUrl = Url.fileURLToPath(import.meta.url);
86
- const importPath = Path.join(Path.dirname(importUrl), `../runtime-pi/webflo-${which}/index.js`);
86
+ const importPath = Path.join(Path.dirname(importUrl), `../webflo-runtime/webflo-${which}/index.js`);
87
87
  $source.imports[importPath] = `{ start }`;
88
88
  }
89
89
 
@@ -1,7 +1,3 @@
1
-
2
- /**
3
- * imports
4
- */
5
1
  import { Dotfile } from '@webqit/backpack';
6
2
 
7
3
  export default class Env extends Dotfile {
@@ -1,7 +1,3 @@
1
-
2
- /**
3
- * imports
4
- */
5
1
  import { Dotfile } from '@webqit/backpack';
6
2
 
7
3
  export default class Layout extends Dotfile {
@@ -1,7 +1,3 @@
1
-
2
- /**
3
- * imports
4
- */
5
1
  import Url from 'url';
6
2
  import { _before } from '@webqit/util/str/index.js';
7
3
  import { _isTypeObject } from '@webqit/util/js/index.js';
@@ -1,7 +1,3 @@
1
-
2
- /**
3
- * imports
4
- */
5
1
  import { Dotfile } from '@webqit/backpack';
6
2
 
7
3
  export default class Proxy extends Dotfile {
@@ -1,15 +1,8 @@
1
-
2
- /**
3
- * i@mports
4
- */
5
1
  import Env from './Env.js';
6
2
  import Layout from './Layout.js';
7
3
  import Origins from './Origins.js';
8
4
  import Proxy from './Proxy.js';
9
5
 
10
- /**
11
- * @exports
12
- */
13
6
  export {
14
7
  Env,
15
8
  Layout,
@@ -1,14 +1,7 @@
1
-
2
- /**
3
- * @imports
4
- */
5
1
  import * as deployment from './deployment/index.js';
6
2
  import * as runtime from './runtime/index.js';
7
3
  import * as $static from './static/index.js';
8
4
 
9
- /**
10
- * @exports
11
- */
12
5
  export {
13
6
  deployment,
14
7
  runtime,
@@ -1,7 +1,3 @@
1
-
2
- /**
3
- * imports
4
- */
5
1
  import { Dotfile } from '@webqit/backpack';
6
2
 
7
3
  export default class Init extends Dotfile {
@@ -1,7 +1,3 @@
1
-
2
- /**
3
- * imports
4
- */
5
1
  import Path from 'path';
6
2
  import { _all } from '@webqit/util/arr/index.js';
7
3
  import { _isNumeric } from '@webqit/util/js/index.js';
@@ -1,7 +1,3 @@
1
-
2
- /**
3
- * imports
4
- */
5
1
  import { Dotfile } from '@webqit/backpack';
6
2
 
7
3
  export default class Ssg extends Dotfile {
@@ -1,14 +1,7 @@
1
-
2
- /**
3
- * @imports
4
- */
5
1
  import Init from './Init.js';
6
2
  import Manifest from './Manifest.js';
7
3
  import Ssg from './Ssg.js';
8
4
 
9
- /**
10
- * @exports
11
- */
12
5
  export {
13
6
  Init,
14
7
  Manifest,
@@ -1,10 +1,3 @@
1
-
2
- /**
3
- * imports
4
- */
5
1
  import * as origins from './origins/index.js';
6
2
 
7
- /**
8
- * @exports
9
- */
10
3
  export { origins }
@@ -1,7 +1,3 @@
1
-
2
- /**
3
- * @imports
4
- */
5
1
  import Fs from 'fs';
6
2
  import Path from 'path';
7
3
  import SimpleGit from 'simple-git';
@@ -11,16 +7,10 @@ import { _beforeLast } from '@webqit/util/str/index.js';
11
7
  import { _isObject, _isNumeric } from '@webqit/util/js/index.js';
12
8
  import Webhooks from '@octokit/webhooks';
13
9
 
14
- /**
15
- * @desc
16
- */
17
10
  export const desc = {
18
11
  deploy: 'Deploy project from a remote origin.',
19
12
  };
20
13
 
21
- /**
22
- * @deploy
23
- */
24
14
  export async function deploy(origin) {
25
15
  const cx = this || {};
26
16
  if (!cx.config.deployment?.Origins) {
@@ -149,9 +139,6 @@ export async function deploy(origin) {
149
139
 
150
140
  }
151
141
 
152
- /**
153
- * @hook
154
- */
155
142
  export async function webhook(httpEvent, router, next) {
156
143
  const cx = this || {};
157
144
  if (!cx.config.deployment?.Origins) {
@@ -0,0 +1 @@
1
+ export * from '../../webflo-runtime/webflo-client/webflo-elements.js';
@@ -3,7 +3,7 @@ import Fs2 from 'fs/promises';
3
3
  import Path from 'path';
4
4
  import { exec } from 'child_process';
5
5
  import { _toTitle } from '@webqit/util/str/index.js';
6
- import { readInitConfig } from '../deployment-pi/util.js';
6
+ import { readInitConfig } from '../webflo-deployment/util.js';
7
7
  import { CLIContext } from '../CLIContext.js';
8
8
 
9
9
  export const desc = {
@@ -0,0 +1,173 @@
1
+ class DeviceViewport {
2
+
3
+ #stack = [];
4
+ #ownedElements = new Set(); // Stores elements created by this class
5
+ #elements = {}; // Map of active elements/selectors
6
+ #timer = null; // For commit batching
7
+
8
+ #specials = {
9
+ themeColor: { name: 'theme-color', type: 'meta' },
10
+ appleStatusBarStyle: { name: 'apple-mobile-web-app-status-bar-style', type: 'meta' },
11
+ colorScheme: { name: 'color-scheme', type: 'meta' },
12
+ manifest: { name: 'manifest', type: 'link' }
13
+ };
14
+
15
+ constructor() {
16
+ const initialState = { _isInitial: true };
17
+
18
+ // 1. Ingest Viewport
19
+ const vMeta = document.querySelector('meta[name="viewport"]');
20
+ if (vMeta) {
21
+ this.#elements.viewport = vMeta;
22
+ Object.assign(initialState, this.#parseViewport(vMeta.content));
23
+ }
24
+
25
+ // 2. Ingest Title & Specials
26
+ initialState.title = document.title;
27
+ Object.entries(this.#specials).forEach(([jsKey, config]) => {
28
+ const el = this.#findDom(config);
29
+ if (el) {
30
+ this.#elements[jsKey] = el;
31
+ initialState[jsKey] = config.type === 'link' ? el.getAttribute('href') : el.getAttribute('content');
32
+ }
33
+ });
34
+
35
+ this.#stack.push(initialState);
36
+ }
37
+
38
+ #findDom({ name, type }) {
39
+ return type === 'link'
40
+ ? document.querySelector(`link[rel="${name}"]`)
41
+ : document.querySelector(`meta[name="${name}"]`);
42
+ }
43
+
44
+ #getOrCreate(jsKey, media = null) {
45
+ const cacheKey = media ? `${jsKey}-${media}` : jsKey;
46
+ if (this.#elements[cacheKey]) return this.#elements[cacheKey];
47
+
48
+ const config = this.#specials[jsKey] || { name: 'viewport', type: 'meta' };
49
+ const el = document.createElement(config.type);
50
+
51
+ if (config.type === 'link') el.rel = config.name;
52
+ else el.name = config.name;
53
+ if (media) el.setAttribute('media', media);
54
+
55
+ document.head.appendChild(el);
56
+ this.#ownedElements.add(el);
57
+ this.#elements[cacheKey] = el;
58
+ return el;
59
+ }
60
+
61
+ #scheduleRender() {
62
+ if (this.#timer) return;
63
+ this.#timer = requestAnimationFrame(() => {
64
+ this.#render();
65
+ this.#timer = null;
66
+ });
67
+ }
68
+
69
+ #render() {
70
+ const state = this.peek();
71
+ const viewportDirectives = [];
72
+ const activeKeys = new Set(Object.keys(state).filter(k => !k.startsWith('_')));
73
+
74
+ // 1. Handle Title
75
+ if ('title' in state) {
76
+ document.title = state.title || '';
77
+ activeKeys.delete('title');
78
+ }
79
+
80
+ // 2. Handle Specials (with Media Query Support)
81
+ Object.keys(this.#specials).forEach(jsKey => {
82
+ const val = state[jsKey]; // Can be string or { default, dark, light, "(media)": color }
83
+ activeKeys.delete(jsKey);
84
+
85
+ const seen = new Set();
86
+
87
+ if (val && typeof val === 'object' && jsKey === 'themeColor') {
88
+ // Media Query Logic
89
+ Object.entries(val).forEach(([query, color]) => {
90
+ const mediaStr = query === 'dark' ? '(prefers-color-scheme: dark)' :
91
+ query === 'light' ? '(prefers-color-scheme: light)' :
92
+ query === 'default' ? '' : query;
93
+ seen.add(mediaStr);
94
+ this.#setAttr(jsKey, color, mediaStr);
95
+ });
96
+ } else {
97
+ seen.add('');
98
+ this.#setAttr(jsKey, val);
99
+ }
100
+
101
+ // cleanup
102
+ Object.keys(this.#elements)
103
+ .filter(k => k === jsKey || k.startsWith(`${jsKey}-`))
104
+ .forEach(k => {
105
+ const keyId = k === jsKey ? '' : k.slice(jsKey.length + 1);
106
+ if (!seen.has(keyId)) {
107
+ this.#setAttr(jsKey, null, keyId);
108
+ }
109
+ });
110
+ });
111
+
112
+ // 3. Handle Viewport
113
+ activeKeys.forEach(key => {
114
+ const val = state[key];
115
+ if (val === null) return;
116
+ const kebab = key.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
117
+ viewportDirectives.push(val === true ? kebab : `${kebab}=${val}`);
118
+ });
119
+
120
+ const vContent = viewportDirectives.join(', ');
121
+ const vEl = this.#elements.viewport || (vContent ? this.#getOrCreate('viewport') : null);
122
+ if (vEl) {
123
+ vEl.setAttribute('content', vContent);
124
+ if (!vContent && this.#ownedElements.has(vEl)) {
125
+ vEl.remove();
126
+ delete this.#elements.viewport;
127
+ }
128
+ }
129
+ }
130
+
131
+ #setAttr(jsKey, val, media = null) {
132
+ const cacheKey = media ? `${jsKey}-${media}` : jsKey;
133
+ const config = this.#specials[jsKey];
134
+
135
+ if (val !== undefined && val !== null) {
136
+ const el = this.#getOrCreate(jsKey, media);
137
+ el.setAttribute(config.type === 'link' ? 'href' : 'content', val);
138
+ } else {
139
+ const el = this.#elements[cacheKey];
140
+ if (el) {
141
+ if (this.#ownedElements.has(el)) {
142
+ el.remove();
143
+ delete this.#elements[cacheKey];
144
+ } else {
145
+ el.setAttribute(config.type === 'link' ? 'href' : 'content', '');
146
+ }
147
+ }
148
+ }
149
+ }
150
+
151
+ #parseViewport = (c) => Object.fromEntries(c.split(',').filter(Boolean).map(s => {
152
+ const [k, v] = s.split('=').map(p => p.trim());
153
+ return [k.replace(/-([a-z])/g, g => g.toUpperCase()), v || true];
154
+ }));
155
+
156
+ push(id, config) {
157
+ if (!id) throw new Error("push() requires a unique ID");
158
+ if (this.#stack.some(e => e.id === id)) return;
159
+ this.#stack.push({ ...this.peek(), ...config, id, _isInitial: false });
160
+ this.#scheduleRender();
161
+ }
162
+
163
+ pop(id) {
164
+ if (!id) throw new Error("pop() requires a target ID");
165
+ const idx = this.#stack.findIndex(e => e.id === id);
166
+ if (idx > 0) { // Never pop the initial state at index 0
167
+ this.#stack.splice(idx, 1);
168
+ this.#scheduleRender();
169
+ }
170
+ }
171
+
172
+ peek() { return this.#stack[this.#stack.length - 1]; }
173
+ }
@@ -35,9 +35,6 @@ export class WebfloClient extends AppRuntime {
35
35
  #background;
36
36
  get background() { return this.#background; }
37
37
 
38
- #viewport;
39
- get viewport() { return this.#viewport; }
40
-
41
38
  get isClientSide() { return true; }
42
39
 
43
40
  constructor(bootstrap, host) {
@@ -59,68 +56,6 @@ export class WebfloClient extends AppRuntime {
59
56
  phase: 0
60
57
  };
61
58
  this.#background = new StarPort({ handshake: 1, autoClose: false });
62
-
63
- // ---------------------
64
- // Dynamic viewport styling
65
-
66
- const oskToken = 'interactive-widget=resizes-content';
67
- const hasOsk = (content) => content?.includes(oskToken);
68
- const removeOsk = (content) => {
69
- if (content?.includes('interactive-widget')) {
70
- return content
71
- .split(',')
72
- .filter((s) => !s.includes('interactive-widget'))
73
- .map((s) => s.trim())
74
- .join(', ');
75
- }
76
- return content;
77
- };
78
- const addOsk = (content) => {
79
- if (content?.includes('interactive-widget')) {
80
- return content
81
- .split(',')
82
- .map((s) => s.includes('interactive-widget') ? oskToken : s.trim())
83
- .join(', ');
84
- }
85
- return content + ', ' + oskToken;
86
- };
87
-
88
- const viewportMeta = document.querySelector('meta[name="viewport"]');
89
- const viewportMetaInitialContent = viewportMeta?.content;
90
- const themeColorMeta = document.querySelector('meta[name="theme-color"]');
91
- const renderViewportMetas = (entry) => {
92
- viewportMeta?.setAttribute('content', entry.osk ? addOsk(viewportMetaInitialContent) : removeOsk(viewportMetaInitialContent));
93
- themeColorMeta?.setAttribute('content', entry.themeColor);
94
- };
95
-
96
- const initial = {
97
- themeColor: themeColorMeta?.content,
98
- osk: hasOsk(viewportMetaInitialContent),
99
- };
100
- const viewportStack = [initial];
101
-
102
- this.#viewport = {
103
- push(entryId, { themeColor = viewportStack[0].themeColor, osk = viewportStack[0].osk }) {
104
- if (typeof entryId !== 'string' || !entryId?.trim()) {
105
- throw new Error('entryId cannot be ommited');
106
- }
107
- if (viewportStack.find((e) => e.entryId === entryId)) return;
108
- viewportStack.unshift({ entryId, themeColor, osk });
109
- renderViewportMetas(viewportStack[0]);
110
- },
111
- pop(entryId) {
112
- if (typeof entryId !== 'string' || !entryId?.trim()) {
113
- throw new Error('entryId cannot be ommited');
114
- }
115
- const index = viewportStack.findIndex((e) => e.entryId === entryId);
116
- if (index === -1) return;
117
- viewportStack.splice(index, 1);
118
- renderViewportMetas(viewportStack[0]);
119
- },
120
- current() {
121
- return viewportStack[0];
122
- }
123
- };
124
59
  }
125
60
 
126
61
  async initialize() {
@@ -3,6 +3,7 @@ import { LiveResponse } from '@webqit/fetch-plus';
3
3
  import { HttpEvent111 } from '../webflo-routing/HttpEvent111.js';
4
4
  import { ClientSideWorkport } from './ClientSideWorkport.js';
5
5
  import { DeviceCapabilities } from './DeviceCapabilities.js';
6
+ import { DeviceViewport } from './DeviceViewport.js';
6
7
  import { WebfloClient } from './WebfloClient.js';
7
8
  import { WebfloHMR } from './webflo-devmode.js';
8
9
 
@@ -12,6 +13,8 @@ export class WebfloRootClientA extends WebfloClient {
12
13
 
13
14
  static get DeviceCapabilities() { return DeviceCapabilities; }
14
15
 
16
+ static get DeviceViewport() { return DeviceViewport; }
17
+
15
18
  static create(bootstrap, host) {
16
19
  return new this(bootstrap, host);
17
20
  }
@@ -19,12 +22,15 @@ export class WebfloRootClientA extends WebfloClient {
19
22
  #network;
20
23
  get network() { return this.#network; }
21
24
 
22
- #workport;
23
- get workport() { return this.#workport; }
25
+ #viewport;
26
+ get viewport() { return this.#viewport; }
24
27
 
25
28
  #capabilities;
26
29
  get capabilities() { return this.#capabilities; }
27
30
 
31
+ #workport;
32
+ get workport() { return this.#workport; }
33
+
28
34
  #hmr;
29
35
 
30
36
  get withViewTransitions() {
@@ -67,10 +73,14 @@ export class WebfloRootClientA extends WebfloClient {
67
73
 
68
74
  async setupCapabilities() {
69
75
  const instanceController = await super.setupCapabilities();
70
- const cleanups = [];
71
76
 
72
- // Service Worker && Capabilities
77
+ const cleanups = [];
73
78
  instanceController.signal.addEventListener('abort', () => cleanups.forEach((c) => c()), { once: true });
79
+
80
+ // DeviceViewport, DeviceCapabilities, & Service Worker
81
+
82
+ this.#viewport = new this.constructor.DeviceViewport();
83
+
74
84
  this.#capabilities = await this.constructor.DeviceCapabilities.initialize(this, this.config.CLIENT.capabilities);
75
85
  cleanups.push(() => this.#capabilities.close());
76
86
 
@@ -7,7 +7,7 @@ import {
7
7
  readWorkerConfig,
8
8
  scanRoots,
9
9
  scanRouteHandlers,
10
- } from '../../deployment-pi/util.js';
10
+ } from '../../webflo-deployment/util.js';
11
11
 
12
12
  export async function bootstrap(cx, offset = '') {
13
13
  const $init = Fs.existsSync('./init.client.js')