@webqit/webflo 0.8.45 → 0.8.49

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 (38) hide show
  1. package/docker/Dockerfile +25 -0
  2. package/docker/README.md +69 -0
  3. package/package.json +9 -5
  4. package/src/cmd/client.js +68 -97
  5. package/src/cmd/origins.js +2 -2
  6. package/src/modules/Router.js +130 -0
  7. package/src/modules/_FormData.js +60 -0
  8. package/src/modules/_Headers.js +88 -0
  9. package/src/modules/_MessageStream.js +191 -0
  10. package/src/modules/_NavigationEvent.js +89 -0
  11. package/src/modules/_Request.js +61 -0
  12. package/src/modules/_RequestHeaders.js +72 -0
  13. package/src/modules/_Response.js +56 -0
  14. package/src/modules/_ResponseHeaders.js +81 -0
  15. package/src/modules/_URL.js +111 -0
  16. package/src/modules/client/Cache.js +38 -0
  17. package/src/modules/client/Client.js +51 -22
  18. package/src/modules/client/Http.js +26 -11
  19. package/src/modules/client/NavigationEvent.js +20 -0
  20. package/src/modules/client/Router.js +30 -104
  21. package/src/modules/client/StdRequest.js +34 -33
  22. package/src/modules/client/Storage.js +56 -0
  23. package/src/modules/client/Url.js +1 -1
  24. package/src/modules/client/Worker.js +74 -68
  25. package/src/modules/client/WorkerClient.js +102 -0
  26. package/src/modules/client/WorkerComm.js +183 -0
  27. package/src/modules/client/effects/sounds.js +64 -0
  28. package/src/modules/server/NavigationEvent.js +38 -0
  29. package/src/modules/server/Router.js +53 -124
  30. package/src/modules/server/Server.js +195 -87
  31. package/src/modules/util.js +7 -7
  32. package/src/modules/NavigationEvent.js +0 -32
  33. package/src/modules/Response.js +0 -98
  34. package/src/modules/XURL.js +0 -125
  35. package/src/modules/client/ClientNavigationEvent.js +0 -22
  36. package/src/modules/client/Push.js +0 -84
  37. package/src/modules/server/ServerNavigationEvent.js +0 -23
  38. package/src/modules/server/StdIncomingMessage.js +0 -70
