@thepassle/app-tools 0.9.12 → 0.10.0
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 +1 -1
- package/router/index.js +20 -14
- package/state/index.js +83 -13
- package/types/router/index.d.ts +3 -1
- package/types/router/types.d.ts +5 -4
- package/types/state/index copy.d.ts +50 -0
- package/types/state/index.d.ts +41 -8
- package/api/README.md +0 -443
- package/dialog/README.md +0 -252
- package/env/README.md +0 -77
- package/pwa/README.md +0 -103
- package/router/README.md +0 -502
- package/state/README.md +0 -25
- package/utils/README.md +0 -139
package/package.json
CHANGED
package/router/index.js
CHANGED
|
@@ -182,23 +182,29 @@ export class Router extends EventTarget {
|
|
|
182
182
|
/** @type {Plugin[]} */
|
|
183
183
|
let plugins = this._collectPlugins(route);
|
|
184
184
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
185
|
+
let redirecting;
|
|
186
|
+
do {
|
|
187
|
+
redirecting = false;
|
|
188
|
+
for (const plugin of plugins) {
|
|
189
|
+
try {
|
|
190
|
+
const result = await plugin?.shouldNavigate?.(this.context);
|
|
191
|
+
if (result) {
|
|
192
|
+
const condition = await result.condition();
|
|
193
|
+
if (!condition) {
|
|
194
|
+
url = new URL(result.redirect, this.baseUrl);
|
|
195
|
+
route = this._matchRoute(url) || this._matchRoute(this.fallback);
|
|
196
|
+
plugins = this._collectPlugins(route);
|
|
197
|
+
log("Redirecting", { context: this.context, route: this.route });
|
|
198
|
+
redirecting = true;
|
|
199
|
+
break;
|
|
200
|
+
}
|
|
195
201
|
}
|
|
202
|
+
} catch (e) {
|
|
203
|
+
log(`Plugin "${plugin.name}" error on shouldNavigate hook`, e);
|
|
204
|
+
throw e;
|
|
196
205
|
}
|
|
197
|
-
} catch(e) {
|
|
198
|
-
log(`Plugin "${plugin.name}" error on shouldNavigate hook`, e);
|
|
199
|
-
throw e;
|
|
200
206
|
}
|
|
201
|
-
}
|
|
207
|
+
} while (redirecting);
|
|
202
208
|
|
|
203
209
|
this.route = route;
|
|
204
210
|
|
package/state/index.js
CHANGED
|
@@ -1,35 +1,105 @@
|
|
|
1
|
-
import { createLogger } from
|
|
2
|
-
const log = createLogger(
|
|
1
|
+
import { createLogger } from "../utils/log.js";
|
|
2
|
+
const log = createLogger("state");
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* `'state-changed'` event
|
|
6
|
+
* @template T
|
|
6
7
|
* @example this.dispatchEvent(new StateEvent(data));
|
|
7
8
|
*/
|
|
8
9
|
export class StateEvent extends Event {
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
/**
|
|
11
|
+
* @param {T} state
|
|
12
|
+
*/
|
|
13
|
+
constructor(state) {
|
|
14
|
+
super("state-changed");
|
|
15
|
+
/** @type {T} */
|
|
11
16
|
this.state = state;
|
|
12
17
|
}
|
|
13
18
|
}
|
|
14
19
|
|
|
20
|
+
/**
|
|
21
|
+
* @template T
|
|
22
|
+
* @typedef {{
|
|
23
|
+
* name: string,
|
|
24
|
+
* update?: (prevState: T, newState: T) => T,
|
|
25
|
+
* effect?: (prevState: T, newState: T) => void | Promise<void>,
|
|
26
|
+
* }} Plugin
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @template T
|
|
31
|
+
* @extends EventTarget
|
|
32
|
+
*/
|
|
15
33
|
export class State extends EventTarget {
|
|
34
|
+
/** @type {T} */
|
|
16
35
|
#state;
|
|
17
|
-
|
|
18
|
-
|
|
36
|
+
|
|
37
|
+
/** @type {Array<Plugin<T>>} */
|
|
38
|
+
#plugins = [];
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* @param {T} initialState
|
|
42
|
+
* @param {Array<Plugin<T>>} [plugins=[]]
|
|
43
|
+
*/
|
|
44
|
+
constructor(initialState, plugins = []) {
|
|
19
45
|
super();
|
|
20
46
|
this.#state = initialState;
|
|
47
|
+
this.#plugins = plugins;
|
|
21
48
|
}
|
|
22
49
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
50
|
+
/**
|
|
51
|
+
* @param {T | ((prevState: T) => T)} state
|
|
52
|
+
* @param {boolean} [broadcast=true]
|
|
53
|
+
*/
|
|
54
|
+
setState(state, broadcast = true) {
|
|
55
|
+
log("Before: ", this.#state);
|
|
56
|
+
const prevState = this.#state;
|
|
57
|
+
const s =
|
|
58
|
+
typeof state === "function"
|
|
59
|
+
? /** @type {(prevState: T) => T} */ (state)(prevState)
|
|
60
|
+
: state;
|
|
61
|
+
this.#state = this.#plugins.filter(plugin => plugin.update).reduce(
|
|
62
|
+
(newState, plugin) => {
|
|
63
|
+
try {
|
|
64
|
+
const result = plugin.update(prevState, newState);
|
|
65
|
+
if (!result) {
|
|
66
|
+
console.warn(`Plugin "${plugin.name}" returned undefined or null, using new state as is.`);
|
|
67
|
+
return newState;
|
|
68
|
+
}
|
|
69
|
+
return result;
|
|
70
|
+
} catch (error) {
|
|
71
|
+
console.error(`Error in plugin "${plugin.name}":`, error);
|
|
72
|
+
return newState;
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
structuredClone(s)
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
if (broadcast) {
|
|
79
|
+
this.dispatchEvent(new StateEvent(this.#state));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
Promise.all(
|
|
83
|
+
this.#plugins
|
|
84
|
+
.filter((plugin) => plugin.effect)
|
|
85
|
+
.map(async (plugin) => {
|
|
86
|
+
try {
|
|
87
|
+
return await plugin.effect(prevState, this.#state);
|
|
88
|
+
} catch (error) {
|
|
89
|
+
console.error(`Error in plugin "${plugin.name}":`, error);
|
|
90
|
+
}
|
|
91
|
+
})
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
log("After: ", this.#state);
|
|
28
95
|
}
|
|
29
96
|
|
|
97
|
+
/**
|
|
98
|
+
* @returns {T}
|
|
99
|
+
*/
|
|
30
100
|
getState() {
|
|
31
|
-
return this.#state;
|
|
101
|
+
return structuredClone(this.#state);
|
|
32
102
|
}
|
|
33
103
|
}
|
|
34
104
|
|
|
35
|
-
export const state = new State({});
|
|
105
|
+
export const state = new State({});
|
package/types/router/index.d.ts
CHANGED
|
@@ -52,11 +52,13 @@ export class Router extends EventTarget {
|
|
|
52
52
|
/**
|
|
53
53
|
* @param {string | URL} url The URL to navigate to.
|
|
54
54
|
* @param {{
|
|
55
|
-
* backNav?: boolean
|
|
55
|
+
* backNav?: boolean,
|
|
56
|
+
* replace?: boolean,
|
|
56
57
|
* }} options options An options object to configure the navigation. The backNav property specifies whether the navigation is a backward navigation, which doesn't push the navigation into browser nav history.
|
|
57
58
|
*/
|
|
58
59
|
navigate(url: string | URL, options?: {
|
|
59
60
|
backNav?: boolean;
|
|
61
|
+
replace?: boolean;
|
|
60
62
|
}): Promise<void>;
|
|
61
63
|
route: import("./types.js").Route;
|
|
62
64
|
}
|
package/types/router/types.d.ts
CHANGED
|
@@ -3,12 +3,13 @@ export interface Config {
|
|
|
3
3
|
plugins?: Plugin[];
|
|
4
4
|
routes: RouteDefinition[];
|
|
5
5
|
}
|
|
6
|
+
export interface ShouldNavigateResult {
|
|
7
|
+
redirect: string;
|
|
8
|
+
condition: (() => boolean) | (() => Promise<boolean>);
|
|
9
|
+
}
|
|
6
10
|
export interface Plugin {
|
|
7
11
|
name: string;
|
|
8
|
-
shouldNavigate?: (context: Context) =>
|
|
9
|
-
redirect: string;
|
|
10
|
-
condition: () => boolean | (() => Promise<Boolean>);
|
|
11
|
-
};
|
|
12
|
+
shouldNavigate?: ((context: Context) => ShouldNavigateResult) | ((context: Context) => Promise<ShouldNavigateResult>);
|
|
12
13
|
beforeNavigation?: (context: Context) => void;
|
|
13
14
|
afterNavigation?: (context: Context) => void;
|
|
14
15
|
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `'state-changed'` event
|
|
3
|
+
* @template T
|
|
4
|
+
* @example this.dispatchEvent(new StateEvent(data));
|
|
5
|
+
*/
|
|
6
|
+
export class StateEvent<T> extends Event {
|
|
7
|
+
/**
|
|
8
|
+
* @param {T} state
|
|
9
|
+
*/
|
|
10
|
+
constructor(state: T);
|
|
11
|
+
/** @type {T} */
|
|
12
|
+
state: T;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* @template T
|
|
16
|
+
* @typedef {{
|
|
17
|
+
* name: string,
|
|
18
|
+
* update?: (prevState: T, newState: T) => T,
|
|
19
|
+
* effect?: (prevState: T, newState: T) => void | Promise<void>,
|
|
20
|
+
* }} Plugin
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* @template T
|
|
24
|
+
* @extends EventTarget
|
|
25
|
+
*/
|
|
26
|
+
export class State<T> extends EventTarget {
|
|
27
|
+
/**
|
|
28
|
+
* @param {T} initialState
|
|
29
|
+
* @param {Array<{ update: (prevState: T, newState: T) => T }>} [plugins=[]]
|
|
30
|
+
*/
|
|
31
|
+
constructor(initialState: T, plugins?: {
|
|
32
|
+
update: (prevState: T, newState: T) => T;
|
|
33
|
+
}[]);
|
|
34
|
+
/**
|
|
35
|
+
* @param {T | ((prevState: T) => T)} state
|
|
36
|
+
* @param {boolean} [broadcast=true]
|
|
37
|
+
*/
|
|
38
|
+
setState(state: T | ((prevState: T) => T), broadcast?: boolean): void;
|
|
39
|
+
/**
|
|
40
|
+
* @returns {T}
|
|
41
|
+
*/
|
|
42
|
+
getState(): T;
|
|
43
|
+
#private;
|
|
44
|
+
}
|
|
45
|
+
export const state: State<{}>;
|
|
46
|
+
export type Plugin<T> = {
|
|
47
|
+
name: string;
|
|
48
|
+
update?: (prevState: T, newState: T) => T;
|
|
49
|
+
effect?: (prevState: T, newState: T) => void | Promise<void>;
|
|
50
|
+
};
|
package/types/state/index.d.ts
CHANGED
|
@@ -1,15 +1,48 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `'state-changed'` event
|
|
3
|
+
* @template T
|
|
3
4
|
* @example this.dispatchEvent(new StateEvent(data));
|
|
4
5
|
*/
|
|
5
|
-
export class StateEvent extends Event {
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
export class StateEvent<T> extends Event {
|
|
7
|
+
/**
|
|
8
|
+
* @param {T} state
|
|
9
|
+
*/
|
|
10
|
+
constructor(state: T);
|
|
11
|
+
/** @type {T} */
|
|
12
|
+
state: T;
|
|
8
13
|
}
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
14
|
+
/**
|
|
15
|
+
* @template T
|
|
16
|
+
* @typedef {{
|
|
17
|
+
* name: string,
|
|
18
|
+
* update?: (prevState: T, newState: T) => T,
|
|
19
|
+
* effect?: (prevState: T, newState: T) => void | Promise<void>,
|
|
20
|
+
* }} Plugin
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* @template T
|
|
24
|
+
* @extends EventTarget
|
|
25
|
+
*/
|
|
26
|
+
export class State<T> extends EventTarget {
|
|
27
|
+
/**
|
|
28
|
+
* @param {T} initialState
|
|
29
|
+
* @param {Array<Plugin<T>>} [plugins=[]]
|
|
30
|
+
*/
|
|
31
|
+
constructor(initialState: T, plugins?: Array<Plugin<T>>);
|
|
32
|
+
/**
|
|
33
|
+
* @param {T | ((prevState: T) => T)} state
|
|
34
|
+
* @param {boolean} [broadcast=true]
|
|
35
|
+
*/
|
|
36
|
+
setState(state: T | ((prevState: T) => T), broadcast?: boolean): void;
|
|
37
|
+
/**
|
|
38
|
+
* @returns {T}
|
|
39
|
+
*/
|
|
40
|
+
getState(): T;
|
|
13
41
|
#private;
|
|
14
42
|
}
|
|
15
|
-
export const state: State
|
|
43
|
+
export const state: State<{}>;
|
|
44
|
+
export type Plugin<T> = {
|
|
45
|
+
name: string;
|
|
46
|
+
update?: (prevState: T, newState: T) => T;
|
|
47
|
+
effect?: (prevState: T, newState: T) => void | Promise<void>;
|
|
48
|
+
};
|