@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/index.d.ts
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* })
|
|
17
17
|
* .ui(hypen`
|
|
18
18
|
* Column {
|
|
19
|
-
* Text("Count:
|
|
19
|
+
* Text("Count: @{state.count}")
|
|
20
20
|
* Button { Text("+") }
|
|
21
21
|
* .onClick("@actions.increment")
|
|
22
22
|
* }
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
* .build();
|
|
38
38
|
*
|
|
39
39
|
* // component.hypen (separate file)
|
|
40
|
-
* // Column { Text("Count:
|
|
40
|
+
* // Column { Text("Count: @{state.count}") ... }
|
|
41
41
|
* ```
|
|
42
42
|
*/
|
|
43
43
|
export type { Patch, Action, RenderCallback, ActionHandler as EngineActionHandler, ResolvedComponent, ComponentResolver as EngineComponentResolver, } from "./types.js";
|
|
@@ -45,6 +45,7 @@ export { app, HypenApp, HypenAppBuilder, HypenModuleInstance } from "./app.js";
|
|
|
45
45
|
export { hypen, state, item, index } from "./hypen.js";
|
|
46
46
|
export type { StateProxy, ItemProxy } from "./hypen.js";
|
|
47
47
|
export type { IEngine, ActionContext, GlobalContext, LifecycleHandler, ActionHandlerContext, ActionHandler, HypenModuleDefinition, HypenModule, DisconnectContext, ReconnectContext, ExpireContext, DisconnectHandler, ReconnectHandler, ExpireHandler, ErrorContext, ErrorHandler, ErrorHandlerResult, DataSourceAccessor, } from "./app.js";
|
|
48
|
+
export type { StateStore } from "./persistence.js";
|
|
48
49
|
export { DataSourceManager } from "./datasource.js";
|
|
49
50
|
export type { DataSourcePlugin, DataSourceStatus, DataSourceQuery, DataSourceSubscription, DataSourceChange, IDataSourceEngine, } from "./datasource.js";
|
|
50
51
|
export { createObservableState, batchStateUpdates, getStateSnapshot, isStateProxy, unwrapProxy, } from "./state.js";
|
package/dist/index.js
CHANGED
|
@@ -1063,6 +1063,7 @@ class HypenAppBuilder {
|
|
|
1063
1063
|
template;
|
|
1064
1064
|
_registry;
|
|
1065
1065
|
dataSourceEntries = [];
|
|
1066
|
+
_stateStore;
|
|
1066
1067
|
constructor(initialState, options, registry) {
|
|
1067
1068
|
this.initialState = initialState;
|
|
1068
1069
|
this.options = options || {};
|
|
@@ -1096,6 +1097,10 @@ class HypenAppBuilder {
|
|
|
1096
1097
|
this.errorHandler = fn;
|
|
1097
1098
|
return this;
|
|
1098
1099
|
}
|
|
1100
|
+
persist(store) {
|
|
1101
|
+
this._stateStore = store;
|
|
1102
|
+
return this;
|
|
1103
|
+
}
|
|
1099
1104
|
useDataSource(plugin, config2) {
|
|
1100
1105
|
this.dataSourceEntries.push({ plugin, config: config2 });
|
|
1101
1106
|
return this;
|
|
@@ -1120,6 +1125,7 @@ class HypenAppBuilder {
|
|
|
1120
1125
|
initialState: this.initialState,
|
|
1121
1126
|
template: this.template,
|
|
1122
1127
|
dataSources: this.dataSourceEntries.length > 0 ? this.dataSourceEntries : undefined,
|
|
1128
|
+
stateStore: this._stateStore,
|
|
1123
1129
|
handlers: {
|
|
1124
1130
|
onCreated: this.createdHandler,
|
|
1125
1131
|
onAction: this.actionHandlers,
|
|
@@ -1184,22 +1190,33 @@ class HypenModuleInstance {
|
|
|
1184
1190
|
stateChangeCallbacks = [];
|
|
1185
1191
|
dataSourceManager;
|
|
1186
1192
|
dataSourceAccessor = {};
|
|
1187
|
-
|
|
1193
|
+
stateStore;
|
|
1194
|
+
currentPersistKey = null;
|
|
1195
|
+
persistDebounceTimer;
|
|
1196
|
+
sessionId;
|
|
1197
|
+
moduleKey = "";
|
|
1198
|
+
constructor(engine, definition, router, globalContext, sessionId) {
|
|
1188
1199
|
this.engine = engine;
|
|
1189
1200
|
this.definition = definition;
|
|
1190
1201
|
this.router = router ?? null;
|
|
1191
1202
|
this.globalContext = globalContext;
|
|
1192
|
-
|
|
1203
|
+
this.sessionId = sessionId ?? crypto.randomUUID();
|
|
1204
|
+
this.stateStore = definition.stateStore;
|
|
1205
|
+
const moduleKey = (definition.name || "").toLowerCase();
|
|
1206
|
+
this.moduleKey = moduleKey;
|
|
1193
1207
|
this.state = createObservableState(definition.initialState, {
|
|
1194
1208
|
onChange: (change) => {
|
|
1195
|
-
this.engine.
|
|
1209
|
+
this.engine.updateStateSparse(moduleKey || null, change.paths, change.newValues);
|
|
1196
1210
|
this.stateChangeCallbacks.forEach((cb) => cb());
|
|
1197
|
-
|
|
1198
|
-
|
|
1211
|
+
this.persistIfNeeded();
|
|
1212
|
+
}
|
|
1199
1213
|
});
|
|
1200
1214
|
const snapshot = getStateSnapshot(this.state);
|
|
1201
|
-
|
|
1202
|
-
|
|
1215
|
+
if (moduleKey) {
|
|
1216
|
+
this.engine.registerModule(moduleKey, definition.actions, definition.stateKeys, snapshot);
|
|
1217
|
+
} else {
|
|
1218
|
+
this.engine.setModule("AnonymousModule", definition.actions, definition.stateKeys, snapshot);
|
|
1219
|
+
}
|
|
1203
1220
|
for (const [actionName, handler] of definition.handlers.onAction) {
|
|
1204
1221
|
log2.debug(`Registering action handler: ${actionName} for module ${definition.name}`);
|
|
1205
1222
|
this.engine.onAction(actionName, async (action) => {
|
|
@@ -1309,7 +1326,54 @@ class HypenModuleInstance {
|
|
|
1309
1326
|
log2.error(`${context.actionName ? `Action "${context.actionName}"` : `Lifecycle "${context.lifecycle}"`} error:`, error);
|
|
1310
1327
|
return false;
|
|
1311
1328
|
}
|
|
1329
|
+
persistIfNeeded() {
|
|
1330
|
+
if (!this.stateStore)
|
|
1331
|
+
return;
|
|
1332
|
+
const store = this.stateStore;
|
|
1333
|
+
const newKey = store.resolveKey(this.state, this.definition.name || "AnonymousModule", this.sessionId);
|
|
1334
|
+
if (newKey && !this.currentPersistKey) {
|
|
1335
|
+
this.activatePersistence(newKey);
|
|
1336
|
+
return;
|
|
1337
|
+
}
|
|
1338
|
+
if (!newKey && this.currentPersistKey) {
|
|
1339
|
+
this.currentPersistKey = null;
|
|
1340
|
+
return;
|
|
1341
|
+
}
|
|
1342
|
+
if (newKey && newKey !== this.currentPersistKey) {
|
|
1343
|
+
this.activatePersistence(newKey);
|
|
1344
|
+
return;
|
|
1345
|
+
}
|
|
1346
|
+
if (!this.currentPersistKey)
|
|
1347
|
+
return;
|
|
1348
|
+
clearTimeout(this.persistDebounceTimer);
|
|
1349
|
+
this.persistDebounceTimer = setTimeout(() => {
|
|
1350
|
+
const snapshot = getStateSnapshot(this.state);
|
|
1351
|
+
store.save(this.currentPersistKey, snapshot);
|
|
1352
|
+
}, 50);
|
|
1353
|
+
}
|
|
1354
|
+
async activatePersistence(key) {
|
|
1355
|
+
this.currentPersistKey = key;
|
|
1356
|
+
const stored = await this.stateStore.load(key);
|
|
1357
|
+
if (stored) {
|
|
1358
|
+
const merged = { ...this.definition.initialState, ...stored };
|
|
1359
|
+
Object.assign(this.state, merged);
|
|
1360
|
+
} else {
|
|
1361
|
+
const snapshot = getStateSnapshot(this.state);
|
|
1362
|
+
await this.stateStore.save(key, snapshot);
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1312
1365
|
async callCreatedHandler() {
|
|
1366
|
+
if (this.stateStore) {
|
|
1367
|
+
const key = this.stateStore.resolveKey(this.state, this.definition.name || "AnonymousModule", this.sessionId);
|
|
1368
|
+
if (key) {
|
|
1369
|
+
this.currentPersistKey = key;
|
|
1370
|
+
const stored = await this.stateStore.load(key);
|
|
1371
|
+
if (stored) {
|
|
1372
|
+
const merged = { ...this.definition.initialState, ...stored };
|
|
1373
|
+
Object.assign(this.state, merged);
|
|
1374
|
+
}
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1313
1377
|
if (this.definition.dataSources?.length) {
|
|
1314
1378
|
const dsEngine = this.engine;
|
|
1315
1379
|
this.dataSourceManager = new DataSourceManager(dsEngine);
|
|
@@ -1348,6 +1412,11 @@ class HypenModuleInstance {
|
|
|
1348
1412
|
async destroy() {
|
|
1349
1413
|
if (this.isDestroyed)
|
|
1350
1414
|
return;
|
|
1415
|
+
if (this.currentPersistKey && this.stateStore) {
|
|
1416
|
+
clearTimeout(this.persistDebounceTimer);
|
|
1417
|
+
const snapshot = getStateSnapshot(this.state);
|
|
1418
|
+
await this.stateStore.save(this.currentPersistKey, snapshot);
|
|
1419
|
+
}
|
|
1351
1420
|
if (this.dataSourceManager) {
|
|
1352
1421
|
try {
|
|
1353
1422
|
await this.dataSourceManager.disconnectAll();
|
|
@@ -1386,13 +1455,13 @@ function createBindingProxy(root) {
|
|
|
1386
1455
|
const handler = {
|
|
1387
1456
|
get(_, prop) {
|
|
1388
1457
|
if (prop === Symbol.toPrimitive || prop === "toString" || prop === "valueOf") {
|
|
1389
|
-
return () =>
|
|
1458
|
+
return () => `@{${root}}`;
|
|
1390
1459
|
}
|
|
1391
1460
|
if (typeof prop === "symbol") {
|
|
1392
1461
|
return;
|
|
1393
1462
|
}
|
|
1394
1463
|
if (prop === "toJSON") {
|
|
1395
|
-
return () =>
|
|
1464
|
+
return () => `@{${root}}`;
|
|
1396
1465
|
}
|
|
1397
1466
|
return createBindingProxy(`${root}.${prop}`);
|
|
1398
1467
|
},
|
|
@@ -1414,10 +1483,10 @@ function createBindingProxy(root) {
|
|
|
1414
1483
|
var state = createBindingProxy("state");
|
|
1415
1484
|
var item = createBindingProxy("item");
|
|
1416
1485
|
var index = {
|
|
1417
|
-
[Symbol.toPrimitive]: () => "
|
|
1418
|
-
toString: () => "
|
|
1419
|
-
valueOf: () => "
|
|
1420
|
-
toJSON: () => "
|
|
1486
|
+
[Symbol.toPrimitive]: () => "@{index}",
|
|
1487
|
+
toString: () => "@{index}",
|
|
1488
|
+
valueOf: () => "@{index}",
|
|
1489
|
+
toJSON: () => "@{index}"
|
|
1421
1490
|
};
|
|
1422
1491
|
function hypen(strings, ...expressions) {
|
|
1423
1492
|
let result = strings[0];
|
|
@@ -1724,11 +1793,13 @@ class HypenRouter {
|
|
|
1724
1793
|
} else {
|
|
1725
1794
|
window.history.pushState(null, "", url);
|
|
1726
1795
|
}
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1796
|
+
try {
|
|
1797
|
+
const hashChangeEvent = new HashChangeEvent("hashchange", {
|
|
1798
|
+
oldURL: window.location.href.replace(window.location.hash, "#" + oldPath),
|
|
1799
|
+
newURL: window.location.href
|
|
1800
|
+
});
|
|
1801
|
+
window.dispatchEvent(hashChangeEvent);
|
|
1802
|
+
} catch {}
|
|
1732
1803
|
}
|
|
1733
1804
|
} finally {
|
|
1734
1805
|
this.isUpdating = false;
|
|
@@ -2200,7 +2271,8 @@ class RemoteEngine {
|
|
|
2200
2271
|
const hello = {
|
|
2201
2272
|
type: "hello",
|
|
2202
2273
|
sessionId: this.currentSessionId ?? this.sessionOptions?.id,
|
|
2203
|
-
props: this.sessionOptions?.props
|
|
2274
|
+
props: this.sessionOptions?.props,
|
|
2275
|
+
persistKey: this.sessionOptions?.persistKey
|
|
2204
2276
|
};
|
|
2205
2277
|
this.ws.send(JSON.stringify(hello));
|
|
2206
2278
|
}
|
|
@@ -2838,4 +2910,4 @@ export {
|
|
|
2838
2910
|
ActionError
|
|
2839
2911
|
};
|
|
2840
2912
|
|
|
2841
|
-
//# debugId=
|
|
2913
|
+
//# debugId=ABC8BA06546F591964756E2164756E21
|