@delpi/react-struct-z 1.0.0-alz
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/LICENSE +21 -0
- package/README.md +216 -0
- package/build/app/AppRuntime.d.ts +20 -0
- package/build/app/createApp.d.ts +2 -0
- package/build/core/types.d.ts +1 -0
- package/build/di/container.d.ts +5 -0
- package/build/di/injectable.d.ts +1 -0
- package/build/effect/EffectManager.d.ts +3 -0
- package/build/effect/types.d.ts +1 -0
- package/build/index.cjs.js +1 -0
- package/build/index.d.ts +21 -0
- package/build/index.esm.js +1 -0
- package/build/index.umd.min.js +1 -0
- package/build/intent/IntentBus.d.ts +11 -0
- package/build/intent/middleware.d.ts +6 -0
- package/build/intent/types.d.ts +4 -0
- package/build/module/ModuleLoader.d.ts +2 -0
- package/build/module/createModule.d.ts +4 -0
- package/build/module/decorator.d.ts +5 -0
- package/build/module/types.d.ts +9 -0
- package/build/plugin/Plugin.d.ts +5 -0
- package/build/plugin/PluginContext.d.ts +12 -0
- package/build/plugin/PluginManager.d.ts +6 -0
- package/build/plugin/PluginManifest.d.ts +17 -0
- package/build/plugin/RuntimePlugin.d.ts +7 -0
- package/build/plugin/createPluginContext.d.ts +3 -0
- package/build/plugin/observability.d.ts +5 -0
- package/build/plugin/remote/remotePluginLoader.d.ts +6 -0
- package/build/react/RuntimeProvider.d.ts +7 -0
- package/build/react/hooks.d.ts +6 -0
- package/build/react/useIntent.d.ts +4 -0
- package/build/store/Store.d.ts +20 -0
- package/build/store/useStore.d.ts +2 -0
- package/package.json +85 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Delpi.Kye
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
## ποΈ @delpi/react-struct-z
|
|
2
|
+
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@delpi/react-struct-z)
|
|
6
|
+

