plug-code 1.1.15 → 1.1.17

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 CHANGED
@@ -1,21 +1,18 @@
1
- MIT License
1
+ Plug&Code License
2
2
 
3
- Copyright (c) 2025 Alan Mendez
3
+ Copyright (c) 2026 AlaunS
4
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:
5
+ All rights reserved.
11
6
 
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
7
+ Permission is hereby granted to use this software for personal and commercial projects.
8
+ You may modify the software for your own internal use.
14
9
 
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.
10
+ You may NOT:
11
+ - Redistribute this software or any modified version of it.
12
+ - Sell, sublicense, publish, or share this software.
13
+ - Use this software as the basis for another framework, library, or competing product.
14
+
15
+ This software is provided "as is", without warranty of any kind, express or implied,
16
+ including but not limited to the warranties of merchantability, fitness for a
17
+ particular purpose and noninfringement. In no event shall the author be liable
18
+ for any claim, damages or other liability arising from the use of this software.
package/README.md CHANGED
@@ -4,8 +4,8 @@
4
4
  It empowers developers to build complex applications by **plugging in independent feature modules** without tightly coupling the codebase.
5
5
 
6
6
  > **License**
7
- > You may use Plug&Code in personal or commercial projects.
8
- > **Modification or redistribution of the framework source code is prohibited** without explicit permission.
7
+ > This project is licensed under the Plug&Code License.
8
+ > See the LICENSE file for details.
9
9
 
10
10
  ---
11
11
 
@@ -67,8 +67,7 @@ import { PaginationFeature } from './features/PaginationFeature';
67
67
  import { SalesFeature } from './features/SalesFeature';
68
68
 
