@logixjs/react 0.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.
Files changed (42) hide show
  1. package/README.md +64 -0
  2. package/dist/Hooks.cjs +2194 -0
  3. package/dist/Hooks.d.cts +77 -0
  4. package/dist/Hooks.d.ts +77 -0
  5. package/dist/Hooks.js +27 -0
  6. package/dist/ModuleRef-wZSQ3Wwo.d.cts +73 -0
  7. package/dist/ModuleRef-wZSQ3Wwo.d.ts +73 -0
  8. package/dist/ModuleScope.cjs +2695 -0
  9. package/dist/ModuleScope.d.cts +62 -0
  10. package/dist/ModuleScope.d.ts +62 -0
  11. package/dist/ModuleScope.js +9 -0
  12. package/dist/Platform.cjs +60 -0
  13. package/dist/Platform.d.cts +6 -0
  14. package/dist/Platform.d.ts +6 -0
  15. package/dist/Platform.js +6 -0
  16. package/dist/ReactPlatform.cjs +2813 -0
  17. package/dist/ReactPlatform.d.cts +40 -0
  18. package/dist/ReactPlatform.d.ts +40 -0
  19. package/dist/ReactPlatform.js +11 -0
  20. package/dist/RuntimeProvider.cjs +1618 -0
  21. package/dist/RuntimeProvider.d.cts +66 -0
  22. package/dist/RuntimeProvider.d.ts +66 -0
  23. package/dist/RuntimeProvider.js +8 -0
  24. package/dist/chunk-2WFULYPJ.js +856 -0
  25. package/dist/chunk-4G7H66OY.js +54 -0
  26. package/dist/chunk-G5MRIFKK.js +95 -0
  27. package/dist/chunk-JXAJTWSZ.js +773 -0
  28. package/dist/chunk-PYWHL7TA.js +351 -0
  29. package/dist/chunk-UFFCJGSZ.js +981 -0
  30. package/dist/chunk-VFWWMK67.js +0 -0
  31. package/dist/chunk-ZANGOPUQ.js +34 -0
  32. package/dist/global.d.cjs +1 -0
  33. package/dist/global.d.d.cts +9 -0
  34. package/dist/global.d.d.ts +9 -0
  35. package/dist/global.d.js +0 -0
  36. package/dist/index.cjs +3118 -0
  37. package/dist/index.d.cts +10 -0
  38. package/dist/index.d.ts +10 -0
  39. package/dist/index.js +44 -0
  40. package/dist/useDispatch-BnzYVkRE.d.ts +81 -0
  41. package/dist/useDispatch-CnO5-66H.d.cts +81 -0
  42. package/package.json +58 -0
