plug-code 1.1.16 → 1.2.0

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,6 +1,6 @@
1
1
  Plug&Code License
2
2
 
3
- Copyright (c) 2026 [TU NOMBRE]
3
+ Copyright (c) 2026 AlaunS
4
4
 
5
5
  All rights reserved.
6
6
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "plug-code",
3
- "version": "1.1.16",
3
+ "version": "1.2.0",
4
4
  "description": "",
5
5
  "main": "src/plug-code.tsx",
6
6
  "types": "types/plug-code.d.ts",
@@ -1,11 +1,12 @@
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";
7
7
 
8
8
  import type { ChannelRegistry, CommandRegistry, SlotRegistry } from "../types/registry";
9
+ import { isEqual } from "../helpers/core";
9
10
 
10
11
  type ChannelKey = keyof ChannelRegistry | (string & {});
11
12
  type ChannelData<K> = K extends keyof ChannelRegistry ? ChannelRegistry[K] : any;
@@ -20,7 +21,6 @@ type CommandResult<K> = K extends keyof CommandRegistry
20
21
 
21
22
  type SlotKey = keyof SlotRegistry | (string & {});
22
23
 
23
-
24
24
  export class PlcAPI<S extends ObjectType> {
25
25
  private store: PlcStore<S>
26
26
  private pipeline: PlcPipeline<S>
@@ -28,13 +28,24 @@ 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
35
34
  this.pipeline = new PlcPipeline(store)
36
35
  }
37
36
 
