@webqit/webflo 1.0.19 → 1.0.21
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 +7 -4
- package/src/config-pi/runtime/Client.js +50 -46
- package/src/config-pi/runtime/Server.js +77 -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 +8 -84
- package/src/runtime-pi/WebfloCookieStorage.js +28 -12
- package/src/runtime-pi/WebfloRouter.js +2 -2
- package/src/runtime-pi/WebfloRuntime.js +9 -4
- package/src/runtime-pi/WebfloStorage.js +91 -34
- package/src/runtime-pi/client/Capabilities.js +211 -0
- package/src/runtime-pi/client/CookieStorage.js +3 -3
- package/src/runtime-pi/client/SessionStorage.js +8 -25
- package/src/runtime-pi/client/WebfloClient.js +15 -23
- 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 +6 -4
- package/src/runtime-pi/client/worker/SessionStorage.js +3 -7
- 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 +6 -4
- package/src/runtime-pi/server/SessionStorage.js +17 -19
- package/src/runtime-pi/server/WebfloServer.js +66 -12
- 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.21",
|
|
16
16
|
"license": "MIT",
|
|
17
17
|
"repository": {
|
|
18
18
|
"type": "git",
|
|
@@ -35,18 +35,21 @@
|
|
|
35
35
|
"webflo-certbot-http-cleanup-hook": "src/services-pi/cert/http-cleanup-hook.js"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
+
"@linked-db/linked-ql": "^0.30.13",
|
|
38
39
|
"@octokit/webhooks": "^7.15.1",
|
|
39
|
-
"@webqit/backpack": "^0.1.
|
|
40
|
+
"@webqit/backpack": "^0.1.8",
|
|
40
41
|
"@webqit/observer": "^2.0.7",
|
|
41
42
|
"@webqit/oohtml-ssr": "^2.1.1",
|
|
42
43
|
"@webqit/util": "^0.8.11",
|
|
43
|
-
"client-sessions": "^0.8.0",
|
|
44
44
|
"esbuild": "^0.14.38",
|
|
45
|
+
"ioredis": "^5.5.0",
|
|
45
46
|
"jsdom": "^21.1.1",
|
|
46
47
|
"mime-types": "^2.1.33",
|
|
48
|
+
"pg": "^8.13.3",
|
|
47
49
|
"simple-git": "^2.20.1",
|
|
48
50
|
"stream-slice": "^0.1.2",
|
|
49
|
-
"urlpattern-polyfill": "^4.0.3"
|
|
51
|
+
"urlpattern-polyfill": "^4.0.3",
|
|
52
|
+
"web-push": "^3.6.7"
|
|
50
53
|
},
|
|
51
54
|
"devDependencies": {
|
|
52
55
|
"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
|
+
vapid_public_key_variable: 'VAPID_PUBLIC_KEY',
|
|
32
|
+
generic_public_webhook_url_variable: 'GENERIC_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: '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: 'generic_public_webhook_url_variable',
|
|
113
|
+
type: 'text',
|
|
114
|
+
message: 'Enter the environment variable name for GENERIC_PUBLIC_WEBHOOK_URL if not as written',
|
|
115
|
+
},
|
|
116
|
+
]
|
|
117
|
+
}
|
|
114
118
|
];
|
|
115
119
|
}
|
|
116
120
|
}
|
|
@@ -29,7 +29,18 @@ export default class Server extends Dotfile {
|
|
|
29
29
|
force: false,
|
|
30
30
|
},
|
|
31
31
|
force_www: '',
|
|
32
|
-
|
|
32
|
+
session_key_variable: 'SESSION_KEY',
|
|
33
|
+
capabilities: {
|
|
34
|
+
database: false,
|
|
35
|
+
database_dialect: 'postgres',
|
|
36
|
+
database_url_variable: 'DATABASE_URL',
|
|
37
|
+
redis: false,
|
|
38
|
+
redis_url_variable: 'REDIS_URL',
|
|
39
|
+
webpush: false,
|
|
40
|
+
vapid_subject: 'mailto:foo@example.com',
|
|
41
|
+
vapid_public_key_variable: 'VAPID_PUBLIC_KEY',
|
|
42
|
+
vapid_private_key_variable: 'VAPID_PRIVATE_KEY',
|
|
43
|
+
},
|
|
33
44
|
}, config, 'patch');
|
|
34
45
|
}
|
|
35
46
|
|
|
@@ -42,13 +53,6 @@ export default class Server extends Dotfile {
|
|
|
42
53
|
{value: 'add',},
|
|
43
54
|
{value: 'remove',},
|
|
44
55
|
],
|
|
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
56
|
}, choices, 'patch');
|
|
53
57
|
// Questions
|
|
54
58
|
return [
|
|
@@ -63,6 +67,7 @@ export default class Server extends Dotfile {
|
|
|
63
67
|
name: 'domains',
|
|
64
68
|
type: 'list',
|
|
65
69
|
message: '[domains]: Enter a list of allowed domains if necessary (comma-separated)',
|
|
70
|
+
initial: (config.domains || []).join(', '),
|
|
66
71
|
validation: ['important'],
|
|
67
72
|
},
|
|
68
73
|
{
|
|
@@ -72,6 +77,12 @@ export default class Server extends Dotfile {
|
|
|
72
77
|
choices: CHOICES.force_www,
|
|
73
78
|
initial: this.indexOfInitial(CHOICES.force_www, config.force_www),
|
|
74
79
|
},
|
|
80
|
+
{
|
|
81
|
+
name: 'session_key_variable',
|
|
82
|
+
type: 'text',
|
|
83
|
+
message: 'Enter the environment variable name for SESSION_KEY if not as written',
|
|
84
|
+
initial: config.session_key_variable,
|
|
85
|
+
},
|
|
75
86
|
{
|
|
76
87
|
name: 'https',
|
|
77
88
|
controls: {
|
|
@@ -113,12 +124,64 @@ export default class Server extends Dotfile {
|
|
|
113
124
|
],
|
|
114
125
|
},
|
|
115
126
|
{
|
|
116
|
-
name: '
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
initial:
|
|
121
|
-
|
|
127
|
+
name: 'capabilities',
|
|
128
|
+
controls: {
|
|
129
|
+
name: 'capabilities',
|
|
130
|
+
},
|
|
131
|
+
initial: config.capabilities,
|
|
132
|
+
schema: [
|
|
133
|
+
{
|
|
134
|
+
name: 'database',
|
|
135
|
+
type: 'toggle',
|
|
136
|
+
message: 'Add database integration?',
|
|
137
|
+
active: 'YES',
|
|
138
|
+
inactive: 'NO',
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
name: 'database_dialect',
|
|
142
|
+
type: (prev, answers) => !answers.database ? null : 'text',
|
|
143
|
+
message: 'Enter the database dialect (postgres for now)',
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
name: 'database_url_variable',
|
|
147
|
+
type: (prev, answers) => !answers.database ? null : 'text',
|
|
148
|
+
message: 'Enter the environment variable name for DATABASE_URL if not as written',
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
name: 'redis',
|
|
152
|
+
type: 'toggle',
|
|
153
|
+
message: 'Add redis integration?',
|
|
154
|
+
active: 'YES',
|
|
155
|
+
inactive: 'NO',
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
name: 'redis_url_variable',
|
|
159
|
+
type: (prev, answers) => !answers.redis ? null : 'text',
|
|
160
|
+
message: 'Enter the environment variable name for REDIS_URL if not as written',
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
name: 'webpush',
|
|
164
|
+
type: 'toggle',
|
|
165
|
+
message: 'Add webpush integration?',
|
|
166
|
+
active: 'YES',
|
|
167
|
+
inactive: 'NO',
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
name: 'vapid_subject',
|
|
171
|
+
type: (prev, answers) => !answers.webpush ? null : 'text',
|
|
172
|
+
message: 'Enter the vapid_subject URL',
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
name: 'vapid_public_key_variable',
|
|
176
|
+
type: (prev, answers) => !answers.webpush ? null : 'text',
|
|
177
|
+
message: 'Enter the environment variable name for VAPID_PUBLIC_KEY if not as written',
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
name: 'vapid_private_key_variable',
|
|
181
|
+
type: (prev, answers) => !answers.webpush ? null : 'text',
|
|
182
|
+
message: 'Enter the environment variable name for VAPID_PRIVATE_KEY if not as written',
|
|
183
|
+
},
|
|
184
|
+
]
|
|
122
185
|
},
|
|
123
186
|
];
|
|
124
187
|
}
|
|
@@ -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
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { _isObject } from '@webqit/util/js/index.js';
|
|
2
1
|
import { WebfloStorage } from './WebfloStorage.js';
|
|
3
2
|
|
|
4
3
|
export class HttpUser extends WebfloStorage {
|
|
@@ -7,79 +6,15 @@ export class HttpUser extends WebfloStorage {
|
|
|
7
6
|
return new this(request, session, client);
|
|
8
7
|
}
|
|
9
8
|
|
|
10
|
-
#session;
|
|
11
9
|
#client;
|
|
12
10
|
|
|
13
11
|
constructor(request, session, client) {
|
|
14
|
-
super(request, session);
|
|
15
|
-
this.#session = session;
|
|
12
|
+
super(session, '#user', request, session);
|
|
16
13
|
this.#client = client;
|
|
17
|
-
// Trigger this
|
|
18
|
-
this.#dict;
|
|
19
14
|
}
|
|
20
15
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
this.#session.set('user', {});
|
|
24
|
-
}
|
|
25
|
-
return this.#session.get('user');
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
[ Symbol.iterator ]() { return this.entries()[ Symbol.iterator ](); }
|
|
29
|
-
|
|
30
|
-
get size() { return Object.keys(this.#dict).length; }
|
|
31
|
-
|
|
32
|
-
set(key, value) {
|
|
33
|
-
Reflect.set(this.#dict, key, value);
|
|
34
|
-
return this;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
get(key) {
|
|
38
|
-
return Reflect.get(this.#dict, key);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
has(key) {
|
|
42
|
-
return Reflect.has(this.#dict, key);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
delete(key) {
|
|
46
|
-
return Reflect.deleteProperty(this.#dict, key);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
keys() {
|
|
50
|
-
return Object.keys(this.#dict);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
values() {
|
|
54
|
-
return Object.values(this.#dict);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
entries() {
|
|
58
|
-
return Object.entries(this.#dict);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
clear() {
|
|
62
|
-
for (const key of this.keys()) {
|
|
63
|
-
Reflect.deleteProperty(this.#dict, key);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
forEach(callback) {
|
|
68
|
-
this.entries().forEach(callback);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
json(arg = null) {
|
|
72
|
-
if (!arguments.length || typeof arg === 'boolean') {
|
|
73
|
-
return {...this.#dict};
|
|
74
|
-
}
|
|
75
|
-
if (!_isObject(arg)) {
|
|
76
|
-
throw new Error(`Argument must be a valid JSON object`);
|
|
77
|
-
}
|
|
78
|
-
Object.assign(this.#dict, arg);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
isSignedIn() {
|
|
82
|
-
return this.has('id');
|
|
16
|
+
async isSignedIn() {
|
|
17
|
+
return await this.has('id');
|
|
83
18
|
}
|
|
84
19
|
|
|
85
20
|
async signIn(...args) {
|
|
@@ -90,22 +25,11 @@ export class HttpUser extends WebfloStorage {
|
|
|
90
25
|
}
|
|
91
26
|
|
|
92
27
|
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;
|
|
28
|
+
await this.clear();
|
|
105
29
|
}
|
|
106
30
|
|
|
107
|
-
confirm(data, callback, options = {}) {
|
|
108
|
-
return new Promise((resolve) => {
|
|
31
|
+
async confirm(data, callback, options = {}) {
|
|
32
|
+
return await new Promise((resolve) => {
|
|
109
33
|
this.#client.postRequest(
|
|
110
34
|
data,
|
|
111
35
|
(event) => resolve(callback ? callback(event) : event),
|
|
@@ -114,8 +38,8 @@ export class HttpUser extends WebfloStorage {
|
|
|
114
38
|
});
|
|
115
39
|
}
|
|
116
40
|
|
|
117
|
-
prompt(data, callback, options = {}) {
|
|
118
|
-
return new Promise((resolve) => {
|
|
41
|
+
async prompt(data, callback, options = {}) {
|
|
42
|
+
return await new Promise((resolve) => {
|
|
119
43
|
this.#client.postRequest(
|
|
120
44
|
data,
|
|
121
45
|
(event) => resolve(callback ? callback(event) : event),
|
|
@@ -3,25 +3,41 @@ import { renderCookieObj } from './util-http.js';
|
|
|
3
3
|
import { WebfloStorage } from './WebfloStorage.js';
|
|
4
4
|
|
|
5
5
|
export class WebfloCookieStorage extends WebfloStorage {
|
|
6
|
+
|
|
7
|
+
#originals;
|
|
8
|
+
|
|
6
9
|
constructor(request, iterable = []) {
|
|
7
10
|
iterable = [...iterable].map(([key, value]) => [key, !_isObject(value) ? { name: key, value } : value]);
|
|
8
|
-
|
|
9
|
-
|
|
11
|
+
iterable = Object.fromEntries(iterable);
|
|
12
|
+
super(iterable, null, request);
|
|
13
|
+
this.#originals = { ...iterable };
|
|
10
14
|
}
|
|
11
15
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
);
|
|
16
|
+
async set(key, value) {
|
|
17
|
+
if (!_isObject(value)) { value = { name: key, value }; }
|
|
18
|
+
return await super.set(key, value);
|
|
16
19
|
}
|
|
17
20
|
|
|
18
|
-
|
|
19
|
-
if (!
|
|
20
|
-
return super.
|
|
21
|
+
async get(key, withDetail = false) {
|
|
22
|
+
if (!withDetail) return (await super.get(key))?.value;
|
|
23
|
+
return await super.get(key);
|
|
21
24
|
}
|
|
22
25
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
+
async render() {
|
|
27
|
+
const entries = await Promise.all((await this.keys()).concat(Object.keys(this.#originals)).map(async (key) => {
|
|
28
|
+
const a = Reflect.get(this.#originals, key);
|
|
29
|
+
const b = await this.get(key, true);
|
|
30
|
+
if (a === b || (_isObject(a) && _isObject(b) && _even(a, b))) {
|
|
31
|
+
// Same
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
if ([undefined, null].includes(b)) {
|
|
35
|
+
// Deleted
|
|
36
|
+
return { name: key, value: '', maxAge: 0 };
|
|
37
|
+
}
|
|
38
|
+
// Added or modified
|
|
39
|
+
return { name: key, ...(await this.get(key, true)) };
|
|
40
|
+
})).then((entries) => entries.filter((e) => e));
|
|
41
|
+
return entries.map((e) => renderCookieObj(e));
|
|
26
42
|
}
|
|
27
43
|
}
|
|
@@ -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, requestLifecycle = 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, []);
|