plug-code 1.0.3 → 1.1.1
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 +103 -3
- package/index.d.ts +0 -9
- package/package.json +1 -1
- package/src/core/plcAPI.tsx +123 -26
- package/src/plug-code.tsx +6 -7
- package/src/types/api.ts +7 -1
- package/src/types/registry.ts +4 -0
- package/types/plug-code.d.ts +156 -13
- package/types/contexts/pipeline.d.ts +0 -2
- package/types/core/plcAPI.d.ts +0 -30
- package/types/core/plcPipeline.d.ts +0 -16
- package/types/core/plcStore.d.ts +0 -17
- package/types/types/api.d.ts +0 -5
- package/types/types/features.d.ts +0 -6
- package/types/types/general.d.ts +0 -1
- package/types/types/pipeline.d.ts +0 -6
- package/types/types/store.d.ts +0 -4
package/README.md
CHANGED
|
@@ -1,16 +1,116 @@
|
|
|
1
1
|
# Plug&Code
|
|
2
2
|
|
|
3
|
-
Plug&Code is a multipurpose framework for React
|
|
3
|
+
Plug&Code is a multipurpose framework for React designed for **scalability, reusability, and modular organization**. It allows you to build complex applications by "plugging in" features without tightly coupling your codebase.
|
|
4
4
|
|
|
5
5
|
## Usage
|
|
6
6
|
|
|
7
|
-
You are welcome to use Plug&Code in your projects, **personal or commercial**, as long as you **do not modify or redistribute the framework** without explicit permission from the author.
|
|
7
|
+
You are welcome to use Plug&Code in your projects, **personal or commercial**, as long as you **do not modify or redistribute the framework** without explicit permission from the author.
|
|
8
|
+
|
|
9
|
+
---
|
|
8
10
|
|
|
9
11
|
### Installation
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
Install the framework via npm or yarn:
|
|
12
14
|
|
|
13
15
|
```bash
|
|
14
16
|
npm install plug-code
|
|
15
17
|
# or
|
|
16
18
|
yarn add plug-code
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### Core Concepts
|
|
23
|
+
|
|
24
|
+
Plug&Code is built around the **PLC (Pipeline-Logic-Command)** pattern:
|
|
25
|
+
|
|
26
|
+
* **Slots (UI Pipeline):** Inject components into pre-defined locations from any feature.
|
|
27
|
+
* **Commands (Logic):** Execute and wrap business actions (e.g., `checkout`, `print`, `save`).
|
|
28
|
+
* **Transforms (Data):** Pass data through named channels to be modified by different features.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
### Quick Start Guide
|
|
33
|
+
|
|
34
|
+
#### 1. Initialize your System
|
|
35
|
+
|
|
36
|
+
Define your system and register features using the fluent API.
|
|
37
|
+
|
|
38
|
+
```tsx
|
|
39
|
+
import { createPlugAndCode } from 'plug-code';
|
|
40
|
+
|
|
41
|
+
export const { useSystemPlc, SystemPlcRoot } = createPlugAndCode((api) => {
|
|
42
|
+
|
|
43
|
+
// Define a Sales Feature
|
|
44
|
+
api.feature("sales", (api) => {
|
|
45
|
+
// Register a UI component into a slot
|
|
46
|
+
api.register("header.cart", () => <CartIcon />);
|
|
47
|
+
|
|
48
|
+
// Register a business command
|
|
49
|
+
api.registerCommand("sales.checkout", async (items) => {
|
|
50
|
+
console.log("Saving items to database...", items);
|
|
51
|
+
return { success: true };
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// Define a Logger Feature that wraps existing logic
|
|
56
|
+
api.feature("logger", (api) => {
|
|
57
|
+
api.wrapCommand("sales.checkout", (next) => async (items) => {
|
|
58
|
+
console.log("Checkout started...");
|
|
59
|
+
const result = await next(items);
|
|
60
|
+
console.log("Checkout finished:", result);
|
|
61
|
+
return result;
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
#### 2. Wrap your Application
|
|
69
|
+
|
|
70
|
+
Use the `useSystemPlc` hook to manage the state and `SystemPlcRoot` to provide the context.
|
|
71
|
+
|
|
72
|
+
```tsx
|
|
73
|
+
function App() {
|
|
74
|
+
// Initialize state with initial properties
|
|
75
|
+
const { api, useSelector } = useSystemPlc({ shopName: "My Store" });
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<SystemPlcRoot api={api}>
|
|
79
|
+
<nav>
|
|
80
|
+
{/* Render slots from any feature */}
|
|
81
|
+
{api.render("header.cart")}
|
|
82
|
+
</nav>
|
|
83
|
+
<main>
|
|
84
|
+
<h1>Welcome to {useSelector(s => s.root.shopName)}</h1>
|
|
85
|
+
</main>
|
|
86
|
+
</SystemPlcRoot>
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
### API Reference
|
|
95
|
+
|
|
96
|
+
#### **UI Management (Slots)**
|
|
97
|
+
|
|
98
|
+
* `api.register(slotName, component)`: Adds a UI component to a slot.
|
|
99
|
+
* `api.render(slotName)`: Renders all components registered in that slot.
|
|
100
|
+
* `api.wrap(slot, wrapper)`: Wraps a slot's content with higher-order components.
|
|
101
|
+
|
|
102
|
+
#### **Business Logic (Commands)**
|
|
103
|
+
|
|
104
|
+
* `api.registerCommand(id, fn)`: Registers an executable action.
|
|
105
|
+
* `api.execute(id, payload)`: Runs a command and returns a Promise.
|
|
106
|
+
* `api.wrapCommand(id, (next) => ...)`: Intercepts a command to add side-effects or validations.
|
|
107
|
+
|
|
108
|
+
#### **Data Processing (Transforms)**
|
|
109
|
+
|
|
110
|
+
* `api.send(channel, id, fn, priority)`: Adds a data transformer to a specific channel.
|
|
111
|
+
* `api.receive(channel, initialData)`: Pipes data through all transformers in the channel.
|
|
112
|
+
|
|
113
|
+
#### **State Management**
|
|
114
|
+
|
|
115
|
+
* `useSelector(selector)`: Reactively listen to state changes.
|
|
116
|
+
* `api.update(key, updater)`: Update store data using Immer-powered drafts.
|
package/index.d.ts
CHANGED
|
@@ -1,10 +1 @@
|
|
|
1
1
|
export * from "./types/plug-code";
|
|
2
|
-
export * from "./types/core/plcAPI";
|
|
3
|
-
export * from "./types/core/plcStore";
|
|
4
|
-
export * from "./types/contexts/pipeline";
|
|
5
|
-
|
|
6
|
-
export * from "./types/types/api";
|
|
7
|
-
export * from "./types/types/features";
|
|
8
|
-
export * from "./types/types/general";
|
|
9
|
-
export * from "./types/types/pipeline";
|
|
10
|
-
export * from "./types/types/store";
|
package/package.json
CHANGED
package/src/core/plcAPI.tsx
CHANGED
|
@@ -3,22 +3,57 @@ import type { ObjectType } from "../types/general";
|
|
|
3
3
|
import { useEffect, useState } from "react";
|
|
4
4
|
import { PlcPipeline } from "./plcPipeline";
|
|
5
5
|
import type { PlcStore } from "./plcStore";
|
|
6
|
-
import type { transformerType } from "../types/api";
|
|
6
|
+
import type { CommandFn, transformerType } from "../types/api";
|
|
7
|
+
|
|
8
|
+
import type { ChannelRegistry, CommandRegistry, SlotRegistry } from "../types/registry";
|
|
9
|
+
|
|
10
|
+
type ChannelKey = keyof ChannelRegistry | (string & {});
|
|
11
|
+
type ChannelData<K> = K extends keyof ChannelRegistry ? ChannelRegistry[K] : any;
|
|
12
|
+
|
|
13
|
+
type CommandKey = keyof CommandRegistry | (string & {});
|
|
14
|
+
type CommandPayload<K> = K extends keyof CommandRegistry
|
|
15
|
+
? (CommandRegistry[K] extends { payload: infer P } ? P : any)
|
|
16
|
+
: any;
|
|
17
|
+
type CommandResult<K> = K extends keyof CommandRegistry
|
|
18
|
+
? (CommandRegistry[K] extends { result: infer R } ? R : any)
|
|
19
|
+
: any;
|
|
20
|
+
|
|
21
|
+
type SlotKey = keyof SlotRegistry | (string & {});
|
|
22
|
+
|
|
7
23
|
|
|
8
24
|
export class PlcAPI<S extends ObjectType> {
|
|
9
25
|
private store: PlcStore<S>
|
|
10
26
|
private pipeline: PlcPipeline<S>
|
|
11
27
|
private substores = new Map<string, any>()
|
|
12
|
-
|
|
28
|
+
|
|
29
|
+
private transformers = new Map<string, transformerType[]>()
|
|
30
|
+
private commands = new Map<string, CommandFn>();
|
|
31
|
+
private installedFeatures = new Set<string>();
|
|
13
32
|
|
|
14
33
|
constructor(store: PlcStore<S>) {
|
|
15
34
|
this.store = store
|
|
16
35
|
this.pipeline = new PlcPipeline(store)
|
|
17
36
|
}
|
|
18
37
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
38
|
+
createFeature(name: string, setupFn: (api: PlcAPI<S>) => void): PlcAPI<S> {
|
|
39
|
+
if (this.installedFeatures.has(name)) {
|
|
40
|
+
console.warn(`[PlcFramework] La feature '${name}' ya fue registrada. Se omitirá para evitar conflictos.`);
|
|
41
|
+
return this;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
setupFn(this);
|
|
46
|
+
this.installedFeatures.add(name);
|
|
47
|
+
} catch (error) {
|
|
48
|
+
console.error(`[PlcFramework] 💥 Error crítico inicializando la feature '${name}':`, error);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return this;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
register(slot: SlotKey, node: () => React.ReactNode): void;
|
|
55
|
+
register<K extends string>(slot: SlotKey, node: (data: any) => React.ReactNode, dependencyKey: K): void;
|
|
56
|
+
register(slot: SlotKey, node: (data?: any) => React.ReactNode, dependencyKey?: string) {
|
|
22
57
|
if (dependencyKey) {
|
|
23
58
|
const ConnectedWrapper = () => {
|
|
24
59
|
const [data, setData] = useState(() => this.substores.get(dependencyKey));
|
|
@@ -34,12 +69,12 @@ export class PlcAPI<S extends ObjectType> {
|
|
|
34
69
|
};
|
|
35
70
|
|
|
36
71
|
this.store.batch(() => {
|
|
37
|
-
this.pipeline.register(slot, () => <ConnectedWrapper />);
|
|
72
|
+
this.pipeline.register(slot as string, () => <ConnectedWrapper />);
|
|
38
73
|
});
|
|
39
74
|
}
|
|
40
75
|
else {
|
|
41
76
|
this.store.batch(() => {
|
|
42
|
-
this.pipeline.register(slot, node as () => React.ReactNode);
|
|
77
|
+
this.pipeline.register(slot as string, node as () => React.ReactNode);
|
|
43
78
|
});
|
|
44
79
|
}
|
|
45
80
|
}
|
|
@@ -48,7 +83,7 @@ export class PlcAPI<S extends ObjectType> {
|
|
|
48
83
|
get: () => T;
|
|
49
84
|
update: (updater: (draft: T) => void) => void;
|
|
50
85
|
connect: (renderer: (data: T) => React.ReactNode) => React.FC;
|
|
51
|
-
render: (slotName:
|
|
86
|
+
render: (slotName: SlotKey) => React.ReactNode | null;
|
|
52
87
|
receive: (context?: any) => any;
|
|
53
88
|
root: PlcAPI<S>;
|
|
54
89
|
} {
|
|
@@ -63,15 +98,15 @@ export class PlcAPI<S extends ObjectType> {
|
|
|
63
98
|
return this.connect(key, renderer);
|
|
64
99
|
},
|
|
65
100
|
|
|
66
|
-
render: (slotName:
|
|
101
|
+
render: (slotName: SlotKey) => {
|
|
67
102
|
return this.connect(key, (localData) => {
|
|
68
|
-
return this.pipeline.render(slotName, localData) as React.ReactNode;
|
|
103
|
+
return this.pipeline.render(slotName as string, localData) as React.ReactNode;
|
|
69
104
|
}) as any;
|
|
70
105
|
},
|
|
71
106
|
|
|
72
107
|
receive: (context: any = {}) => {
|
|
73
108
|
const currentData = this.getData(key);
|
|
74
|
-
return this.receive(currentData, context);
|
|
109
|
+
return this.receive(key as any, currentData, context);
|
|
75
110
|
},
|
|
76
111
|
|
|
77
112
|
root: this
|
|
@@ -97,43 +132,105 @@ export class PlcAPI<S extends ObjectType> {
|
|
|
97
132
|
};
|
|
98
133
|
}
|
|
99
134
|
|
|
100
|
-
wrap(slot:
|
|
135
|
+
wrap(slot: SlotKey, fn: (next: () => React.ReactNode) => () => React.ReactNode) {
|
|
101
136
|
this.store.batch(() => {
|
|
102
|
-
this.pipeline.wrap(slot, fn)
|
|
137
|
+
this.pipeline.wrap(slot as string, fn)
|
|
103
138
|
})
|
|
104
139
|
}
|
|
105
140
|
|
|
106
|
-
after(slot:
|
|
141
|
+
after(slot: SlotKey, node: () => React.ReactNode) {
|
|
107
142
|
this.store.batch(() => {
|
|
108
|
-
this.pipeline.register(slot, node)
|
|
143
|
+
this.pipeline.register(slot as string, node)
|
|
109
144
|
})
|
|
110
145
|
}
|
|
111
146
|
|
|
112
|
-
render(slot:
|
|
113
|
-
return this.pipeline.render(slot)
|
|
147
|
+
render(slot: SlotKey) {
|
|
148
|
+
return this.pipeline.render(slot as string)
|
|
114
149
|
}
|
|
115
150
|
|
|
116
|
-
invalidate(slot?:
|
|
117
|
-
this.pipeline.invalidate(slot)
|
|
151
|
+
invalidate(slot?: SlotKey) {
|
|
152
|
+
this.pipeline.invalidate(slot as string)
|
|
118
153
|
}
|
|
119
154
|
|
|
120
|
-
send
|
|
121
|
-
|
|
122
|
-
|
|
155
|
+
send<K extends ChannelKey>(
|
|
156
|
+
channel: K,
|
|
157
|
+
id: string,
|
|
158
|
+
fn: (data: ChannelData<K>, context: any) => ChannelData<K>,
|
|
159
|
+
priority: number = 0
|
|
160
|
+
) {
|
|
161
|
+
const channelStr = channel as string;
|
|
162
|
+
if (!this.transformers.has(channelStr)) {
|
|
163
|
+
this.transformers.set(channelStr, []);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const channelList = this.transformers.get(channelStr)!;
|
|
167
|
+
|
|
168
|
+
const existingIdx = channelList.findIndex(t => t.id === id);
|
|
169
|
+
if (existingIdx >= 0) {
|
|
170
|
+
channelList[existingIdx] = { id, priority, fn };
|
|
171
|
+
} else {
|
|
172
|
+
channelList.push({ id, priority, fn });
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
channelList.sort((a, b) => a.priority - b.priority);
|
|
123
176
|
}
|
|
124
177
|
|
|
125
|
-
receive
|
|
178
|
+
receive<K extends ChannelKey>(
|
|
179
|
+
channel: K,
|
|
180
|
+
initialData: ChannelData<K>,
|
|
181
|
+
context: any = {}
|
|
182
|
+
): ChannelData<K> {
|
|
126
183
|
let currentData = initialData;
|
|
184
|
+
const channelList = this.transformers.get(channel as string) || [];
|
|
127
185
|
|
|
128
|
-
for (const transformer of
|
|
186
|
+
for (const transformer of channelList) {
|
|
129
187
|
try {
|
|
130
188
|
currentData = transformer.fn(currentData, context);
|
|
131
189
|
} catch (error) {
|
|
132
|
-
console.error(`[
|
|
190
|
+
console.error(`[PlcAPI] Error en transform '${channel as string}/${transformer.id}':`, error);
|
|
133
191
|
}
|
|
134
192
|
}
|
|
135
193
|
|
|
136
|
-
return currentData
|
|
194
|
+
return currentData as ChannelData<K>;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
registerCommand<K extends CommandKey>(
|
|
198
|
+
id: K,
|
|
199
|
+
fn: CommandFn<CommandPayload<K>, CommandResult<K>>
|
|
200
|
+
) {
|
|
201
|
+
if (this.commands.has(id as string)) {
|
|
202
|
+
console.warn(`[PlcAPI] Sobrescribiendo comando '${id as string}'`);
|
|
203
|
+
}
|
|
204
|
+
this.commands.set(id as string, fn as any);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
wrapCommand<K extends CommandKey>(
|
|
208
|
+
id: K,
|
|
209
|
+
wrapper: (next: CommandFn<CommandPayload<K>, CommandResult<K>>) => CommandFn<CommandPayload<K>, CommandResult<K>>
|
|
210
|
+
) {
|
|
211
|
+
const currentFn = this.commands.get(id as string);
|
|
212
|
+
if (!currentFn) {
|
|
213
|
+
console.error(`[PlcAPI] No se puede envolver '${id as string}', comando no existe.`);
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
this.commands.set(id as string, wrapper(currentFn) as any);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
async execute<K extends CommandKey>(
|
|
220
|
+
id: K,
|
|
221
|
+
payload?: CommandPayload<K>
|
|
222
|
+
): Promise<CommandResult<K>> {
|
|
223
|
+
const fn = this.commands.get(id as string);
|
|
224
|
+
if (!fn) {
|
|
225
|
+
throw new Error(`[PlcAPI] Comando '${id as string}' no encontrado.`);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
try {
|
|
229
|
+
return await fn(payload);
|
|
230
|
+
} catch (error) {
|
|
231
|
+
console.error(`[PlcAPI] Error ejecutando '${id as string}':`, error);
|
|
232
|
+
throw error;
|
|
233
|
+
}
|
|
137
234
|
}
|
|
138
235
|
|
|
139
236
|
getData(key: string): any {
|
package/src/plug-code.tsx
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import { enableMapSet } from "immer";
|
|
2
2
|
import type { ObjectType } from "./types/general";
|
|
3
|
-
import type { FeatureType } from "./types/features";
|
|
4
3
|
import { PlcAPI } from "./core/plcAPI";
|
|
5
4
|
import { useEffect, useMemo, useState } from "react";
|
|
6
5
|
import { PlcStore } from "./core/plcStore";
|
|
7
6
|
|
|
8
|
-
enableMapSet()
|
|
9
|
-
|
|
7
|
+
enableMapSet();
|
|
8
|
+
|
|
9
|
+
export function createPlugAndCode<S extends ObjectType>(
|
|
10
|
+
setupSystem: (api: PlcAPI<S>) => void
|
|
11
|
+
) {
|
|
10
12
|
const FeatureHost = ({ api, children }: { api: PlcAPI<any>, children?: React.ReactNode }) => (
|
|
11
13
|
<>
|
|
12
|
-
{/* Renderizamos el slot principal */}
|
|
13
14
|
{api.render("root")}
|
|
14
15
|
{children}
|
|
15
16
|
</>
|
|
@@ -32,9 +33,7 @@ export function createPlugAndCode<S extends ObjectType>(features: FeatureType<S>
|
|
|
32
33
|
|
|
33
34
|
api.createData("root", initialProps);
|
|
34
35
|
|
|
35
|
-
|
|
36
|
-
feature.setup?.(api as any);
|
|
37
|
-
}
|
|
36
|
+
setupSystem(api as unknown as PlcAPI<S>);
|
|
38
37
|
|
|
39
38
|
return api;
|
|
40
39
|
}, []);
|
package/src/types/api.ts
CHANGED
|
@@ -1,2 +1,8 @@
|
|
|
1
1
|
|
|
2
|
-
export type transformerType = {
|
|
2
|
+
export type transformerType = {
|
|
3
|
+
id: string,
|
|
4
|
+
priority: number,
|
|
5
|
+
fn: (data: any, context: any) => any
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export type CommandFn<T = any, R = any> = (payload: T) => Promise<R> | R;
|
package/types/plug-code.d.ts
CHANGED
|
@@ -1,13 +1,156 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
// ==========================================
|
|
4
|
+
// Tipos Generales
|
|
5
|
+
// ==========================================
|
|
6
|
+
|
|
7
|
+
export type ObjectType = Record<string, any>;
|
|
8
|
+
|
|
9
|
+
export type Slot = () => React.ReactNode;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Función para ejecutar lógica de negocio (Acciones).
|
|
13
|
+
* Puede ser síncrona o asíncrona.
|
|
14
|
+
*/
|
|
15
|
+
export type CommandFn<T = any, R = any> = (payload: T) => Promise<R> | R;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Función para transformar datos (Pipes).
|
|
19
|
+
* Debe ser pura y síncrona preferiblemente.
|
|
20
|
+
*/
|
|
21
|
+
export type TransformerFn<D = any, C = any> = (data: D, context: C) => D;
|
|
22
|
+
|
|
23
|
+
// ==========================================
|
|
24
|
+
// Core: Store
|
|
25
|
+
// ==========================================
|
|
26
|
+
|
|
27
|
+
export declare class PlcStore<S extends ObjectType> {
|
|
28
|
+
constructor(initial: S, debug: boolean);
|
|
29
|
+
|
|
30
|
+
get<K extends keyof S>(key: K): S[K];
|
|
31
|
+
getState(): S;
|
|
32
|
+
set<K extends keyof S>(key: K, value: S[K]): void;
|
|
33
|
+
|
|
34
|
+
/** Agrupa múltiples actualizaciones en un solo evento de notificación */
|
|
35
|
+
batch(updater: (draft: S) => void): void;
|
|
36
|
+
|
|
37
|
+
subscribe(listener: () => void): () => void;
|
|
38
|
+
subscribe<K extends keyof S>(key: K, listener: () => void): () => void;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// ==========================================
|
|
42
|
+
// Core: API Principal
|
|
43
|
+
// ==========================================
|
|
44
|
+
|
|
45
|
+
export declare class PlcAPI<S extends ObjectType> {
|
|
46
|
+
constructor(store: PlcStore<S>);
|
|
47
|
+
|
|
48
|
+
// --- Configuración del Sistema (Fluent Interface) ---
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Registra un módulo o feature en el sistema.
|
|
52
|
+
* @param name Identificador único para debugging y prevención de duplicados.
|
|
53
|
+
* @param setupFn Función de configuración donde registras slots, comandos, etc.
|
|
54
|
+
*/
|
|
55
|
+
feature(name: string, setupFn: (api: PlcAPI<S>) => void): PlcAPI<S>;
|
|
56
|
+
|
|
57
|
+
// --- Gestión de UI (Slots & Rendering) ---
|
|
58
|
+
|
|
59
|
+
register(slot: string, node: () => React.ReactNode): void;
|
|
60
|
+
register<K extends string>(slot: string, node: (data: any) => React.ReactNode, dependencyKey: K): void;
|
|
61
|
+
|
|
62
|
+
/** Envuelve un slot existente (Decorador/Wrapper) */
|
|
63
|
+
wrap(slot: string, fn: (next: () => React.ReactNode) => () => React.ReactNode): void;
|
|
64
|
+
|
|
65
|
+
/** Agrega contenido después de un slot existente */
|
|
66
|
+
after(slot: string, node: () => React.ReactNode): void;
|
|
67
|
+
|
|
68
|
+
/** Renderiza el contenido de un slot */
|
|
69
|
+
render(slot: string, contextData?: any): React.ReactNode;
|
|
70
|
+
|
|
71
|
+
/** Fuerza la regeneración del caché de un slot */
|
|
72
|
+
invalidate(slot?: string): void;
|
|
73
|
+
|
|
74
|
+
// --- Gestión de Datos (Scope & State) ---
|
|
75
|
+
|
|
76
|
+
createData<K extends string, T>(key: K, initialState: T): void;
|
|
77
|
+
|
|
78
|
+
getData(key: string): any;
|
|
79
|
+
|
|
80
|
+
update<K extends keyof S>(key: string & "root", updater: (draft: any) => void, slot?: string): void;
|
|
81
|
+
|
|
82
|
+
subscribe(listener: () => void): () => void;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Obtiene una interfaz tipada para interactuar con una parte específica del estado.
|
|
86
|
+
*/
|
|
87
|
+
scope<T = any>(key: string): {
|
|
88
|
+
get: () => T;
|
|
89
|
+
update: (updater: (draft: T) => void) => void;
|
|
90
|
+
connect: (renderer: (data: T) => React.ReactNode) => React.FC;
|
|
91
|
+
render: (slotName: string) => React.ReactNode | null;
|
|
92
|
+
receive: (context?: any) => any;
|
|
93
|
+
root: PlcAPI<S>;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
/** Conecta un componente a una parte del estado (HOC) */
|
|
97
|
+
connect<T = any>(key: string, renderer: (data: T) => React.ReactNode): React.FC;
|
|
98
|
+
|
|
99
|
+
// --- Pipeline de Datos (Transforms) ---
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Registra un transformador en un canal específico.
|
|
103
|
+
* @param channel Nombre del canal (ej: 'calculo-impuestos')
|
|
104
|
+
* @param id Identificador único del transformador
|
|
105
|
+
* @param fn Función transformadora
|
|
106
|
+
* @param priority Mayor número se ejecuta al final (default: 0)
|
|
107
|
+
*/
|
|
108
|
+
send(channel: string, id: string, fn: TransformerFn, priority?: number): void;
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Ejecuta la tubería de transformación para un canal.
|
|
112
|
+
*/
|
|
113
|
+
receive(channel: string, initialData: any, context?: any): any;
|
|
114
|
+
|
|
115
|
+
// --- Sistema de Comandos (Actions) ---
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Registra una acción ejecutable.
|
|
119
|
+
*/
|
|
120
|
+
registerCommand<T = any, R = any>(id: string, fn: CommandFn<T, R>): void;
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Envuelve o intercepta un comando existente.
|
|
124
|
+
*/
|
|
125
|
+
wrapCommand<T = any, R = any>(id: string, wrapper: (next: CommandFn<T, R>) => CommandFn<T, R>): void;
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Ejecuta una acción registrada.
|
|
129
|
+
* @returns Promesa con el resultado del comando.
|
|
130
|
+
*/
|
|
131
|
+
execute<T = any, R = any>(id: string, payload?: T): Promise<R>;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// ==========================================
|
|
135
|
+
// Entry Point & Hooks
|
|
136
|
+
// ==========================================
|
|
137
|
+
|
|
138
|
+
export type SystemInstance<S extends ObjectType> = {
|
|
139
|
+
api: PlcAPI<S>;
|
|
140
|
+
/** Hook para seleccionar datos del store reactivamente */
|
|
141
|
+
useSelector: <Result>(selector: (state: S) => Result) => Result;
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Inicializa el framework.
|
|
146
|
+
* @param setupSystem Función callback para configurar las features iniciales.
|
|
147
|
+
*/
|
|
148
|
+
export declare function createPlugAndCode<S extends ObjectType>(
|
|
149
|
+
setupSystem: (api: PlcAPI<S>) => void
|
|
150
|
+
): {
|
|
151
|
+
useSystemPlc: (initialProps: S) => SystemInstance<S>;
|
|
152
|
+
SystemPlcRoot: React.FC<{ api: PlcAPI<S>; children?: React.ReactNode }>;
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
/** Hook para acceder al contexto local dentro de un slot renderizado */
|
|
156
|
+
export declare function useScopeData<T>(): T;
|
package/types/core/plcAPI.d.ts
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import type { ObjectType } from "../types/general";
|
|
2
|
-
import type { PlcStore } from "./plcStore";
|
|
3
|
-
export declare class PlcAPI<S extends ObjectType> {
|
|
4
|
-
private store;
|
|
5
|
-
private pipeline;
|
|
6
|
-
private substores;
|
|
7
|
-
private transformers;
|
|
8
|
-
constructor(store: PlcStore<S>);
|
|
9
|
-
register(slot: string, node: () => React.ReactNode): void;
|
|
10
|
-
register<K extends string>(slot: string, node: (data: any) => React.ReactNode, dependencyKey: K): void;
|
|
11
|
-
scope<T = any>(key: string & "root"): {
|
|
12
|
-
get: () => T;
|
|
13
|
-
update: (updater: (draft: T) => void) => void;
|
|
14
|
-
connect: (renderer: (data: T) => React.ReactNode) => React.FC;
|
|
15
|
-
render: (slotName: string) => React.ReactNode | null;
|
|
16
|
-
receive: (context?: any) => any;
|
|
17
|
-
root: PlcAPI<S>;
|
|
18
|
-
};
|
|
19
|
-
connect<T = any>(key: string, renderer: (data: T) => React.ReactNode): React.FC;
|
|
20
|
-
wrap(slot: string, fn: (next: () => React.ReactNode) => () => React.ReactNode): void;
|
|
21
|
-
after(slot: string, node: () => React.ReactNode): void;
|
|
22
|
-
render(slot: string): import("react").ReactNode[] | import("react/jsx-runtime").JSX.Element;
|
|
23
|
-
invalidate(slot?: string): void;
|
|
24
|
-
send(id: string, fn: (data: any, context: any) => any, priority: number): void;
|
|
25
|
-
receive(initialData: any, context?: any): any;
|
|
26
|
-
getData(key: string): any;
|
|
27
|
-
subscribe(listener: () => void): () => void;
|
|
28
|
-
createData<K extends string, T>(key: K, initialState: T): void;
|
|
29
|
-
update<K extends keyof S>(key: string & "root", updater: (draft: any) => void, slot?: string): void;
|
|
30
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import type { ObjectType } from "../types/general";
|
|
3
|
-
import type { Slot } from "../types/pipeline";
|
|
4
|
-
import type { PlcStore } from "./plcStore";
|
|
5
|
-
export declare class PlcPipeline<S extends ObjectType> {
|
|
6
|
-
private slots;
|
|
7
|
-
private store;
|
|
8
|
-
private cache;
|
|
9
|
-
private scheduleQueue;
|
|
10
|
-
constructor(store: PlcStore<S>);
|
|
11
|
-
register(slot: string, fn: Slot, priority?: number): void;
|
|
12
|
-
wrap(slot: string, wrapper: (next: Slot) => Slot, priority?: number): void;
|
|
13
|
-
render(slot: string, contextData?: any): React.ReactNode[] | import("react/jsx-runtime").JSX.Element;
|
|
14
|
-
invalidate(slot?: string): void;
|
|
15
|
-
private regenerateCache;
|
|
16
|
-
}
|
package/types/core/plcStore.d.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { type Draft } from "immer";
|
|
2
|
-
import type { ObjectType } from "../types/general";
|
|
3
|
-
export declare class PlcStore<S extends ObjectType> {
|
|
4
|
-
private state;
|
|
5
|
-
private listeners;
|
|
6
|
-
private batchQueue;
|
|
7
|
-
private debug;
|
|
8
|
-
private isBatching;
|
|
9
|
-
constructor(initial: S, debug: boolean);
|
|
10
|
-
get<K extends keyof S>(key: K): S[K];
|
|
11
|
-
getState(): S;
|
|
12
|
-
set<K extends keyof S>(key: K, value: S[K]): void;
|
|
13
|
-
batch(updater: (draft: Draft<S>) => void): void;
|
|
14
|
-
subscribe(listener: () => void): () => void;
|
|
15
|
-
subscribe<K extends keyof S>(key: K, listener: () => void): () => void;
|
|
16
|
-
private emit;
|
|
17
|
-
}
|
package/types/types/api.d.ts
DELETED
package/types/types/general.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export type ObjectType = Record<string, any>;
|
package/types/types/store.d.ts
DELETED