@hypen-space/core 0.4.31 → 0.4.35
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/dist/app.d.ts +73 -2
- package/dist/app.js +112 -9
- package/dist/app.js.map +6 -5
- package/dist/components/builtin.js +112 -9
- package/dist/components/builtin.js.map +6 -5
- package/dist/context.d.ts +8 -0
- package/dist/context.js +6 -2
- package/dist/context.js.map +3 -3
- package/dist/datasource.d.ts +168 -0
- package/dist/discovery.js +114 -9
- package/dist/discovery.js.map +7 -6
- package/dist/disposable.js +6 -2
- package/dist/disposable.js.map +2 -2
- package/dist/engine.browser.d.ts +15 -0
- package/dist/engine.browser.js +19 -2
- package/dist/engine.browser.js.map +3 -3
- package/dist/engine.d.ts +22 -0
- package/dist/engine.js +37 -3
- package/dist/engine.js.map +3 -3
- package/dist/events.js +6 -2
- package/dist/events.js.map +2 -2
- package/dist/index.browser.js +295 -179
- package/dist/index.browser.js.map +9 -8
- package/dist/index.d.ts +3 -1
- package/dist/index.js +298 -179
- package/dist/index.js.map +10 -9
- package/dist/loader.js +6 -2
- package/dist/loader.js.map +2 -2
- package/dist/logger.js +6 -2
- package/dist/logger.js.map +2 -2
- package/dist/plugin.js +6 -2
- package/dist/plugin.js.map +2 -2
- package/dist/remote/client.js +6 -2
- package/dist/remote/client.js.map +2 -2
- package/dist/remote/index.js +152 -11
- package/dist/remote/index.js.map +9 -8
- package/dist/remote/server.js +152 -11
- package/dist/remote/server.js.map +9 -8
- package/dist/renderer.js +6 -2
- package/dist/renderer.js.map +2 -2
- package/dist/resolver.js +6 -2
- package/dist/resolver.js.map +2 -2
- package/dist/router.d.ts +9 -0
- package/dist/router.js +186 -18
- package/dist/router.js.map +6 -5
- package/dist/state.js +13 -6
- package/dist/state.js.map +3 -3
- package/package.json +1 -1
- package/src/app.ts +121 -4
- package/src/context.ts +10 -2
- package/src/datasource.ts +252 -0
- package/src/discovery.ts +2 -0
- package/src/engine.browser.ts +30 -0
- package/src/engine.ts +56 -1
- package/src/index.ts +16 -0
- package/src/managed-router.ts +3 -0
- package/src/remote/server.ts +11 -2
- package/src/router.ts +36 -10
- package/src/state.ts +10 -3
- package/wasm-browser/README.md +10 -10
- package/wasm-browser/hypen_engine.d.ts +368 -131
- package/wasm-browser/hypen_engine.js +896 -645
- package/wasm-browser/hypen_engine_bg.wasm +0 -0
- package/wasm-browser/hypen_engine_bg.wasm.d.ts +20 -13
- package/wasm-browser/package.json +1 -1
- package/wasm-node/README.md +10 -10
- package/wasm-node/hypen_engine.d.ts +318 -89
- package/wasm-node/hypen_engine.js +832 -613
- package/wasm-node/hypen_engine_bg.wasm +0 -0
- package/wasm-node/hypen_engine_bg.wasm.d.ts +20 -13
- package/wasm-node/package.json +1 -1
package/dist/app.d.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import type { Action } from "./types.js";
|
|
6
6
|
import type { Session } from "./remote/types.js";
|
|
7
7
|
import { HypenError } from "./result.js";
|
|
8
|
+
import { type DataSourcePlugin } from "./datasource.js";
|
|
8
9
|
export interface IEngine {
|
|
9
10
|
setModule(name: string, actions: string[], stateKeys: string[], initialState: unknown): void;
|
|
10
11
|
onAction(actionName: string, handler: (action: Action) => void | Promise<void>): void;
|
|
@@ -34,6 +35,19 @@ export interface ActionHandlerContext<T, P = unknown> {
|
|
|
34
35
|
action: ActionContext<P>;
|
|
35
36
|
state: T;
|
|
36
37
|
context: GlobalContext;
|
|
38
|
+
/** Access to registered data source plugins for mutations */
|
|
39
|
+
dataSources: DataSourceAccessor;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Provides typed access to registered data source plugins.
|
|
43
|
+
* Unknown method calls are forwarded to `plugin.call()`:
|
|
44
|
+
* `dataSources.spacetime.sendMessage(text)` → `plugin.call("sendMessage", text)`
|
|
45
|
+
*
|
|
46
|
+
* You can also call `.call()` directly if you prefer:
|
|
47
|
+
* `dataSources.spacetime.call("sendMessage", text)`
|
|
48
|
+
*/
|
|
49
|
+
export interface DataSourceAccessor {
|
|
50
|
+
[providerName: string]: DataSourcePlugin & Record<string, (...args: unknown[]) => Promise<unknown>>;
|
|
37
51
|
}
|
|
38
52
|
/**
|
|
39
53
|
* Action handler - receives all context in a single object
|
|
@@ -115,6 +129,14 @@ export interface HypenModuleDefinition<T = unknown> {
|
|
|
115
129
|
* Set via the `.ui(hypen`...`)` method.
|
|
116
130
|
*/
|
|
117
131
|
template?: string;
|
|
132
|
+
/**
|
|
133
|
+
* Data source plugins registered via `.useDataSource()`.
|
|
134
|
+
* Each entry contains the plugin and its configuration.
|
|
135
|
+
*/
|
|
136
|
+
dataSources?: Array<{
|
|
137
|
+
plugin: DataSourcePlugin;
|
|
138
|
+
config: unknown;
|
|
139
|
+
}>;
|
|
118
140
|
handlers: {
|
|
119
141
|
onCreated?: LifecycleHandler<T>;
|
|
120
142
|
onAction: Map<string, ActionHandler<T, any>>;
|
|
@@ -148,6 +170,7 @@ export declare class HypenAppBuilder<T> {
|
|
|
148
170
|
private errorHandler?;
|
|
149
171
|
private template?;
|
|
150
172
|
private _registry?;
|
|
173
|
+
private dataSourceEntries;
|
|
151
174
|
constructor(initialState: T, options?: {
|
|
152
175
|
persist?: boolean;
|
|
153
176
|
version?: number;
|
|
@@ -225,6 +248,35 @@ export declare class HypenAppBuilder<T> {
|
|
|
225
248
|
* @returns Builder for chaining
|
|
226
249
|
*/
|
|
227
250
|
onError(fn: ErrorHandler<T>): this;
|
|
251
|
+
/**
|
|
252
|
+
* Register a data source plugin for live database subscriptions.
|
|
253
|
+
*
|
|
254
|
+
* @example
|
|
255
|
+
* ```typescript
|
|
256
|
+
* import { SpacetimeDBPlugin } from "@hypen-space/plugin-spacetimedb";
|
|
257
|
+
*
|
|
258
|
+
* export default app
|
|
259
|
+
* .defineState({ messageText: "" })
|
|
260
|
+
* .useDataSource(new SpacetimeDBPlugin(), {
|
|
261
|
+
* uri: "ws://localhost:3000",
|
|
262
|
+
* moduleName: "chat",
|
|
263
|
+
* tables: ["user", "message"],
|
|
264
|
+
* })
|
|
265
|
+
* .onAction("sendMessage", async ({ state, dataSources }) => {
|
|
266
|
+
* await dataSources.spacetime.sendMessage(state.messageText);
|
|
267
|
+
* state.messageText = "";
|
|
268
|
+
* })
|
|
269
|
+
* .build();
|
|
270
|
+
* ```
|
|
271
|
+
*
|
|
272
|
+
* In Hypen DSL, bind to data source tables with `$provider.table`:
|
|
273
|
+
* ```hypen
|
|
274
|
+
* ForEach(items: $spacetime.message, key: "id") {
|
|
275
|
+
* Text("${item.text}")
|
|
276
|
+
* }
|
|
277
|
+
* ```
|
|
278
|
+
*/
|
|
279
|
+
useDataSource<C>(plugin: DataSourcePlugin<C>, config: C): this;
|
|
228
280
|
/**
|
|
229
281
|
* Define the component's UI template inline (single-file component).
|
|
230
282
|
*
|
|
@@ -252,6 +304,23 @@ export declare class HypenAppBuilder<T> {
|
|
|
252
304
|
* @returns The built module definition (calls build() internally)
|
|
253
305
|
*/
|
|
254
306
|
ui(template: string): HypenModuleDefinition<T>;
|
|
307
|
+
/**
|
|
308
|
+
* Load a UI template from a .hypen file on disk.
|
|
309
|
+
*
|
|
310
|
+
* @example
|
|
311
|
+
* ```typescript
|
|
312
|
+
* import { app } from "@hypen-space/core";
|
|
313
|
+
*
|
|
314
|
+
* export default app
|
|
315
|
+
* .defineState({ count: 0 })
|
|
316
|
+
* .onAction("increment", ({ state }) => { state.count += 1; })
|
|
317
|
+
* .uiFile("./counter.hypen");
|
|
318
|
+
* ```
|
|
319
|
+
*
|
|
320
|
+
* @param path - Path to a .hypen template file
|
|
321
|
+
* @returns The built module definition (calls build() internally)
|
|
322
|
+
*/
|
|
323
|
+
uiFile(path: string): HypenModuleDefinition<T>;
|
|
255
324
|
/**
|
|
256
325
|
* Build the module definition
|
|
257
326
|
*/
|
|
@@ -336,6 +405,8 @@ export declare class HypenModuleInstance<T extends object = any> {
|
|
|
336
405
|
private router;
|
|
337
406
|
private globalContext?;
|
|
338
407
|
private stateChangeCallbacks;
|
|
408
|
+
private dataSourceManager?;
|
|
409
|
+
private dataSourceAccessor;
|
|
339
410
|
constructor(engine: IEngine, definition: HypenModuleDefinition<T>, router?: HypenRouter | null, globalContext?: HypenGlobalContext);
|
|
340
411
|
/**
|
|
341
412
|
* Create the global context API for this module
|
|
@@ -353,7 +424,7 @@ export declare class HypenModuleInstance<T extends object = any> {
|
|
|
353
424
|
*/
|
|
354
425
|
private handleError;
|
|
355
426
|
/**
|
|
356
|
-
* Call the onCreated handler
|
|
427
|
+
* Call the onCreated handler and connect data source plugins
|
|
357
428
|
*/
|
|
358
429
|
private callCreatedHandler;
|
|
359
430
|
/**
|
|
@@ -361,7 +432,7 @@ export declare class HypenModuleInstance<T extends object = any> {
|
|
|
361
432
|
*/
|
|
362
433
|
onStateChange(callback: () => void): void;
|
|
363
434
|
/**
|
|
364
|
-
* Destroy the module instance
|
|
435
|
+
* Destroy the module instance and disconnect all data source plugins
|
|
365
436
|
*/
|
|
366
437
|
destroy(): Promise<void>;
|
|
367
438
|
/**
|
package/dist/app.js
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
var __defProp = Object.defineProperty;
|
|
2
|
+
var __returnValue = (v) => v;
|
|
3
|
+
function __exportSetter(name, newValue) {
|
|
4
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
5
|
+
}
|
|
2
6
|
var __export = (target, all) => {
|
|
3
7
|
for (var name in all)
|
|
4
8
|
__defProp(target, name, {
|
|
5
9
|
get: all[name],
|
|
6
10
|
enumerable: true,
|
|
7
11
|
configurable: true,
|
|
8
|
-
set: (
|
|
12
|
+
set: __exportSetter.bind(all, name)
|
|
9
13
|
});
|
|
10
14
|
};
|
|
11
15
|
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
@@ -156,9 +160,11 @@ function createObservableState(initialState, options) {
|
|
|
156
160
|
notificationPending = false;
|
|
157
161
|
if (batchDepth === 0) {
|
|
158
162
|
notifyChange();
|
|
159
|
-
}
|
|
163
|
+
} else {}
|
|
160
164
|
});
|
|
161
165
|
}
|
|
166
|
+
} else {
|
|
167
|
+
pendingChange = pendingChange || { paths: [], newValues: {} };
|
|
162
168
|
}
|
|
163
169
|
}
|
|
164
170
|
const proxyCache = new WeakMap;
|
|
@@ -217,11 +223,12 @@ function createObservableState(initialState, options) {
|
|
|
217
223
|
return true;
|
|
218
224
|
},
|
|
219
225
|
deleteProperty(obj, prop) {
|
|
220
|
-
|
|
221
|
-
|
|
226
|
+
const existed = Object.prototype.hasOwnProperty.call(obj, prop);
|
|
227
|
+
const result = delete obj[prop];
|
|
228
|
+
if (existed) {
|
|
222
229
|
scheduleBatch();
|
|
223
230
|
}
|
|
224
|
-
return
|
|
231
|
+
return result;
|
|
225
232
|
}
|
|
226
233
|
});
|
|
227
234
|
proxyCache.set(target, proxy);
|
|
@@ -555,6 +562,59 @@ var init_result = __esm(() => {
|
|
|
555
562
|
};
|
|
556
563
|
});
|
|
557
564
|
|
|
565
|
+
// src/datasource.ts
|
|
566
|
+
class DataSourceManager {
|
|
567
|
+
plugins = new Map;
|
|
568
|
+
configs = new Map;
|
|
569
|
+
state = new Map;
|
|
570
|
+
engine;
|
|
571
|
+
constructor(engine) {
|
|
572
|
+
this.engine = engine;
|
|
573
|
+
}
|
|
574
|
+
async use(plugin, config2) {
|
|
575
|
+
const { name } = plugin;
|
|
576
|
+
this.state.set(name, {});
|
|
577
|
+
this.plugins.set(name, plugin);
|
|
578
|
+
this.configs.set(name, config2);
|
|
579
|
+
await plugin.connect(config2, (change) => {
|
|
580
|
+
const current = this.state.get(name) ?? {};
|
|
581
|
+
for (const path of change.paths) {
|
|
582
|
+
if (path in change.values) {
|
|
583
|
+
current[path] = change.values[path];
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
this.state.set(name, current);
|
|
587
|
+
this.engine.setContext(name, current);
|
|
588
|
+
});
|
|
589
|
+
}
|
|
590
|
+
get(name) {
|
|
591
|
+
return this.plugins.get(name);
|
|
592
|
+
}
|
|
593
|
+
has(name) {
|
|
594
|
+
return this.plugins.has(name);
|
|
595
|
+
}
|
|
596
|
+
getNames() {
|
|
597
|
+
return Array.from(this.plugins.keys());
|
|
598
|
+
}
|
|
599
|
+
getStatus(name) {
|
|
600
|
+
return this.plugins.get(name)?.status;
|
|
601
|
+
}
|
|
602
|
+
async remove(name) {
|
|
603
|
+
const plugin = this.plugins.get(name);
|
|
604
|
+
if (plugin) {
|
|
605
|
+
await plugin.disconnect();
|
|
606
|
+
this.plugins.delete(name);
|
|
607
|
+
this.configs.delete(name);
|
|
608
|
+
this.state.delete(name);
|
|
609
|
+
this.engine.removeContext(name);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
async disconnectAll() {
|
|
613
|
+
const names = Array.from(this.plugins.keys());
|
|
614
|
+
await Promise.all(names.map((name) => this.remove(name)));
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
|
|
558
618
|
// src/app.ts
|
|
559
619
|
var exports_app = {};
|
|
560
620
|
__export(exports_app, {
|
|
@@ -576,6 +636,7 @@ class HypenAppBuilder {
|
|
|
576
636
|
errorHandler;
|
|
577
637
|
template;
|
|
578
638
|
_registry;
|
|
639
|
+
dataSourceEntries = [];
|
|
579
640
|
constructor(initialState, options, registry) {
|
|
580
641
|
this.initialState = initialState;
|
|
581
642
|
this.options = options || {};
|
|
@@ -609,10 +670,19 @@ class HypenAppBuilder {
|
|
|
609
670
|
this.errorHandler = fn;
|
|
610
671
|
return this;
|
|
611
672
|
}
|
|
673
|
+
useDataSource(plugin, config2) {
|
|
674
|
+
this.dataSourceEntries.push({ plugin, config: config2 });
|
|
675
|
+
return this;
|
|
676
|
+
}
|
|
612
677
|
ui(template) {
|
|
613
678
|
this.template = template;
|
|
614
679
|
return this.build();
|
|
615
680
|
}
|
|
681
|
+
uiFile(path) {
|
|
682
|
+
const fs = (() => ({}));
|
|
683
|
+
this.template = fs.readFileSync(path, "utf-8").trim();
|
|
684
|
+
return this.build();
|
|
685
|
+
}
|
|
616
686
|
build() {
|
|
617
687
|
const stateKeys = this.initialState !== null && typeof this.initialState === "object" ? Object.keys(this.initialState) : [];
|
|
618
688
|
const definition = {
|
|
@@ -623,6 +693,7 @@ class HypenAppBuilder {
|
|
|
623
693
|
version: this.options.version,
|
|
624
694
|
initialState: this.initialState,
|
|
625
695
|
template: this.template,
|
|
696
|
+
dataSources: this.dataSourceEntries.length > 0 ? this.dataSourceEntries : undefined,
|
|
626
697
|
handlers: {
|
|
627
698
|
onCreated: this.createdHandler,
|
|
628
699
|
onAction: this.actionHandlers,
|
|
@@ -684,6 +755,8 @@ class HypenModuleInstance {
|
|
|
684
755
|
router;
|
|
685
756
|
globalContext;
|
|
686
757
|
stateChangeCallbacks = [];
|
|
758
|
+
dataSourceManager;
|
|
759
|
+
dataSourceAccessor = {};
|
|
687
760
|
constructor(engine, definition, router, globalContext) {
|
|
688
761
|
this.engine = engine;
|
|
689
762
|
this.definition = definition;
|
|
@@ -713,7 +786,8 @@ class HypenModuleInstance {
|
|
|
713
786
|
const result = await this.executeAction(actionName, handler, {
|
|
714
787
|
action: actionCtx,
|
|
715
788
|
state: this.state,
|
|
716
|
-
context
|
|
789
|
+
context,
|
|
790
|
+
dataSources: this.dataSourceAccessor
|
|
717
791
|
});
|
|
718
792
|
if (!result.ok) {
|
|
719
793
|
const shouldRethrow = await this.handleError(result.error, { actionName });
|
|
@@ -756,8 +830,9 @@ class HypenModuleInstance {
|
|
|
756
830
|
on: (event, handler) => ctx.on(event, handler),
|
|
757
831
|
router: this.router
|
|
758
832
|
};
|
|
759
|
-
|
|
760
|
-
|
|
833
|
+
const ctxRecord = ctx;
|
|
834
|
+
if (ctxRecord.__hypenEngine) {
|
|
835
|
+
api.__hypenEngine = ctxRecord.__hypenEngine;
|
|
761
836
|
}
|
|
762
837
|
return api;
|
|
763
838
|
}
|
|
@@ -804,6 +879,25 @@ class HypenModuleInstance {
|
|
|
804
879
|
return false;
|
|
805
880
|
}
|
|
806
881
|
async callCreatedHandler() {
|
|
882
|
+
if (this.definition.dataSources?.length) {
|
|
883
|
+
const dsEngine = this.engine;
|
|
884
|
+
this.dataSourceManager = new DataSourceManager(dsEngine);
|
|
885
|
+
for (const { plugin, config: config2 } of this.definition.dataSources) {
|
|
886
|
+
try {
|
|
887
|
+
await this.dataSourceManager.use(plugin, config2);
|
|
888
|
+
this.dataSourceAccessor[plugin.name] = new Proxy(plugin, {
|
|
889
|
+
get(target, prop) {
|
|
890
|
+
if (typeof prop === "string" && !(prop in target)) {
|
|
891
|
+
return (...args) => target.call(prop, ...args);
|
|
892
|
+
}
|
|
893
|
+
return target[prop];
|
|
894
|
+
}
|
|
895
|
+
});
|
|
896
|
+
} catch (e) {
|
|
897
|
+
log2.error(`Failed to connect data source "${plugin.name}":`, e);
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
}
|
|
807
901
|
if (this.definition.handlers.onCreated) {
|
|
808
902
|
const context = this.globalContext ? this.createGlobalContextAPI() : undefined;
|
|
809
903
|
try {
|
|
@@ -823,6 +917,15 @@ class HypenModuleInstance {
|
|
|
823
917
|
async destroy() {
|
|
824
918
|
if (this.isDestroyed)
|
|
825
919
|
return;
|
|
920
|
+
if (this.dataSourceManager) {
|
|
921
|
+
try {
|
|
922
|
+
await this.dataSourceManager.disconnectAll();
|
|
923
|
+
} catch (e) {
|
|
924
|
+
log2.error("Error disconnecting data sources:", e);
|
|
925
|
+
}
|
|
926
|
+
this.dataSourceManager = undefined;
|
|
927
|
+
this.dataSourceAccessor = {};
|
|
928
|
+
}
|
|
826
929
|
if (this.definition.handlers.onDestroyed) {
|
|
827
930
|
try {
|
|
828
931
|
await this.definition.handlers.onDestroyed(this.state);
|
|
@@ -863,4 +966,4 @@ export {
|
|
|
863
966
|
HypenApp
|
|
864
967
|
};
|
|
865
968
|
|
|
866
|
-
//# debugId=
|
|
969
|
+
//# debugId=DF0AC5B74AEC489464756E2164756E21
|