@@ -0,0 +1,351 @@
1
+ import {
2
+ RuntimeProviderNotFoundError,
3
+ makeModuleActions,
4
+ makeModuleDispatchers,
5
+ resolveImportedModuleRef,
6
+ useModuleRuntime,
7
+ useRuntime,
8
+ useStableId
9
+ } from "./chunk-UFFCJGSZ.js";
10
+ import {
11
+ RuntimeContext,
12
+ getModuleCache,
13
+ isDevEnv,
14
+ stableHash
15
+ } from "./chunk-2WFULYPJ.js";
16
+
17
+ // src/internal/hooks/useLocalModule.ts
18
+ import React, { useEffect, useMemo } from "react";
19
+ import * as Logix from "@logixjs/core";
20
+ import { Context, Effect, Layer, Scope } from "effect";
21
+ function isModuleTag(source) {
22
+ if (!source || typeof source !== "object" && typeof source !== "function") {
23
+ return false;
24
+ }
25
+ const candidate = source;
26
+ return candidate._kind === "ModuleTag";
27
+ }
28
+ function useLocalModule(source, second) {
29
+ const runtime = useRuntime();
30
+ const runtimeContext = React.useContext(RuntimeContext);
31
+ if (!runtimeContext) {
32
+ throw new RuntimeProviderNotFoundError("useLocalModule");
33
+ }
34
+ const cache = useMemo(
35
+ () => getModuleCache(runtime, runtimeContext.reactConfigSnapshot, runtimeContext.configVersion),
36
+ [runtime, runtimeContext.reactConfigSnapshot, runtimeContext.configVersion]
37
+ );
38
+ const componentId = useStableId();
39
+ const moduleTag = React.useMemo(() => {
40
+ if (Logix.Module.is(source)) {
41
+ return source.tag;
42
+ }
43
+ if (isModuleTag(source)) {
44
+ return source;
45
+ }
46
+ return null;
47
+ }, [source]);
48
+ const def = React.useMemo(() => {
49
+ if (Logix.Module.is(source) || isModuleTag(source)) {
50
+ return source;
51
+ }
52
+ return void 0;
53
+ }, [source]);
54
+ const isModule = moduleTag !== null;
55
+ const moduleOptions = isModule ? second : void 0;
56
+ const deps = isModule ? moduleOptions?.deps ?? [] : second ?? [];
57
+ const { key, ownerId } = useMemo(() => {
58
+ const depsHash = stableHash(deps);
59
+ if (isModule) {
60
+ const module = moduleTag;
61
+ const baseKey = moduleOptions?.key ?? module.id ?? "module";
62
+ return {
63
+ key: `local:${baseKey}:${componentId}:${depsHash}`,
64
+ ownerId: module.id ?? "Module"
65
+ };
66
+ }
67
+ return {
68
+ key: `local:factory:${componentId}:${depsHash}`,
69
+ ownerId: void 0
70
+ };
71
+ }, [isModule, moduleTag, moduleOptions?.key, deps, componentId]);
72
+ const factory = useMemo(() => {
73
+ if (isModule) {
74
+ return createModuleTagFactory(moduleTag, moduleOptions);
75
+ }
76
+ const factoryFn = source;
77
+ return (scope) => factoryFn().pipe(Scope.extend(scope));
78
+ }, [isModule, moduleTag, source, moduleOptions]);
79
+ const moduleRuntime = cache.readSync(key, factory, void 0, ownerId, {
80
+ entrypoint: "react.useLocalModule",
81
+ policyMode: runtimeContext.policy.mode,
82
+ warnSyncBlockingThresholdMs: 5
83
+ });
84
+ useEffect(() => cache.retain(key), [cache, key]);
85
+ const dispatch = useMemo(() => {
86
+ const base = (action) => {
87
+ runtime.runFork(moduleRuntime.dispatch(action));
88
+ };
89
+ return Object.assign(base, {
90
+ batch: (actions2) => {
91
+ runtime.runFork(moduleRuntime.dispatchBatch(actions2));
92
+ },
93
+ lowPriority: (action) => {
94
+ runtime.runFork(moduleRuntime.dispatchLowPriority(action));
95
+ }
96
+ });
97
+ }, [runtime, moduleRuntime]);
98
+ const tokens = useMemo(() => {
99
+ if (!def || typeof def !== "object" && typeof def !== "function") {
100
+ return void 0;
101
+ }
102
+ const candidate = def;
103
+ if (!candidate.actions || typeof candidate.actions !== "object") {
104
+ return void 0;
105
+ }
106
+ return candidate.actions;
107
+ }, [def]);
108
+ const actions = useMemo(() => makeModuleActions(dispatch), [dispatch]);
109
+ const dispatchers = useMemo(
110
+ () => tokens ? makeModuleDispatchers(dispatch, tokens) : makeModuleDispatchers(dispatch),
111
+ [dispatch, tokens]
112
+ );
113
+ return useMemo(
114
+ () => ({
115
+ def,
116
+ runtime: moduleRuntime,
117
+ dispatch,
118
+ actions,
119
+ dispatchers,
120
+ imports: {
121
+ get: (module) => resolveImportedModuleRef(runtime, moduleRuntime, module)
122
+ },
123
+ getState: moduleRuntime.getState,
124
+ setState: moduleRuntime.setState,
125
+ actions$: moduleRuntime.actions$,
126
+ changes: moduleRuntime.changes,
127
+ ref: moduleRuntime.ref
128
+ }),
129
+ [runtime, moduleRuntime, dispatch, actions, dispatchers, def]
130
+ );
131
+ }
132
+ function createModuleTagFactory(module, options) {
133
+ if (!options || options.initial === void 0) {
134
+ throw new Error("useLocalModule(module, options) \u9700\u8981\u63D0\u4F9B initial \u72B6\u6001");
135
+ }
136
+ const logics = options.logics ?? [];
137
+ return (scope) => Layer.buildWithScope(module.live(options.initial, ...logics), scope).pipe(
138
+ Effect.map((context) => {
139
+ const runtime = Context.get(context, module);
140
+ return runtime;
141
+ })
142
+ );
143
+ }
144
+
145
+ // src/internal/hooks/useLayerModule.ts
146
+ import React2 from "react";
147
+ import { Context as Context2, Effect as Effect2, Layer as Layer2 } from "effect";
148
+ function useLayerModule(module, layer, deps = []) {
149
+ const factory = React2.useCallback(
150
+ () => Layer2.build(layer).pipe(
151
+ Effect2.scoped,
152
+ Effect2.map((context) => Context2.get(context, module))
153
+ ),
154
+ // layer/module are typically constants; deps lets callers opt into rebuilding when needed.
155
+ [layer, module]
156
+ );
157
+ return useLocalModule(factory, deps);
158
+ }
159
+
160
+ // src/internal/hooks/useDispatch.ts
161
+ import { useMemo as useMemo2 } from "react";
162
+ function useDispatch(handle) {
163
+ const runtime = useRuntime();
164
+ const moduleRuntime = useModuleRuntime(handle);
165
+ return useMemo2(() => {
166
+ const base = (action) => {
167
+ runtime.runFork(moduleRuntime.dispatch(action));
168
+ };
169
+ return Object.assign(base, {
170
+ batch: (actions) => {
171
+ runtime.runFork(moduleRuntime.dispatchBatch(actions));
172
+ },
173
+ lowPriority: (action) => {
174
+ runtime.runFork(moduleRuntime.dispatchLowPriority(action));
175
+ }
176
+ });
177
+ }, [runtime, moduleRuntime]);
178
+ }
179
+
180
+ // src/internal/hooks/useModuleList.ts
181
+ import { useMemo as useMemo3, useRef } from "react";
182
+ function useModuleList(items, keyFn, factory) {
183
+ const cache = useMemo3(() => /* @__PURE__ */ new Map(), []);
184
+ const keyFnRef = useRef(keyFn);
185
+ keyFnRef.current = keyFn;
186
+ const factoryRef = useRef(factory);
187
+ factoryRef.current = factory;
188
+ return useMemo3(() => {
189
+ return items.map((item) => {
190
+ const key = keyFnRef.current(item);
191
+ if (!cache.has(key)) {
192
+ cache.set(key, factoryRef.current(key, item));
193
+ }
194
+ return cache.get(key);
195
+ });
196
+ }, [items, cache]);
197
+ }
198
+
199
+ // src/internal/hooks/useImportedModule.ts
200
+ import React3 from "react";
201
+ function useImportedModule(parent, module) {
202
+ const runtime = useRuntime();
203
+ const parentRuntime = useModuleRuntime(parent);
204
+ return React3.useMemo(() => resolveImportedModuleRef(runtime, parentRuntime, module), [runtime, parentRuntime, module]);
205
+ }
206
+
207
+ // src/internal/hooks/useProcesses.ts
208
+ import React4, { useContext, useEffect as useEffect2, useMemo as useMemo4, useRef as useRef2 } from "react";
209
+ import { Effect as Effect3, Exit, Scope as Scope2 } from "effect";
210
+ import * as Logix2 from "@logixjs/core";
211
+ var ProcessSubtreeRegistry = class {
212
+ constructor(runtime) {
213
+ this.runtime = runtime;
214
+ this.entries = /* @__PURE__ */ new Map();
215
+ }
216
+ retain(args) {
217
+ const existing = this.entries.get(args.key);
218
+ if (existing) {
219
+ if (existing.signature !== args.signature) {
220
+ throw new Error(
221
+ `[useProcesses] subtreeId "${args.key}" has already been claimed by a different process set. previous="${existing.signature}" current="${args.signature}". Use a different subtreeId or keep the processes list stable for the same subtree.`
222
+ );
223
+ }
224
+ existing.refCount += 1;
225
+ if (existing.gcTimeout) {
226
+ clearTimeout(existing.gcTimeout);
227
+ existing.gcTimeout = void 0;
228
+ }
229
+ return () => this.release({ key: args.key, gcTime: args.gcTime });
230
+ }
231
+ const scope = Effect3.runSync(Scope2.make());
232
+ const entry = {
233
+ key: args.key,
234
+ signature: args.signature,
235
+ scope,
236
+ refCount: 1
237
+ };
238
+ this.entries.set(args.key, entry);
239
+ args.install(scope);
240
+ return () => this.release({ key: args.key, gcTime: args.gcTime });
241
+ }
242
+ release(args) {
243
+ const entry = this.entries.get(args.key);
244
+ if (!entry) return;
245
+ entry.refCount = Math.max(0, entry.refCount - 1);
246
+ if (entry.refCount > 0) return;
247
+ this.scheduleGC(entry, args.gcTime);
248
+ }
249
+ scheduleGC(entry, gcTime) {
250
+ if (entry.gcTimeout) return;
251
+ if (!Number.isFinite(gcTime)) {
252
+ return;
253
+ }
254
+ const timeoutMs = gcTime <= 0 ? 0 : gcTime;
255
+ entry.gcTimeout = setTimeout(() => {
256
+ const current = this.entries.get(entry.key);
257
+ if (!current || current !== entry) return;
258
+ if (current.refCount > 0) return;
259
+ void this.runtime.runPromise(Scope2.close(entry.scope, Exit.void)).catch(() => {
260
+ });
261
+ this.entries.delete(entry.key);
262
+ }, timeoutMs);
263
+ }
264
+ };
265
+ var RUNTIME_PROCESS_REGISTRY = /* @__PURE__ */ new WeakMap();
266
+ var getRegistry = (runtime) => {
267
+ const existing = RUNTIME_PROCESS_REGISTRY.get(runtime);
268
+ if (existing) return existing;
269
+ const created = new ProcessSubtreeRegistry(runtime);
270
+ RUNTIME_PROCESS_REGISTRY.set(runtime, created);
271
+ return created;
272
+ };
273
+ var stableProcessSignature = (processes) => {
274
+ const ids = [];
275
+ for (let i = 0; i < processes.length; i++) {
276
+ const def = Logix2.Process.getDefinition(processes[i]);
277
+ ids.push(def?.processId ?? `legacy#${i}`);
278
+ }
279
+ return ids.join("|");
280
+ };
281
+ function useProcesses(processes, options) {
282
+ const context = useContext(RuntimeContext);
283
+ if (!context) {
284
+ throw new RuntimeProviderNotFoundError("useProcesses");
285
+ }
286
+ const isDev = isDevEnv();
287
+ const lastProcessesRef = useRef2(null);
288
+ const shallowEqualProcesses = (a, b) => !!a && !!b && a.length === b.length && a.every((item, idx) => item === b[idx]);
289
+ useEffect2(() => {
290
+ if (!isDev) return;
291
+ if (!lastProcessesRef.current) {
292
+ lastProcessesRef.current = processes;
293
+ return;
294
+ }
295
+ if (lastProcessesRef.current !== processes && !shallowEqualProcesses(lastProcessesRef.current, processes)) {
296
+ console.warn(
297
+ "[useProcesses] received a new processes list. Memoize the list (useMemo) to avoid unnecessary install/uninstall churn."
298
+ );
299
+ }
300
+ lastProcessesRef.current = processes;
301
+ }, [isDev, processes]);
302
+ const defaultSubtreeId = React4.useId();
303
+ const subtreeId = options?.subtreeId ?? `ui:${defaultSubtreeId}`;
304
+ const signature = useMemo4(() => stableProcessSignature(processes), [processes]);
305
+ const gcTime = options?.gcTime ?? context.reactConfigSnapshot.gcTime;
306
+ const mode = options?.mode ?? "switch";
307
+ const deps = options?.deps ? [...options.deps, context.runtime, subtreeId, signature, gcTime, mode] : [context.runtime, subtreeId, signature, gcTime, mode];
308
+ useEffect2(() => {
309
+ if (!processes || processes.length === 0) {
310
+ return;
311
+ }
312
+ const runtime = context.runtime;
313
+ const registry = getRegistry(runtime);
314
+ return registry.retain({
315
+ key: subtreeId,
316
+ signature,
317
+ gcTime,
318
+ install: (scope) => {
319
+ const program = Effect3.forEach(
320
+ processes,
321
+ (process) => Logix2.InternalContracts.installProcess(process, {
322
+ scope: { type: "uiSubtree", subtreeId },
323
+ enabled: true,
324
+ installedAt: "uiSubtree",
325
+ mode
326
+ }).pipe(
327
+ Effect3.flatMap((installation) => {
328
+ if (installation !== void 0) {
329
+ return Effect3.void;
330
+ }
331
+ return Effect3.forkScoped(process).pipe(Effect3.asVoid);
332
+ }),
333
+ Effect3.catchAll(() => Effect3.forkScoped(process).pipe(Effect3.asVoid))
334
+ ),
335
+ { discard: true }
336
+ ).pipe(Effect3.provideService(Scope2.Scope, scope));
337
+ void runtime.runPromise(program).catch(() => {
338
+ });
339
+ }
340
+ });
341
+ }, deps);
342
+ }
343
+
344
+ export {
345
+ useLocalModule,
346
+ useLayerModule,
347
+ useDispatch,
348
+ useModuleList,
349
+ useImportedModule,
350
+ useProcesses
351
+ };