@webqit/webflo 0.20.28 → 0.20.29
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 +2 -2
- package/src/runtime-pi/AppRuntime.js +4 -4
- package/src/runtime-pi/webflo-client/WebfloClient.js +51 -23
- package/src/runtime-pi/webflo-client/WebfloRootClientA.js +3 -3
- package/src/runtime-pi/webflo-client/WebfloSubClient.js +18 -21
- package/src/runtime-pi/webflo-client/index.js +6 -2
- package/src/runtime-pi/webflo-client/webflo-elements.js +1500 -0
- package/src/runtime-pi/webflo-client/webflo-embedded.js +7 -3
- package/src/runtime-pi/webflo-routing/HttpCookies101.js +4 -1
- package/src/runtime-pi/webflo-routing/HttpEvent111.js +7 -4
- package/src/runtime-pi/webflo-routing/HttpKeyvalInterface.js +5 -4
- package/src/runtime-pi/webflo-routing/HttpSession001.js +1 -4
- package/src/runtime-pi/webflo-routing/HttpThread111.js +26 -38
- package/src/runtime-pi/webflo-routing/KeyvalsFactory001.js +2 -2
- package/src/runtime-pi/webflo-routing/KeyvalsFactory110.js +2 -2
- package/src/runtime-pi/webflo-server/WebfloServer.js +9 -5
- package/src/runtime-pi/webflo-worker/WebfloWorker.js +1 -1
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { WebfloSubClient } from './WebfloSubClient.js';
|
|
2
|
+
|
|
3
|
+
export function defineElement() {
|
|
4
|
+
const embedTagName = 'webflo-embedded';
|
|
5
|
+
|
|
6
|
+
window.customElements.define(embedTagName, class extends HTMLElement {
|
|
3
7
|
|
|
4
8
|
#superRuntime;
|
|
5
9
|
#lifecycleController;
|
|
@@ -39,7 +43,7 @@ export function defineElement(WebfloSubClient, embedTagNames = 'webflo-embedded'
|
|
|
39
43
|
}
|
|
40
44
|
|
|
41
45
|
async connectedCallback() {
|
|
42
|
-
this.#superRuntime = (this.parentNode?.closest(
|
|
46
|
+
this.#superRuntime = (this.parentNode?.closest(embedTagName) || document).webfloRuntime;
|
|
43
47
|
this.#lifecycleController = await WebfloSubClient.create(this.#superRuntime, this).initialize();
|
|
44
48
|
}
|
|
45
49
|
|
|
@@ -20,7 +20,7 @@ export class HttpCookies101 extends HttpKeyvalInterface {
|
|
|
20
20
|
// ------ extras
|
|
21
21
|
|
|
22
22
|
async render() {
|
|
23
|
-
const json = await this.json(true);
|
|
23
|
+
const json = await this.json({ meta: true });
|
|
24
24
|
|
|
25
25
|
const entries = Object.keys(json).concat(Object.keys(this.#initial)).map((key) => {
|
|
26
26
|
const a = this.#initial[key];
|
|
@@ -45,8 +45,11 @@ export function renderCookieObjToString(cookieObj) {
|
|
|
45
45
|
const attrsArr = [`${cookieObj.name}=${/*encodeURIComponent*/(cookieObj.value)}`];
|
|
46
46
|
for (const attrName in cookieObj) {
|
|
47
47
|
if (['name', 'value'].includes(attrName)) continue;
|
|
48
|
+
|
|
48
49
|
let _attrName = attrName[0].toUpperCase() + attrName.substring(1);
|
|
49
50
|
if (_attrName === 'MaxAge') { _attrName = 'Max-Age' };
|
|
51
|
+
|
|
52
|
+
if (cookieObj[attrName] === false) continue;
|
|
50
53
|
attrsArr.push(cookieObj[attrName] === true ? _attrName : `${_attrName}=${cookieObj[attrName]}`);
|
|
51
54
|
}
|
|
52
55
|
return attrsArr.join('; ');
|
|
@@ -30,7 +30,7 @@ export class HttpEvent111 {
|
|
|
30
30
|
node._context.parentEvent = this;
|
|
31
31
|
});
|
|
32
32
|
|
|
33
|
-
this.#url = new URLPlus(this.#init.request.url);
|
|
33
|
+
this.#url = new URLPlus(this.#init.request.url, undefined, { immutable: true });
|
|
34
34
|
|
|
35
35
|
this._parentEvent?.signal.addEventListener('abort', () => this.#abortController.abort(), { once: true });
|
|
36
36
|
this.#init.request.signal?.addEventListener('abort', () => this.#abortController.abort(), { once: true });
|
|
@@ -168,9 +168,12 @@ export class HttpEvent111 {
|
|
|
168
168
|
const urlRewrite = new URL(url, this.request.url);
|
|
169
169
|
const newThread = this.thread.spawn(urlRewrite.searchParams.get('_thread'));
|
|
170
170
|
urlRewrite.searchParams.set('_thread', newThread.threadID);
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
171
|
+
|
|
172
|
+
const back = this.request.url.replace(urlRewrite.origin, '');
|
|
173
|
+
await newThread.appendEach({ back, ...data });
|
|
174
|
+
|
|
175
|
+
if (this.url.query['_thread']) {
|
|
176
|
+
this.thread.extend();
|
|
174
177
|
}
|
|
175
178
|
//-----
|
|
176
179
|
return await this.respondWith(null, { status, ...options, headers: { Location: urlRewrite.href } });
|
|
@@ -46,10 +46,11 @@ export class HttpKeyvalInterface {
|
|
|
46
46
|
async keys() { return [...await this.#store.keys()]; }
|
|
47
47
|
async values() { return [...await this.#store.values()]; }
|
|
48
48
|
async entries() { return [...await this.#store.entries()]; }
|
|
49
|
-
async has(
|
|
50
|
-
async get(
|
|
51
|
-
async set(
|
|
52
|
-
async
|
|
49
|
+
async has(...args) { return await this.#store.has(...args); }
|
|
50
|
+
async get(...args) { return await this.#store.get(...args); }
|
|
51
|
+
async set(...args) { await this.#store.set(...args); }
|
|
52
|
+
async patch(...args) { await this.#store.patch(...args); }
|
|
53
|
+
async delete(...args) { await this.#store.delete(...args); }
|
|
53
54
|
async clear() { await this.#store.clear(); }
|
|
54
55
|
async json(...args) { return await this.#store.json(...args); }
|
|
55
56
|
|
|
@@ -4,9 +4,8 @@ export class HttpSession001 extends HttpKeyvalInterface {
|
|
|
4
4
|
|
|
5
5
|
#sessionID;
|
|
6
6
|
get sessionID() { return this.#sessionID; }
|
|
7
|
-
#ttl;
|
|
8
7
|
|
|
9
|
-
constructor({ context = {}, store, request, thread, sessionID
|
|
8
|
+
constructor({ context = {}, store, request, thread, sessionID }) {
|
|
10
9
|
if (!sessionID) {
|
|
11
10
|
throw new Error(`sessionID is required`);
|
|
12
11
|
}
|
|
@@ -16,9 +15,7 @@ export class HttpSession001 extends HttpKeyvalInterface {
|
|
|
16
15
|
request,
|
|
17
16
|
thread,
|
|
18
17
|
sessionID,
|
|
19
|
-
ttl
|
|
20
18
|
});
|
|
21
19
|
this.#sessionID = sessionID;
|
|
22
|
-
this.#ttl = ttl;
|
|
23
20
|
}
|
|
24
21
|
}
|
|
@@ -6,8 +6,8 @@ export class HttpThread111 {
|
|
|
6
6
|
|
|
7
7
|
static create({ context = {}, store, threadID, realm = 0 }) {
|
|
8
8
|
let hydrationMode = true;
|
|
9
|
-
if (!threadID ||
|
|
10
|
-
threadID =
|
|
9
|
+
if (!threadID || !/^[01]{3}-/.test(threadID)) {
|
|
10
|
+
threadID = `${realm === 3 ? '001' : '110'}-${(0 | Math.random() * 9e6).toString(36)}`;
|
|
11
11
|
hydrationMode = false;
|
|
12
12
|
}
|
|
13
13
|
return new this({ context, store, threadID, realm, lifecycle: { dirty: hydrationMode, extended: false } });
|
|
@@ -75,63 +75,51 @@ export class HttpThread111 {
|
|
|
75
75
|
return Object.keys(thread);
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
async has(key
|
|
78
|
+
async has(key) {
|
|
79
79
|
if (!this.#lifecycle.dirty) return false;
|
|
80
|
-
|
|
81
|
-
const thread = await this.#store.get(this.#threadID) || {};
|
|
82
|
-
const values = [].concat(thread[key] ?? []);
|
|
83
|
-
return values.findIndex(filter) !== -1;
|
|
80
|
+
return (await this.keys()).includes(key);
|
|
84
81
|
}
|
|
85
82
|
|
|
86
83
|
async append(key, value) {
|
|
87
84
|
this.#lifecycle.dirty = true;
|
|
85
|
+
|
|
88
86
|
const thread = await this.#store.get(this.#threadID) || {};
|
|
89
|
-
thread[key] =
|
|
90
|
-
|
|
87
|
+
thread[key] = { content: value, context: thread[key] };
|
|
88
|
+
|
|
91
89
|
await this.#store.set(this.#threadID, thread);
|
|
92
90
|
}
|
|
93
91
|
|
|
94
|
-
async
|
|
95
|
-
|
|
92
|
+
async appendEach(hash) {
|
|
93
|
+
this.#lifecycle.dirty = true;
|
|
94
|
+
|
|
96
95
|
const thread = await this.#store.get(this.#threadID) || {};
|
|
97
|
-
const
|
|
96
|
+
for (const [key, value] of Object.entries(hash)) {
|
|
97
|
+
thread[key] = { content: value, context: thread[key] };
|
|
98
|
+
}
|
|
98
99
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
value = values;
|
|
102
|
-
} else if (filter) {
|
|
103
|
-
value = values.find(filter);
|
|
104
|
-
} else { value = values[values.length - 1]; }
|
|
100
|
+
await this.#store.set(this.#threadID, thread);
|
|
101
|
+
}
|
|
105
102
|
|
|
106
|
-
|
|
103
|
+
async get(key, withContext = false) {
|
|
104
|
+
if (!this.#lifecycle.dirty) return null;
|
|
105
|
+
const thread = await this.#store.get(this.#threadID) || {};
|
|
106
|
+
return (withContext ? thread[key] : thread[key]?.content) || null;
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
-
async consume(key,
|
|
110
|
-
if (!this.#lifecycle.dirty) return
|
|
109
|
+
async consume(key, withContext = false) {
|
|
110
|
+
if (!this.#lifecycle.dirty) return null;
|
|
111
111
|
const thread = await this.#store.get(this.#threadID) || {};
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
delete thread[key];
|
|
117
|
-
value = values;
|
|
118
|
-
} else if (filter) {
|
|
119
|
-
const i = values.findIndex(filter);
|
|
120
|
-
if (i !== -1) {
|
|
121
|
-
value = values.splice(i, 1)[0];
|
|
122
|
-
}
|
|
123
|
-
} else { value = values.pop(); }
|
|
124
|
-
|
|
125
|
-
if (!values.length) {
|
|
126
|
-
delete thread[key];
|
|
127
|
-
}
|
|
112
|
+
|
|
113
|
+
const value = thread[key];
|
|
114
|
+
delete thread[key];
|
|
115
|
+
|
|
128
116
|
if (!Object.keys(thread).length) {
|
|
129
117
|
await this.#store.delete(this.#threadID);
|
|
130
118
|
} else {
|
|
131
119
|
await this.#store.set(this.#threadID, thread);
|
|
132
120
|
}
|
|
133
121
|
|
|
134
|
-
return value;
|
|
122
|
+
return (withContext ? value : value?.content) || null;
|
|
135
123
|
}
|
|
136
124
|
|
|
137
125
|
async clear() {
|
|
@@ -37,11 +37,11 @@ export class KeyvalsFactory001 extends KeyvalsFactoryInterface {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
create({ type, ...options }) {
|
|
40
|
-
const { path, ttl =
|
|
40
|
+
const { path, ttl = null } = options;
|
|
41
41
|
|
|
42
42
|
if (!Array.isArray(path) || path.length !== 2) throw new Error('Path must be an array of length 2');
|
|
43
43
|
if (options.origins && (!Array.isArray(options.origins) || options.origins.length !== 1)) throw new Error('Origins must be an array of length 1');
|
|
44
|
-
if (ttl && typeof ttl !== 'number') throw new Error('TTL must be a number');
|
|
44
|
+
if (ttl !== null && typeof ttl !== 'number') throw new Error('TTL must be a number');
|
|
45
45
|
|
|
46
46
|
const origins = (options.origins || this.defaultOrigins).concat(this.instanceID);
|
|
47
47
|
|
|
@@ -32,11 +32,11 @@ export class KeyvalsFactory110 extends KeyvalsFactoryInterface {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
create({ type = 'indexeddb', ...options }) {
|
|
35
|
-
const { path, ttl =
|
|
35
|
+
const { path, ttl = null } = options;
|
|
36
36
|
|
|
37
37
|
if (!Array.isArray(path) || path.length !== 2) throw new Error('Path must be an array of length 2');
|
|
38
38
|
if (options.origins && (!Array.isArray(options.origins) || options.origins.length !== 1)) throw new Error('Origins must be an array of length 1');
|
|
39
|
-
if (ttl && typeof ttl !== 'number') throw new Error('TTL must be a number');
|
|
39
|
+
if (ttl !== null && typeof ttl !== 'number') throw new Error('TTL must be a number');
|
|
40
40
|
|
|
41
41
|
const origins = (options.origins || this.defaultOrigins).concat(this.instanceID);
|
|
42
42
|
|
|
@@ -249,7 +249,9 @@ export class WebfloServer extends AppRuntime {
|
|
|
249
249
|
|
|
250
250
|
identifyIncoming(request, autoGenerateID = false) {
|
|
251
251
|
const secret = this.env('SESSION_KEY');
|
|
252
|
+
|
|
252
253
|
let tenantID = request.headers.get('Cookie', true).find((c) => c.name === '__sessid')?.value;
|
|
254
|
+
|
|
253
255
|
if (tenantID?.includes('.')) {
|
|
254
256
|
if (secret) {
|
|
255
257
|
const [rand, signature] = tenantID.split('.');
|
|
@@ -262,10 +264,12 @@ export class WebfloServer extends AppRuntime {
|
|
|
262
264
|
} else {
|
|
263
265
|
tenantID = null;
|
|
264
266
|
}
|
|
265
|
-
}
|
|
267
|
+
} else tenantID = null;
|
|
268
|
+
|
|
266
269
|
if (!tenantID) {
|
|
267
270
|
tenantID = request.headers.get('Authorization')?.replace(/\s+/, '_');
|
|
268
271
|
}
|
|
272
|
+
|
|
269
273
|
if (!tenantID && autoGenerateID) {
|
|
270
274
|
if (secret) {
|
|
271
275
|
const rand = `${(0 | Math.random() * 9e6).toString(36)}`;
|
|
@@ -277,6 +281,7 @@ export class WebfloServer extends AppRuntime {
|
|
|
277
281
|
tenantID = crypto.randomUUID();
|
|
278
282
|
}
|
|
279
283
|
}
|
|
284
|
+
|
|
280
285
|
return tenantID;
|
|
281
286
|
}
|
|
282
287
|
|
|
@@ -708,15 +713,15 @@ export class WebfloServer extends AppRuntime {
|
|
|
708
713
|
// Thread
|
|
709
714
|
scopeObj.thread = HttpThread111.create({
|
|
710
715
|
context: {},
|
|
711
|
-
store: this.#keyvals.create({ path: ['thread', scopeObj.tenantID], origins }),
|
|
716
|
+
store: this.#keyvals.create({ path: ['thread', scopeObj.tenantID], origins, ttl: 60*60*24*30/* 30 days */ }),
|
|
712
717
|
threadID: scopeObj.url.searchParams.get('_thread'),
|
|
713
718
|
realm: 3
|
|
714
719
|
});
|
|
715
720
|
|
|
716
721
|
// Cookies
|
|
717
|
-
const entries = scopeObj.request.headers.get('Cookie', true).map((c) => [c.name, c]);
|
|
722
|
+
const entries = scopeObj.request.headers.get('Cookie', true).map((c) => [c.name, c.value]);
|
|
718
723
|
const store = InMemoryKV.create({ path: ['cookies', scopeObj.tenantID] });
|
|
719
|
-
entries.forEach(([key, value]) => store.set(key,
|
|
724
|
+
entries.forEach(([key, value]) => store.set({ key, value }));
|
|
720
725
|
const initial = Object.fromEntries(entries);
|
|
721
726
|
scopeObj.cookies = HttpCookies101.create({
|
|
722
727
|
context: { handlersRegistry: this.#keyvals.getHandlers('cookies', true) },
|
|
@@ -730,7 +735,6 @@ export class WebfloServer extends AppRuntime {
|
|
|
730
735
|
context: { handlersRegistry: this.#keyvals.getHandlers('session', true) },
|
|
731
736
|
store: this.#keyvals.create({ path: ['session', scopeObj.tenantID], ttl: scopeObj.sessionTTL, origins }),
|
|
732
737
|
sessionID: scopeObj.tenantID,
|
|
733
|
-
ttl: scopeObj.sessionTTL,
|
|
734
738
|
realm: 3
|
|
735
739
|
});
|
|
736
740
|
|
|
@@ -142,7 +142,7 @@ export class WebfloWorker extends AppRuntime {
|
|
|
142
142
|
// Thread
|
|
143
143
|
scopeObj.thread = HttpThread111.create({
|
|
144
144
|
context: {},
|
|
145
|
-
store: this.#keyvals.create({ path: ['thread', scopeObj.tenantID], origins }),
|
|
145
|
+
store: this.#keyvals.create({ path: ['thread', scopeObj.tenantID], origins, ttl: 60*60*24*30/* 30 days */ }),
|
|
146
146
|
threadID: scopeObj.url.searchParams.get('_thread'),
|
|
147
147
|
realm: 2
|
|
148
148
|
});
|