@webqit/webflo 0.20.26 → 0.20.27
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 +8 -5
- package/src/build-pi/index.js +6 -4
- package/src/init-pi/index.js +0 -1
- package/src/runtime-pi/{WebfloRuntime.js → AppRuntime.js} +57 -113
- package/src/runtime-pi/webflo-client/DeviceCapabilities.js +1 -1
- package/src/runtime-pi/webflo-client/WebfloClient.js +163 -95
- package/src/runtime-pi/webflo-client/{WebfloRootClient1.js → WebfloRootClientA.js} +39 -56
- package/src/runtime-pi/webflo-client/{WebfloRootClient2.js → WebfloRootClientB.js} +3 -3
- package/src/runtime-pi/webflo-client/WebfloSubClient.js +28 -15
- package/src/runtime-pi/webflo-client/index.js +3 -3
- package/src/runtime-pi/webflo-messaging/ClientPortMixin.js +13 -0
- package/src/runtime-pi/{webflo-server/messaging/ClientRequestRealtime.js → webflo-messaging/ClientRequestPort001.js} +13 -9
- package/src/runtime-pi/webflo-messaging/ClientRequestPort010.js +4 -0
- package/src/runtime-pi/webflo-messaging/ClientRequestPort100.js +17 -0
- package/src/runtime-pi/webflo-messaging/WebfloTenancy001.js +27 -0
- package/src/runtime-pi/webflo-messaging/WebfloTenant001.js +27 -0
- package/src/runtime-pi/webflo-routing/HttpCookies101.js +53 -0
- package/src/runtime-pi/webflo-routing/HttpCookies110.js +3 -0
- package/src/runtime-pi/webflo-routing/{HttpEvent.js → HttpEvent111.js} +95 -73
- package/src/runtime-pi/webflo-routing/HttpKeyvalInterface.js +120 -0
- package/src/runtime-pi/webflo-routing/HttpSession001.js +24 -0
- package/src/runtime-pi/webflo-routing/HttpSession110.js +3 -0
- package/src/runtime-pi/webflo-routing/{HttpThread.js → HttpThread111.js} +54 -13
- package/src/runtime-pi/webflo-routing/{HttpUser.js → HttpUser111.js} +10 -23
- package/src/runtime-pi/webflo-routing/KeyvalsFactory001.js +53 -0
- package/src/runtime-pi/webflo-routing/KeyvalsFactory110.js +48 -0
- package/src/runtime-pi/webflo-routing/KeyvalsFactoryInterface.js +56 -0
- package/src/runtime-pi/webflo-routing/{WebfloRouter.js → WebfloRouter111.js} +5 -6
- package/src/runtime-pi/webflo-server/WebfloServer.js +262 -266
- package/src/runtime-pi/webflo-worker/WebfloWorker.js +97 -44
- package/src/util.js +3 -2
- package/src/runtime-pi/apis.js +0 -9
- package/src/runtime-pi/webflo-client/ClientSideCookies.js +0 -18
- package/src/runtime-pi/webflo-fetch/LiveResponse.js +0 -476
- package/src/runtime-pi/webflo-fetch/index.js +0 -419
- package/src/runtime-pi/webflo-fetch/util.js +0 -28
- package/src/runtime-pi/webflo-messaging/WQBroadcastChannel.js +0 -10
- package/src/runtime-pi/webflo-messaging/WQMessageChannel.js +0 -26
- package/src/runtime-pi/webflo-messaging/WQMessageEvent.js +0 -87
- package/src/runtime-pi/webflo-messaging/WQMessagePort.js +0 -38
- package/src/runtime-pi/webflo-messaging/WQRelayPort.js +0 -47
- package/src/runtime-pi/webflo-messaging/WQSockPort.js +0 -111
- package/src/runtime-pi/webflo-messaging/WQStarPort.js +0 -112
- package/src/runtime-pi/webflo-messaging/wq-message-port.js +0 -413
- package/src/runtime-pi/webflo-routing/HttpCookies.js +0 -43
- package/src/runtime-pi/webflo-routing/HttpSession.js +0 -11
- package/src/runtime-pi/webflo-routing/HttpState.js +0 -182
- package/src/runtime-pi/webflo-server/ServerSideCookies.js +0 -22
- package/src/runtime-pi/webflo-server/ServerSideSession.js +0 -40
- package/src/runtime-pi/webflo-server/messaging/Client.js +0 -27
- package/src/runtime-pi/webflo-server/messaging/Clients.js +0 -25
- package/src/runtime-pi/webflo-url/Url.js +0 -156
- package/src/runtime-pi/webflo-url/index.js +0 -1
- package/src/runtime-pi/webflo-url/urlpattern.js +0 -38
- package/src/runtime-pi/webflo-url/util.js +0 -109
- package/src/runtime-pi/webflo-url/xURL.js +0 -94
- package/src/runtime-pi/webflo-worker/WorkerSideCookies.js +0 -21
|
@@ -1,182 +0,0 @@
|
|
|
1
|
-
import { _isObject } from '@webqit/util/js/index.js';
|
|
2
|
-
import { _even } from '@webqit/util/obj/index.js';
|
|
3
|
-
import { HttpEvent } from './HttpEvent.js';
|
|
4
|
-
|
|
5
|
-
export class HttpState {
|
|
6
|
-
|
|
7
|
-
#store;
|
|
8
|
-
#request;
|
|
9
|
-
#thread;
|
|
10
|
-
#modified = false;
|
|
11
|
-
|
|
12
|
-
constructor({ store, request, thread }) {
|
|
13
|
-
this.#store = store || new Map;
|
|
14
|
-
this.#request = request;
|
|
15
|
-
this.#thread = thread === true ? this : thread;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
async has(key) { return await this.#store.has(key); }
|
|
19
|
-
|
|
20
|
-
async get(key) { return await this.#store.get(key); }
|
|
21
|
-
|
|
22
|
-
async set(key, value) {
|
|
23
|
-
await this.#store.set(key, value);
|
|
24
|
-
this.#modified = true;
|
|
25
|
-
await this.emit(key, value);
|
|
26
|
-
return this;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
async delete(key) {
|
|
30
|
-
await this.#store.delete(key);
|
|
31
|
-
this.#modified = true;
|
|
32
|
-
await this.emit(key);
|
|
33
|
-
return this;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
async clear() {
|
|
37
|
-
await this.#store.clear();
|
|
38
|
-
this.#modified = true;
|
|
39
|
-
await this.emit();
|
|
40
|
-
return this;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
async keys() { return [...await this.#store.keys()]; }
|
|
44
|
-
|
|
45
|
-
async values() { return [...await this.#store.values()]; }
|
|
46
|
-
|
|
47
|
-
async entries() { return [...await this.#store.entries()]; }
|
|
48
|
-
|
|
49
|
-
async json(arg = null) {
|
|
50
|
-
if (!arguments.length || typeof arg === 'boolean') {
|
|
51
|
-
return Object.fromEntries(await this.#store.entries());
|
|
52
|
-
}
|
|
53
|
-
if (!_isObject(arg)) {
|
|
54
|
-
throw new Error(`Argument must be a valid JSON object`);
|
|
55
|
-
}
|
|
56
|
-
return await Promise.all(Object.entries(arg).map(([key, value]) => {
|
|
57
|
-
return this.set(key, value);
|
|
58
|
-
}));
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
async forEach(callback) { (await this.entries()).forEach(([key, value], i) => callback(value, key, i)); }
|
|
62
|
-
|
|
63
|
-
[Symbol.iterator]() { return this.entries().then((entries) => entries[Symbol.iterator]()); }
|
|
64
|
-
|
|
65
|
-
get size() { return this.#store.sizs; }
|
|
66
|
-
|
|
67
|
-
async commit() {
|
|
68
|
-
this.#modified = false;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
#listeners = new Set;
|
|
72
|
-
async emit(attr, value) {
|
|
73
|
-
const returnValues = [];
|
|
74
|
-
for (const subscription of this.#listeners) {
|
|
75
|
-
const { attr: $attr, handler, options } = subscription;
|
|
76
|
-
if (arguments.length && $attr !== attr) continue;
|
|
77
|
-
if (arguments.length > 1) {
|
|
78
|
-
returnValues.push(handler(value));
|
|
79
|
-
} else {
|
|
80
|
-
returnValues.push(handler());
|
|
81
|
-
}
|
|
82
|
-
if (options.once) this.#listeners.delete(subscription);
|
|
83
|
-
}
|
|
84
|
-
await Promise.all(returnValues);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
observe(attr, handler, options = {}) {
|
|
88
|
-
if (typeof this.#store.observe === 'function') {
|
|
89
|
-
return this.#store.observe(attr, handler, options);
|
|
90
|
-
}
|
|
91
|
-
const subscription = { attr, handler, options };
|
|
92
|
-
this.#listeners.add(subscription);
|
|
93
|
-
if (options.signal) {
|
|
94
|
-
options.signal.addEventListener('abort', () => {
|
|
95
|
-
this.#listeners.delete(subscription);
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
return () => {
|
|
99
|
-
this.#listeners.delete(subscription);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
cleanup() {
|
|
104
|
-
if (typeof this.#store.cleanup === 'function') {
|
|
105
|
-
this.#store.cleanup();
|
|
106
|
-
}
|
|
107
|
-
this.#listeners.clear();
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
#handlers = new Map;
|
|
111
|
-
defineHandler(attr, ...handlers) {
|
|
112
|
-
const $handlers = [];
|
|
113
|
-
for (let handler of handlers) {
|
|
114
|
-
if (typeof handler === 'function') {
|
|
115
|
-
handler = { callback: handler };
|
|
116
|
-
} else if (typeof handler === 'string') {
|
|
117
|
-
handler = { url: handler };
|
|
118
|
-
} else if (!(_isObject(handler) && (handler = { ...handler }))
|
|
119
|
-
|| typeof handler.callback !== 'function' && typeof handler.url !== 'string') {
|
|
120
|
-
throw new Error(`Handler must be either an URL or a function or an object specifying either an URL (handler.url) or a function (handler.callback)`);
|
|
121
|
-
}
|
|
122
|
-
if (_isObject(handler.with)) {
|
|
123
|
-
handler.with = { ...handler.with };
|
|
124
|
-
} else if (handler.with) {
|
|
125
|
-
throw new Error(`The "with" parameter must be a valid JSON object`);
|
|
126
|
-
}
|
|
127
|
-
$handlers.push(handler);
|
|
128
|
-
}
|
|
129
|
-
this.#handlers.set(attr, $handlers);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
getHandlers() { return this.#handlers; }
|
|
133
|
-
|
|
134
|
-
async require(attrs, callback = null, noNulls = false) {
|
|
135
|
-
const entries = [];
|
|
136
|
-
main: for await (const attr of [].concat(attrs)) {
|
|
137
|
-
if (!(await this.has(attr)) || (noNulls && [undefined, null].includes(await this.get(attr)))) {
|
|
138
|
-
const handlers = this.#handlers.get(attr);
|
|
139
|
-
if (!handlers) {
|
|
140
|
-
throw new Error(`No handler defined for the user attribute: ${attr}`);
|
|
141
|
-
}
|
|
142
|
-
for (let i = 0; i < handlers.length; i++) {
|
|
143
|
-
const handler = handlers[i];
|
|
144
|
-
if (handler.callback) {
|
|
145
|
-
const returnValue = await handler.callback(this, attr);
|
|
146
|
-
if (returnValue instanceof Response) {
|
|
147
|
-
return returnValue;
|
|
148
|
-
}
|
|
149
|
-
if ((typeof returnValue === 'undefined' || (noNulls && returnValue === null)) && i < handlers.length - 1) {
|
|
150
|
-
continue;
|
|
151
|
-
}
|
|
152
|
-
entries.push(returnValue);
|
|
153
|
-
continue main;
|
|
154
|
-
}
|
|
155
|
-
if (callback instanceof HttpEvent) {
|
|
156
|
-
await callback.redirectWith(handler.url, handler.with || {});
|
|
157
|
-
return new Promise(() => { });
|
|
158
|
-
}
|
|
159
|
-
const urlRewrite = new URL(handler.url, this.#request.url);
|
|
160
|
-
const newThread = this.#thread.spawn(urlRewrite.searchParams.get('_thread')/* show */);
|
|
161
|
-
urlRewrite.searchParams.set('_thread', newThread.threadID);
|
|
162
|
-
await newThread.append('back', this.#request.url.replace(urlRewrite.origin, ''));
|
|
163
|
-
if (handler.with) {
|
|
164
|
-
for (const [key, value] of Object.entries(handler.with)) {
|
|
165
|
-
await newThread.append(key, value);
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
return new Response(null, {
|
|
169
|
-
status: 302, headers: {
|
|
170
|
-
Location: urlRewrite
|
|
171
|
-
}
|
|
172
|
-
});
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
entries.push(await this.get(attr));
|
|
176
|
-
}
|
|
177
|
-
if (callback && !(callback instanceof HttpEvent)) {
|
|
178
|
-
return await callback(...entries);
|
|
179
|
-
}
|
|
180
|
-
return entries;
|
|
181
|
-
}
|
|
182
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { headers as headersShim } from '../webflo-fetch/index.js';
|
|
2
|
-
import { HttpCookies } from '../webflo-routing/HttpCookies.js';
|
|
3
|
-
|
|
4
|
-
export class ServerSideCookies extends HttpCookies {
|
|
5
|
-
static create({ request, thread }) {
|
|
6
|
-
const cookies = headersShim.get.value.call(request.headers, 'Cookie', true);
|
|
7
|
-
return new this({
|
|
8
|
-
request,
|
|
9
|
-
thread,
|
|
10
|
-
entries: cookies.map((c) => [c.name, c])
|
|
11
|
-
});
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
async commit(response = null) {
|
|
15
|
-
if (response) {
|
|
16
|
-
for (const cookieStr of await this.render()) {
|
|
17
|
-
response.headers.append('Set-Cookie', cookieStr);
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
await super.commit();
|
|
21
|
-
}
|
|
22
|
-
}
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { HttpSession } from '../webflo-routing/HttpSession.js';
|
|
2
|
-
import { headers as headersShim } from '../webflo-fetch/index.js';
|
|
3
|
-
|
|
4
|
-
export class ServerSideSession extends HttpSession {
|
|
5
|
-
|
|
6
|
-
static create({ store, request, thread, sessionID, ttl }) {
|
|
7
|
-
return new this({
|
|
8
|
-
store,
|
|
9
|
-
request,
|
|
10
|
-
thread,
|
|
11
|
-
sessionID,
|
|
12
|
-
ttl
|
|
13
|
-
});
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
#sessionID;
|
|
17
|
-
get sessionID() { return this.#sessionID; }
|
|
18
|
-
#ttl;
|
|
19
|
-
|
|
20
|
-
constructor({ store, request, thread, sessionID, ttl }) {
|
|
21
|
-
if (!sessionID) {
|
|
22
|
-
throw new Error(`sessionID is required`);
|
|
23
|
-
}
|
|
24
|
-
super({
|
|
25
|
-
store,
|
|
26
|
-
request,
|
|
27
|
-
thread,
|
|
28
|
-
});
|
|
29
|
-
this.#sessionID = sessionID;
|
|
30
|
-
this.#ttl = ttl;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
async commit(response = null, devMode = false) {
|
|
34
|
-
if (response && !headersShim.get.value.call(response.headers, 'Set-Cookie', true).find((c) => c.name === '__sessid')) {
|
|
35
|
-
// expires six months
|
|
36
|
-
response.headers.append('Set-Cookie', `__sessid=${this.#sessionID}; Path=/; ${!devMode ? 'Secure; ' : ''}HttpOnly; SameSite=Lax${this.#ttl ? `; Max-Age=${this.#ttl}` : ''}`);
|
|
37
|
-
}
|
|
38
|
-
await super.commit();
|
|
39
|
-
}
|
|
40
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { WQStarPort } from '../../webflo-messaging/WQStarPort.js';
|
|
2
|
-
import { ClientRequestRealtime } from './ClientRequestRealtime.js';
|
|
3
|
-
|
|
4
|
-
export class Client extends WQStarPort {
|
|
5
|
-
|
|
6
|
-
#clientID;
|
|
7
|
-
get clientID() { return this.#clientID; }
|
|
8
|
-
|
|
9
|
-
constructor(clientID) {
|
|
10
|
-
super();
|
|
11
|
-
this.#clientID = clientID;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
getRequestRealtime(portID) {
|
|
15
|
-
return this.findPort((port) => port.portID === portID);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
createRequestRealtime(portID, url = null) {
|
|
19
|
-
const requestPort = new ClientRequestRealtime(portID, url);
|
|
20
|
-
this.addPort(requestPort);
|
|
21
|
-
setTimeout(() => {
|
|
22
|
-
if (requestPort.length || !this.findPort((port) => port === requestPort)) return;
|
|
23
|
-
requestPort.close(true);
|
|
24
|
-
}, 15000/*15sec*/);
|
|
25
|
-
return requestPort;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { WQStarPort } from '../../webflo-messaging/WQStarPort.js';
|
|
2
|
-
import { WQRelayPort } from '../../webflo-messaging/WQRelayPort.js';
|
|
3
|
-
import { Client } from './Client.js';
|
|
4
|
-
|
|
5
|
-
export class Clients extends WQStarPort {
|
|
6
|
-
#channels = new Map;
|
|
7
|
-
|
|
8
|
-
getClient(clientID, autoCreate = false) {
|
|
9
|
-
if (autoCreate && !this.findPort((client) => client.clientID === clientID)) {
|
|
10
|
-
const client = new Client(clientID);
|
|
11
|
-
const cleanup = this.addPort(client);
|
|
12
|
-
client.wqLifecycle.close.then(cleanup);
|
|
13
|
-
}
|
|
14
|
-
return this.findPort((client) => client.clientID === clientID);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
getChannel(channelName, autoCreate = false) {
|
|
18
|
-
if (!this.#channels.has(channelName) && autoCreate) {
|
|
19
|
-
const channel = new WQRelayPort(channelName);
|
|
20
|
-
this.#channels.set(channelName, channel);
|
|
21
|
-
channel.wqLifecycle.close.then(() => this.#channels.delete(channelName));
|
|
22
|
-
}
|
|
23
|
-
return this.#channels.get(channelName);
|
|
24
|
-
}
|
|
25
|
-
}
|
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
import { _with } from '@webqit/util/obj/index.js';
|
|
2
|
-
import { _isArray, _isObject, _isTypeObject, _isString, _isEmpty } from '@webqit/util/js/index.js';
|
|
3
|
-
import { DeepURLSearchParams } from './util.js';
|
|
4
|
-
import { Observer } from '@webqit/use-live';
|
|
5
|
-
|
|
6
|
-
export class Url {
|
|
7
|
-
|
|
8
|
-
constructor(input) {
|
|
9
|
-
const Self = this.constructor;
|
|
10
|
-
// -----------------------
|
|
11
|
-
// Helpers
|
|
12
|
-
var _strictEven = (a, b) => {
|
|
13
|
-
if (_isObject(a) && _isObject(b)) {
|
|
14
|
-
return _strictEven(Object.keys(a), Object.keys(b))
|
|
15
|
-
&& _strictEven(Object.values(a), Object.values(b));
|
|
16
|
-
}
|
|
17
|
-
if (_isArray(a) && _isArray(b)) {
|
|
18
|
-
return a.length === b.length
|
|
19
|
-
&& a.reduce((recieved, item, i) => recieved && item === b[i], true);
|
|
20
|
-
}
|
|
21
|
-
return a === b;
|
|
22
|
-
};
|
|
23
|
-
Observer.intercept(this, 'set', (e, prev, next) => {
|
|
24
|
-
if (e.key === 'hash' && e.value && !e.value.startsWith('#')) {
|
|
25
|
-
e.value = '#' + e.value;
|
|
26
|
-
} else if (e.key === 'search' && e.value && !e.value.startsWith('?')) {
|
|
27
|
-
e.value = '?' + e.value;
|
|
28
|
-
}
|
|
29
|
-
return next();
|
|
30
|
-
});
|
|
31
|
-
// -----------------------
|
|
32
|
-
// When any one of these properties change,
|
|
33
|
-
// the others are automatically derived
|
|
34
|
-
Observer.observe(this, changes => {
|
|
35
|
-
var urlObj = {};
|
|
36
|
-
var onlyHrefChanged;
|
|
37
|
-
for (var e of changes) {
|
|
38
|
-
// ----------
|
|
39
|
-
if (e.key === 'href' && e.related.length === 1) {
|
|
40
|
-
var urlObj = Self.parseUrl(e.value);
|
|
41
|
-
if (urlObj.pathname) {
|
|
42
|
-
urlObj.pathname = '/' + urlObj.pathname.split('/').filter(s => s.trim()).join('/');
|
|
43
|
-
}
|
|
44
|
-
delete urlObj.query;
|
|
45
|
-
delete urlObj.href;
|
|
46
|
-
onlyHrefChanged = true;
|
|
47
|
-
}
|
|
48
|
-
// ----------
|
|
49
|
-
if (e.key === 'query' && !e.related.includes('search')) {
|
|
50
|
-
// "query" was updated. So we update "search"
|
|
51
|
-
var search = Self.toSearch(this.query); // Not e.value, as that might be a subtree value
|
|
52
|
-
if (search !== this.search) {
|
|
53
|
-
urlObj.search = search;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
if (e.key === 'search' && !e.related.includes('query')) {
|
|
57
|
-
// "search" was updated. So we update "query"
|
|
58
|
-
var query = Self.toQuery(urlObj.search || this.search); // Not e.value, as that might be a href value
|
|
59
|
-
if (!_strictEven(query, this.query)) {
|
|
60
|
-
urlObj.query = query;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
if (e.key === 'pathname' && !e.related.includes('ancestorPathname')) {
|
|
64
|
-
// "pathname" was updated. So we update "ancestorPathname"
|
|
65
|
-
var ancestorPathname = (urlObj.pathname || this.pathname).replace(new RegExp('/[^/]+(?:/)?$'), '');
|
|
66
|
-
if (ancestorPathname !== this.ancestorPathname) {
|
|
67
|
-
urlObj.ancestorPathname = ancestorPathname;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
if (e.key === 'ancestorPathname' && !e.related.includes('pathname')) {
|
|
71
|
-
// "ancestorPathname" was updated. So we update "pathname"
|
|
72
|
-
var pathname = '/' + (urlObj.ancestorPathname || this.ancestorPathname).split('/').filter(s => s).concat((urlObj.pathname || this.pathname).split('/').filter(s => s).pop()).join('/');
|
|
73
|
-
if (pathname !== this.pathname) {
|
|
74
|
-
urlObj.pathname = pathname;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
if (!onlyHrefChanged) {
|
|
79
|
-
var fullOrigin = this.origin,
|
|
80
|
-
usernamePassword = [ this.username, this.password ].filter(a => a);
|
|
81
|
-
if (usernamePassword.length === 2) {
|
|
82
|
-
fullOrigin = `${this.protocol}//${usernamePassword.join(':')}@${this.hostname}${(this.port ? `:${this.port}` : '')}`;
|
|
83
|
-
}
|
|
84
|
-
var href = [ fullOrigin, urlObj.pathname || this.pathname, urlObj.search || this.search || (this.href.includes('?') ? '?' : ''), this.hash || (this.href.includes('#') ? '#' : '') ].join('');
|
|
85
|
-
if (href !== this.href) {
|
|
86
|
-
urlObj.href = href;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
if (!_isEmpty(urlObj)) {
|
|
90
|
-
return Observer.set(this, urlObj);
|
|
91
|
-
}
|
|
92
|
-
}, { diff: true });
|
|
93
|
-
// -----------------------
|
|
94
|
-
// Validate e.detail
|
|
95
|
-
Observer.observe(this, changes => {
|
|
96
|
-
changes.forEach(e => {
|
|
97
|
-
if (e && e.detail) {
|
|
98
|
-
if (!_isTypeObject(e.detail)) {
|
|
99
|
-
throw new Error('"e.detail" can only be of type object.');
|
|
100
|
-
}
|
|
101
|
-
if (e.detail.request && !_isObject(e.detail.request)) {
|
|
102
|
-
throw new Error('"e.detail.request" can only be of type object.');
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
});
|
|
106
|
-
}, { diff: true });
|
|
107
|
-
// -----------------------
|
|
108
|
-
// Startup properties
|
|
109
|
-
Observer.set(this, _isString(input) ? Self.parseUrl(input) : Url.copy(input));
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
toString() {
|
|
113
|
-
return this.href;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
static from(href) {
|
|
117
|
-
return new this(_isObject(href) ? href : this.parseUrl(href));
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
static copy(urlObj) {
|
|
121
|
-
var url = urlProperties.reduce((obj, prop) => _with(obj, prop, urlObj[prop] || ''), {});
|
|
122
|
-
if (!('query' in urlObj)) {
|
|
123
|
-
delete url.query;
|
|
124
|
-
}
|
|
125
|
-
return url;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
static parseUrl(href) {
|
|
129
|
-
var a = new URL(href);
|
|
130
|
-
return this.copy(a);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
static toQuery(search) {
|
|
134
|
-
return DeepURLSearchParams.eval({}, search);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
static toSearch(query) {
|
|
138
|
-
var search = DeepURLSearchParams.stringify(query);
|
|
139
|
-
return search ? '?' + search : '';
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
const urlProperties = [
|
|
144
|
-
'protocol',
|
|
145
|
-
'username',
|
|
146
|
-
'password',
|
|
147
|
-
'host',
|
|
148
|
-
'hostname',
|
|
149
|
-
'port',
|
|
150
|
-
'origin',
|
|
151
|
-
'pathname',
|
|
152
|
-
'search',
|
|
153
|
-
'query',
|
|
154
|
-
'hash',
|
|
155
|
-
'href',
|
|
156
|
-
];
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import './urlpattern.js';
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { _isNumeric } from '@webqit/util/js/index.js';
|
|
2
|
-
if (typeof URLPattern === 'undefined') {
|
|
3
|
-
await import('urlpattern-polyfill');
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
const { exec: execMethod } = URLPattern.prototype;
|
|
7
|
-
const urlPatternMethods = {
|
|
8
|
-
isPattern: {
|
|
9
|
-
value: function () {
|
|
10
|
-
return Object.keys(this.keys || {}).some((compName) => this.keys[compName].length);
|
|
11
|
-
}
|
|
12
|
-
},
|
|
13
|
-
exec: {
|
|
14
|
-
value: function (...args) {
|
|
15
|
-
let components = execMethod.call(this, ...args);
|
|
16
|
-
if (!components) return;
|
|
17
|
-
components.vars = Object.keys(this.keys).reduce(({ named, unnamed }, compName) => {
|
|
18
|
-
this.keys[compName].forEach(key => {
|
|
19
|
-
let value = components[compName].groups[key.name];
|
|
20
|
-
if (typeof key.name === 'number') {
|
|
21
|
-
unnamed.push(value);
|
|
22
|
-
} else {
|
|
23
|
-
named[key.name] = value;
|
|
24
|
-
}
|
|
25
|
-
});
|
|
26
|
-
return { named, unnamed };
|
|
27
|
-
}, { named: {}, unnamed: [] });
|
|
28
|
-
components.render = (str) => {
|
|
29
|
-
return str.replace(/\$(\$|[0-9A-Z]+)/gi, (a, b) => {
|
|
30
|
-
return b === '$' ? '$' : (_isNumeric(b) ? components.vars.unnamed[b - 1] : components.vars.named[b]) || '';
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
return components;
|
|
34
|
-
}
|
|
35
|
-
},
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
Object.defineProperties(URLPattern.prototype, urlPatternMethods);
|
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
import { _isString, _isNumeric, _isArray, _isTypeObject } from '@webqit/util/js/index.js';
|
|
2
|
-
|
|
3
|
-
export const DeepURLSearchParams = {
|
|
4
|
-
// Parse a search params string into an object
|
|
5
|
-
eval(targetObject, str, delim = '&') {
|
|
6
|
-
str = str || '';
|
|
7
|
-
(str.startsWith('?') ? str.substr(1) : str)
|
|
8
|
-
.split(delim).filter(q => q).map(q => q.split('=').map(q => q.trim()))
|
|
9
|
-
.forEach(q => this.set(targetObject, q[0], decodeURIComponent(q[1])));
|
|
10
|
-
return targetObject;
|
|
11
|
-
},
|
|
12
|
-
// Stringify an object into a search params string
|
|
13
|
-
stringify(targetObject, delim = '&') {
|
|
14
|
-
const q = [];
|
|
15
|
-
Object.keys(targetObject).forEach(key => {
|
|
16
|
-
this.reduceValue(targetObject[key], key, (_value, _pathNotation, suggestedKeys = undefined) => {
|
|
17
|
-
if (suggestedKeys) return suggestedKeys;
|
|
18
|
-
q.push(`${_pathNotation}=${encodeURIComponent(_value)}`);
|
|
19
|
-
});
|
|
20
|
-
});
|
|
21
|
-
return q.join(delim);
|
|
22
|
-
},
|
|
23
|
-
// Get value by path notation
|
|
24
|
-
get(targetObject, pathNotation) {
|
|
25
|
-
return this.reducePath(pathNotation, targetObject, (key, _targetObject) => {
|
|
26
|
-
if (!_targetObject && _targetObject !== 0) return;
|
|
27
|
-
return _targetObject[key];
|
|
28
|
-
});
|
|
29
|
-
},
|
|
30
|
-
// Set value by path notation
|
|
31
|
-
set(targetObject, pathNotation, value) {
|
|
32
|
-
this.reducePath(pathNotation, targetObject, function(_key, _targetObject, suggestedBranch = undefined) {
|
|
33
|
-
let _value = value;
|
|
34
|
-
if (suggestedBranch) { _value = suggestedBranch; }
|
|
35
|
-
if (_key === '' && _isArray(_targetObject)) {
|
|
36
|
-
_targetObject.push(_value);
|
|
37
|
-
} else {
|
|
38
|
-
_targetObject[_key] = _value;
|
|
39
|
-
}
|
|
40
|
-
return _value;
|
|
41
|
-
});
|
|
42
|
-
},
|
|
43
|
-
|
|
44
|
-
// Resolve a value to its leaf nodes
|
|
45
|
-
reduceValue(value, contextPath, callback) {
|
|
46
|
-
if (_isTypeObject(value)) {
|
|
47
|
-
let suggestedKeys = Object.keys(value);
|
|
48
|
-
let keys = callback(value, contextPath, suggestedKeys);
|
|
49
|
-
if (_isArray(keys)) {
|
|
50
|
-
return keys.forEach(key => {
|
|
51
|
-
this.reduceValue(value[key], contextPath ? `${contextPath}[${key}]` : key, callback);
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
callback(value, contextPath);
|
|
56
|
-
},
|
|
57
|
-
// Resolve a path to its leaf index
|
|
58
|
-
reducePath(pathNotation, contextObject, callback) {
|
|
59
|
-
if (_isString(pathNotation) && pathNotation.endsWith(']') && _isTypeObject(contextObject)) {
|
|
60
|
-
let [ key, ...rest ] = pathNotation.split('[');
|
|
61
|
-
if (_isNumeric(key)) { key = parseInt(key); }
|
|
62
|
-
rest = rest.join('[').replace(']', '');
|
|
63
|
-
let branch;
|
|
64
|
-
if (key in contextObject) {
|
|
65
|
-
branch = contextObject[key];
|
|
66
|
-
} else {
|
|
67
|
-
let suggestedBranch = rest === '' || _isNumeric(rest.split('[')[0]) ? [] : {};
|
|
68
|
-
branch = callback(key, contextObject, suggestedBranch);
|
|
69
|
-
}
|
|
70
|
-
return this.reducePath(rest, branch, callback);
|
|
71
|
-
}
|
|
72
|
-
if (_isNumeric(pathNotation)) { pathNotation = parseInt(pathNotation); }
|
|
73
|
-
return callback(pathNotation, contextObject);
|
|
74
|
-
},
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
export const path = {
|
|
78
|
-
join(/* path segments */) {
|
|
79
|
-
// Split the inputs into a list of path commands.
|
|
80
|
-
let parts = [], backsteps = 0;
|
|
81
|
-
for ( let i = 0, l = arguments.length; i < l; i++ ) {
|
|
82
|
-
parts = parts.concat( arguments[ i ].split( '/' ) );
|
|
83
|
-
}
|
|
84
|
-
// Interpret the path commands to get the new resolved path.
|
|
85
|
-
let newParts = [];
|
|
86
|
-
for ( let i = 0, l = parts.length; i < l; i++ ) {
|
|
87
|
-
let part = parts[ i ];
|
|
88
|
-
// Remove leading and trailing slashes
|
|
89
|
-
// Also remove "." segments
|
|
90
|
-
if ( !part || part === '.' ) continue;
|
|
91
|
-
// Interpret ".." to pop the last segment
|
|
92
|
-
if ( part === '..' ) {
|
|
93
|
-
if ( !newParts.length ) backsteps ++;
|
|
94
|
-
else newParts.pop();
|
|
95
|
-
}
|
|
96
|
-
// Push new path segments.
|
|
97
|
-
else newParts.push( part );
|
|
98
|
-
}
|
|
99
|
-
// Preserve the initial slash if there was one.
|
|
100
|
-
if ( parts[ 0 ] === '' ) newParts.unshift( '' );
|
|
101
|
-
// Turn back into a single string path.
|
|
102
|
-
return '../'.repeat( backsteps ) + newParts.join( '/' ) || ( newParts.length ? '/' : '.' );
|
|
103
|
-
},
|
|
104
|
-
// A simple function to get the dirname of a path
|
|
105
|
-
// Trailing slashes are ignored. Leading slash is preserved.
|
|
106
|
-
dirname( path ) {
|
|
107
|
-
return this.join( path, '..' );
|
|
108
|
-
}
|
|
109
|
-
};
|