plug-code 1.0.2 → 1.1.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/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
@@ -1,9 +1,15 @@
1
1
  {
2
2
  "name": "plug-code",
3
- "version": "1.0.2",
3
+ "version": "1.1.0",
4
4
  "description": "",
5
- "main": "index.js",
6
- "types": "types/src/plug-code.d.ts",
5
+ "main": "src/plug-code.tsx",
6
+ "types": "types/plug-code.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "import": "./src/plug-code.tsx",
10
+ "require": "./src/plug-code.tsx"
11
+ }
12
+ },
7
13
  "scripts": {
8
14
  "test": "echo \"Error: no test specified\" && exit 1",
9
15
  "build:types": "tsc -p tsconfig.json"
@@ -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
- private transformers: transformerType[] = []
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
- register(slot: string, node: () => React.ReactNode): void;
20
- register<K extends string>(slot: string, node: (data: any) => React.ReactNode, dependencyKey: K): void;
21
- register(slot: string, node: (data?: any) => React.ReactNode, dependencyKey?: string) {
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: string) => React.ReactNode | null;
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: string) => {
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: string, fn: (next: () => React.ReactNode) => () => React.ReactNode) {
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: string, node: () => React.ReactNode) {
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: string) {
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?: string) {
117
- this.pipeline.invalidate(slot)
151
+ invalidate(slot?: SlotKey) {
152
+ this.pipeline.invalidate(slot as string)
118
153
  }
119
154
 
120
- send(id: string, fn: (data: any, context: any) => any, priority: number) {
121
- this.transformers.push({ id, priority, fn });
122
- this.transformers.sort((a, b) => a.priority - b.priority);
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(initialData: any, context: any = {}) {
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 this.transformers) {
186
+ for (const transformer of channelList) {
129
187
  try {
130
188
  currentData = transformer.fn(currentData, context);
131
189
  } catch (error) {
132
- console.error(`[Pipeline] Error en transformador '${transformer.id}':`, 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
- export function createPlugAndCode<S extends ObjectType>(features: FeatureType<S>[]) {
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
- for (const feature of features) {
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 = { id: string, priority: number, fn: (data: any, context: any) => any }
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;
@@ -0,0 +1,4 @@
1
+
2
+ export interface ChannelRegistry {}
3
+ export interface CommandRegistry {}
4
+ export interface SlotRegistry {}
@@ -1,13 +1,156 @@
1
- import type { ObjectType } from "./types/general";
2
- import type { FeatureType } from "./types/features";
3
- import { PlcAPI } from "./core/plcAPI";
4
- export declare function createPlugAndCode<S extends ObjectType>(features: FeatureType<S>[]): {
5
- useSystemPlc: <T extends object>(initialProps: T) => {
6
- api: PlcAPI<{}>;
7
- useSelector: <Result>(selector: (state: any) => Result) => Result;
8
- };
9
- SystemPlcRoot: ({ api, children }: {
10
- api: PlcAPI<any>;
11
- children?: React.ReactNode;
12
- }) => import("react/jsx-runtime").JSX.Element;
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;
@@ -1,2 +0,0 @@
1
- export declare const ScopeContext: import("react").Context<any>;
2
- export declare const useScopeData: <T>() => T;
@@ -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
- }
@@ -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
- }
@@ -1,5 +0,0 @@
1
- export type transformerType = {
2
- id: string;
3
- priority: number;
4
- fn: (data: any, context: any) => any;
5
- };
@@ -1,6 +0,0 @@
1
- import type { PlcAPI } from "../core/plcAPI";
2
- import type { ObjectType } from "./general";
3
- export type FeatureType<S extends ObjectType> = {
4
- name: string;
5
- setup?: (api: PlcAPI<S>) => void | (() => void);
6
- };
@@ -1 +0,0 @@
1
- export type ObjectType = Record<string, any>;
@@ -1,6 +0,0 @@
1
- export type Slot = () => React.ReactNode;
2
- export type ScheduledSlot = {
3
- slot: string;
4
- fn: Slot;
5
- priority: number;
6
- };
@@ -1,4 +0,0 @@
1
- export type Listener<S> = {
2
- key?: keyof S;
3
- callback: () => void;
4
- };