@@ -0,0 +1,25 @@
1
+ # Base installations
2
+ FROM node:12-alpine
3
+ RUN apk --no-cache add git
4
+
5
+ # We'll install npm packages at one-level higher than
6
+ # actuall app root, so that we can bind-mount host system's app root
7
+ # without overriding the container's node_modules directory
8
+ WORKDIR /home/www
9
+ RUN npm install
10
+ RUN npm install @webqit/webflo -g
11
+ RUN npm install @webqit/playui-cli -g
12
+
13
+ # Move one-level in, for the reasons above
14
+ WORKDIR /home/www/app
15
+
16
+ # To auto-start app (flags optional), we would add...
17
+ # CMD ["webflo", "start", "--env=dev", "--http-only"]
18
+
19
+ # To build the image locally...
20
+ # docker build --no-cache -t webflo ./docker
21
+
22
+ # To publish to docker hub...
23
+ # docker login -u webqit
24
+ # docker tag webflo webqit/webflo
25
+ # docker push webqit/webflo
@@ -0,0 +1,69 @@
1
+ # Webflo Container
2
+ This is simply a node.js container with the `@webqit/webflo` framework installed. Once started, any webflo app can be deployed into the container from any git repository.
3
+
4
+ ## Usage
5
+ This container image lives on Docker Hub and can be pulled to a local machine or a remote Virtual Machine (VM) on any cloud platform.
6
+
7
+ + [To Use Locally](#to-use-locally)
8
+ + [To Use In the Cloud](#to-use-in-the-cloud)
9
+ * [To Deploy An App From Any Repo](#to-deploy-an-app-from-any-repo)
10
+
11
+ ### To Use Locally
12
+ Ensure you have docker installed on your computer and run the following command from any location on your terminal:
13
+
14
+ ```shell
15
+ docker pull webqit/webflo:latest
16
+ ```
17
+
18
+ The above command pulls the `webqit/webflo` image to your local machine. (But this can be automatically done by docker on running any docker commands that reference the `webqit/webflo` image.)
19
+
20
+ Next is to use the following commands to start the container and the Webflo runtime. In each case, the first part of the command starts the container, while the second part (from `webflo start`) starts the application.
21
+
22
+ #### To Start
23
+ Start the container using `docker run`; map a port (e.g `80`) of your host machine to `3000` of the container (unless changed webflo expects to run on port `3000`); optionally, give your container a name; reference `webqit/webflo` as the image to use; and lastly, start webflo using `webflo start`.
24
+
25
+ ```shell
26
+ docker run -d -p 80:3000 --name my-app webqit/webflo webflo start
27
+ ```
28
+
29
+ Visit [localhost](http://localhost) to view your app.
30
+
31
+ #### To Start In Dev Mode
32
+ Webflo's *dev* mode is the perfect mode for developing locally. All you do is append the `--env=dev` flag to your webflo commands. [(Learn more)](#)
33
+
34
+ ```shell
35
+ docker run -d -p 80:3000 --name my-app webqit/webflo webflo start --env=dev
36
+ ```
37
+
38
+ In *dev* mode, webflo automatically restarts as you make changes to your codebase. Since webflo now lives inside a container, you'll need to *bind* the directory of your source code on your host machine to the `/home/www/app` directory of the container.
39
+
40
+ ```shell
41
+ docker run -d -v /Users/me/my-app:/home/www/app -p 80:3000 --name my-app webqit/webflo webflo start --env=dev
42
+ ```
43
+
44
+ ### To Use In the Cloud
45
+ TODO
46
+
47
+ ### To Deploy An App From Any Repo
48
+ Whether running locally or in the cloud, webflo can easily take your application from any git repo. This follows webflo's normal `deploy` command.
49
+
50
+ Simply point docker at your container (using `docker exec [container-name]`) and execute the `webflo deploy` command.
51
+
52
+ ```shell
53
+ docker exec my-app webflo deploy https://github.com/me/my-app
54
+ ```
55
+
56
+ If you will need to install any npm dependencies, you would run `npm install` on the appropriate directory in your container.
57
+
58
+ ```shell
59
+ docker exec my-app npm install
60
+ ```
61
+
62
+ If you will need to run additional webflo commands (e.g `webflo restart` to restart the application), you would follow the same pattern above.
63
+
64
+ ```shell
65
+ docker exec my-app webflo restart
66
+ ```
67
+
68
+ ## Extending this Build
69
+ TODO
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.8.45",
15
+ "version": "0.8.49",
16
16
  "license": "MIT",
17
17
  "repository": {
18
18
  "type": "git",
@@ -38,20 +38,24 @@
38
38
  "@octokit/webhooks": "^7.15.1",
39
39
  "@webqit/backpack": "^0.0.37",
40
40
  "@webqit/browser-pie": "^0.0.16",
41
- "@webqit/pseudo-browser": "^0.3.28",
41
+ "@webqit/pseudo-browser": "^0.3.35",
42
42
  "@webqit/util": "^0.8.7",
43
- "accepts": "^1.3.7",
43
+ "client-sessions": "^0.8.0",
44
44
  "cookie": "^0.4.1",
45
+ "form-data-encoder": "^1.6.0",
46
+ "formdata-node": "^4.3.0",
45
47
  "formidable": "^2.0.0-dev.20200131.2",
46
48
  "is-glob": "^4.0.1",
47
49
  "micromatch": "^3.1.10",
50
+ "mime-types": "^2.1.33",
48
51
  "minimatch": "^3.0.4",
49
- "minimist": "^1.2.5",
52
+ "node-fetch": "^2.6.1",
50
53
  "pm2": "^5.1.0",
51
54
  "simple-git": "^2.20.1",
55
+ "stream-slice": "^0.1.2",
52
56
  "touch": "^3.1.0",
53
57
  "undici": "^4.3.1",
54
- "uuid": "^8.3.1",
58
+ "uuid": "^8.3.2",
55
59
  "webpack": "^5.50.0"
56
60
  },
57
61
  "devDependencies": {
package/src/cmd/client.js CHANGED
@@ -6,8 +6,8 @@ import Fs from 'fs';
6
6
  import Url from 'url';
7
7
  import Path from 'path';
8
8
  import Webpack from 'webpack';
9
- import _isEmpty from '@webqit/util/js/isEmpty.js';
10
- import _beforeLast from '@webqit/util/str/beforeLast.js';
9
+ import { _beforeLast } from '@webqit/util/str/index.js';
10
+ import { _isObject, _isArray, _isEmpty } from '@webqit/util/js/index.js';
11
11
  import * as DotJs from '@webqit/backpack/src/dotfiles/DotJs.js';
12
12
  import * as client from '../config/client.js'
13
13
 
@@ -23,147 +23,94 @@ export const desc = {
23
23
  * @build
24
24
  */
25
25
  export async function build(Ui, flags = {}, layout = {}) {
26
+
26
27
  const config = await client.read(flags, layout);
27
28
  // Consistent forward slashing
28
29
  const forwardSlash = str => str.replace(/\\/g, '/');
29
- var clientModulesDir = forwardSlash(Url.fileURLToPath(Path.join(import.meta.url, '../../modules/client')));
30
+ var modulesDir = forwardSlash(Url.fileURLToPath(Path.join(import.meta.url, '../../modules/client')));
31
+
32
+ var workerDirSplit = Path.resolve(layout.WORKER_DIR).replace(/\\/g, '/').split('/');
33
+ var workerParams = config.worker || {};
34
+ const workerBundlingConfig = workerParams.bundling || {};
35
+
30
36
  var clientDirSplit = Path.resolve(layout.CLIENT_DIR).replace(/\\/g, '/').split('/');
31
37
  var clientParams = config; // Yes root config object
32
- var workerParams = config.worker || {};
33
- var createWorker = !_isEmpty(workerParams);
38
+ const clientBundlingConfig = clientParams.bundling || {};
39
+
34
40
  var waiting;
35
41
 
36
42
  // -------------------
37
43
  // Create the Service Worker file
38
44
  // -------------------
39
45
 
40
- if (createWorker) {
41
-
42
- var workerBundlingConfig = workerParams.bundling || {};
43
- if (!workerBundlingConfig.intermediate) {
44
- workerBundlingConfig.intermediate = clientDirSplit.join('/') + '/worker.js';
45
- }
46
- if (!workerBundlingConfig.output) {
47
- workerBundlingConfig.output = {
48
- filename: 'worker.js',
49
- path: Path.resolve(layout.PUBLIC_DIR),
50
- };
51
- }
52
-
53
- var workerBuild = {
54
- imports: {},
55
- code: [],
56
- };
57
-
58
- // >> Import the Webflo Client file
59
- var navigatorWorker = clientModulesDir + '/Worker.js';
60
- workerBuild.imports[navigatorWorker] = 'Worker';
46
+ if (!_isEmpty(workerParams)) {
61
47
 
62
48
  Ui.log('');
63
49
  Ui.title(`SERVICE WORKER BUILD`);
64
- // >> Routes mapping
65
- buildRoutes(Ui, Path.resolve(layout.WORKER_DIR), workerBuild, 'Worker-Side Routing:');
66
50
 
67
- // >> Params
51
+ const workerBuild = { imports: {}, code: [], };
52
+ workerBuild.imports[modulesDir + '/Worker.js'] = 'Worker';
53
+
54
+ // ------------------
55
+ // >> Routes mapping
56
+ buildRoutes(workerBuild, Ui, Path.resolve(layout.WORKER_DIR), 'Worker-Side Routing:');
68
57
  workerBuild.code.push(``);
69
58
  workerBuild.code.push(`// >> Worker Params`);
70
- workerBuild.code.push(`const params = {`);
71
- Object.keys(workerParams).forEach(name => {
72
- if (name !== 'bundling') {
73
- workerBuild.code.push(` ${name}: ${[ 'boolean', 'number' ].includes(typeof workerParams[name]) ? workerParams[name] : (Array.isArray(workerParams[name]) ? (!workerParams[name].length ? `[]` : `['${workerParams[name].join(`', '`)}']`) : `'${workerParams[name]}'`)},`);
74
- }
75
- });
76
- workerBuild.code.push(`};`);
77
-
78
- // >> instantiation
59
+ buildParams(workerBuild, workerParams, 'params');
79
60
  workerBuild.code.push(``);
80
61
  workerBuild.code.push(`// >> Worker Instantiation`);
81
62
  workerBuild.code.push(`Worker.call(null, layout, params);`);
63
+ // ------------------
82
64
 
65
+ // ------------------
83
66
  // >> Write to file...
67
+ workerBundlingConfig.intermediate = workerBundlingConfig.intermediate || `${clientDirSplit.join('/')}/worker.js`;
68
+ workerBundlingConfig.output = workerBundlingConfig.output || {
69
+ filename: 'worker.js',
70
+ path: Path.resolve(layout.PUBLIC_DIR),
71
+ };
84
72
  waiting = Ui.waiting(Ui.f`Writing the Service Worker file: ${workerBundlingConfig.intermediate}`);
85
73
  waiting.start();
86
-
87
- // Write
88
74
  DotJs.write(workerBuild, workerBundlingConfig.intermediate, 'Service Worker File');
89
-
90
75
  waiting.stop();
91
76
  Ui.info(Ui.f`Service Worker file: ${workerBundlingConfig.intermediate}`);
92
-
77
+ // ------------------
93
78
  }
94
79
 
95
80
  // -------------------
96
81
  // Create the Client file
97
82
  // -------------------
98
83
 
99
- var clientBundlingConfig = clientParams.bundling || {};
100
- if (!clientBundlingConfig.intermediate) {
101
- clientBundlingConfig.intermediate = clientDirSplit.join('/') + '/bundle.js';
102
- }
103
- if (!clientBundlingConfig.output) {
104
- clientBundlingConfig.output = {
105
- filename: 'bundle.js',
106
- path: Path.resolve(layout.PUBLIC_DIR),
107
- };
108
- }
109
-
110
- var clientBuild = {
111
- imports: {},
112
- code: [],
113
- };
114
-
115
- // >> Import the Webflo Client file
116
- clientBuild.imports[clientModulesDir + '/Client.js'] = 'Client';
117
-
118
84
  Ui.log('');
119
85
  Ui.title(`CLIENT BUILD`);
120
- // >> Routes mapping
121
- buildRoutes(Ui, Path.resolve(layout.CLIENT_DIR), clientBuild, 'Client-Side Routing:');
122
86
 
123
- // >> Client Params
87
+ const clientBuild = { imports: {}, code: [], };
88
+ clientBuild.imports[modulesDir + '/Client.js'] = 'Client';
89
+
90
+ // ------------------
91
+ // >> Routes mapping
92
+ buildRoutes(clientBuild, Ui, Path.resolve(layout.CLIENT_DIR), 'Client-Side Routing:');
124
93
  clientBuild.code.push(``);
125
94
  clientBuild.code.push(`// >> Client Params`);
126
- clientBuild.code.push(`const params = {`);
127
- clientBuild.code.push(`};`);
128
-
129
- // >> Client Instantiation
95
+ buildParams(clientBuild, clientParams, 'params');
130
96
  clientBuild.code.push(``);
131
97
  clientBuild.code.push(`// >> Client Instantiation`);
132
98
  clientBuild.code.push(`Client.call(null, layout, params);`);
133
-
134
- // Service Worker registration code?
135
- if (createWorker) {
136
- if (workerParams.support_push) {
137
- clientBuild.imports[clientModulesDir + '/Push.js'] = 'Push';
138
- }
139
- clientBuild.code.push(...[
140
- ``,
141
- `// >> Service Worker Registration`,
142
- `if ('serviceWorker' in navigator) {`,
143
- ` window.addEventListener('load', () => {`,
144
- ` navigator.serviceWorker.register('/${workerBundlingConfig.output.filename}', {scope: '${workerParams.scope}'}).then(async registration => {`,
145
- ` console.log('Service worker registered.');`,
146
- ` await /*SUPPORT_PUSH*/${workerParams.support_push} ? new Push(registration, {`,
147
- ` registration_url: '${workerParams.push_registration_url}',`,
148
- ` deregistration_url: '${workerParams.push_deregistration_url}',`,
149
- ` public_key: '${workerParams.push_public_key}',`,
150
- ` }) : null;`,
151
- ` });`,
152
- ` });`,
153
- `}`,
154
- ``,
155
- ]);
156
- }
99
+ // ------------------
157
100
 
101
+ // ------------------
158
102
  // >> Write to file...
103
+ clientBundlingConfig.intermediate = clientBundlingConfig.intermediate || clientDirSplit.join('/') + '/bundle.js';
104
+ clientBundlingConfig.output = clientBundlingConfig.output || {
105
+ filename: 'bundle.js',
106
+ path: Path.resolve(layout.PUBLIC_DIR),
107
+ };
159
108
  waiting = Ui.waiting(`Writing the client entry file: ${clientBundlingConfig.intermediate}`);
160
109
  waiting.start();
161
-
162
- // Write
163
110
  DotJs.write(clientBuild, clientBundlingConfig.intermediate, 'Client Build File');
164
-
165
111
  waiting.stop();
166
112
  Ui.info(Ui.f`Client Build file: ${clientBundlingConfig.intermediate}`);
113
+ // ------------------
167
114
 
168
115
  // -------------------
169
116
  // Run webpack
@@ -230,7 +177,7 @@ const createBundle = (Ui, config, desc) => {
230
177
  *
231
178
  * @return void
232
179
  */
233
- const buildRoutes = (Ui, entry, build, desc) => {
180
+ const buildRoutes = (build, Ui, entry, desc) => {
234
181
  // -------------------
235
182
  // Helper functions
236
183
  // -------------------
@@ -256,13 +203,16 @@ const buildRoutes = (Ui, entry, build, desc) => {
256
203
 
257
204
  var indexCount = 0;
258
205
  if (entry && Fs.existsSync(entry)) {
206
+ var clientDirname = entry.replace(/\\/g, '/').split('/').pop();
259
207
  walk(entry, (file, ext) => {
260
208
  //relativePath = relativePath.replace(/\\/g, '/');
261
209
  if (file.replace(/\\/g, '/').endsWith('/index.js')) {
262
210
  var relativePath = Path.relative(entry, file).replace(/\\/g, '/');
263
211
  // Import code
264
212
  var routeName = 'index' + (++ indexCount);
265
- build.imports['./' + relativePath] = '* as ' + routeName;
213
+ // IMPORTANT: we;re taking a step back here so that the parent-child relationship for
214
+ // the directories be involved
215
+ build.imports[`../${clientDirname}/${relativePath}`] = '* as ' + routeName;
266
216
  // Definition code
267
217
  var routePath = _beforeLast('/' + relativePath, '/index.js');
268
218
  build.code.push(`layout['${routePath || '/'}'] = ${routeName};`);
@@ -275,3 +225,24 @@ const buildRoutes = (Ui, entry, build, desc) => {
275
225
  Ui.log(`> (none)`);
276
226
  }
277
227
  };
228
+
229
+ const buildParams = (build, params, varName = null, indentation = 0) => {
230
+ if (varName) build.code.push(`const ${varName} = {`);
231
+ Object.keys(params).forEach(name => {
232
+ var _name = ` ${' '.repeat(indentation)}${(_isArray(params) ? '' : (name.includes(' ') ? `'${name}'` : name) + ': ')}`;
233
+ if ([ 'boolean', 'number' ].includes(typeof params[name])) {
234
+ build.code.push(`${_name}${params[name]},`);
235
+ } else if (_isArray(params[name])) {
236
+ build.code.push(`${_name}[`);
237
+ buildParams(build, params[name], null, indentation + 1);
238
+ build.code.push(` ${' '.repeat(indentation)}],`);
239
+ } else if (_isObject(params[name])) {
240
+ build.code.push(`${_name}{`);
241
+ buildParams(build, params[name], null, indentation + 1);
242
+ build.code.push(` ${' '.repeat(indentation)}},`);
243
+ } else {
244
+ build.code.push(`${_name}'${params[name]}',`);
245
+ }
246
+ });
247
+ if (varName) build.code.push(`};`);
248
+ }
@@ -64,9 +64,9 @@ export async function deploy(Ui, origin, flags = {}, layout = {}) {
64
64
  Fs.mkdirSync(origin.deploy_path, {recursive: true});
65
65
  }
66
66
  }
67
- git.cwd(origin.deploy_path);
67
+ await git.cwd(origin.deploy_path);
68
68
  // Must come after git.cwd()
69
- git.init();
69
+ await git.init();
70
70
 
71
71
  const hosts = {
72
72
  github: 'https://github.com',
@@ -0,0 +1,130 @@
1
+
2
+ /**
3
+ * @imports
4
+ */
5
+ import _isString from '@webqit/util/js/isString.js';
6
+ import _isFunction from '@webqit/util/js/isFunction.js';
7
+ import _isArray from '@webqit/util/js/isArray.js';
8
+ import _arrFrom from '@webqit/util/arr/from.js';
9
+
10
+ /**
11
+ * ---------------------------
12
+ * The Router class
13
+ * ---------------------------
14
+ */
15
+
16
+ export default class Router {
17
+
18
+ /**
19
+ * Constructs a new Router instance
20
+ * over route definitions.
21
+ *
22
+ * @param string|array path
23
+ * @param object layout
24
+ * @param object context
25
+ *
26
+ * @return void
27
+ */
28
+ constructor(path, layout, context) {
29
+ this.path = _isArray(path) ? path : (path + '').split('/').filter(a => a);
30
+ this.layout = layout;
31
+ this.context = context;
32
+ }
33
+
34
+ /**
35
+ * Performs dynamic routing.
36
+ *
37
+ * @param array|string method
38
+ * @param Object event
39
+ * @param any arg
40
+ * @param function _default
41
+ *
42
+ * @return object
43
+ */
44
+ async route(method, event, arg, _default) {
45
+
46
+ const $this = this;
47
+
48
+ // ----------------
49
+ // The loop
50
+ // ----------------
51
+ const next = async function(thisTick) {
52
+ const _this = { ...$this.context };
53
+ if (!thisTick.trail || thisTick.trail.length < thisTick.destination.length) {
54
+ thisTick = await $this.readTick(thisTick);
55
+ // -------------
56
+ _this.pathname = `/${thisTick.trail.join('/')}`;
57
+ _this.stepname = thisTick.trail[thisTick.trail.length - 1];
58
+ $this.finalizeHandlerContext(_this, thisTick);
59
+ // -------------
60
+ if (thisTick.exports) {
61
+ const methods = _arrFrom(thisTick.method);
62
+ const handler = _isFunction(thisTick.exports) && methods.includes('default') ? thisTick.exports : methods.reduce((_handler, name) => _handler || thisTick.exports[name.toLowerCase()], null);
63
+ if (handler) {
64
+ // -------------
65
+ // Dynamic response
66
+ // -------------
67
+ const _next = async (..._args) => {
68
+ const nextTick = { ...thisTick, arg: _args[0] };
69
+ if (_args.length > 1) {
70
+ var _url = _args[1], _request, requestInit = { ...(_args[2] || {}) };
71
+ if (_args[1] instanceof nextTick.event.Request) {
72
+ _request = _args[1];
73
+ _url = _request.url;
74
+ } else if (!_isString(_url)) {
75
+ throw new Error('Router redirect url must be a string!');
76
+ }
77
+ var newDestination = _url.startsWith('/') ? _url : $this.pathJoin(`/${thisTick.trail.join('/')}`, _url);
78
+ if (newDestination.startsWith('../')) {
79
+ throw new Error('Router redirect cannot traverse beyond the routing directory! (' + _url + ' >> ' + newDestination + ')');
80
+ }
81
+ if (requestInit.method) {
82
+ nextTick.method = requestInit.method;
83
+ if (_isArray(requestInit.method)) {
84
+ requestInit.method = requestInit.method[0];
85
+ }
86
+ }
87
+ if (_request) {
88
+ nextTick.event = thisTick.event.retarget(_request, { ...requestInit, _proxy: { url: newDestination/** non-standard but works */ } });
89
+ } else {
90
+ nextTick.event = thisTick.event.retarget(newDestination, requestInit);
91
+ }
92
+
93
+ nextTick.source = thisTick.destination.join('/');
94
+ nextTick.destination = newDestination.split('?').shift().split('/').map(a => a.trim()).filter(a => a);
95
+ nextTick.trail = _args[1].startsWith('/') ? [] : thisTick.trail.reduce((_commonRoot, _seg, i) => _commonRoot.length === i && _seg === nextTick.destination[i] ? _commonRoot.concat(_seg) : _commonRoot, []);
96
+ nextTick.trailOnFile = thisTick.trailOnFile.slice(0, nextTick.trail.length);
97
+ }
98
+ return next(nextTick);
99
+ };
100
+ // -------------
101
+ const nextPathname = thisTick.destination.slice(thisTick.trail.length);
102
+ _next.pathname = nextPathname.join('/');
103
+ _next.stepname = nextPathname[0];
104
+ // -------------
105
+ return await handler.call(_this, thisTick.event, thisTick.arg, _next/*next*/);
106
+ }
107
+ // Handler not found but exports found
108
+ return next(thisTick);
109
+ } else if ((thisTick.currentSegmentOnFile || {}).dirExists) {
110
+ // Exports not found but directory found
111
+ return next(thisTick);
112
+ }
113
+ }
114
+ // -------------
115
+ // Local file
116
+ // -------------
117
+ if (_default) {
118
+ return await _default.call(_this, thisTick.event, thisTick.arg);
119
+ }
120
+ };
121
+
122
+ return next({
123
+ destination: this.path,
124
+ event,
125
+ method,
126
+ arg,
127
+ });
128
+
129
+ }
130
+ };
@@ -0,0 +1,60 @@
1
+
2
+ /**
3
+ * @imports
4
+ */
5
+ import { _isTypeObject, _isNumeric } from '@webqit/util/js/index.js';
6
+ import { _before } from '@webqit/util/str/index.js';
7
+ import { wwwFormSet, wwwFormPathSerializeCallback } from './util.js';
8
+
9
+ /**
10
+ * The _Headers Mixin
11
+ */
12
+ const _FormData = NativeFormData => class extends NativeFormData {
13
+
14
+ tee(callback = null) {
15
+ const formData1 = new this.constructor, formData2 = new this.constructor;
16
+ for (var [ name, value ] of this.entries()) {
17
+ const formDataType = formDataType(value);
18
+ if ((callback && callback(value, name, formDataType)) || (!callback && !formDataType)) {
19
+ formData1.append(name, value);
20
+ } else {
21
+ formData2.append(name, value);
22
+ }
23
+ }
24
+ return [ formData1, formData2 ];
25
+ }
26
+
27
+ json(data = {}, callback = null) {
28
+ if (arguments.length) {
29
+ Object.keys(data).forEach(key => {
30
+ wwwFormPathSerializeCallback(key, data[key], (_wwwFormPath, _value) => {
31
+ if (!callback || callback(_wwwFormPath, _value, _isTypeObject(_value))) {
32
+ this.append(_wwwFormPath, _value);
33
+ }
34
+ }, value => !formDataType(value));
35
+ });
36
+ return;
37
+ }
38
+ var jsonBuild; // We'll dynamically determine if this should be an array or an object
39
+ for (var [ name, value ] of this.entries()) {
40
+ if (!jsonBuild) {
41
+ jsonBuild = _isNumeric(_before(name, '[')) ? [] : {};
42
+ }
43
+ wwwFormSet(jsonBuild, name, value);
44
+ }
45
+ return jsonBuild;
46
+ }
47
+
48
+ }
49
+
50
+ export default _FormData;
51
+
52
+ export const formDataType = (value, list = null) => {
53
+ if (!_isTypeObject(value)) {
54
+ return;
55
+ }
56
+ const toStringTag = value[Symbol.toStringTag];
57
+ return (list || [
58
+ 'Uint8Array', 'Uint16Array', 'Uint32Array', 'ArrayBuffer', 'Blob', 'File', 'FormData', 'Stream'
59
+ ]).reduce((_toStringTag, type) => _toStringTag || (toStringTag === type ? type : null), null);
60
+ };
@@ -0,0 +1,88 @@
1
+
2
+ /**
3
+ * @imports
4
+ */
5
+ import { _after, _beforeLast } from "@webqit/util/str/index.js";
6
+ import { _isString, _getType, _isObject, _isFunction } from "@webqit/util/js/index.js";
7
+ import { _isTypeObject } from '@webqit/util/js/index.js';
8
+
9
+ /**
10
+ * The _Headers Mixin
11
+ */
12
+ const _Headers = NativeHeaders => class extends NativeHeaders {
13
+
14
+ // construct
15
+ constructor(definition = {}) {
16
+ if (definition instanceof NativeHeaders) {
17
+ // It's another Headers instance
18
+ super(definition);
19
+ } else {
20
+ super();
21
+ this.json(definition);
22
+ }
23
+ }
24
+
25
+ json(headers = {}, replace = true) {
26
+ if (arguments.length) {
27
+ const _setters = getAllPropertyDescriptors(this);
28
+ const setters = Object.keys(_setters).reduce((list, key) => list.concat((typeof key !== 'symbol') && ('set' in _setters[key]) ? key : []), []);
29
+ Object.keys(headers).forEach(name => {
30
+ var nameCs = setters.reduce((prev, curr) => prev || (curr === name || curr.toLocaleLowerCase() === name ? curr : null), null);
31
+ if (nameCs) {
32
+ if (replace || this[nameCs] === undefined) {
33
+ this[nameCs] = headers[name];
34
+ }
35
+ } else {
36
+ if (replace || !this.has(name)) {
37
+ this.set(name, headers[name]);
38
+ }
39
+ }
40
+ });
41
+ return;
42
+ }
43
+ const _headers = {};
44
+ for (var [ name, value ] of this) {
45
+ _headers[name] = value;
46
+ }
47
+ return _headers;
48
+ }
49
+
50
+ set cacheControl(value) {
51
+ return this.set('Cache-Control', value);
52
+ }
53
+
54
+ get cacheControl() {
55
+ return this.get('Cache-Control');
56
+ }
57
+
58
+ set contentLength(value) {
59
+ return this.set('Content-Length', value);
60
+ }
61
+
62
+ get contentLength() {
63
+ return this.get('Content-Length');
64
+ }
65
+
66
+ set contentType(value) {
67
+ return this.set('Content-Type', value);
68
+ }
69
+
70
+ get contentType() {
71
+ return this.get('Content-Type');
72
+ }
73
+
74
+ }
75
+
76
+ export default _Headers;
77
+
78
+ function getAllPropertyDescriptors(obj) {
79
+ if (!obj) {
80
+ return Object.create(null);
81
+ } else {
82
+ const proto = Object.getPrototypeOf(obj);
83
+ return proto === Object.prototype ? {} : {
84
+ ...getAllPropertyDescriptors(proto),
85
+ ...Object.getOwnPropertyDescriptors(obj)
86
+ };
87
+ }
88
+ }