37
+ redraw(key: string): void {
38
+ const currentData = this.substores.get(key);
39
+ if (currentData === undefined) return;
40
+
41
+ const dataRef = typeof currentData === 'object' && currentData !== null
42
+ ? (Array.isArray(currentData) ? [...currentData] : { ...currentData })
43
+ : currentData;
44
+
45
+ this.substores.set(key, dataRef);
46
+ this.store.set(key as any, dataRef);
47
+ }
48
+
38
49
  watch<T>(
39
50
  storeKey: string,
40
51
  selector: (data: any) => T,
@@ -122,57 +133,67 @@ export class PlcAPI<S extends ObjectType> {
122
133
  return () => lastValue
123
134
  }
124
135
 
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) {
136
+ register(slot: SlotKey, node: (props?: any) => React.ReactNode): void;
137
+ register<K extends string>(slot: SlotKey, node: (data: any, props?: any) => React.ReactNode, dependencyKey: K): void;
138
+ register(slot: SlotKey, node: (arg1?: any, arg2?: any) => React.ReactNode, dependencyKey?: string) {
128
139
  if (dependencyKey) {
129
- const ConnectedWrapper = () => {
130
- const [data, setData] = useState(() => this.substores.get(dependencyKey));
140
+ const ConnectedWrapper = (props: any) => {
141
+ const [storeData, setStoreData] = useState(() => this.substores.get(dependencyKey));
131
142
 
132
143
  useEffect(() => {
133
144
  const unsubscribe = this.store.subscribe(dependencyKey as any, () => {
134
- setData(this.substores.get(dependencyKey));
145
+ setStoreData(this.substores.get(dependencyKey));
135
146
  });
136
147
  return unsubscribe;
137
148
  }, []);
138
149
 
139
- return node(data);
150
+ return <>{node(storeData, props)}</>;
140
151
  };
141
152
 
142
153
  this.store.batch(() => {
143
- this.pipeline.register(slot as string, () => <ConnectedWrapper />);
154
+ this.pipeline.register(slot as string, (p: any) => <ConnectedWrapper {...p} />);
144
155
  });
145
156
  }
146
157
  else {
147
158
  this.store.batch(() => {
148
- this.pipeline.register(slot as string, node as () => React.ReactNode);
159
+ this.pipeline.register(slot as string, node);
149
160
  });
150
161
  }
151
162
  }
152
163
 
153
164
  scope<T = any>(key: string | "root"): {
154
165
  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;
166
+ update: (updater: (draft: T) => void, slot?: string, triggerKey?: string) => void;
167
+ connect: <P = {}, R = any>(
168
+ selector: (data: T, props: P) => R
169
+ ) => (WrappedComponent: React.ComponentType<P & R>) => React.FC<P>;
170
+
171
+ render: (slotName: SlotKey) => React.FC;
158
172
  receive: (context?: any) => any;
159
173
  root: PlcAPI<S>;
160
174
  } {
161
175
  return {
162
176
  get: (): T => this.getData(key),
163
177
 
164
- update: (updater: (draft: T) => void) => {
165
- this.update(key as any, updater);
178
+ update: (updater: (draft: T) => void, slot?: string, triggerKey?: string) => {
179
+ this.update(key as any, updater, slot, triggerKey);
166
180
  },
167
181
 
168
- connect: (renderer: (data: T) => React.ReactNode) => {
169
- return this.connect(key, renderer);
182
+ connect: <P = {}, R = any>(
183
+ selector: (data: T, props: P) => R
184
+ ) => {
185
+ return this.connect(key, selector);
170
186
  },
171
187
 
172
188
  render: (slotName: SlotKey) => {
173
- return this.connect(key, (localData) => {
174
- return this.pipeline.render(slotName as string, localData) as React.ReactNode;
175
- }) as any;
189
+ const ScopedSlotRenderer = ({ _scopeData }: { _scopeData: T }) => {
190
+ return <>{this.pipeline.render(slotName as string, _scopeData)}</>;
191
+ };
192
+
193
+ return this.connect(
194
+ key,
195
+ (data: T) => ({ _scopeData: data })
196
+ )(ScopedSlotRenderer) as any;
176
197
  },
177
198
 
178
199
  receive: (context: any = {}) => {
@@ -184,22 +205,52 @@ export class PlcAPI<S extends ObjectType> {
184
205
  };
185
206
  }
186
207
 
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));
208
+ connect<State = any, OwnProps = {}, ResultProps = {}>(
209
+ key: string,
210
+ selector: (state: State, props: OwnProps) => ResultProps
211
+ ): (WrappedComponent: React.ComponentType<OwnProps & ResultProps>) => React.FC<OwnProps> {
212
+ return (WrappedComponent: React.ComponentType<OwnProps & ResultProps>) => {
213
+
214
+ const ConnectedComponent: React.FC<OwnProps> = (props) => {
215
+ const propsRef = useRef(props);
216
+ propsRef.current = props;
190
217
 
191
- useEffect(() => {
192
- const unsubscribe = this.store.subscribe(key as any, () => {
193
- setData(this.substores.get(key));
218
+ const [slice, setSlice] = useState<ResultProps>(() => {
219
+ const data = this.substores.get(key);
220
+ return selector(data, props);
194
221
  });
195
222
 
196
- return () => {
197
- unsubscribe();
198
- };
199
- }, []);
223
+ useEffect(() => {
224
+ const unsubscribe = this.store.subscribe(key as any, () => {
225
+ const currentData = this.substores.get(key);
226
+ const newSlice = selector(currentData, propsRef.current);
227
+ setSlice(prev => {
228
+ if (isEqual(prev, newSlice)) return prev;
229
+ return newSlice;
230
+ });
231
+ });
232
+ return unsubscribe;
233
+ }, []);
234
+
235
+ useEffect(() => {
236
+ const currentData = this.substores.get(key);
237
+ const newSlice = selector(currentData, props);
238
+
239
+ setSlice(prev => {
240
+ if (isEqual(prev, newSlice)) return prev;
241
+ return newSlice;
242
+ });
243
+ }, [props]);
200
244
 
201
- if (data === undefined) return null;
202
- return <>{renderer(data)}</>;
245
+ if (slice === undefined && this.substores.get(key) === undefined) return null;
246
+
247
+ return <WrappedComponent {...props} {...slice} />;
248
+ };
249
+
250
+ const displayName = WrappedComponent.displayName || WrappedComponent.name || 'Component';
251
+ ConnectedComponent.displayName = `Connect(${displayName})`;
252
+
253
+ return ConnectedComponent;
203
254
  };
204
255
  }
205
256
 
@@ -215,8 +266,8 @@ export class PlcAPI<S extends ObjectType> {
215
266
  })
216
267
  }
217
268
 
218
- render(slot: SlotKey) {
219
- return this.pipeline.render(slot as string)
269
+ render(slot: SlotKey, props?: any) {
270
+ return this.pipeline.render(slot as string, props)
220
271
  }
221
272
 
222
273
  invalidate(slot?: SlotKey) {
@@ -330,7 +381,6 @@ export class PlcAPI<S extends ObjectType> {
330
381
  }
331
382
 
332
383
  if (triggerKey && triggerKey !== key) {
333
-
334
384
  const triggerData = this.substores.get(triggerKey);
335
385
 
336
386
  if (triggerData) {
@@ -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
  }
@@ -0,0 +1,10 @@
1
+ export const isEqual = (a: any, b: any) => {
2
+ if (a === b) return true;
3
+ if (typeof a === 'object' && a !== null && typeof b === 'object' && b !== null) {
4
+ const keysA = Object.keys(a);
5
+ const keysB = Object.keys(b);
6
+ if (keysA.length !== keysB.length) return false;
7
+ return keysA.every(k => a[k] === b[k]);
8
+ }
9
+ return false;
10
+ }
@@ -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).
@@ -55,9 +57,10 @@ export declare class PlcAPI<S extends ObjectType> {
55
57
  watch<T>(storeKey: string, selector: (data: any) => T, callback: (newValue: T, oldValue: T) => void): () => void
56
58
  override<K extends string>(key: K & "root", data: any, slot?: string): void
57
59
  // --- 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;
60
+
61
+ redraw(key: string): void
62
+ register(slot: string, node: (props?: any) => React.ReactNode): void;
63
+ register<K extends string>(slot: string, node: (data: any, props?: any) => React.ReactNode, dependencyKey: K): void;
61
64
 
62
65
  /** Envuelve un slot existente (Decorador/Wrapper) */
63
66
  wrap(slot: string, fn: (next: () => React.ReactNode) => () => React.ReactNode): void;
@@ -88,15 +91,18 @@ export declare class PlcAPI<S extends ObjectType> {
88
91
  */
89
92
  scope<T = any>(key: string): {
90
93
  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;
94
+ update: (updater: (draft: T) => void, slot?: string, triggerKey?: string) => void;
95
+ connect: <P = {}, R = any>(
96
+ selector: (data: T, props: P) => R
97
+ ) => (WrappedComponent: React.ComponentType<P & R>) => React.FC<P>;
98
+
99
+ render: (slotName: SlotKey) => React.FC;
94
100
  receive: (context?: any) => any;
95
101
  root: PlcAPI<S>;
96
102
  };
97
103
 
98
104
  /** Conecta un componente a una parte del estado (HOC) */
99
- connect<T = any>(key: string, renderer: (data: T) => React.ReactNode): React.FC;
105
+ connect<State = any, OwnProps = {}, ResultProps = {}>(key: string, selector: (state: State, props: OwnProps) => ResultProps): (WrappedComponent: React.ComponentType<OwnProps & ResultProps>) => React.FC<OwnProps>;
100
106
 
101
107
  // --- Pipeline de Datos (Transforms) ---
102
108