@hypen-space/core 0.4.46 → 0.4.81
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/README.md +8 -9
- package/dist/app.d.ts +56 -6
- package/dist/app.js +77 -8
- package/dist/app.js.map +4 -4
- package/dist/components/builtin.js +77 -8
- package/dist/components/builtin.js.map +4 -4
- package/dist/datasource.d.ts +4 -4
- package/dist/datasource.js.map +1 -1
- package/dist/engine-base.d.ts +193 -0
- package/dist/engine-base.js +540 -0
- package/dist/engine-base.js.map +12 -0
- package/dist/hypen.d.ts +24 -19
- package/dist/hypen.js +7 -7
- package/dist/hypen.js.map +3 -3
- package/dist/index.browser.js +86 -14
- package/dist/index.browser.js.map +6 -6
- package/dist/index.d.ts +3 -2
- package/dist/index.js +92 -20
- package/dist/index.js.map +7 -7
- package/dist/persistence.d.ts +21 -0
- package/dist/remote/client.d.ts +2 -0
- package/dist/remote/client.js +3 -2
- package/dist/remote/client.js.map +3 -3
- package/dist/remote/index.js +3 -2
- package/dist/remote/index.js.map +3 -3
- package/dist/remote/types.d.ts +2 -0
- package/dist/router.js +8 -6
- package/dist/router.js.map +3 -3
- package/package.json +7 -1
- package/src/app.ts +177 -29
- package/src/datasource.ts +4 -4
- package/src/engine-base.ts +358 -0
- package/src/hypen.ts +34 -28
- package/src/index.ts +8 -2
- package/src/persistence.ts +25 -0
- package/src/remote/client.ts +3 -0
- package/src/remote/types.ts +2 -0
- package/src/router.ts +13 -6
- package/src/types.ts +1 -0
package/dist/hypen.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Enables single-file components with inline UI templates.
|
|
5
5
|
*
|
|
6
|
-
* The `hypen` tagged template preserves
|
|
6
|
+
* The `hypen` tagged template preserves @{state.x} and @{item.x} bindings
|
|
7
7
|
* instead of interpolating them, allowing you to write:
|
|
8
8
|
*
|
|
9
9
|
* @example
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* })
|
|
18
18
|
* .ui(hypen`
|
|
19
19
|
* Column {
|
|
20
|
-
* Text("Count:
|
|
20
|
+
* Text("Count: @{state.count}")
|
|
21
21
|
* Button { Text("+") }
|
|
22
22
|
* .onClick("@actions.increment")
|
|
23
23
|
* }
|
|
@@ -27,12 +27,18 @@
|
|
|
27
27
|
/**
|
|
28
28
|
* Proxy for state bindings.
|
|
29
29
|
*
|
|
30
|
-
*
|
|
30
|
+
* In hypen templates, prefer the direct `@{state.x}` syntax. This proxy
|
|
31
|
+
* exists for cases where you want to interpolate JS values into templates
|
|
32
|
+
* — e.g., `hypen\`Text("${state.user.name}")\`` produces the same string
|
|
33
|
+
* as `hypen\`Text("@{state.user.name}")\``.
|
|
31
34
|
*
|
|
32
35
|
* @example
|
|
33
36
|
* ```typescript
|
|
37
|
+
* // Preferred: direct syntax
|
|
38
|
+
* hypen`Text("Hello, @{state.user.name}")`
|
|
39
|
+
*
|
|
40
|
+
* // Also works: proxy interpolation
|
|
34
41
|
* hypen`Text("Hello, ${state.user.name}")`
|
|
35
|
-
* // Produces: Text("Hello, ${state.user.name}")
|
|
36
42
|
* ```
|
|
37
43
|
*/
|
|
38
44
|
export declare const state: any;
|
|
@@ -45,7 +51,7 @@ export declare const state: any;
|
|
|
45
51
|
* ```typescript
|
|
46
52
|
* hypen`
|
|
47
53
|
* List(@state.items) {
|
|
48
|
-
* Text("
|
|
54
|
+
* Text("@{item.name}: @{item.price}")
|
|
49
55
|
* }
|
|
50
56
|
* `
|
|
51
57
|
* ```
|
|
@@ -60,7 +66,7 @@ export declare const item: any;
|
|
|
60
66
|
* ```typescript
|
|
61
67
|
* hypen`
|
|
62
68
|
* List(@state.items) {
|
|
63
|
-
* Text("Item
|
|
69
|
+
* Text("Item #@{index}: @{item.name}")
|
|
64
70
|
* }
|
|
65
71
|
* `
|
|
66
72
|
* ```
|
|
@@ -69,36 +75,35 @@ export declare const index: any;
|
|
|
69
75
|
/**
|
|
70
76
|
* Tagged template literal for Hypen DSL templates.
|
|
71
77
|
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
78
|
+
* Prefer the direct `@{state.x}` / `@{item.x}` syntax. The `state`, `item`,
|
|
79
|
+
* and `index` proxies are available for interpolating JS variables or
|
|
80
|
+
* mixing dynamic content with bindings.
|
|
74
81
|
*
|
|
75
82
|
* @example
|
|
76
83
|
* ```typescript
|
|
77
84
|
* import { hypen, state, item } from "@hypen-space/core";
|
|
78
85
|
*
|
|
79
|
-
* //
|
|
80
|
-
* const t1 = hypen`Text("Count:
|
|
81
|
-
* // Result: 'Text("Count: ${state.count}")'
|
|
86
|
+
* // Preferred: direct binding syntax
|
|
87
|
+
* const t1 = hypen`Text("Count: @{state.count}")`;
|
|
82
88
|
*
|
|
83
|
-
* // Nested
|
|
84
|
-
* const t2 = hypen`Text("Hello,
|
|
85
|
-
* // Result: 'Text("Hello, ${state.user.profile.name}")'
|
|
89
|
+
* // Nested paths work the same way
|
|
90
|
+
* const t2 = hypen`Text("Hello, @{state.user.profile.name}")`;
|
|
86
91
|
*
|
|
87
92
|
* // List with item binding
|
|
88
93
|
* const t3 = hypen`
|
|
89
94
|
* List(@state.products) {
|
|
90
|
-
* Text("
|
|
95
|
+
* Text("@{item.name} - $@{item.price}")
|
|
91
96
|
* }
|
|
92
97
|
* `;
|
|
93
98
|
*
|
|
94
|
-
* //
|
|
99
|
+
* // Interpolate JS variables alongside bindings
|
|
95
100
|
* const title = "My App";
|
|
96
|
-
* const t4 = hypen`Text("${title}:
|
|
97
|
-
* // Result: 'Text("My App:
|
|
101
|
+
* const t4 = hypen`Text("${title}: @{state.count}")`;
|
|
102
|
+
* // Result: 'Text("My App: @{state.count}")'
|
|
98
103
|
* ```
|
|
99
104
|
*
|
|
100
105
|
* @param strings - Template literal string parts
|
|
101
|
-
* @param expressions - Interpolated
|
|
106
|
+
* @param expressions - Interpolated JS values or binding proxies
|
|
102
107
|
* @returns The template string with bindings preserved
|
|
103
108
|
*/
|
|
104
109
|
export declare function hypen(strings: TemplateStringsArray, ...expressions: unknown[]): string;
|
package/dist/hypen.js
CHANGED
|
@@ -26,13 +26,13 @@ function createBindingProxy(root) {
|
|
|
26
26
|
const handler = {
|
|
27
27
|
get(_, prop) {
|
|
28
28
|
if (prop === Symbol.toPrimitive || prop === "toString" || prop === "valueOf") {
|
|
29
|
-
return () =>
|
|
29
|
+
return () => `@{${root}}`;
|
|
30
30
|
}
|
|
31
31
|
if (typeof prop === "symbol") {
|
|
32
32
|
return;
|
|
33
33
|
}
|
|
34
34
|
if (prop === "toJSON") {
|
|
35
|
-
return () =>
|
|
35
|
+
return () => `@{${root}}`;
|
|
36
36
|
}
|
|
37
37
|
return createBindingProxy(`${root}.${prop}`);
|
|
38
38
|
},
|
|
@@ -54,10 +54,10 @@ function createBindingProxy(root) {
|
|
|
54
54
|
var state = createBindingProxy("state");
|
|
55
55
|
var item = createBindingProxy("item");
|
|
56
56
|
var index = {
|
|
57
|
-
[Symbol.toPrimitive]: () => "
|
|
58
|
-
toString: () => "
|
|
59
|
-
valueOf: () => "
|
|
60
|
-
toJSON: () => "
|
|
57
|
+
[Symbol.toPrimitive]: () => "@{index}",
|
|
58
|
+
toString: () => "@{index}",
|
|
59
|
+
valueOf: () => "@{index}",
|
|
60
|
+
toJSON: () => "@{index}"
|
|
61
61
|
};
|
|
62
62
|
function hypen(strings, ...expressions) {
|
|
63
63
|
let result = strings[0];
|
|
@@ -75,4 +75,4 @@ export {
|
|
|
75
75
|
hypen
|
|
76
76
|
};
|
|
77
77
|
|
|
78
|
-
//# debugId=
|
|
78
|
+
//# debugId=E5F0C092159197CD64756E2164756E21
|
package/dist/hypen.js.map
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/hypen.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"/**\n * Hypen Tagged Template Literal\n *\n * Enables single-file components with inline UI templates.\n *\n * The `hypen` tagged template preserves
|
|
5
|
+
"/**\n * Hypen Tagged Template Literal\n *\n * Enables single-file components with inline UI templates.\n *\n * The `hypen` tagged template preserves @{state.x} and @{item.x} bindings\n * instead of interpolating them, allowing you to write:\n *\n * @example\n * ```typescript\n * import { app, hypen, state } from \"@hypen-space/core\";\n *\n * export default app\n * .defineState({ count: 0 })\n * .onAction(\"increment\", ({ state }) => {\n * state.count += 1;\n * })\n * .ui(hypen`\n * Column {\n * Text(\"Count: @{state.count}\")\n * Button { Text(\"+\") }\n * .onClick(\"@actions.increment\")\n * }\n * `);\n * ```\n */\n\n/**\n * Creates a proxy that captures property access as a binding path string.\n *\n * When used in a template literal, the proxy's toString() returns the\n * full binding expression (e.g., \"@{state.user.name}\"). This lets you\n * interpolate JS variables into hypen templates without losing type safety.\n *\n * @example\n * ```typescript\n * const state = createBindingProxy('state');\n * String(state.user.name) // Returns: \"@{state.user.name}\"\n * ```\n */\nfunction createBindingProxy(root: string): any {\n const handler: ProxyHandler<object> = {\n get(_, prop: string | symbol): any {\n // Handle Symbol.toPrimitive, toString, and valueOf for string conversion\n if (\n prop === Symbol.toPrimitive ||\n prop === \"toString\" ||\n prop === \"valueOf\"\n ) {\n return () => `@{${root}}`;\n }\n\n // Handle other Symbol properties (e.g., Symbol.toStringTag)\n if (typeof prop === \"symbol\") {\n return undefined;\n }\n\n // Handle JSON.stringify\n if (prop === \"toJSON\") {\n return () => `@{${root}}`;\n }\n\n // Chain to nested property: state.user -> state.user.name\n return createBindingProxy(`${root}.${prop}`);\n },\n\n // Support for `in` operator\n has() {\n return true;\n },\n\n // Support for Object.keys() - return empty to avoid enumeration issues\n ownKeys() {\n return [];\n },\n\n getOwnPropertyDescriptor() {\n return {\n configurable: true,\n enumerable: true,\n };\n },\n };\n\n return new Proxy({} as any, handler);\n}\n\n/**\n * Proxy for state bindings.\n *\n * In hypen templates, prefer the direct `@{state.x}` syntax. This proxy\n * exists for cases where you want to interpolate JS values into templates\n * — e.g., `hypen\\`Text(\"${state.user.name}\")\\`` produces the same string\n * as `hypen\\`Text(\"@{state.user.name}\")\\``.\n *\n * @example\n * ```typescript\n * // Preferred: direct syntax\n * hypen`Text(\"Hello, @{state.user.name}\")`\n *\n * // Also works: proxy interpolation\n * hypen`Text(\"Hello, ${state.user.name}\")`\n * ```\n */\nexport const state: any = createBindingProxy(\"state\");\n\n/**\n * Proxy for item bindings in list iteration.\n *\n * Use inside List components to reference the current item:\n *\n * @example\n * ```typescript\n * hypen`\n * List(@state.items) {\n * Text(\"@{item.name}: @{item.price}\")\n * }\n * `\n * ```\n */\nexport const item: any = createBindingProxy(\"item\");\n\n/**\n * Proxy for index in list iteration.\n *\n * Use inside List components to reference the current index:\n *\n * @example\n * ```typescript\n * hypen`\n * List(@state.items) {\n * Text(\"Item #@{index}: @{item.name}\")\n * }\n * `\n * ```\n */\nexport const index: any = {\n [Symbol.toPrimitive]: () => \"@{index}\",\n toString: () => \"@{index}\",\n valueOf: () => \"@{index}\",\n toJSON: () => \"@{index}\",\n};\n\n/**\n * Tagged template literal for Hypen DSL templates.\n *\n * Prefer the direct `@{state.x}` / `@{item.x}` syntax. The `state`, `item`,\n * and `index` proxies are available for interpolating JS variables or\n * mixing dynamic content with bindings.\n *\n * @example\n * ```typescript\n * import { hypen, state, item } from \"@hypen-space/core\";\n *\n * // Preferred: direct binding syntax\n * const t1 = hypen`Text(\"Count: @{state.count}\")`;\n *\n * // Nested paths work the same way\n * const t2 = hypen`Text(\"Hello, @{state.user.profile.name}\")`;\n *\n * // List with item binding\n * const t3 = hypen`\n * List(@state.products) {\n * Text(\"@{item.name} - $@{item.price}\")\n * }\n * `;\n *\n * // Interpolate JS variables alongside bindings\n * const title = \"My App\";\n * const t4 = hypen`Text(\"${title}: @{state.count}\")`;\n * // Result: 'Text(\"My App: @{state.count}\")'\n * ```\n *\n * @param strings - Template literal string parts\n * @param expressions - Interpolated JS values or binding proxies\n * @returns The template string with bindings preserved\n */\nexport function hypen(\n strings: TemplateStringsArray,\n ...expressions: unknown[]\n): string {\n let result = strings[0];\n\n for (let i = 0; i < expressions.length; i++) {\n const expr = expressions[i];\n\n // Convert expression to string\n // Binding proxies will return \"@{state.x}\" via their toString()\n result += String(expr);\n result += strings[i + 1]!;\n }\n\n return result!.trim();\n}\n\n/**\n * Type helper for defining state shape.\n * Use with state proxy for better IDE support in complex scenarios.\n *\n * @example\n * ```typescript\n * type MyState = { user: { name: string; age: number } };\n * const typedState = state as StateProxy<MyState>;\n * ```\n */\nexport type StateProxy<T> = {\n [K in keyof T]: T[K] extends object\n ? StateProxy<T[K]> & { toString(): string }\n : { toString(): string };\n} & { toString(): string };\n\n/**\n * Type helper for item proxy in lists.\n */\nexport type ItemProxy<T> = StateProxy<T>;\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;AAwCA,SAAS,kBAAkB,CAAC,MAAmB;AAAA,EAC7C,MAAM,UAAgC;AAAA,IACpC,GAAG,CAAC,GAAG,MAA4B;AAAA,MAEjC,IACE,SAAS,OAAO,eAChB,SAAS,cACT,SAAS,WACT;AAAA,QACA,OAAO,MAAM,KAAK;AAAA,MACpB;AAAA,MAGA,IAAI,OAAO,SAAS,UAAU;AAAA,QAC5B;AAAA,MACF;AAAA,MAGA,IAAI,SAAS,UAAU;AAAA,QACrB,OAAO,MAAM,KAAK;AAAA,MACpB;AAAA,MAGA,OAAO,mBAAmB,GAAG,QAAQ,MAAM;AAAA;AAAA,IAI7C,GAAG,GAAG;AAAA,MACJ,OAAO;AAAA;AAAA,IAIT,OAAO,GAAG;AAAA,MACR,OAAO,CAAC;AAAA;AAAA,IAGV,wBAAwB,GAAG;AAAA,MACzB,OAAO;AAAA,QACL,cAAc;AAAA,QACd,YAAY;AAAA,MACd;AAAA;AAAA,EAEJ;AAAA,EAEA,OAAO,IAAI,MAAM,CAAC,GAAU,OAAO;AAAA;AAoB9B,IAAM,QAAa,mBAAmB,OAAO;AAgB7C,IAAM,OAAY,mBAAmB,MAAM;AAgB3C,IAAM,QAAa;AAAA,GACvB,OAAO,cAAc,MAAM;AAAA,EAC5B,UAAU,MAAM;AAAA,EAChB,SAAS,MAAM;AAAA,EACf,QAAQ,MAAM;AAChB;AAoCO,SAAS,KAAK,CACnB,YACG,aACK;AAAA,EACR,IAAI,SAAS,QAAQ;AAAA,EAErB,SAAS,IAAI,EAAG,IAAI,YAAY,QAAQ,KAAK;AAAA,IAC3C,MAAM,OAAO,YAAY;AAAA,IAIzB,UAAU,OAAO,IAAI;AAAA,IACrB,UAAU,QAAQ,IAAI;AAAA,EACxB;AAAA,EAEA,OAAO,OAAQ,KAAK;AAAA;",
|
|
8
|
+
"debugId": "E5F0C092159197CD64756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
package/dist/index.browser.js
CHANGED
|
@@ -723,6 +723,7 @@ class HypenAppBuilder {
|
|
|
723
723
|
template;
|
|
724
724
|
_registry;
|
|
725
725
|
dataSourceEntries = [];
|
|
726
|
+
_stateStore;
|
|
726
727
|
constructor(initialState, options, registry) {
|
|
727
728
|
this.initialState = initialState;
|
|
728
729
|
this.options = options || {};
|
|
@@ -756,6 +757,10 @@ class HypenAppBuilder {
|
|
|
756
757
|
this.errorHandler = fn;
|
|
757
758
|
return this;
|
|
758
759
|
}
|
|
760
|
+
persist(store) {
|
|
761
|
+
this._stateStore = store;
|
|
762
|
+
return this;
|
|
763
|
+
}
|
|
759
764
|
useDataSource(plugin, config2) {
|
|
760
765
|
this.dataSourceEntries.push({ plugin, config: config2 });
|
|
761
766
|
return this;
|
|
@@ -780,6 +785,7 @@ class HypenAppBuilder {
|
|
|
780
785
|
initialState: this.initialState,
|
|
781
786
|
template: this.template,
|
|
782
787
|
dataSources: this.dataSourceEntries.length > 0 ? this.dataSourceEntries : undefined,
|
|
788
|
+
stateStore: this._stateStore,
|
|
783
789
|
handlers: {
|
|
784
790
|
onCreated: this.createdHandler,
|
|
785
791
|
onAction: this.actionHandlers,
|
|
@@ -844,22 +850,33 @@ class HypenModuleInstance {
|
|
|
844
850
|
stateChangeCallbacks = [];
|
|
845
851
|
dataSourceManager;
|
|
846
852
|
dataSourceAccessor = {};
|
|
847
|
-
|
|
853
|
+
stateStore;
|
|
854
|
+
currentPersistKey = null;
|
|
855
|
+
persistDebounceTimer;
|
|
856
|
+
sessionId;
|
|
857
|
+
moduleKey = "";
|
|
858
|
+
constructor(engine, definition, router, globalContext, sessionId) {
|
|
848
859
|
this.engine = engine;
|
|
849
860
|
this.definition = definition;
|
|
850
861
|
this.router = router ?? null;
|
|
851
862
|
this.globalContext = globalContext;
|
|
852
|
-
|
|
863
|
+
this.sessionId = sessionId ?? crypto.randomUUID();
|
|
864
|
+
this.stateStore = definition.stateStore;
|
|
865
|
+
const moduleKey = (definition.name || "").toLowerCase();
|
|
866
|
+
this.moduleKey = moduleKey;
|
|
853
867
|
this.state = createObservableState(definition.initialState, {
|
|
854
868
|
onChange: (change) => {
|
|
855
|
-
this.engine.
|
|
869
|
+
this.engine.updateStateSparse(moduleKey || null, change.paths, change.newValues);
|
|
856
870
|
this.stateChangeCallbacks.forEach((cb) => cb());
|
|
857
|
-
|
|
858
|
-
|
|
871
|
+
this.persistIfNeeded();
|
|
872
|
+
}
|
|
859
873
|
});
|
|
860
874
|
const snapshot = getStateSnapshot(this.state);
|
|
861
|
-
|
|
862
|
-
|
|
875
|
+
if (moduleKey) {
|
|
876
|
+
this.engine.registerModule(moduleKey, definition.actions, definition.stateKeys, snapshot);
|
|
877
|
+
} else {
|
|
878
|
+
this.engine.setModule("AnonymousModule", definition.actions, definition.stateKeys, snapshot);
|
|
879
|
+
}
|
|
863
880
|
for (const [actionName, handler] of definition.handlers.onAction) {
|
|
864
881
|
log2.debug(`Registering action handler: ${actionName} for module ${definition.name}`);
|
|
865
882
|
this.engine.onAction(actionName, async (action) => {
|
|
@@ -969,7 +986,54 @@ class HypenModuleInstance {
|
|
|
969
986
|
log2.error(`${context.actionName ? `Action "${context.actionName}"` : `Lifecycle "${context.lifecycle}"`} error:`, error);
|
|
970
987
|
return false;
|
|
971
988
|
}
|
|
989
|
+
persistIfNeeded() {
|
|
990
|
+
if (!this.stateStore)
|
|
991
|
+
return;
|
|
992
|
+
const store = this.stateStore;
|
|
993
|
+
const newKey = store.resolveKey(this.state, this.definition.name || "AnonymousModule", this.sessionId);
|
|
994
|
+
if (newKey && !this.currentPersistKey) {
|
|
995
|
+
this.activatePersistence(newKey);
|
|
996
|
+
return;
|
|
997
|
+
}
|
|
998
|
+
if (!newKey && this.currentPersistKey) {
|
|
999
|
+
this.currentPersistKey = null;
|
|
1000
|
+
return;
|
|
1001
|
+
}
|
|
1002
|
+
if (newKey && newKey !== this.currentPersistKey) {
|
|
1003
|
+
this.activatePersistence(newKey);
|
|
1004
|
+
return;
|
|
1005
|
+
}
|
|
1006
|
+
if (!this.currentPersistKey)
|
|
1007
|
+
return;
|
|
1008
|
+
clearTimeout(this.persistDebounceTimer);
|
|
1009
|
+
this.persistDebounceTimer = setTimeout(() => {
|
|
1010
|
+
const snapshot = getStateSnapshot(this.state);
|
|
1011
|
+
store.save(this.currentPersistKey, snapshot);
|
|
1012
|
+
}, 50);
|
|
1013
|
+
}
|
|
1014
|
+
async activatePersistence(key) {
|
|
1015
|
+
this.currentPersistKey = key;
|
|
1016
|
+
const stored = await this.stateStore.load(key);
|
|
1017
|
+
if (stored) {
|
|
1018
|
+
const merged = { ...this.definition.initialState, ...stored };
|
|
1019
|
+
Object.assign(this.state, merged);
|
|
1020
|
+
} else {
|
|
1021
|
+
const snapshot = getStateSnapshot(this.state);
|
|
1022
|
+
await this.stateStore.save(key, snapshot);
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
972
1025
|
async callCreatedHandler() {
|
|
1026
|
+
if (this.stateStore) {
|
|
1027
|
+
const key = this.stateStore.resolveKey(this.state, this.definition.name || "AnonymousModule", this.sessionId);
|
|
1028
|
+
if (key) {
|
|
1029
|
+
this.currentPersistKey = key;
|
|
1030
|
+
const stored = await this.stateStore.load(key);
|
|
1031
|
+
if (stored) {
|
|
1032
|
+
const merged = { ...this.definition.initialState, ...stored };
|
|
1033
|
+
Object.assign(this.state, merged);
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
973
1037
|
if (this.definition.dataSources?.length) {
|
|
974
1038
|
const dsEngine = this.engine;
|
|
975
1039
|
this.dataSourceManager = new DataSourceManager(dsEngine);
|
|
@@ -1008,6 +1072,11 @@ class HypenModuleInstance {
|
|
|
1008
1072
|
async destroy() {
|
|
1009
1073
|
if (this.isDestroyed)
|
|
1010
1074
|
return;
|
|
1075
|
+
if (this.currentPersistKey && this.stateStore) {
|
|
1076
|
+
clearTimeout(this.persistDebounceTimer);
|
|
1077
|
+
const snapshot = getStateSnapshot(this.state);
|
|
1078
|
+
await this.stateStore.save(this.currentPersistKey, snapshot);
|
|
1079
|
+
}
|
|
1011
1080
|
if (this.dataSourceManager) {
|
|
1012
1081
|
try {
|
|
1013
1082
|
await this.dataSourceManager.disconnectAll();
|
|
@@ -1336,11 +1405,13 @@ class HypenRouter {
|
|
|
1336
1405
|
} else {
|
|
1337
1406
|
window.history.pushState(null, "", url);
|
|
1338
1407
|
}
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1408
|
+
try {
|
|
1409
|
+
const hashChangeEvent = new HashChangeEvent("hashchange", {
|
|
1410
|
+
oldURL: window.location.href.replace(window.location.hash, "#" + oldPath),
|
|
1411
|
+
newURL: window.location.href
|
|
1412
|
+
});
|
|
1413
|
+
window.dispatchEvent(hashChangeEvent);
|
|
1414
|
+
} catch {}
|
|
1344
1415
|
}
|
|
1345
1416
|
} finally {
|
|
1346
1417
|
this.isUpdating = false;
|
|
@@ -1812,7 +1883,8 @@ class RemoteEngine {
|
|
|
1812
1883
|
const hello = {
|
|
1813
1884
|
type: "hello",
|
|
1814
1885
|
sessionId: this.currentSessionId ?? this.sessionOptions?.id,
|
|
1815
|
-
props: this.sessionOptions?.props
|
|
1886
|
+
props: this.sessionOptions?.props,
|
|
1887
|
+
persistKey: this.sessionOptions?.persistKey
|
|
1816
1888
|
};
|
|
1817
1889
|
this.ws.send(JSON.stringify(hello));
|
|
1818
1890
|
}
|
|
@@ -2082,4 +2154,4 @@ export {
|
|
|
2082
2154
|
BaseRenderer
|
|
2083
2155
|
};
|
|
2084
2156
|
|
|
2085
|
-
//# debugId=
|
|
2157
|
+
//# debugId=DD07DC2DABBCE3FD64756E2164756E21
|