|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
[Live Demo](https://codesandbox.io/p/sandbox/c8l2pf)
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## π Description
|
|
14
|
+
|
|
15
|
+
- @delpi/react-struct-z is a frontend runtime framework combining:
|
|
16
|
+
- React-style composable UI - module β controller β service β DI β lifecycle
|
|
17
|
+
- It provides a runtime orchestration layer for intents, effects, reactive stores, DI, and plugins, keeping business logic separate from UI.
|
|
18
|
+
|
|
19
|
+
> Think of it as: βFrontend behavior runtime, modular & testable.β
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## β¨ When to Use
|
|
24
|
+
|
|
25
|
+
+ Business logic should not live inside UI components
|
|
26
|
+
+ UI only emits intents, does not orchestrate logic
|
|
27
|
+
+ Complex async flows or multiple side effects
|
|
28
|
+
+ Headless, testable without rendering React
|
|
29
|
+
+ Multi-module, multi-engine architecture
|
|
30
|
+
+ Following DDD, hexagonal, or layered frontend architecture
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## π¦ Installation
|
|
35
|
+
```ts
|
|
36
|
+
npm install @delpi/react-struct-z
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## π§© Mental Model
|
|
42
|
+
```bash
|
|
43
|
+
UI Layer (React)
|
|
44
|
+
ββ emits intents
|
|
45
|
+
β
|
|
46
|
+
AppRuntime (FE Struct Framework)
|
|
47
|
+
ββ DI Container
|
|
48
|
+
ββ IntentBus + Middleware
|
|
49
|
+
ββ EffectManager
|
|
50
|
+
ββ Store (Reactive Runtime)
|
|
51
|
+
ββ Module System
|
|
52
|
+
ββ Plugin System
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## π Basic Usage (Headless)
|
|
58
|
+
|
|
59
|
+
#### 1οΈβ£ Basic Headless Example
|
|
60
|
+
```ts
|
|
61
|
+
// counter.store.ts
|
|
62
|
+
import { Store } from "@delpi/react-struct-z"
|
|
63
|
+
export const counterStore = new Store({ value: 0 })
|
|
64
|
+
|
|
65
|
+
// counter.module.ts
|
|
66
|
+
export const CounterModule = {
|
|
67
|
+
setup(app: any) {
|
|
68
|
+
app.intent.on("counter/inc", () => {
|
|
69
|
+
counterStore.set({ value: counterStore.get().value + 1 })
|
|
70
|
+
})
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// app.ts
|
|
75
|
+
import { AppRuntime, loadRemotePlugin } from "@delpi/react-struct-z"
|
|
76
|
+
import { CounterModule } from "./counter.module"
|
|
77
|
+
export const app = new AppRuntime()
|
|
78
|
+
CounterModule.setup(app)
|
|
79
|
+
|
|
80
|
+
// Async + Remote Plugin (if needed)
|
|
81
|
+
// await loadRemotePlugin({
|
|
82
|
+
// url: "https://cdn.myapp.com/remote-logger.js"
|
|
83
|
+
// }).then(plugin => app.use(plugin))
|
|
84
|
+
|
|
85
|
+
// Dispatch
|
|
86
|
+
app.intent.dispatch({ type: "counter/inc" })
|
|
87
|
+
console.log(counterStore.get().value) // 1
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
> β
Quick start, headless, testable, no DI.
|
|
91
|
+
|
|
92
|
+
----
|
|
93
|
+
|
|
94
|
+
#### 2οΈβ£ React Integration Example
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
// counter.store.ts
|
|
98
|
+
import { Store } from "@delpi/react-struct-z"
|
|
99
|
+
export const counterStore = new Store({ value: 0 })
|
|
100
|
+
|
|
101
|
+
// counter.service.ts
|
|
102
|
+
import { counterStore } from "./counter.store"
|
|
103
|
+
// Injectable is currently no-op
|
|
104
|
+
export class CounterService {
|
|
105
|
+
constructor(private store = counterStore) {} // manual DI
|
|
106
|
+
increment() {
|
|
107
|
+
this.store.set({ value: this.store.get().value + 1 })
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// counter.module.ts
|
|
112
|
+
import { CounterService } from "./counter.service"
|
|
113
|
+
import { counterStore } from "./counter.store"
|
|
114
|
+
|
|
115
|
+
export const CounterModule = {
|
|
116
|
+
name: "counter",
|
|
117
|
+
setup(app: any) {
|
|
118
|
+
// Resolve service manually from container
|
|
119
|
+
const service = app.container.resolve(CounterService)
|
|
120
|
+
|
|
121
|
+
// Namespaced intent
|
|
122
|
+
app.intent.on("counter/inc", () => service.increment())
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// app.ts
|
|
127
|
+
import { AppRuntime } from "@delpi/react-struct-z"
|
|
128
|
+
import { CounterModule } from "./counter.module"
|
|
129
|
+
|
|
130
|
+
// Create runtime
|
|
131
|
+
export const app = new AppRuntime()
|
|
132
|
+
|
|
133
|
+
// Optionally register service in container
|
|
134
|
+
app.container.provide(CounterService, () => new CounterService())
|
|
135
|
+
|
|
136
|
+
// Setup module
|
|
137
|
+
CounterModule.setup(app)
|
|
138
|
+
|
|
139
|
+
// Headless dispatch
|
|
140
|
+
app.intent.dispatch({ type: "counter/inc" })
|
|
141
|
+
console.log("Counter value:", app.container.resolve(CounterService)["store"].get().value)
|
|
142
|
+
|
|
143
|
+
// CounterView.tsx
|
|
144
|
+
import React from "react"
|
|
145
|
+
import { useStore, useIntent } from "@delpi/react-struct-z"
|
|
146
|
+
import { counterStore } from "./counter.store"
|
|
147
|
+
|
|
148
|
+
export function CounterView() {
|
|
149
|
+
const state = useStore(counterStore)
|
|
150
|
+
const dispatch = useIntent("counter") // namespaced
|
|
151
|
+
|
|
152
|
+
return (
|
|
153
|
+
<button onClick={() => dispatch({ type: "counter/inc" })}>
|
|
154
|
+
Count: {state.value}
|
|
155
|
+
</button>
|
|
156
|
+
)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// main.tsx
|
|
160
|
+
import React from "react"
|
|
161
|
+
import { createRoot } from "react-dom/client"
|
|
162
|
+
import { RuntimeProvider } from "@delpi/react-struct-z"
|
|
163
|
+
import { app } from "./app"
|
|
164
|
+
import { CounterView } from "./CounterView"
|
|
165
|
+
|
|
166
|
+
createRoot(document.getElementById("root")!).render(
|
|
167
|
+
<RuntimeProvider runtime={app}>
|
|
168
|
+
<CounterView />
|
|
169
|
+
</RuntimeProvider>
|
|
170
|
+
)
|
|
171
|
+
```
|
|
172
|
+
> β
Full React integration, composable UI, runtime orchestration + store + intent.
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## π§ͺ Testing Example
|
|
177
|
+
```ts
|
|
178
|
+
import { AppRuntime, Store } from "@delpi/react-struct-z"
|
|
179
|
+
|
|
180
|
+
test("counter increments", () => {
|
|
181
|
+
const app = new AppRuntime()
|
|
182
|
+
const store = new Store({ value: 0 })
|
|
183
|
+
|
|
184
|
+
app.intent.on("inc", () => store.set({ value: store.get().value + 1 }))
|
|
185
|
+
|
|
186
|
+
app.intent.dispatch({ type: "inc" })
|
|
187
|
+
app.intent.dispatch({ type: "inc" })
|
|
188
|
+
|
|
189
|
+
expect(store.get().value).toBe(2)
|
|
190
|
+
})
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## βοΈ Key Features
|
|
196
|
+
| Feature / Capability | FE Struct Framework | Next.js | Remix | Gatsby |
|
|
197
|
+
| ---------------------------- | -------------------------- | ---------------------------------- | ------------------------ | ------------------------ |
|
|
198
|
+
| React-oriented | β
Full React | β
Full React | β
Full React | β
Full React |
|
|
199
|
+
| Intent / Action-based | β
Scoped intents | β | β | β |
|
|
200
|
+
| Async flows built-in | β
Built-in + effects | β οΈ Needs fetcher / hooks | β οΈ Needs fetcher / hooks | β οΈ Needs fetcher / hooks |
|
|
201
|
+
| Side effects management | β
First-class | β οΈ Via React hooks | β οΈ Via React hooks | β οΈ Via React hooks |
|
|
202
|
+
| Reactive Store | β
Store + computed | β Only React state | β Only React state | β Only React state |
|
|
203
|
+
| Module System / DI | β
Modules + lifecycle + DI | β Not supported | β Not supported | β Not supported |
|
|
204
|
+
| Computed / Derived state | β
Sync + Async | β Only via useMemo / custom hooks | β | β |
|
|
205
|
+
| Scoped / Namespaced Intents | β
Yes | β | β | β |
|
|
206
|
+
| Headless / Testable | β
Fully headless | β οΈ Needs mock SSR | β οΈ Needs mock SSR | β οΈ Needs mock SSR |
|
|
207
|
+
| Routing / Navigation | β Not supported | β
Built-in | β
Built-in | β
Built-in |
|
|
208
|
+
| SSR / SSG | β Not supported | β
Built-in | β
Built-in | β
Built-in |
|
|
209
|
+
| Rendering Layer | β Not supported | β
Full rendering | β
Full rendering | β
Full rendering |
|
|
210
|
+
| Build / Bundling | β Not included | β
Built-in | β
Built-in | β
Built-in |
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
## π License
|
|
215
|
+
|
|
216
|
+
MIT
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Container } from "@/di/container";
|
|
2
|
+
import { IntentBus } from "@/intent/IntentBus";
|
|
3
|
+
import { EffectManager } from "@/effect/EffectManager";
|
|
4
|
+
import { RuntimePlugin } from "@/plugin/Plugin";
|
|
5
|
+
import { Module } from "@/module/types";
|
|
6
|
+
export declare class AppRuntime {
|
|
7
|
+
container: Container;
|
|
8
|
+
intent: IntentBus;
|
|
9
|
+
effect: EffectManager;
|
|
10
|
+
private modules;
|
|
11
|
+
use(plugin: RuntimePlugin): void;
|
|
12
|
+
$on: (type: string, handler: Function) => void;
|
|
13
|
+
$emit: (intent: import("..").Intent) => Promise<void>;
|
|
14
|
+
$onIntent: (scope: string, type: string, handler: Function) => void;
|
|
15
|
+
$emitIntent: (scope: string, type: string, payload?: any) => Promise<void>;
|
|
16
|
+
useModule(module: Module): void;
|
|
17
|
+
initModules(): Promise<void>;
|
|
18
|
+
destroyModules(): void;
|
|
19
|
+
getModule<T extends Module = Module>(name: string): T | undefined;
|
|
20
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type Constructor<T = any> = new (...args: any[]) => T;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function Injectable(): () => void;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type Effect<T = any> = () => Promise<T>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";require("reflect-metadata");var t=require("react"),e=require("react/jsx-runtime");class s{constructor(){this.instances=new Map}resolve(t){if(!this.instances.has(t)){const e=(Reflect.getMetadata("design:paramtypes",t)||[]).map(t=>this.resolve(t));this.instances.set(t,new t(...e))}return this.instances.get(t)}}class n{constructor(){this.handlers=new Map,this.middlewares=[]}use(t){this.middlewares.push(t)}on(t,e){const s=this.handlers.get(t)||[];s.push(e),this.handlers.set(t,s)}async dispatch(t){const e={intent:t,metadata:{}};let s=-1;const n=async i=>{var o;if(i<=s)throw new Error("next() called multiple times");s=i;const r=this.middlewares[i];r?await r(e,()=>n(i+1)):null===(o=this.handlers.get(t.type))||void 0===o||o.forEach(e=>e(t))};await n(0)}onScoped(t,e,s){return this.on(`${t}/${e}`,s)}dispatchScoped(t,e,s){return this.dispatch({type:`${t}/${e}`,payload:s})}}class i{async run(t){return t()}}class o{constructor(){this.container=new s,this.intent=new n,this.effect=new i,this.modules=new Map,this.$on=this.intent.on.bind(this.intent),this.$emit=this.intent.dispatch.bind(this.intent),this.$onIntent=this.intent.onScoped.bind(this.intent),this.$emitIntent=this.intent.dispatchScoped.bind(this.intent)}use(t){t.setup(this)}useModule(t){this.modules.set(t.name,t)}async initModules(){this.modules.forEach(t=>{var e,s;null===(e=t.dependencies)||void 0===e||e.forEach(e=>{var s;const n=this.modules.get(e);null===(s=null==n?void 0:n.onModuleLoaded)||void 0===s||s.call(n,t)}),t.setup(this),null===(s=t.onInit)||void 0===s||s.call(t,this)})}destroyModules(){this.modules.forEach(t=>{var e;return null===(e=t.onDestroy)||void 0===e?void 0:e.call(t)})}getModule(t){return this.modules.get(t)}}const r=t.createContext(null),a=()=>{const e=t.useContext(r);if(!e)throw new Error("No Runtime");return e};exports.AppRuntime=o,exports.Module=function(t){return function(e){e.__moduleOptions=t}},exports.ObservabilityPlugin=class{setup(t){t.intent.use(async(t,e)=>{const s=performance.now();try{await e(),console.log("[Intent]",t.intent.type,"OK",performance.now()-s)}catch(e){throw console.error("[Intent]",t.intent.type,"ERR",e),e}})}},exports.RuntimeProvider=({runtime:t,children:s})=>e.jsx(r.Provider,{value:t,children:s}),exports.Store=class{constructor(t){this.state=t,this.listeners=new Set,this.computedMap=new Map,this.asyncComputedMap=new Map,this.asyncComputedCache=new Map}get(){return this.state}set(t){this.state=Object.assign(Object.assign({},this.state),t),this.updateComputed(),this.listeners.forEach(t=>t(this.state))}subscribe(t){return this.listeners.add(t),()=>this.listeners.delete(t)}computed(t,e){this.computedMap.set(t,e)}computedAsync(t,e){this.asyncComputedMap.set(t,e),this.updateAsyncComputed(t)}getComputed(t){var e,s;return null!==(s=null===(e=this.computedMap.get(t))||void 0===e?void 0:e(this.state))&&void 0!==s?s:this.asyncComputedCache.get(t)}updateComputed(){this.computedMap.forEach((t,e)=>t(this.state))}async updateAsyncComputed(t){const e=this.asyncComputedMap.get(t);if(e){const s=await e(this.state);this.asyncComputedCache.set(t,s),this.listeners.forEach(t=>t(this.state))}}},exports.createApp=function(){return new o},exports.createModule=function(t){return t},exports.createPluginContext=function(t){return{intent:{tap(e){t.intent.use(async(t,s)=>{e(t.intent),await s()})}},effect:{track(t){}},store:{observe(t){}}}},exports.loadRemotePlugin=async function(t){const e=await import(t.url),s=t.exportName?e[t.exportName]:e.default;if(!s)throw new Error(`Remote plugin not found: ${t.exportName||"default"}`);const n="function"==typeof s?new s:s;if(!n.manifest||!n.setup)throw new Error("Invalid RuntimePlugin");return n},exports.useComputed=function(e,s){return t.useSyncExternalStore(e.subscribe.bind(e),()=>e.getComputed(s))},exports.useEffectIntent=function(t,e){const s=a();return()=>s.intent.dispatch({type:t,payload:e})},exports.useIntent=function(t){const e=a();return s=>{const n=t?`${t}/${s.type}`:s.type;e.intent.dispatch({type:n,payload:s.payload})}},exports.useModule=function(t){return a().container.resolve(t)},exports.useRuntime=a,exports.useStore=function(e){return t.useSyncExternalStore(e.subscribe.bind(e),()=>e.get())};
|
package/build/index.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import "reflect-metadata";
|
|
2
|
+
export { createApp } from "@/app/createApp";
|
|
3
|
+
export { AppRuntime } from "@/app/AppRuntime";
|
|
4
|
+
export { createModule } from "@/module/createModule";
|
|
5
|
+
export type { Module as ModuleType } from "@/module/types";
|
|
6
|
+
export { Module } from "@/module/decorator";
|
|
7
|
+
export type { ModuleOptions } from "@/module/decorator";
|
|
8
|
+
export type { Intent } from "@/intent/types";
|
|
9
|
+
export type { IntentMiddleware } from "@/intent/middleware";
|
|
10
|
+
export { Store } from "@/store/Store";
|
|
11
|
+
export { useStore } from "@/store/useStore";
|
|
12
|
+
export { loadRemotePlugin } from "@/plugin/remote/remotePluginLoader";
|
|
13
|
+
export type { RemotePluginConfig } from "@/plugin/remote/remotePluginLoader";
|
|
14
|
+
export type { RuntimePlugin } from "@/plugin/Plugin";
|
|
15
|
+
export type { PluginContext } from "@/plugin/PluginContext";
|
|
16
|
+
export type { PluginManifest, PluginCapability } from "@/plugin/PluginManifest";
|
|
17
|
+
export { createPluginContext } from "@/plugin/createPluginContext";
|
|
18
|
+
export { ObservabilityPlugin } from "@/plugin/observability";
|
|
19
|
+
export { RuntimeProvider, useRuntime } from "@/react/RuntimeProvider";
|
|
20
|
+
export { useIntent } from "@/react/useIntent";
|
|
21
|
+
export { useComputed, useEffectIntent, useModule } from "@/react/hooks";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"reflect-metadata";import{useSyncExternalStore as t,createContext as e,useContext as n}from"react";import{jsx as s}from"react/jsx-runtime";class i{constructor(){this.instances=new Map}resolve(t){if(!this.instances.has(t)){const e=(Reflect.getMetadata("design:paramtypes",t)||[]).map(t=>this.resolve(t));this.instances.set(t,new t(...e))}return this.instances.get(t)}}class o{constructor(){this.handlers=new Map,this.middlewares=[]}use(t){this.middlewares.push(t)}on(t,e){const n=this.handlers.get(t)||[];n.push(e),this.handlers.set(t,n)}async dispatch(t){const e={intent:t,metadata:{}};let n=-1;const s=async i=>{var o;if(i<=n)throw new Error("next() called multiple times");n=i;const a=this.middlewares[i];a?await a(e,()=>s(i+1)):null===(o=this.handlers.get(t.type))||void 0===o||o.forEach(e=>e(t))};await s(0)}onScoped(t,e,n){return this.on(`${t}/${e}`,n)}dispatchScoped(t,e,n){return this.dispatch({type:`${t}/${e}`,payload:n})}}class a{async run(t){return t()}}class r{constructor(){this.container=new i,this.intent=new o,this.effect=new a,this.modules=new Map,this.$on=this.intent.on.bind(this.intent),this.$emit=this.intent.dispatch.bind(this.intent),this.$onIntent=this.intent.onScoped.bind(this.intent),this.$emitIntent=this.intent.dispatchScoped.bind(this.intent)}use(t){t.setup(this)}useModule(t){this.modules.set(t.name,t)}async initModules(){this.modules.forEach(t=>{var e,n;null===(e=t.dependencies)||void 0===e||e.forEach(e=>{var n;const s=this.modules.get(e);null===(n=null==s?void 0:s.onModuleLoaded)||void 0===n||n.call(s,t)}),t.setup(this),null===(n=t.onInit)||void 0===n||n.call(t,this)})}destroyModules(){this.modules.forEach(t=>{var e;return null===(e=t.onDestroy)||void 0===e?void 0:e.call(t)})}getModule(t){return this.modules.get(t)}}function c(){return new r}function u(t){return t}function h(t){return function(e){e.__moduleOptions=t}}class d{constructor(t){this.state=t,this.listeners=new Set,this.computedMap=new Map,this.asyncComputedMap=new Map,this.asyncComputedCache=new Map}get(){return this.state}set(t){this.state=Object.assign(Object.assign({},this.state),t),this.updateComputed(),this.listeners.forEach(t=>t(this.state))}subscribe(t){return this.listeners.add(t),()=>this.listeners.delete(t)}computed(t,e){this.computedMap.set(t,e)}computedAsync(t,e){this.asyncComputedMap.set(t,e),this.updateAsyncComputed(t)}getComputed(t){var e,n;return null!==(n=null===(e=this.computedMap.get(t))||void 0===e?void 0:e(this.state))&&void 0!==n?n:this.asyncComputedCache.get(t)}updateComputed(){this.computedMap.forEach((t,e)=>t(this.state))}async updateAsyncComputed(t){const e=this.asyncComputedMap.get(t);if(e){const n=await e(this.state);this.asyncComputedCache.set(t,n),this.listeners.forEach(t=>t(this.state))}}}function p(e){return t(e.subscribe.bind(e),()=>e.get())}async function l(t){const e=await import(t.url),n=t.exportName?e[t.exportName]:e.default;if(!n)throw new Error(`Remote plugin not found: ${t.exportName||"default"}`);const s="function"==typeof n?new n:n;if(!s.manifest||!s.setup)throw new Error("Invalid RuntimePlugin");return s}function m(t){return{intent:{tap(e){t.intent.use(async(t,n)=>{e(t.intent),await n()})}},effect:{track(t){}},store:{observe(t){}}}}class f{setup(t){t.intent.use(async(t,e)=>{const n=performance.now();try{await e(),console.log("[Intent]",t.intent.type,"OK",performance.now()-n)}catch(e){throw console.error("[Intent]",t.intent.type,"ERR",e),e}})}}const y=e(null),w=({runtime:t,children:e})=>s(y.Provider,{value:t,children:e}),v=()=>{const t=n(y);if(!t)throw new Error("No Runtime");return t};function g(t){const e=v();return n=>{const s=t?`${t}/${n.type}`:n.type;e.intent.dispatch({type:s,payload:n.payload})}}function M(t){return v().container.resolve(t)}function b(t,e){const n=v();return()=>n.intent.dispatch({type:t,payload:e})}function C(e,n){return t(e.subscribe.bind(e),()=>e.getComputed(n))}export{r as AppRuntime,h as Module,f as ObservabilityPlugin,w as RuntimeProvider,d as Store,c as createApp,u as createModule,m as createPluginContext,l as loadRemotePlugin,C as useComputed,b as useEffectIntent,g as useIntent,M as useModule,v as useRuntime,p as useStore};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("reflect-metadata"),require("react"),require("react/jsx-runtime")):"function"==typeof define&&define.amd?define(["exports","reflect-metadata","react","react/jsx-runtime"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).ReactStruct={},null,t.react,t.jsxRuntime)}(this,function(t,e,n,s){"use strict";class i{constructor(){this.instances=new Map}resolve(t){if(!this.instances.has(t)){const e=(Reflect.getMetadata("design:paramtypes",t)||[]).map(t=>this.resolve(t));this.instances.set(t,new t(...e))}return this.instances.get(t)}}class o{constructor(){this.handlers=new Map,this.middlewares=[]}use(t){this.middlewares.push(t)}on(t,e){const n=this.handlers.get(t)||[];n.push(e),this.handlers.set(t,n)}async dispatch(t){const e={intent:t,metadata:{}};let n=-1;const s=async i=>{var o;if(i<=n)throw new Error("next() called multiple times");n=i;const a=this.middlewares[i];a?await a(e,()=>s(i+1)):null===(o=this.handlers.get(t.type))||void 0===o||o.forEach(e=>e(t))};await s(0)}onScoped(t,e,n){return this.on(`${t}/${e}`,n)}dispatchScoped(t,e,n){return this.dispatch({type:`${t}/${e}`,payload:n})}}class a{async run(t){return t()}}class r{constructor(){this.container=new i,this.intent=new o,this.effect=new a,this.modules=new Map,this.$on=this.intent.on.bind(this.intent),this.$emit=this.intent.dispatch.bind(this.intent),this.$onIntent=this.intent.onScoped.bind(this.intent),this.$emitIntent=this.intent.dispatchScoped.bind(this.intent)}use(t){t.setup(this)}useModule(t){this.modules.set(t.name,t)}async initModules(){this.modules.forEach(t=>{var e,n;null===(e=t.dependencies)||void 0===e||e.forEach(e=>{var n;const s=this.modules.get(e);null===(n=null==s?void 0:s.onModuleLoaded)||void 0===n||n.call(s,t)}),t.setup(this),null===(n=t.onInit)||void 0===n||n.call(t,this)})}destroyModules(){this.modules.forEach(t=>{var e;return null===(e=t.onDestroy)||void 0===e?void 0:e.call(t)})}getModule(t){return this.modules.get(t)}}const u=n.createContext(null),c=()=>{const t=n.useContext(u);if(!t)throw new Error("No Runtime");return t};t.AppRuntime=r,t.Module=function(t){return function(e){e.__moduleOptions=t}},t.ObservabilityPlugin=class{setup(t){t.intent.use(async(t,e)=>{const n=performance.now();try{await e(),console.log("[Intent]",t.intent.type,"OK",performance.now()-n)}catch(e){throw console.error("[Intent]",t.intent.type,"ERR",e),e}})}},t.RuntimeProvider=({runtime:t,children:e})=>s.jsx(u.Provider,{value:t,children:e}),t.Store=class{constructor(t){this.state=t,this.listeners=new Set,this.computedMap=new Map,this.asyncComputedMap=new Map,this.asyncComputedCache=new Map}get(){return this.state}set(t){this.state=Object.assign(Object.assign({},this.state),t),this.updateComputed(),this.listeners.forEach(t=>t(this.state))}subscribe(t){return this.listeners.add(t),()=>this.listeners.delete(t)}computed(t,e){this.computedMap.set(t,e)}computedAsync(t,e){this.asyncComputedMap.set(t,e),this.updateAsyncComputed(t)}getComputed(t){var e,n;return null!==(n=null===(e=this.computedMap.get(t))||void 0===e?void 0:e(this.state))&&void 0!==n?n:this.asyncComputedCache.get(t)}updateComputed(){this.computedMap.forEach((t,e)=>t(this.state))}async updateAsyncComputed(t){const e=this.asyncComputedMap.get(t);if(e){const n=await e(this.state);this.asyncComputedCache.set(t,n),this.listeners.forEach(t=>t(this.state))}}},t.createApp=function(){return new r},t.createModule=function(t){return t},t.createPluginContext=function(t){return{intent:{tap(e){t.intent.use(async(t,n)=>{e(t.intent),await n()})}},effect:{track(t){}},store:{observe(t){}}}},t.loadRemotePlugin=async function(t){const e=await import(t.url),n=t.exportName?e[t.exportName]:e.default;if(!n)throw new Error(`Remote plugin not found: ${t.exportName||"default"}`);const s="function"==typeof n?new n:n;if(!s.manifest||!s.setup)throw new Error("Invalid RuntimePlugin");return s},t.useComputed=function(t,e){return n.useSyncExternalStore(t.subscribe.bind(t),()=>t.getComputed(e))},t.useEffectIntent=function(t,e){const n=c();return()=>n.intent.dispatch({type:t,payload:e})},t.useIntent=function(t){const e=c();return n=>{const s=t?`${t}/${n.type}`:n.type;e.intent.dispatch({type:s,payload:n.payload})}},t.useModule=function(t){return c().container.resolve(t)},t.useRuntime=c,t.useStore=function(t){return n.useSyncExternalStore(t.subscribe.bind(t),()=>t.get())}});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Intent } from "./types";
|
|
2
|
+
import { IntentMiddleware } from "./middleware";
|
|
3
|
+
export declare class IntentBus {
|
|
4
|
+
private handlers;
|
|
5
|
+
private middlewares;
|
|
6
|
+
use(mw: IntentMiddleware): void;
|
|
7
|
+
on(type: string, handler: Function): void;
|
|
8
|
+
dispatch(intent: Intent): Promise<void>;
|
|
9
|
+
onScoped(scope: string, type: string, handler: Function): void;
|
|
10
|
+
dispatchScoped(scope: string, type: string, payload?: any): Promise<void>;
|
|
11
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Intent } from "@/intent/types";
|
|
2
|
+
export interface PluginContext {
|
|
3
|
+
intent: {
|
|
4
|
+
tap(fn: (intent: Intent) => void): void;
|
|
5
|
+
};
|
|
6
|
+
effect: {
|
|
7
|
+
track(fn: (name: string, duration: number) => void): void;
|
|
8
|
+
};
|
|
9
|
+
store: {
|
|
10
|
+
observe(fn: (storeId: string, nextState: any) => void): void;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type PluginCapability = "intent" | "effect" | "store" | "ui" | "devtools";
|
|
2
|
+
export interface PluginManifest {
|
|
3
|
+
id: string;
|
|
4
|
+
name: string;
|
|
5
|
+
version: string;
|
|
6
|
+
description?: string;
|
|
7
|
+
author?: string;
|
|
8
|
+
homepage?: string;
|
|
9
|
+
license?: string;
|
|
10
|
+
capabilities: PluginCapability[];
|
|
11
|
+
runtime: {
|
|
12
|
+
minVersion: string;
|
|
13
|
+
};
|
|
14
|
+
pricing?: {
|
|
15
|
+
type: "free" | "paid" | "trial";
|
|
16
|
+
};
|
|
17
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare function useModule(moduleClass: any): unknown;
|
|
2
|
+
export declare function useEffectIntent(intentType: string, payload?: any): () => Promise<void>;
|
|
3
|
+
export declare function useComputed<T>(store: {
|
|
4
|
+
getComputed: (key: string) => T;
|
|
5
|
+
subscribe: any;
|
|
6
|
+
}, key: string): T;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
type Listener<T> = (state: T) => void;
|
|
2
|
+
type ComputedFn<T> = (state: T) => any;
|
|
3
|
+
type AsyncComputedFn<T> = (state: T) => Promise<any>;
|
|
4
|
+
export declare class Store<T> {
|
|
5
|
+
private state;
|
|
6
|
+
private listeners;
|
|
7
|
+
private computedMap;
|
|
8
|
+
private asyncComputedMap;
|
|
9
|
+
private asyncComputedCache;
|
|
10
|
+
constructor(state: T);
|
|
11
|
+
get(): T;
|
|
12
|
+
set(partial: Partial<T>): void;
|
|
13
|
+
subscribe(fn: Listener<T>): () => boolean;
|
|
14
|
+
computed(key: string, fn: ComputedFn<T>): void;
|
|
15
|
+
computedAsync(key: string, fn: AsyncComputedFn<T>): void;
|
|
16
|
+
getComputed(key: string): any;
|
|
17
|
+
private updateComputed;
|
|
18
|
+
private updateAsyncComputed;
|
|
19
|
+
}
|
|
20
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@delpi/react-struct-z",
|
|
3
|
+
"version": "1.0.0-alz",
|
|
4
|
+
"description": "React-oriented modular framework for intent-driven orchestration, reactive store, effects, and DI-enabled modules",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Delpi.Kye",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"sideEffects": false,
|
|
9
|
+
"main": "build/index.cjs.js",
|
|
10
|
+
"module": "build/index.esm.js",
|
|
11
|
+
"types": "build/index.d.ts",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./build/index.d.ts",
|
|
15
|
+
"import": "./build/index.esm.js",
|
|
16
|
+
"require": "./build/index.cjs.js"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"build"
|
|
21
|
+
],
|
|
22
|
+
"scripts": {
|
|
23
|
+
"clean": "rimraf build",
|
|
24
|
+
"build": "rollup -c",
|
|
25
|
+
"cb": "npm run clean && npm run build",
|
|
26
|
+
"prepublishOnly": "npm run cb"
|
|
27
|
+
},
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "https://github.com/delpikye-v/react-struct.git"
|
|
31
|
+
},
|
|
32
|
+
"homepage": "https://github.com/delpikye-v/react-struct#readme",
|
|
33
|
+
"bugs": {
|
|
34
|
+
"url": "https://github.com/delpikye-v/react-struct/issues"
|
|
35
|
+
},
|
|
36
|
+
"keywords": [
|
|
37
|
+
"react",
|
|
38
|
+
"react-framework",
|
|
39
|
+
"intent",
|
|
40
|
+
"intent-engine",
|
|
41
|
+
"orchestration",
|
|
42
|
+
"modular",
|
|
43
|
+
"module-system",
|
|
44
|
+
"computed-graph",
|
|
45
|
+
"state-engine",
|
|
46
|
+
"effect-pipeline",
|
|
47
|
+
"di-container",
|
|
48
|
+
"plugin-system",
|
|
49
|
+
"side-effects",
|
|
50
|
+
"async-flow",
|
|
51
|
+
"reactive-store",
|
|
52
|
+
"ddd",
|
|
53
|
+
"hexagonal",
|
|
54
|
+
"frontend-architecture"
|
|
55
|
+
],
|
|
56
|
+
"devDependencies": {
|
|
57
|
+
"@rollup/plugin-commonjs": "^25.0.0",
|
|
58
|
+
"@rollup/plugin-node-resolve": "^15.2.3",
|
|
59
|
+
"@rollup/plugin-terser": "^0.4.4",
|
|
60
|
+
|
|
61
|
+
"rollup": "^4.9.6",
|
|
62
|
+
"rollup-plugin-typescript2": "^0.36.0",
|
|
63
|
+
|
|
64
|
+
"typescript": "^5.3.3",
|
|
65
|
+
"tslib": "^2.6.2",
|
|
66
|
+
"rimraf": "^5.0.5",
|
|
67
|
+
|
|
68
|
+
"react": "^18.2.0",
|
|
69
|
+
"react-dom": "^18.2.0",
|
|
70
|
+
"@types/react": "^18.2.45",
|
|
71
|
+
"@types/react-dom": "^18.2.18"
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
"peerDependencies": {
|
|
75
|
+
"react": ">=16.8",
|
|
76
|
+
"react-dom": ">=16.8"
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
"dependencies": {
|
|
80
|
+
"reflect-metadata": "^0.1.13"
|
|
81
|
+
},
|
|
82
|
+
"publishConfig": {
|
|
83
|
+
"access": "public"
|
|
84
|
+
}
|
|
85
|
+
}
|