plug-code 1.2.0 → 2.0.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 +135 -94
- package/dist/core/helpers/core.d.ts +2 -0
- package/dist/core/helpers/core.js +32 -0
- package/dist/core/hooks/plcHooks.d.ts +15 -0
- package/dist/core/hooks/plcHooks.js +47 -0
- package/dist/core/plcAPI.d.ts +83 -0
- package/dist/core/plcAPI.js +472 -0
- package/dist/core/plcPipeline.d.ts +8 -0
- package/dist/core/plcPipeline.js +35 -0
- package/dist/core/plcScheduler.d.ts +7 -0
- package/dist/core/plcScheduler.js +22 -0
- package/dist/core/plcStore.d.ts +33 -0
- package/dist/core/plcStore.js +159 -0
- package/dist/core/ui/plcCore.d.ts +8 -0
- package/dist/core/ui/plcCore.js +40 -0
- package/dist/core/ui/plcErrorBoundary.d.ts +17 -0
- package/dist/core/ui/plcErrorBoundary.js +17 -0
- package/dist/core/ui/plcInspector.d.ts +5 -0
- package/dist/core/ui/plcInspector.js +27 -0
- package/dist/core/ui/plcLayout.d.ts +28 -0
- package/dist/core/ui/plcLayout.js +125 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +8 -0
- package/dist/types/core/api.d.ts +7 -0
- package/dist/types/core/api.js +1 -0
- package/dist/types/core/general.d.ts +15 -0
- package/dist/types/core/general.js +1 -0
- package/dist/types/core/registry.d.ts +7 -0
- package/dist/types/core/registry.js +1 -0
- package/dist/types/core/ui.d.ts +7 -0
- package/dist/types/core/ui.js +1 -0
- package/dist/types/registry.d.ts +20 -0
- package/dist/types/registry.js +1 -0
- package/package.json +16 -22
- package/index.d.ts +0 -1
- package/src/contexts/pipeline.tsx +0 -4
- package/src/core/plcAPI.tsx +0 -393
- package/src/core/plcPipeline.tsx +0 -87
- package/src/core/plcStore.tsx +0 -94
- package/src/helpers/core.ts +0 -10
- package/src/plug-code.tsx +0 -64
- package/src/types/api.ts +0 -8
- package/src/types/features.ts +0 -7
- package/src/types/general.ts +0 -2
- package/src/types/pipeline.ts +0 -3
- package/src/types/registry.ts +0 -4
- package/src/types/store.ts +0 -2
- package/tsconfig.json +0 -15
- package/types/plug-code.d.ts +0 -164
package/src/core/plcAPI.tsx
DELETED
|
@@ -1,393 +0,0 @@
|
|
|
1
|
-
import { produce } from "immer";
|
|
2
|
-
import type { ObjectType } from "../types/general";
|
|
3
|
-
import { useEffect, useRef, useState } from "react";
|
|
4
|
-
import { PlcPipeline } from "./plcPipeline";
|
|
5
|
-
import type { PlcStore } from "./plcStore";
|
|
6
|
-
import type { CommandFn, transformerType } from "../types/api";
|
|
7
|
-
|
|
8
|
-
import type { ChannelRegistry, CommandRegistry, SlotRegistry } from "../types/registry";
|
|
9
|
-
import { isEqual } from "../helpers/core";
|
|
10
|
-
|
|
11
|
-
type ChannelKey = keyof ChannelRegistry | (string & {});
|
|
12
|
-
type ChannelData<K> = K extends keyof ChannelRegistry ? ChannelRegistry[K] : any;
|
|
13
|
-
|
|
14
|
-
type CommandKey = keyof CommandRegistry | (string & {});
|
|
15
|
-
type CommandPayload<K> = K extends keyof CommandRegistry
|
|
16
|
-
? (CommandRegistry[K] extends { payload: infer P } ? P : any)
|
|
17
|
-
: any;
|
|
18
|
-
type CommandResult<K> = K extends keyof CommandRegistry
|
|
19
|
-
? (CommandRegistry[K] extends { result: infer R } ? R : any)
|
|
20
|
-
: any;
|
|
21
|
-
|
|
22
|
-
type SlotKey = keyof SlotRegistry | (string & {});
|
|
23
|
-
|
|
24
|
-
export class PlcAPI<S extends ObjectType> {
|
|
25
|
-
private store: PlcStore<S>
|
|
26
|
-
private pipeline: PlcPipeline<S>
|
|
27
|
-
private substores = new Map<string, any>()
|
|
28
|
-
|
|
29
|
-
private transformers = new Map<string, transformerType[]>()
|
|
30
|
-
private commands = new Map<string, CommandFn>();
|
|
31
|
-
|
|
32
|
-
constructor(store: PlcStore<S>) {
|
|
33
|
-
this.store = store
|
|
34
|
-
this.pipeline = new PlcPipeline(store)
|
|
35
|
-
}
|
|
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
|
-
|
|
49
|
-
watch<T>(
|
|
50
|
-
storeKey: string,
|
|
51
|
-
selector: (data: any) => T,
|
|
52
|
-
callback: (newValue: T, oldValue: T) => void
|
|
53
|
-
): () => void {
|
|
54
|
-
let previousValue = selector(this.getData(storeKey));
|
|
55
|
-
const unsubscribe = this.store.subscribe(storeKey as any, () => {
|
|
56
|
-
const currentData = this.getData(storeKey);
|
|
57
|
-
const newValue = selector(currentData);
|
|
58
|
-
|
|
59
|
-
if (newValue !== previousValue) {
|
|
60
|
-
const old = previousValue;
|
|
61
|
-
previousValue = newValue;
|
|
62
|
-
callback(newValue, old);
|
|
63
|
-
}
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
return unsubscribe;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
override<K extends string>(key: K & "root", data: any, slot?: string) {
|
|
70
|
-
this.substores.set(key, data);
|
|
71
|
-
this.store.set(key as any, data);
|
|
72
|
-
|
|
73
|
-
if (slot) {
|
|
74
|
-
this.invalidate(slot);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
replace<K extends string>(key: K & "root", data: Partial<any>, slot?: string) {
|
|
79
|
-
const currentSub = this.substores.get(key) || {};
|
|
80
|
-
const newSub = { ...currentSub, ...data };
|
|
81
|
-
|
|
82
|
-
this.substores.set(key, newSub);
|
|
83
|
-
this.store.set(key as any, newSub);
|
|
84
|
-
|
|
85
|
-
if (slot) {
|
|
86
|
-
this.invalidate(slot);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
derive<K extends string>(
|
|
91
|
-
outputKey: K,
|
|
92
|
-
dependencies: string[],
|
|
93
|
-
calculator: () => any
|
|
94
|
-
) {
|
|
95
|
-
let lastValue: any
|
|
96
|
-
let isComputing = false
|
|
97
|
-
|
|
98
|
-
let scheduled = false
|
|
99
|
-
|
|
100
|
-
const scheduleUpdate = () => {
|
|
101
|
-
if (scheduled) return
|
|
102
|
-
scheduled = true
|
|
103
|
-
|
|
104
|
-
queueMicrotask(() => {
|
|
105
|
-
scheduled = false
|
|
106
|
-
runCompute()
|
|
107
|
-
})
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
const runCompute = () => {
|
|
111
|
-
if (isComputing) return
|
|
112
|
-
|
|
113
|
-
isComputing = true
|
|
114
|
-
try {
|
|
115
|
-
const result = calculator()
|
|
116
|
-
lastValue = result
|
|
117
|
-
this.replace("root" as any, { [outputKey]: result })
|
|
118
|
-
} finally {
|
|
119
|
-
isComputing = false
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
runCompute()
|
|
125
|
-
|
|
126
|
-
dependencies.forEach(dep => {
|
|
127
|
-
this.store.subscribe(dep as any, () => {
|
|
128
|
-
if (isComputing) return
|
|
129
|
-
scheduleUpdate()
|
|
130
|
-
})
|
|
131
|
-
})
|
|
132
|
-
|
|
133
|
-
return () => lastValue
|
|
134
|
-
}
|
|
135
|
-
|
|
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) {
|
|
139
|
-
if (dependencyKey) {
|
|
140
|
-
const ConnectedWrapper = (props: any) => {
|
|
141
|
-
const [storeData, setStoreData] = useState(() => this.substores.get(dependencyKey));
|
|
142
|
-
|
|
143
|
-
useEffect(() => {
|
|
144
|
-
const unsubscribe = this.store.subscribe(dependencyKey as any, () => {
|
|
145
|
-
setStoreData(this.substores.get(dependencyKey));
|
|
146
|
-
});
|
|
147
|
-
return unsubscribe;
|
|
148
|
-
}, []);
|
|
149
|
-
|
|
150
|
-
return <>{node(storeData, props)}</>;
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
this.store.batch(() => {
|
|
154
|
-
this.pipeline.register(slot as string, (p: any) => <ConnectedWrapper {...p} />);
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
else {
|
|
158
|
-
this.store.batch(() => {
|
|
159
|
-
this.pipeline.register(slot as string, node);
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
scope<T = any>(key: string | "root"): {
|
|
165
|
-
get: () => T;
|
|
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;
|
|
172
|
-
receive: (context?: any) => any;
|
|
173
|
-
root: PlcAPI<S>;
|
|
174
|
-
} {
|
|
175
|
-
return {
|
|
176
|
-
get: (): T => this.getData(key),
|
|
177
|
-
|
|
178
|
-
update: (updater: (draft: T) => void, slot?: string, triggerKey?: string) => {
|
|
179
|
-
this.update(key as any, updater, slot, triggerKey);
|
|
180
|
-
},
|
|
181
|
-
|
|
182
|
-
connect: <P = {}, R = any>(
|
|
183
|
-
selector: (data: T, props: P) => R
|
|
184
|
-
) => {
|
|
185
|
-
return this.connect(key, selector);
|
|
186
|
-
},
|
|
187
|
-
|
|
188
|
-
render: (slotName: SlotKey) => {
|
|
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;
|
|
197
|
-
},
|
|
198
|
-
|
|
199
|
-
receive: (context: any = {}) => {
|
|
200
|
-
const currentData = this.getData(key);
|
|
201
|
-
return this.receive(key as any, currentData, context);
|
|
202
|
-
},
|
|
203
|
-
|
|
204
|
-
root: this
|
|
205
|
-
};
|
|
206
|
-
}
|
|
207
|
-
|
|
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;
|
|
217
|
-
|
|
218
|
-
const [slice, setSlice] = useState<ResultProps>(() => {
|
|
219
|
-
const data = this.substores.get(key);
|
|
220
|
-
return selector(data, props);
|
|
221
|
-
});
|
|
222
|
-
|
|
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]);
|
|
244
|
-
|
|
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;
|
|
254
|
-
};
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
wrap(slot: SlotKey, fn: (next: () => React.ReactNode) => () => React.ReactNode) {
|
|
258
|
-
this.store.batch(() => {
|
|
259
|
-
this.pipeline.wrap(slot as string, fn)
|
|
260
|
-
})
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
after(slot: SlotKey, node: () => React.ReactNode) {
|
|
264
|
-
this.store.batch(() => {
|
|
265
|
-
this.pipeline.register(slot as string, node)
|
|
266
|
-
})
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
render(slot: SlotKey, props?: any) {
|
|
270
|
-
return this.pipeline.render(slot as string, props)
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
invalidate(slot?: SlotKey) {
|
|
274
|
-
this.pipeline.invalidate(slot as string)
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
send<K extends ChannelKey>(
|
|
278
|
-
channel: K,
|
|
279
|
-
id: string,
|
|
280
|
-
fn: (data: ChannelData<K>, context: any) => ChannelData<K>,
|
|
281
|
-
priority: number = 0
|
|
282
|
-
) {
|
|
283
|
-
const channelStr = channel as string;
|
|
284
|
-
if (!this.transformers.has(channelStr)) {
|
|
285
|
-
this.transformers.set(channelStr, []);
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
const channelList = this.transformers.get(channelStr)!;
|
|
289
|
-
|
|
290
|
-
const existingIdx = channelList.findIndex(t => t.id === id);
|
|
291
|
-
if (existingIdx >= 0) {
|
|
292
|
-
channelList[existingIdx] = { id, priority, fn };
|
|
293
|
-
} else {
|
|
294
|
-
channelList.push({ id, priority, fn });
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
channelList.sort((a, b) => a.priority - b.priority);
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
receive<K extends ChannelKey>(
|
|
301
|
-
channel: K,
|
|
302
|
-
initialData: ChannelData<K>,
|
|
303
|
-
context: any = {}
|
|
304
|
-
): ChannelData<K> {
|
|
305
|
-
let currentData = initialData;
|
|
306
|
-
const channelList = this.transformers.get(channel as string) || [];
|
|
307
|
-
|
|
308
|
-
for (const transformer of channelList) {
|
|
309
|
-
try {
|
|
310
|
-
currentData = transformer.fn(currentData, context);
|
|
311
|
-
} catch (error) {
|
|
312
|
-
console.error(`[PlcAPI] Error in transform '${channel as string}/${transformer.id}':`, error);
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
return currentData as ChannelData<K>;
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
registerCommand<K extends CommandKey>(
|
|
320
|
-
id: K,
|
|
321
|
-
fn: CommandFn<CommandPayload<K>, CommandResult<K>>
|
|
322
|
-
) {
|
|
323
|
-
if (this.commands.has(id as string)) {
|
|
324
|
-
console.warn(`[PlcAPI] Overwriting command '${id as string}'`);
|
|
325
|
-
}
|
|
326
|
-
this.commands.set(id as string, fn as any);
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
wrapCommand<K extends CommandKey>(
|
|
330
|
-
id: K,
|
|
331
|
-
wrapper: (next: CommandFn<CommandPayload<K>, CommandResult<K>>) => CommandFn<CommandPayload<K>, CommandResult<K>>
|
|
332
|
-
) {
|
|
333
|
-
const currentFn = this.commands.get(id as string);
|
|
334
|
-
if (!currentFn) {
|
|
335
|
-
console.error(`[PlcAPI] Cannot wrap '${id as string}', command does not exist.`);
|
|
336
|
-
return;
|
|
337
|
-
}
|
|
338
|
-
this.commands.set(id as string, wrapper(currentFn) as any);
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
async execute<K extends CommandKey>(
|
|
342
|
-
id: K,
|
|
343
|
-
payload?: CommandPayload<K>
|
|
344
|
-
): Promise<CommandResult<K>> {
|
|
345
|
-
const fn = this.commands.get(id as string);
|
|
346
|
-
if (!fn) {
|
|
347
|
-
throw new Error(`[PlcAPI] Command '${id as string}' not found.`);
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
try {
|
|
351
|
-
return await fn(payload);
|
|
352
|
-
} catch (error) {
|
|
353
|
-
console.error(`[PlcAPI] Error executing '${id as string}':`, error);
|
|
354
|
-
throw error;
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
getData(key: string): any {
|
|
359
|
-
return this.substores.get(key)
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
subscribe(listener: () => void) {
|
|
363
|
-
return this.store.subscribe(listener);
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
createData<K extends string, T>(key: K, initialState: T) {
|
|
367
|
-
if (this.substores.has(key)) return
|
|
368
|
-
this.substores.set(key, initialState)
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
update<K extends keyof S>(key: string & "root", updater: (draft: any) => void, slot?: string, triggerKey?: string) {
|
|
372
|
-
const sub = this.substores.get(key)
|
|
373
|
-
if (!sub) return
|
|
374
|
-
|
|
375
|
-
const newSub = produce(sub, updater)
|
|
376
|
-
this.substores.set(key, newSub)
|
|
377
|
-
|
|
378
|
-
this.store.set(key as K, newSub as S[K])
|
|
379
|
-
if (slot) {
|
|
380
|
-
this.invalidate(slot)
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
if (triggerKey && triggerKey !== key) {
|
|
384
|
-
const triggerData = this.substores.get(triggerKey);
|
|
385
|
-
|
|
386
|
-
if (triggerData) {
|
|
387
|
-
const newTriggerRef = { ...triggerData };
|
|
388
|
-
this.substores.set(triggerKey, newTriggerRef);
|
|
389
|
-
this.store.set(triggerKey as any, newTriggerRef);
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
}
|
package/src/core/plcPipeline.tsx
DELETED
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import React, { useContext } from "react";
|
|
2
|
-
import { ScopeContext } from "../contexts/pipeline";
|
|
3
|
-
import type { ObjectType } from "../types/general";
|
|
4
|
-
import type { ScheduledSlot, Slot } from "../types/pipeline";
|
|
5
|
-
import type { PlcStore } from "./plcStore";
|
|
6
|
-
|
|
7
|
-
export class PlcPipeline<S extends ObjectType> {
|
|
8
|
-
private slots = new Map<string, Slot[]>()
|
|
9
|
-
private store: PlcStore<S>
|
|
10
|
-
private cache = new Map<string, React.ReactNode[]>()
|
|
11
|
-
private scheduleQueue: ScheduledSlot[] = []
|
|
12
|
-
|
|
13
|
-
constructor(store: PlcStore<S>) {
|
|
14
|
-
this.store = store
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
register(slot: string, fn: Slot, priority: number = 0) {
|
|
18
|
-
if (!this.slots.has(slot)) this.slots.set(slot, [])
|
|
19
|
-
this.slots.get(slot)!.push(fn)
|
|
20
|
-
|
|
21
|
-
this.scheduleQueue.push({ slot, fn, priority })
|
|
22
|
-
this.store.set(`slot:${slot}`, Math.random() as any)
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
wrap(slot: string, wrapper: (next: Slot) => Slot, priority: number = 0) {
|
|
26
|
-
const current = this.slots.get(slot) || []
|
|
27
|
-
this.slots.set(
|
|
28
|
-
slot,
|
|
29
|
-
current.map(fn => wrapper(fn))
|
|
30
|
-
)
|
|
31
|
-
|
|
32
|
-
current.forEach(fn => this.scheduleQueue.push({ slot, fn, priority }))
|
|
33
|
-
this.store.set(`slot:${slot}`, Math.random() as any)
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
render(slot: string, contextData?: any) {
|
|
37
|
-
if (this.scheduleQueue.length > 0) {
|
|
38
|
-
this.scheduleQueue.sort((a, b) => b.priority - a.priority);
|
|
39
|
-
|
|
40
|
-
const slotsToUpdate = new Set<string>();
|
|
41
|
-
this.scheduleQueue.forEach(item => slotsToUpdate.add(item.slot));
|
|
42
|
-
|
|
43
|
-
slotsToUpdate.forEach(slotName => {
|
|
44
|
-
this.regenerateCache(slotName);
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
this.scheduleQueue = [];
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
if (!this.cache.has(slot) && this.slots.has(slot)) {
|
|
51
|
-
this.regenerateCache(slot);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const content = this.cache.get(slot);
|
|
55
|
-
|
|
56
|
-
if (contextData !== undefined) {
|
|
57
|
-
return (
|
|
58
|
-
<ScopeContext.Provider value={contextData}>
|
|
59
|
-
{content}
|
|
60
|
-
</ScopeContext.Provider>
|
|
61
|
-
);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return content;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
invalidate(slot?: string) {
|
|
68
|
-
if (slot) {
|
|
69
|
-
this.cache.delete(slot)
|
|
70
|
-
} else {
|
|
71
|
-
this.cache.clear()
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
private regenerateCache(slot: string) {
|
|
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
|
-
|
|
85
|
-
this.cache.set(slot, nodes);
|
|
86
|
-
}
|
|
87
|
-
}
|
package/src/core/plcStore.tsx
DELETED
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import { produce, type Draft } from "immer";
|
|
2
|
-
import type { ObjectType } from "../types/general";
|
|
3
|
-
import type { Listener } from "../types/store";
|
|
4
|
-
|
|
5
|
-
export class PlcStore<S extends ObjectType> {
|
|
6
|
-
private state: S;
|
|
7
|
-
private listeners = new Set<Listener<S>>();
|
|
8
|
-
private batchQueue = new Set<keyof S | undefined>();
|
|
9
|
-
private debug: boolean;
|
|
10
|
-
|
|
11
|
-
private isBatching = false
|
|
12
|
-
|
|
13
|
-
constructor(initial: S, debug: boolean) {
|
|
14
|
-
debug = false;
|
|
15
|
-
|
|
16
|
-
this.state = initial;
|
|
17
|
-
this.debug = debug;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
get<K extends keyof S>(key: K): S[K] {
|
|
21
|
-
return this.state[key]
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
getState(): S {
|
|
25
|
-
return this.state
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
set<K extends keyof S>(key: K, value: S[K]) {
|
|
29
|
-
if (this.state[key] === value) return
|
|
30
|
-
|
|
31
|
-
const oldValue = this.state[key]
|
|
32
|
-
this.state = produce(this.state, draft => {
|
|
33
|
-
(draft as any)[key] = value
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
if (this.debug) {
|
|
37
|
-
console.log(`[Store][set] ${String(key)}:`, { oldValue, newValue: value })
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
if (this.isBatching) {
|
|
42
|
-
this.batchQueue.add(key)
|
|
43
|
-
} else {
|
|
44
|
-
this.emit(key)
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
batch(updater: (draft: Draft<S>) => void) {
|
|
49
|
-
this.isBatching = true
|
|
50
|
-
|
|
51
|
-
const nextState = produce(this.state, draft => {
|
|
52
|
-
updater(draft)
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
if (nextState === this.state) {
|
|
56
|
-
this.isBatching = false
|
|
57
|
-
return
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
this.state = nextState
|
|
61
|
-
this.isBatching = false
|
|
62
|
-
|
|
63
|
-
this.batchQueue.forEach(key => this.emit(key))
|
|
64
|
-
this.batchQueue.clear()
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
subscribe(listener: () => void): () => void
|
|
68
|
-
subscribe<K extends keyof S>(key: K, listener: () => void): () => void
|
|
69
|
-
subscribe<K extends keyof S>(keyOrListener: K | (() => void), maybeListener?: () => void): () => void {
|
|
70
|
-
let listenerObj: Listener<S>
|
|
71
|
-
|
|
72
|
-
if (typeof keyOrListener === "function") {
|
|
73
|
-
listenerObj = { callback: keyOrListener }
|
|
74
|
-
} else {
|
|
75
|
-
listenerObj = { key: keyOrListener, callback: maybeListener! }
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
this.listeners.add(listenerObj)
|
|
79
|
-
return () => {
|
|
80
|
-
this.listeners.delete(listenerObj)
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
private emit(changedKey?: keyof S) {
|
|
85
|
-
this.listeners.forEach(l => {
|
|
86
|
-
if (!l.key || l.key === changedKey) {
|
|
87
|
-
if (this.debug) {
|
|
88
|
-
console.log(`[Store][emit] key: ${String(changedKey)}`, l)
|
|
89
|
-
}
|
|
90
|
-
l.callback()
|
|
91
|
-
}
|
|
92
|
-
})
|
|
93
|
-
}
|
|
94
|
-
}
|
package/src/helpers/core.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
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
|
-
}
|
package/src/plug-code.tsx
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import { enableMapSet } from "immer";
|
|
2
|
-
import type { ObjectType } from "./types/general";
|
|
3
|
-
import { PlcAPI } from "./core/plcAPI";
|
|
4
|
-
import { useEffect, useMemo, useState } from "react";
|
|
5
|
-
import { PlcStore } from "./core/plcStore";
|
|
6
|
-
|
|
7
|
-
enableMapSet();
|
|
8
|
-
|
|
9
|
-
export function createPlugAndCode<S extends ObjectType>(
|
|
10
|
-
setupSystem: (api: PlcAPI<S>) => void
|
|
11
|
-
) {
|
|
12
|
-
const FeatureHost = ({ api, children }: { api: PlcAPI<any>, children?: React.ReactNode }) => (
|
|
13
|
-
<>
|
|
14
|
-
{api.render("root")}
|
|
15
|
-
{children}
|
|
16
|
-
</>
|
|
17
|
-
)
|
|
18
|
-
|
|
19
|
-
function SystemPlcRoot({ api, children }: { api: PlcAPI<any>, children?: React.ReactNode }) {
|
|
20
|
-
if (!api) return null;
|
|
21
|
-
return (
|
|
22
|
-
<FeatureHost api={api}>
|
|
23
|
-
{children}
|
|
24
|
-
</FeatureHost>
|
|
25
|
-
)
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function useSystemPlc<T extends object>(initialProps: T) {
|
|
29
|
-
|
|
30
|
-
const api = useMemo(() => {
|
|
31
|
-
const store = new PlcStore({}, true);
|
|
32
|
-
const api = new PlcAPI(store);
|
|
33
|
-
|
|
34
|
-
api.createData("root", initialProps);
|
|
35
|
-
|
|
36
|
-
setupSystem(api as unknown as PlcAPI<S>);
|
|
37
|
-
|
|
38
|
-
return api;
|
|
39
|
-
}, []);
|
|
40
|
-
|
|
41
|
-
useEffect(() => {
|
|
42
|
-
api.override("root", initialProps)
|
|
43
|
-
}, [initialProps, api]);
|
|
44
|
-
|
|
45
|
-
const useSelector = <Result,>(selector: (state: any) => Result): Result => {
|
|
46
|
-
const [snap, setSnap] = useState(() => selector(api.getData("root")));
|
|
47
|
-
|
|
48
|
-
useEffect(() => {
|
|
49
|
-
return api.subscribe(() => {
|
|
50
|
-
const fullState = api.getData("root");
|
|
51
|
-
setSnap(selector(fullState));
|
|
52
|
-
});
|
|
53
|
-
}, [selector]);
|
|
54
|
-
|
|
55
|
-
return snap;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
return {
|
|
59
|
-
api,
|
|
60
|
-
useSelector
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
return { useSystemPlc, SystemPlcRoot }
|
|
64
|
-
}
|
package/src/types/api.ts
DELETED
package/src/types/features.ts
DELETED
package/src/types/general.ts
DELETED
package/src/types/pipeline.ts
DELETED
package/src/types/registry.ts
DELETED
package/src/types/store.ts
DELETED