@webqit/webflo 1.0.18 → 1.0.20
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/package.json +4 -3
- package/src/config-pi/runtime/Client.js +50 -46
- package/src/config-pi/runtime/Server.js +37 -14
- package/src/config-pi/runtime/client/Worker.js +22 -20
- package/src/runtime-pi/HttpEvent.js +34 -19
- package/src/runtime-pi/HttpUser.js +35 -36
- package/src/runtime-pi/WebfloCookieStorage.js +8 -8
- package/src/runtime-pi/WebfloRouter.js +4 -3
- package/src/runtime-pi/WebfloRuntime.js +27 -19
- package/src/runtime-pi/WebfloStorage.js +47 -16
- package/src/runtime-pi/client/Capabilities.js +211 -0
- package/src/runtime-pi/client/CookieStorage.js +2 -2
- package/src/runtime-pi/client/SessionStorage.js +2 -2
- package/src/runtime-pi/client/WebfloClient.js +17 -25
- package/src/runtime-pi/client/WebfloRootClient1.js +55 -34
- package/src/runtime-pi/client/WebfloRootClient2.js +2 -2
- package/src/runtime-pi/client/WebfloSubClient.js +9 -5
- package/src/runtime-pi/client/Workport.js +64 -91
- package/src/runtime-pi/client/generate.js +25 -16
- package/src/runtime-pi/client/index.js +3 -2
- package/src/runtime-pi/client/worker/CookieStorage.js +2 -2
- package/src/runtime-pi/client/worker/SessionStorage.js +1 -1
- package/src/runtime-pi/client/worker/WebfloWorker.js +70 -56
- package/src/runtime-pi/client/worker/index.js +3 -2
- package/src/runtime-pi/server/CookieStorage.js +2 -2
- package/src/runtime-pi/server/SessionStorage.js +3 -3
- package/src/runtime-pi/server/WebfloServer.js +32 -13
- package/src/runtime-pi/server/index.js +1 -0
- package/src/runtime-pi/util-http.js +15 -2
- package/src/services-pi/index.js +2 -0
- package/src/services-pi/push/index.js +23 -0
- package/src/static-pi/index.js +1 -1
package/package.json
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"vanila-javascript"
|
|
13
13
|
],
|
|
14
14
|
"homepage": "https://webqit.io/tooling/webflo",
|
|
15
|
-
"version": "1.0.
|
|
15
|
+
"version": "1.0.20",
|
|
16
16
|
"license": "MIT",
|
|
17
17
|
"repository": {
|
|
18
18
|
"type": "git",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"@octokit/webhooks": "^7.15.1",
|
|
39
|
-
"@webqit/backpack": "^0.1.
|
|
39
|
+
"@webqit/backpack": "^0.1.8",
|
|
40
40
|
"@webqit/observer": "^2.0.7",
|
|
41
41
|
"@webqit/oohtml-ssr": "^2.1.1",
|
|
42
42
|
"@webqit/util": "^0.8.11",
|
|
@@ -46,7 +46,8 @@
|
|
|
46
46
|
"mime-types": "^2.1.33",
|
|
47
47
|
"simple-git": "^2.20.1",
|
|
48
48
|
"stream-slice": "^0.1.2",
|
|
49
|
-
"urlpattern-polyfill": "^4.0.3"
|
|
49
|
+
"urlpattern-polyfill": "^4.0.3",
|
|
50
|
+
"web-push": "^3.6.7"
|
|
50
51
|
},
|
|
51
52
|
"devDependencies": {
|
|
52
53
|
"chai": "^4.3.6",
|
|
@@ -19,31 +19,34 @@ export default class Client extends Dotfile {
|
|
|
19
19
|
// Defaults merger
|
|
20
20
|
withDefaults(config) {
|
|
21
21
|
return this.merge({
|
|
22
|
+
spa_routing: true,
|
|
22
23
|
bundle_filename: 'bundle.js',
|
|
23
24
|
public_base_url: '/',
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
25
|
+
copy_public_variables: true,
|
|
26
|
+
capabilities: {
|
|
27
|
+
service_worker: true,
|
|
28
|
+
webpush: false,
|
|
29
|
+
custom_install: false,
|
|
30
|
+
exposed: ['display-mode', 'notifications'],
|
|
31
|
+
app_vapid_public_key_variable: 'APP_VAPID_PUBLIC_KEY',
|
|
32
|
+
app_public_webhook_url_variable: 'APP_PUBLIC_WEBHOOK_URL',
|
|
31
33
|
},
|
|
32
|
-
bundle_public_env: false,
|
|
33
34
|
}, config, 'patch');
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
// Questions generator
|
|
37
38
|
getSchema(config, choices = {}) {
|
|
38
|
-
// Choices
|
|
39
|
-
const CHOICES = this.merge({
|
|
40
|
-
webqit_dependencies: [
|
|
41
|
-
{value: 'externalize', title: 'Externalize'},
|
|
42
|
-
{value: 'internalize', title: 'Internalize'},
|
|
43
|
-
],
|
|
44
|
-
}, choices, 'patch');
|
|
45
39
|
// Questions
|
|
46
40
|
return [
|
|
41
|
+
{
|
|
42
|
+
name: 'spa_routing',
|
|
43
|
+
type: 'toggle',
|
|
44
|
+
message: '[spa_routing]: Enable Single Page Routing Mode',
|
|
45
|
+
active: 'YES',
|
|
46
|
+
inactive: 'NO',
|
|
47
|
+
initial: config.spa_routing,
|
|
48
|
+
validation: ['important'],
|
|
49
|
+
},
|
|
47
50
|
{
|
|
48
51
|
name: 'bundle_filename',
|
|
49
52
|
type: 'text',
|
|
@@ -58,59 +61,60 @@ export default class Client extends Dotfile {
|
|
|
58
61
|
validation: ['important'],
|
|
59
62
|
},
|
|
60
63
|
{
|
|
61
|
-
name: '
|
|
64
|
+
name: 'copy_public_variables',
|
|
62
65
|
type: 'toggle',
|
|
63
|
-
message: '[
|
|
66
|
+
message: '[copy_public_variables]: Bundle public ENV variables?',
|
|
64
67
|
active: 'YES',
|
|
65
68
|
inactive: 'NO',
|
|
66
|
-
initial: config.
|
|
69
|
+
initial: config.copy_public_variables,
|
|
67
70
|
validation: ['important'],
|
|
68
71
|
},
|
|
69
72
|
{
|
|
70
|
-
name: '
|
|
73
|
+
name: 'capabilities',
|
|
71
74
|
controls: {
|
|
72
|
-
name: '
|
|
75
|
+
name: 'capabilities',
|
|
73
76
|
},
|
|
74
|
-
initial: config.
|
|
77
|
+
initial: config.capabilities,
|
|
75
78
|
schema: [
|
|
76
79
|
{
|
|
77
|
-
name: '
|
|
78
|
-
type: '
|
|
79
|
-
message: '
|
|
80
|
+
name: 'service_worker',
|
|
81
|
+
type: 'toggle',
|
|
82
|
+
message: 'Enable service worker?',
|
|
83
|
+
active: 'YES',
|
|
84
|
+
inactive: 'NO',
|
|
80
85
|
},
|
|
81
86
|
{
|
|
82
|
-
name: '
|
|
83
|
-
type: '
|
|
84
|
-
message: '
|
|
87
|
+
name: 'webpush',
|
|
88
|
+
type: 'toggle',
|
|
89
|
+
message: 'Support push-notifications?',
|
|
90
|
+
active: 'YES',
|
|
91
|
+
inactive: 'NO',
|
|
85
92
|
},
|
|
86
93
|
{
|
|
87
|
-
name: '
|
|
94
|
+
name: 'custom_install',
|
|
88
95
|
type: 'toggle',
|
|
89
|
-
message: '
|
|
96
|
+
message: 'Enable custom PWA install prompt?',
|
|
90
97
|
active: 'YES',
|
|
91
98
|
inactive: 'NO',
|
|
92
99
|
},
|
|
93
100
|
{
|
|
94
|
-
name: '
|
|
95
|
-
type:
|
|
96
|
-
message: '
|
|
101
|
+
name: 'exposed',
|
|
102
|
+
type: 'list',
|
|
103
|
+
message: 'Specify features exposed on capabilities.exposed',
|
|
104
|
+
initial: (config.exposed || []).join(', '),
|
|
97
105
|
},
|
|
98
106
|
{
|
|
99
|
-
name: '
|
|
100
|
-
type: (prev, answers) => answers.
|
|
101
|
-
message: 'Enter the
|
|
107
|
+
name: 'app_vapid_public_key_variable',
|
|
108
|
+
type: (prev, answers) => !answers.webpush ? null : 'text',
|
|
109
|
+
message: 'Enter the environment variable name for APP_VAPID_PUBLIC_KEY if not as written',
|
|
102
110
|
},
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
inactive: 'NO',
|
|
111
|
-
initial: config.bundle_public_env,
|
|
112
|
-
validation: ['important'],
|
|
113
|
-
},
|
|
111
|
+
{
|
|
112
|
+
name: 'app_public_webhook_url_variable',
|
|
113
|
+
type: 'text',
|
|
114
|
+
message: 'Enter the environment variable name for APP_PUBLIC_WEBHOOK_URL if not as written',
|
|
115
|
+
},
|
|
116
|
+
]
|
|
117
|
+
}
|
|
114
118
|
];
|
|
115
119
|
}
|
|
116
120
|
}
|
|
@@ -29,7 +29,12 @@ export default class Server extends Dotfile {
|
|
|
29
29
|
force: false,
|
|
30
30
|
},
|
|
31
31
|
force_www: '',
|
|
32
|
-
|
|
32
|
+
session_key_variable: 'APP_SESSION_KEY',
|
|
33
|
+
capabilities: {
|
|
34
|
+
webpush: false,
|
|
35
|
+
app_vapid_public_key_variable: 'APP_VAPID_PUBLIC_KEY',
|
|
36
|
+
app_vapid_private_key_variable: 'APP_VAPID_PRIVATE_KEY'
|
|
37
|
+
},
|
|
33
38
|
}, config, 'patch');
|
|
34
39
|
}
|
|
35
40
|
|
|
@@ -42,13 +47,6 @@ export default class Server extends Dotfile {
|
|
|
42
47
|
{value: 'add',},
|
|
43
48
|
{value: 'remove',},
|
|
44
49
|
],
|
|
45
|
-
oohtml_support: [
|
|
46
|
-
{value: 'full', title: 'full'},
|
|
47
|
-
{value: 'namespacing', title: 'namespacing'},
|
|
48
|
-
{value: 'scripting', title: 'scripting'},
|
|
49
|
-
{value: 'templating', title: 'templating'},
|
|
50
|
-
{value: 'none', title: 'none'},
|
|
51
|
-
],
|
|
52
50
|
}, choices, 'patch');
|
|
53
51
|
// Questions
|
|
54
52
|
return [
|
|
@@ -63,6 +61,7 @@ export default class Server extends Dotfile {
|
|
|
63
61
|
name: 'domains',
|
|
64
62
|
type: 'list',
|
|
65
63
|
message: '[domains]: Enter a list of allowed domains if necessary (comma-separated)',
|
|
64
|
+
initial: (config.domains || []).join(', '),
|
|
66
65
|
validation: ['important'],
|
|
67
66
|
},
|
|
68
67
|
{
|
|
@@ -72,6 +71,12 @@ export default class Server extends Dotfile {
|
|
|
72
71
|
choices: CHOICES.force_www,
|
|
73
72
|
initial: this.indexOfInitial(CHOICES.force_www, config.force_www),
|
|
74
73
|
},
|
|
74
|
+
{
|
|
75
|
+
name: 'session_key_variable',
|
|
76
|
+
type: 'text',
|
|
77
|
+
message: 'Enter the environment variable name for APP_SESSION_KEY if not as written',
|
|
78
|
+
initial: config.session_key_variable,
|
|
79
|
+
},
|
|
75
80
|
{
|
|
76
81
|
name: 'https',
|
|
77
82
|
controls: {
|
|
@@ -113,12 +118,30 @@ export default class Server extends Dotfile {
|
|
|
113
118
|
],
|
|
114
119
|
},
|
|
115
120
|
{
|
|
116
|
-
name: '
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
initial:
|
|
121
|
-
|
|
121
|
+
name: 'capabilities',
|
|
122
|
+
controls: {
|
|
123
|
+
name: 'capabilities',
|
|
124
|
+
},
|
|
125
|
+
initial: config.capabilities,
|
|
126
|
+
schema: [
|
|
127
|
+
{
|
|
128
|
+
name: 'webpush',
|
|
129
|
+
type: 'toggle',
|
|
130
|
+
message: 'Support push-notifications?',
|
|
131
|
+
active: 'YES',
|
|
132
|
+
inactive: 'NO',
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
name: 'app_vapid_public_key_variable',
|
|
136
|
+
type: (prev, answers) => !answers.webpush ? null : 'text',
|
|
137
|
+
message: 'Enter the environment variable name for APP_VAPID_PUBLIC_KEY if not as written',
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
name: 'app_vapid_private_key_variable',
|
|
141
|
+
type: (prev, answers) => !answers.webpush ? null : 'text',
|
|
142
|
+
message: 'Enter the environment variable name for APP_VAPID_PRIVATE_KEY if not as written',
|
|
143
|
+
},
|
|
144
|
+
]
|
|
122
145
|
},
|
|
123
146
|
];
|
|
124
147
|
}
|
|
@@ -21,14 +21,15 @@ export default class Worker extends Dotfile {
|
|
|
21
21
|
// Defaults merger
|
|
22
22
|
withDefaults(config) {
|
|
23
23
|
return this.merge({
|
|
24
|
+
filename: 'worker.js',
|
|
25
|
+
scope: '/',
|
|
26
|
+
skip_waiting: true,
|
|
24
27
|
cache_name: 'cache_v0',
|
|
25
28
|
default_fetching_strategy: 'network-first',
|
|
26
29
|
network_first_urls: [],
|
|
27
30
|
cache_first_urls: [],
|
|
28
31
|
network_only_urls: [],
|
|
29
32
|
cache_only_urls: [],
|
|
30
|
-
skip_waiting: true,
|
|
31
|
-
bundle_public_env: false,
|
|
32
33
|
}, config, 'patch');
|
|
33
34
|
}
|
|
34
35
|
|
|
@@ -49,6 +50,24 @@ export default class Worker extends Dotfile {
|
|
|
49
50
|
}, choices, 'patch');
|
|
50
51
|
// Questions
|
|
51
52
|
return [
|
|
53
|
+
{
|
|
54
|
+
name: 'filename',
|
|
55
|
+
type: 'text',
|
|
56
|
+
message: 'Specify the Service Worker filename',
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
name: 'scope',
|
|
60
|
+
type: 'text',
|
|
61
|
+
message: 'Specify the Service Worker scope',
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
name: 'skip_waiting',
|
|
65
|
+
type: 'toggle',
|
|
66
|
+
message: 'Choose whether to skip the "waiting" state for updated Service Workers',
|
|
67
|
+
active: 'YES',
|
|
68
|
+
inactive: 'NO',
|
|
69
|
+
initial: config.skip_waiting,
|
|
70
|
+
},
|
|
52
71
|
{
|
|
53
72
|
name: 'cache_name',
|
|
54
73
|
type: 'text',
|
|
@@ -86,24 +105,7 @@ export default class Worker extends Dotfile {
|
|
|
86
105
|
type: (prev, answers) => answers.default_fetching_strategy === 'cache-only' ? null : 'list',
|
|
87
106
|
message: 'Specify URLs for a "cache-only" fetching strategy (comma-separated, globe supported)',
|
|
88
107
|
initial: (config.cache_only_urls || []).join(', '),
|
|
89
|
-
}
|
|
90
|
-
{
|
|
91
|
-
name: 'skip_waiting',
|
|
92
|
-
type: 'toggle',
|
|
93
|
-
message: 'Choose whether to skip the "waiting" state for updated Service Workers',
|
|
94
|
-
active: 'YES',
|
|
95
|
-
inactive: 'NO',
|
|
96
|
-
initial: config.skip_waiting,
|
|
97
|
-
},
|
|
98
|
-
{
|
|
99
|
-
name: 'bundle_public_env',
|
|
100
|
-
type: 'toggle',
|
|
101
|
-
message: '[bundle_public_env]: Bundle public ENV variables?',
|
|
102
|
-
active: 'YES',
|
|
103
|
-
inactive: 'NO',
|
|
104
|
-
initial: config.bundle_public_env,
|
|
105
|
-
validation: ['important'],
|
|
106
|
-
},
|
|
108
|
+
}
|
|
107
109
|
];
|
|
108
110
|
}
|
|
109
111
|
}
|
|
@@ -31,6 +31,8 @@ export class HttpEvent {
|
|
|
31
31
|
|
|
32
32
|
get client() { return this.#init.client; }
|
|
33
33
|
|
|
34
|
+
get sdk() { return this.#init.sdk; }
|
|
35
|
+
|
|
34
36
|
#requestCloneCallback;
|
|
35
37
|
set onRequestClone(callback) {
|
|
36
38
|
this.#requestCloneCallback = callback;
|
|
@@ -59,7 +61,7 @@ export class HttpEvent {
|
|
|
59
61
|
}
|
|
60
62
|
}
|
|
61
63
|
|
|
62
|
-
#response =
|
|
64
|
+
#response = undefined;
|
|
63
65
|
get response() { return this.#response; }
|
|
64
66
|
|
|
65
67
|
async respondWith(response) {
|
|
@@ -92,7 +94,7 @@ export class HttpEvent {
|
|
|
92
94
|
const messageID = (0 | Math.random() * 9e6).toString(36);
|
|
93
95
|
const $url = new URL(url, this.request.url);
|
|
94
96
|
$url.searchParams.set('redirect-message', messageID);
|
|
95
|
-
this.session.set(`redirect-message:${messageID}`, data);
|
|
97
|
+
await this.session.set(`redirect-message:${messageID}`, data);
|
|
96
98
|
await this.redirect($url, ...args);
|
|
97
99
|
}
|
|
98
100
|
|
|
@@ -114,7 +116,7 @@ export class HttpEvent {
|
|
|
114
116
|
if (endOfStream) {
|
|
115
117
|
res(response);
|
|
116
118
|
} else {
|
|
117
|
-
await this.
|
|
119
|
+
await this.respondWith(response);
|
|
118
120
|
}
|
|
119
121
|
});
|
|
120
122
|
poll(typeof maxClock === 'number' && maxClock > 0 ? --maxClock : maxClock);
|
|
@@ -122,20 +124,32 @@ export class HttpEvent {
|
|
|
122
124
|
poll(maxClock);
|
|
123
125
|
};
|
|
124
126
|
// Life cycle management
|
|
125
|
-
this
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
127
|
+
if (this.#response === undefined) {
|
|
128
|
+
callback(async (response, endOfStream = false) => {
|
|
129
|
+
if (endOfStream) {
|
|
130
|
+
state.earlyTermination = true;
|
|
131
|
+
res(response);
|
|
132
|
+
} else {
|
|
133
|
+
await this.respondWith(response);
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
if (!state.earlyTermination) {
|
|
138
|
+
this.client.on('connected', () => {
|
|
139
|
+
state.connected = true;
|
|
140
|
+
start();
|
|
141
|
+
});
|
|
142
|
+
this.client.on('empty', () => {
|
|
143
|
+
state.connected = false;
|
|
144
|
+
});
|
|
145
|
+
this.client.handleMessages('navigation', (e) => {
|
|
146
|
+
if (!crossNavigation
|
|
147
|
+
|| (crossNavigation === -1 && e.data.pathname === this.url.pathname)
|
|
148
|
+
|| (typeof crossNavigation === 'function' && !crossNavigation(e.data))) {
|
|
149
|
+
state.navigatedAway = true;
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
}
|
|
139
153
|
setTimeout(() => {
|
|
140
154
|
if (!state.connected) {
|
|
141
155
|
res();
|
|
@@ -154,8 +168,9 @@ export class HttpEvent {
|
|
|
154
168
|
request = !_isEmpty(init) ? new Request(url, init) : url;
|
|
155
169
|
} else {
|
|
156
170
|
url = new xURL(url, this.#url.origin);
|
|
157
|
-
init = await Request.copy(this.request, init);
|
|
158
|
-
|
|
171
|
+
const { url: _, ...$init } = await Request.copy(this.request, init);
|
|
172
|
+
init = $init;
|
|
173
|
+
request = Request.create(url, { ...init, referrer: this.request.url });
|
|
159
174
|
}
|
|
160
175
|
return new HttpEvent(this, { ...this.#init, request });
|
|
161
176
|
}
|
|
@@ -19,21 +19,13 @@ export class HttpUser extends WebfloStorage {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
get #dict() {
|
|
22
|
-
|
|
23
|
-
this.#session.set('user', {});
|
|
24
|
-
}
|
|
25
|
-
return this.#session.get('user');
|
|
22
|
+
return this.#session.get('user') || {};
|
|
26
23
|
}
|
|
27
24
|
|
|
28
25
|
[ Symbol.iterator ]() { return this.entries()[ Symbol.iterator ](); }
|
|
29
26
|
|
|
30
27
|
get size() { return Object.keys(this.#dict).length; }
|
|
31
28
|
|
|
32
|
-
set(key, value) {
|
|
33
|
-
Reflect.set(this.#dict, key, value);
|
|
34
|
-
return this;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
29
|
get(key) {
|
|
38
30
|
return Reflect.get(this.#dict, key);
|
|
39
31
|
}
|
|
@@ -42,10 +34,6 @@ export class HttpUser extends WebfloStorage {
|
|
|
42
34
|
return Reflect.has(this.#dict, key);
|
|
43
35
|
}
|
|
44
36
|
|
|
45
|
-
delete(key) {
|
|
46
|
-
return Reflect.deleteProperty(this.#dict, key);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
37
|
keys() {
|
|
50
38
|
return Object.keys(this.#dict);
|
|
51
39
|
}
|
|
@@ -58,24 +46,46 @@ export class HttpUser extends WebfloStorage {
|
|
|
58
46
|
return Object.entries(this.#dict);
|
|
59
47
|
}
|
|
60
48
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
49
|
+
forEach(callback) {
|
|
50
|
+
this.entries().forEach(callback);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async set(key, value) {
|
|
54
|
+
if (!this.#session.has('user')) {
|
|
55
|
+
await this.#session.set('user', {});
|
|
64
56
|
}
|
|
57
|
+
Reflect.set(this.#dict, key, value);
|
|
58
|
+
await this.emit(key, value);
|
|
59
|
+
return this;
|
|
65
60
|
}
|
|
66
61
|
|
|
67
|
-
|
|
68
|
-
this.
|
|
62
|
+
async delete(key) {
|
|
63
|
+
if (!this.#session.has('user')) {
|
|
64
|
+
await this.#session.set('user', {});
|
|
65
|
+
}
|
|
66
|
+
Reflect.deleteProperty(this.#dict, key);
|
|
67
|
+
await this.emit(key);
|
|
68
|
+
return this;
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
|
|
71
|
+
async clear() {
|
|
72
|
+
for (const key of this.keys()) {
|
|
73
|
+
Reflect.deleteProperty(this.#dict, key);
|
|
74
|
+
}
|
|
75
|
+
await this.emit();
|
|
76
|
+
return this;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async json(arg = null) {
|
|
72
80
|
if (!arguments.length || typeof arg === 'boolean') {
|
|
73
81
|
return {...this.#dict};
|
|
74
82
|
}
|
|
75
83
|
if (!_isObject(arg)) {
|
|
76
84
|
throw new Error(`Argument must be a valid JSON object`);
|
|
77
85
|
}
|
|
78
|
-
Object.
|
|
86
|
+
return await Promise.all(Object.entries(arg).map(([key, value]) => {
|
|
87
|
+
return this.set(key, value);
|
|
88
|
+
}));
|
|
79
89
|
}
|
|
80
90
|
|
|
81
91
|
isSignedIn() {
|
|
@@ -90,22 +100,11 @@ export class HttpUser extends WebfloStorage {
|
|
|
90
100
|
}
|
|
91
101
|
|
|
92
102
|
async signOut() {
|
|
93
|
-
|
|
94
|
-
let response;
|
|
95
|
-
if (typeof handler === 'string') {
|
|
96
|
-
response = new Response(null, { status: 302, headers: {
|
|
97
|
-
Location: url
|
|
98
|
-
}});
|
|
99
|
-
}
|
|
100
|
-
if (typeof handler === 'function') {
|
|
101
|
-
response = await handler(this);
|
|
102
|
-
}
|
|
103
|
-
this.clear();
|
|
104
|
-
return response;
|
|
103
|
+
await this.clear();
|
|
105
104
|
}
|
|
106
105
|
|
|
107
|
-
confirm(data, callback, options = {}) {
|
|
108
|
-
return new Promise((resolve) => {
|
|
106
|
+
async confirm(data, callback, options = {}) {
|
|
107
|
+
return await new Promise((resolve) => {
|
|
109
108
|
this.#client.postRequest(
|
|
110
109
|
data,
|
|
111
110
|
(event) => resolve(callback ? callback(event) : event),
|
|
@@ -114,8 +113,8 @@ export class HttpUser extends WebfloStorage {
|
|
|
114
113
|
});
|
|
115
114
|
}
|
|
116
115
|
|
|
117
|
-
prompt(data, callback, options = {}) {
|
|
118
|
-
return new Promise((resolve) => {
|
|
116
|
+
async prompt(data, callback, options = {}) {
|
|
117
|
+
return await new Promise((resolve) => {
|
|
119
118
|
this.#client.postRequest(
|
|
120
119
|
data,
|
|
121
120
|
(event) => resolve(callback ? callback(event) : event),
|
|
@@ -9,19 +9,19 @@ export class WebfloCookieStorage extends WebfloStorage {
|
|
|
9
9
|
this.saveOriginals();
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
return this.getAdded().map((key) => renderCookieObj({ name: key, ...this.get(key, true) })).concat(
|
|
14
|
-
this.getDeleted().map((key) => renderCookieObj({ name: key, value: '', maxAge: 0 }))
|
|
15
|
-
);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
set(key, value) {
|
|
12
|
+
async set(key, value) {
|
|
19
13
|
if (!_isObject(value)) { value = { name: key, value }; }
|
|
20
|
-
return super.set(key, value);
|
|
14
|
+
return await super.set(key, value);
|
|
21
15
|
}
|
|
22
16
|
|
|
23
17
|
get(key, withDetail = false) {
|
|
24
18
|
if (!withDetail) return super.get(key)?.value;
|
|
25
19
|
return super.get(key);
|
|
26
20
|
}
|
|
21
|
+
|
|
22
|
+
render() {
|
|
23
|
+
return this.getAdded().map((key) => renderCookieObj({ name: key, ...this.get(key, true) })).concat(
|
|
24
|
+
this.getDeleted().map((key) => renderCookieObj({ name: key, value: '', maxAge: 0 }))
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
27
|
}
|
|
@@ -8,7 +8,7 @@ export class WebfloRouter {
|
|
|
8
8
|
this.path = _isArray(path) ? path : (path + '').split('/').filter(a => a);
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
async route(method, event, arg, _default, remoteFetch = null) {
|
|
11
|
+
async route(method, event, arg = null, _default = null, remoteFetch = null, requestLifecycle = null) {
|
|
12
12
|
|
|
13
13
|
const $this = this;
|
|
14
14
|
const $runtime = this.cx.runtime;
|
|
@@ -58,7 +58,7 @@ export class WebfloRouter {
|
|
|
58
58
|
nextTick.event = await thisTick.event.with(newDestination, _request, requestInit);
|
|
59
59
|
} else {
|
|
60
60
|
nextTick.event = await thisTick.event.with(newDestination, requestInit);
|
|
61
|
-
|
|
61
|
+
}
|
|
62
62
|
nextTick.source = thisTick.destination.join('/');
|
|
63
63
|
nextTick.destination = newDestination.split('?').shift().split('/').map(a => a.trim()).filter(a => a);
|
|
64
64
|
nextTick.trail = _args[1].startsWith('/') ? [] : thisTick.trail.reduce((_commonRoot, _seg, i) => _commonRoot.length === i && _seg === nextTick.destination[i] ? _commonRoot.concat(_seg) : _commonRoot, []);
|
|
@@ -74,9 +74,10 @@ export class WebfloRouter {
|
|
|
74
74
|
_next.stepname = nextPathname[0];
|
|
75
75
|
// -------------
|
|
76
76
|
return new Promise(async (res) => {
|
|
77
|
-
thisTick.event.onRespondWith = (response) => {
|
|
77
|
+
thisTick.event.onRespondWith = async (response) => {
|
|
78
78
|
thisTick.event.onRespondWith = null;
|
|
79
79
|
res(response);
|
|
80
|
+
await requestLifecycle.responsePromise;
|
|
80
81
|
};
|
|
81
82
|
const $returnValue = Promise.resolve(handler.call(thisContext, thisTick.event, thisTick.arg, _next/*next*/, remoteFetch));
|
|
82
83
|
// This should listen first before waitUntil's listener
|
|
@@ -2,27 +2,35 @@ import { _isObject } from '@webqit/util/js/index.js';
|
|
|
2
2
|
|
|
3
3
|
export class WebfloRuntime {
|
|
4
4
|
|
|
5
|
-
async
|
|
6
|
-
// Exec routing
|
|
5
|
+
async setup(httpEvent) {
|
|
7
6
|
const router = new this.constructor.Router(this.cx, httpEvent.url.pathname);
|
|
8
|
-
|
|
9
|
-
return await router.route([httpEvent.request.method, 'default'], httpEvent, context, async (event) => {
|
|
10
|
-
return crossLayerFetch(event);
|
|
11
|
-
}, (...args) => this.remoteFetch(...args));
|
|
12
|
-
};
|
|
13
|
-
try {
|
|
14
|
-
// Route for response
|
|
15
|
-
return await (this.cx.middlewares || []).concat(route).reverse().reduce((next, fn) => {
|
|
16
|
-
return () => fn.call(this.cx, httpEvent, router, next);
|
|
17
|
-
}, null)();
|
|
18
|
-
|
|
19
|
-
} catch (e) {
|
|
20
|
-
console.error(e);
|
|
21
|
-
return new Response(null, { status: 500, statusText: e.message });
|
|
22
|
-
}
|
|
7
|
+
return await router.route(['SETUP'], httpEvent);
|
|
23
8
|
}
|
|
24
9
|
|
|
25
|
-
async
|
|
10
|
+
async dispatch(httpEvent, context, crossLayerFetch) {
|
|
11
|
+
const requestLifecycle = {};
|
|
12
|
+
requestLifecycle.responsePromise = new Promise(async (res) => {
|
|
13
|
+
// Exec routing
|
|
14
|
+
const router = new this.constructor.Router(this.cx, httpEvent.url.pathname);
|
|
15
|
+
const route = async () => {
|
|
16
|
+
return await router.route([httpEvent.request.method, 'default'], httpEvent, context, async (event) => {
|
|
17
|
+
return crossLayerFetch(event);
|
|
18
|
+
}, (...args) => this.remoteFetch(...args), requestLifecycle);
|
|
19
|
+
};
|
|
20
|
+
try {
|
|
21
|
+
// Route for response
|
|
22
|
+
res(await (this.cx.middlewares || []).concat(route).reverse().reduce((next, fn) => {
|
|
23
|
+
return () => fn.call(this.cx, httpEvent, router, next);
|
|
24
|
+
}, null)());
|
|
25
|
+
} catch (e) {
|
|
26
|
+
console.error(e);
|
|
27
|
+
res(new Response(null, { status: 500, statusText: e.message }));
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
return await requestLifecycle.responsePromise;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async normalizeResponse(httpEvent, response) {
|
|
26
34
|
// Normalize response
|
|
27
35
|
if (!(response instanceof Response)) {
|
|
28
36
|
response = typeof response === 'undefined'
|
|
@@ -31,7 +39,7 @@ export class WebfloRuntime {
|
|
|
31
39
|
}
|
|
32
40
|
// Commit data
|
|
33
41
|
for (const storage of [httpEvent.cookies, httpEvent.session, httpEvent.storage]) {
|
|
34
|
-
await storage?.commit?.(response
|
|
42
|
+
await storage?.commit?.(response);
|
|
35
43
|
}
|
|
36
44
|
return response;
|
|
37
45
|
}
|