plug-code 1.1.0 → 1.1.2
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/package.json +1 -1
- package/src/core/plcAPI.tsx +7 -7
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/package.json
CHANGED
package/src/core/plcAPI.tsx
CHANGED
|
@@ -37,7 +37,7 @@ export class PlcAPI<S extends ObjectType> {
|
|
|
37
37
|
|
|
38
38
|
createFeature(name: string, setupFn: (api: PlcAPI<S>) => void): PlcAPI<S> {
|
|
39
39
|
if (this.installedFeatures.has(name)) {
|
|
40
|
-
console.warn(`[PlcFramework]
|
|
40
|
+
console.warn(`[PlcFramework] Feature '${name}' is already registered. It will be skipped to avoid conflicts.`);
|
|
41
41
|
return this;
|
|
42
42
|
}
|
|
43
43
|
|
|
@@ -45,7 +45,7 @@ export class PlcAPI<S extends ObjectType> {
|
|
|
45
45
|
setupFn(this);
|
|
46
46
|
this.installedFeatures.add(name);
|
|
47
47
|
} catch (error) {
|
|
48
|
-
console.error(`[PlcFramework] 💥
|
|
48
|
+
console.error(`[PlcFramework] 💥 Critical error initializing feature '${name}':`, error);
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
return this;
|
|
@@ -187,7 +187,7 @@ export class PlcAPI<S extends ObjectType> {
|
|
|
187
187
|
try {
|
|
188
188
|
currentData = transformer.fn(currentData, context);
|
|
189
189
|
} catch (error) {
|
|
190
|
-
console.error(`[PlcAPI] Error
|
|
190
|
+
console.error(`[PlcAPI] Error in transform '${channel as string}/${transformer.id}':`, error);
|
|
191
191
|
}
|
|
192
192
|
}
|
|
193
193
|
|
|
@@ -199,7 +199,7 @@ export class PlcAPI<S extends ObjectType> {
|
|
|
199
199
|
fn: CommandFn<CommandPayload<K>, CommandResult<K>>
|
|
200
200
|
) {
|
|
201
201
|
if (this.commands.has(id as string)) {
|
|
202
|
-
console.warn(`[PlcAPI]
|
|
202
|
+
console.warn(`[PlcAPI] Overwriting command '${id as string}'`);
|
|
203
203
|
}
|
|
204
204
|
this.commands.set(id as string, fn as any);
|
|
205
205
|
}
|
|
@@ -210,7 +210,7 @@ export class PlcAPI<S extends ObjectType> {
|
|
|
210
210
|
) {
|
|
211
211
|
const currentFn = this.commands.get(id as string);
|
|
212
212
|
if (!currentFn) {
|
|
213
|
-
console.error(`[PlcAPI]
|
|
213
|
+
console.error(`[PlcAPI] Cannot wrap '${id as string}', command does not exist.`);
|
|
214
214
|
return;
|
|
215
215
|
}
|
|
216
216
|
this.commands.set(id as string, wrapper(currentFn) as any);
|
|
@@ -222,13 +222,13 @@ export class PlcAPI<S extends ObjectType> {
|
|
|
222
222
|
): Promise<CommandResult<K>> {
|
|
223
223
|
const fn = this.commands.get(id as string);
|
|
224
224
|
if (!fn) {
|
|
225
|
-
throw new Error(`[PlcAPI]
|
|
225
|
+
throw new Error(`[PlcAPI] Command '${id as string}' not found.`);
|
|
226
226
|
}
|
|
227
227
|
|
|
228
228
|
try {
|
|
229
229
|
return await fn(payload);
|
|
230
230
|
} catch (error) {
|
|
231
|
-
console.error(`[PlcAPI] Error
|
|
231
|
+
console.error(`[PlcAPI] Error executing '${id as string}':`, error);
|
|
232
232
|
throw error;
|
|
233
233
|
}
|
|
234
234
|
}
|