69
69
  export const { useSystemPlc, SystemPlcRoot } = createPlugAndCode((api) => {
70
- api.createData("root", { appName: "My Dashboard", theme: "dark" });
71
-
70
+ // It creates root automatically | root = {}
72
71
  PaginationFeature(api);
73
72
  SalesFeature(api);
74
73
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "plug-code",
3
- "version": "1.1.15",
3
+ "version": "1.1.17",
4
4
  "description": "",
5
5
  "main": "src/plug-code.tsx",
6
6
  "types": "types/plug-code.d.ts",
@@ -1,6 +1,6 @@
1
1
  import { produce } from "immer";
2
2
  import type { ObjectType } from "../types/general";
3
- import { useEffect, useState } from "react";
3
+ import { useEffect, useRef, useState } from "react";
4
4
  import { PlcPipeline } from "./plcPipeline";
5
5
  import type { PlcStore } from "./plcStore";
6
6
  import type { CommandFn, transformerType } from "../types/api";
@@ -28,7 +28,6 @@ export class PlcAPI<S extends ObjectType> {
28
28
 
29
29
  private transformers = new Map<string, transformerType[]>()
30
30
  private commands = new Map<string, CommandFn>();
31
- private installedFeatures = new Set<string>();
32
31
 
33
32
  constructor(store: PlcStore<S>) {
34
33
  this.store = store
@@ -122,57 +121,67 @@ export class PlcAPI<S extends ObjectType> {
122
121
  return () => lastValue
123
122
  }
124
123
 
125
- register(slot: SlotKey, node: () => React.ReactNode): void;
126
- register<K extends string>(slot: SlotKey, node: (data: any) => React.ReactNode, dependencyKey: K): void;
127
- register(slot: SlotKey, node: (data?: any) => React.ReactNode, dependencyKey?: string) {
124
+ register(slot: SlotKey, node: (props?: any) => React.ReactNode): void;
125
+ register<K extends string>(slot: SlotKey, node: (data: any, props?: any) => React.ReactNode, dependencyKey: K): void;
126
+ register(slot: SlotKey, node: (data?: any, props?: any) => React.ReactNode, dependencyKey?: string) {
128
127
  if (dependencyKey) {
129
- const ConnectedWrapper = () => {
130
- const [data, setData] = useState(() => this.substores.get(dependencyKey));
128
+ const ConnectedWrapper = (props: any) => {
129
+ const [storeData, setStoreData] = useState(() => this.substores.get(dependencyKey));
131
130
 
132
131
  useEffect(() => {
133
132
  const unsubscribe = this.store.subscribe(dependencyKey as any, () => {
134
- setData(this.substores.get(dependencyKey));
133
+ setStoreData(this.substores.get(dependencyKey));
135
134
  });
136
135
  return unsubscribe;
137
136
  }, []);
138
137
 
139
- return node(data);
138
+ return <>{node(storeData, props)}</>;
140
139
  };
141
140
 
142
141
  this.store.batch(() => {
143
- this.pipeline.register(slot as string, () => <ConnectedWrapper />);
142
+ this.pipeline.register(slot as string, ConnectedWrapper);
144
143
  });
145
144
  }
146
145
  else {
147
146
  this.store.batch(() => {
148
- this.pipeline.register(slot as string, node as () => React.ReactNode);
147
+ this.pipeline.register(slot as string, node);
149
148
  });
150
149
  }
151
150
  }
152
151
 
153
152
  scope<T = any>(key: string | "root"): {
154
153
  get: () => T;
155
- update: (updater: (draft: T) => void) => void;
156
- connect: (renderer: (data: T) => React.ReactNode) => React.FC;
157
- render: (slotName: SlotKey) => React.ReactNode | null;
154
+ update: (updater: (draft: T) => void, slot?: string, triggerKey?: string) => void;
155
+ connect: <P = {}, R = any>(
156
+ selector: (data: T, props: P) => R
157
+ ) => (WrappedComponent: React.ComponentType<P & R>) => React.FC<P>;
158
+
159
+ render: (slotName: SlotKey) => React.FC;
158
160
  receive: (context?: any) => any;
159
161
  root: PlcAPI<S>;
160
162
  } {
161
163
  return {
162
164
  get: (): T => this.getData(key),
163
165
 
164
- update: (updater: (draft: T) => void) => {
165
- this.update(key as any, updater);
166
+ update: (updater: (draft: T) => void, slot?: string, triggerKey?: string) => {
167
+ this.update(key as any, updater, slot, triggerKey);
166
168
  },
167
169
 
168
- connect: (renderer: (data: T) => React.ReactNode) => {
169
- return this.connect(key, renderer);
170
+ connect: <P = {}, R = any>(
171
+ selector: (data: T, props: P) => R
172
+ ) => {
173
+ return this.connect(key, selector);
170
174
  },
171
175
 
172
176
  render: (slotName: SlotKey) => {
173
- return this.connect(key, (localData) => {
174
- return this.pipeline.render(slotName as string, localData) as React.ReactNode;
175
- }) as any;
177
+ const ScopedSlotRenderer = ({ _scopeData }: { _scopeData: T }) => {
178
+ return <>{this.pipeline.render(slotName as string, _scopeData)}</>;
179
+ };
180
+
181
+ return this.connect(
182
+ key,
183
+ (data: T) => ({ _scopeData: data })
184
+ )(ScopedSlotRenderer) as any;
176
185
  },
177
186
 
178
187
  receive: (context: any = {}) => {
@@ -184,25 +193,53 @@ export class PlcAPI<S extends ObjectType> {
184
193
  };
185
194
  }
186
195
 
187
- connect<T = any>(key: string, renderer: (data: T) => React.ReactNode): React.FC {
188
- return () => {
189
- const [data, setData] = useState<T>(() => this.substores.get(key));
196
+ connect<State = any, OwnProps = {}, ResultProps = {}>(
197
+ key: string,
198
+ selector: (state: State, props: OwnProps) => ResultProps
199
+ ): (WrappedComponent: React.ComponentType<OwnProps & ResultProps>) => React.FC<OwnProps> {
200
+ return (WrappedComponent: React.ComponentType<OwnProps & ResultProps>) => {
201
+
202
+ const ConnectedComponent = (props: OwnProps) => {
203
+ const propsRef = useRef(props);
204
+ propsRef.current = props;
190
205
 
191
- useEffect(() => {
192
- const unsubscribe = this.store.subscribe(key as any, () => {
193
- setData(this.substores.get(key));
206
+ const [slice, setSlice] = useState<ResultProps>(() => {
207
+ const data = this.substores.get(key);
208
+ return selector(data, props);
194
209
  });
195
210
 
196
- return () => {
197
- unsubscribe();
198
- };
199
- }, []);
211
+ useEffect(() => {
212
+ const unsubscribe = this.store.subscribe(key as any, () => {
213
+ const currentData = this.substores.get(key);
214
+ const newSlice = selector(currentData, propsRef.current);
215
+
216
+ setSlice(prev => {
217
+ if (prev === newSlice) return prev;
218
+
219
+ if (typeof prev === 'object' && prev !== null && typeof newSlice === 'object' && newSlice !== null) {
220
+ const keysA = Object.keys(prev) as Array<keyof ResultProps>;
221
+ const keysB = Object.keys(newSlice) as Array<keyof ResultProps>;
222
+ if (keysA.length === keysB.length && keysA.every(k => prev[k] === newSlice[k])) {
223
+ return prev;
224
+ }
225
+ }
226
+
227
+ return newSlice;
228
+ });
229
+ });
230
+ return unsubscribe;
231
+ }, []);
232
+
233
+ if (slice === undefined && this.substores.get(key) === undefined) return null;
234
+ return <WrappedComponent {...props} {...slice} />;
235
+ };
236
+
237
+ const displayName = WrappedComponent.displayName || WrappedComponent.name || 'Component';
238
+ ConnectedComponent.displayName = `Connect(${displayName})`;
200
239
 
201
- if (data === undefined) return null;
202
- return <>{renderer(data)}</>;
240
+ return ConnectedComponent;
203
241
  };
204
242
  }
205
-
206
243
  wrap(slot: SlotKey, fn: (next: () => React.ReactNode) => () => React.ReactNode) {
207
244
  this.store.batch(() => {
208
245
  this.pipeline.wrap(slot as string, fn)
@@ -215,8 +252,8 @@ export class PlcAPI<S extends ObjectType> {
215
252
  })
216
253
  }
217
254
 
218
- render(slot: SlotKey) {
219
- return this.pipeline.render(slot as string)
255
+ render(slot: SlotKey, props?: any) {
256
+ return this.pipeline.render(slot as string, props)
220
257
  }
221
258
 
222
259
  invalidate(slot?: SlotKey) {
@@ -1,4 +1,4 @@
1
- import React from "react";
1
+ import React, { useContext } from "react";
2
2
  import { ScopeContext } from "../contexts/pipeline";
3
3
  import type { ObjectType } from "../types/general";
4
4
  import type { ScheduledSlot, Slot } from "../types/pipeline";
@@ -73,9 +73,15 @@ export class PlcPipeline<S extends ObjectType> {
73
73
  }
74
74
 
75
75
  private regenerateCache(slot: string) {
76
- const nodes = this.slots.get(slot)?.map((fn, i) => (
77
- <React.Fragment key={i}>{fn()}</React.Fragment>
78
- )) || [];
76
+ const nodes = this.slots.get(slot)?.map((fn, i) => {
77
+ const SlotBridge = () => {
78
+ const dynamicProps = useContext(ScopeContext);
79
+ return <React.Fragment>{fn(dynamicProps)}</React.Fragment>;
80
+ };
81
+
82
+ return <SlotBridge key={i} />;
83
+ }) || [];
84
+
79
85
  this.cache.set(slot, nodes);
80
86
  }
81
87
  }
@@ -1,3 +1,3 @@
1
1
 
2
- export type Slot = () => React.ReactNode
2
+ export type Slot = (props?: any) => React.ReactNode
3
3
  export type ScheduledSlot = { slot: string, fn: Slot, priority: number }
@@ -7,6 +7,8 @@ import * as React from 'react';
7
7
  export declare type ObjectType = Record<string, any>;
8
8
 
9
9
  export type Slot = () => React.ReactNode;
10
+ interface SlotRegistry {}
11
+ type SlotKey = keyof SlotRegistry | (string & {});
10
12
 
11
13
  /**
12
14
  * Función para ejecutar lógica de negocio (Acciones).
@@ -56,8 +58,8 @@ export declare class PlcAPI<S extends ObjectType> {
56
58
  override<K extends string>(key: K & "root", data: any, slot?: string): void
57
59
  // --- Gestión de UI (Slots & Rendering) ---
58
60
 
59
- register(slot: string, node: () => React.ReactNode): void;
60
- register<K extends string>(slot: string, node: (data: any) => React.ReactNode, dependencyKey: K): void;
61
+ register(slot: string, node: (props?: any) => React.ReactNode): void;
62
+ register<K extends string>(slot: string, node: (data: any, props?: any) => React.ReactNode, dependencyKey: K): void;
61
63
 
62
64
  /** Envuelve un slot existente (Decorador/Wrapper) */
63
65
  wrap(slot: string, fn: (next: () => React.ReactNode) => () => React.ReactNode): void;
@@ -88,15 +90,18 @@ export declare class PlcAPI<S extends ObjectType> {
88
90
  */
89
91
  scope<T = any>(key: string): {
90
92
  get: () => T;
91
- update: (updater: (draft: T) => void) => void;
92
- connect: (renderer: (data: T) => React.ReactNode) => React.FC;
93
- render: (slotName: string) => React.ReactNode | null;
93
+ update: (updater: (draft: T) => void, slot?: string, triggerKey?: string) => void;
94
+ connect: <P = {}, R = any>(
95
+ selector: (data: T, props: P) => R
96
+ ) => (WrappedComponent: React.ComponentType<P & R>) => React.FC<P>;
97
+
98
+ render: (slotName: SlotKey) => React.FC;
94
99
  receive: (context?: any) => any;
95
100
  root: PlcAPI<S>;
96
101
  };
97
102
 
98
103
  /** Conecta un componente a una parte del estado (HOC) */
99
- connect<T = any>(key: string, renderer: (data: T) => React.ReactNode): React.FC;
104
+ connect<State = any, OwnProps = {}, ResultProps = {}>(key: string, selector: (state: State, props: OwnProps) => ResultProps): (WrappedComponent: React.ComponentType<OwnProps & ResultProps>) => React.FC<OwnProps>;
100
105
 
101
106
  // --- Pipeline de Datos (Transforms) ---
102
107