@logixjs/react 0.1.1 → 1.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.
@@ -5,7 +5,7 @@ import {
5
5
  isDevEnv,
6
6
  stableHash,
7
7
  useLayerBinding
8
- } from "./chunk-2WFULYPJ.js";
8
+ } from "./chunk-L7KTYBXN.js";
9
9
 
10
10
  // src/internal/hooks/useRuntime.ts
11
11
  import { useContext, useEffect, useMemo, useRef } from "react";
@@ -138,12 +138,12 @@ var shallow = (previous, next) => {
138
138
  // src/internal/hooks/useSelector.ts
139
139
  import { useContext as useContext3, useEffect as useEffect3, useMemo as useMemo3 } from "react";
140
140
  import { useSyncExternalStoreWithSelector } from "use-sync-external-store/shim/with-selector";
141
- import * as Logix3 from "@logixjs/core";
141
+ import * as Logix4 from "@logixjs/core";
142
142
 
143
143
  // src/internal/hooks/useModuleRuntime.ts
144
- import { useEffect as useEffect2, useMemo as useMemo2, useContext as useContext2 } from "react";
145
- import * as Logix from "@logixjs/core";
146
- import { Scope } from "effect";
144
+ import { useEffect as useEffect2, useMemo as useMemo2, useContext as useContext2, useRef as useRef2 } from "react";
145
+ import * as Logix2 from "@logixjs/core";
146
+ import { Effect as Effect2, Scope } from "effect";
147
147
 
148
148
  // src/internal/store/ModuleRef.ts
149
149
  var isModuleRef = (value) => typeof value === "object" && value !== null && "runtime" in value && "actions" in value && "dispatch" in value;
@@ -234,11 +234,26 @@ var applyHandleExtend = (tag, runtime, base) => {
234
234
  return { ...base, ...next };
235
235
  };
236
236
 
237
+ // src/internal/provider/runtimeDebugBridge.ts
238
+ import { Effect } from "effect";
239
+ import * as Logix from "@logixjs/core";
240
+ var readRuntimeDiagnosticsLevel = (runtime) => {
241
+ try {
242
+ return runtime.runSync(Effect.service(Logix.Debug.internal.currentDiagnosticsLevel).pipe(Effect.orDie));
243
+ } catch {
244
+ return isDevEnv() ? "light" : "off";
245
+ }
246
+ };
247
+ var emitRuntimeDebugEventBestEffort = (runtime, event) => {
248
+ runtime.runFork(event);
249
+ };
250
+
237
251
  // src/internal/hooks/useModuleRuntime.ts
238
252
  var isModuleRuntime = (value) => typeof value === "object" && value !== null && "dispatch" in value && "getState" in value;
239
253
  function useModuleRuntime(handle) {
240
254
  const runtime = useRuntime();
241
255
  const runtimeContext = useContext2(RuntimeContext);
256
+ const moduleTagResolveTraceRef = useRef2(void 0);
242
257
  if (!runtimeContext) {
243
258
  throw new RuntimeProviderNotFoundError("useModuleRuntime");
244
259
  }
@@ -259,42 +274,54 @@ function useModuleRuntime(handle) {
259
274
  const preloadKey = runtimeContext.policy.preload?.keysByTagId.get(tokenId);
260
275
  const key = preloadKey ?? `tag:${tokenId}`;
261
276
  const mode = runtimeContext.policy.moduleTagMode;
262
- const factory = (scope) => tag.pipe(Scope.extend(scope));
263
- return mode === "suspend" ? cache.read(key, factory, void 0, tokenId, {
277
+ const startedAtMs = performance.now();
278
+ const factory = (scope) => Scope.provide(scope)(Effect2.service(tag).pipe(Effect2.orDie));
279
+ const resolvedRuntime = mode === "suspend" ? cache.read(key, factory, void 0, tokenId, {
264
280
  entrypoint: "react.useModuleRuntime",
265
281
  policyMode: runtimeContext.policy.mode,
266
- yield: runtimeContext.policy.yield
282
+ yield: runtimeContext.policy.yield,
283
+ optimisticSyncBudgetMs: runtimeContext.policy.syncBudgetMs
267
284
  }) : cache.readSync(key, factory, void 0, tokenId, {
268
285
  entrypoint: "react.useModuleRuntime",
269
286
  policyMode: runtimeContext.policy.mode,
270
287
  warnSyncBlockingThresholdMs: 5
271
288
  });
289
+ moduleTagResolveTraceRef.current = {
290
+ tokenId,
291
+ durationMs: Math.round((performance.now() - startedAtMs) * 100) / 100,
292
+ cacheMode: mode
293
+ };
294
+ return resolvedRuntime;
272
295
  }, [cache, runtimeContext.policy, handle]);
273
296
  useEffect2(() => {
274
297
  if (!isTagHandle) {
275
298
  return;
276
299
  }
277
- if (!isDevEnv() && !Logix.Debug.isDevtoolsEnabled()) {
300
+ const diagnosticsLevel = readRuntimeDiagnosticsLevel(runtime);
301
+ if (diagnosticsLevel === "off") {
278
302
  return;
279
303
  }
280
304
  const tokenId = handle?.id ?? "ModuleTag";
281
- const effect = Logix.Debug.record({
305
+ const trace = moduleTagResolveTraceRef.current;
306
+ const effect = Logix2.Debug.record({
282
307
  type: "trace:react.moduleTag.resolve",
283
308
  moduleId: resolved.moduleId,
284
309
  instanceId: resolved.instanceId,
285
310
  data: {
286
311
  mode: runtimeContext.policy.moduleTagMode,
287
312
  tokenId,
288
- yieldStrategy: runtimeContext.policy.yield.strategy
313
+ yieldStrategy: runtimeContext.policy.yield.strategy,
314
+ durationMs: trace?.durationMs,
315
+ cacheMode: trace?.cacheMode ?? runtimeContext.policy.moduleTagMode
289
316
  }
290
317
  });
291
- runtime.runFork(effect);
318
+ emitRuntimeDebugEventBestEffort(runtime, effect);
292
319
  }, [runtime, runtimeContext.policy, resolved, handle, isTagHandle]);
293
320
  return resolved;
294
321
  }
295
322
 
296
323
  // src/internal/store/RuntimeExternalStore.ts
297
- import * as Logix2 from "@logixjs/core";
324
+ import * as Logix3 from "@logixjs/core";
298
325
  import { Fiber, Stream } from "effect";
299
326
  var storesByRuntime = /* @__PURE__ */ new WeakMap();
300
327
  var getStoreMapForRuntime = (runtime) => {
@@ -306,8 +333,8 @@ var getStoreMapForRuntime = (runtime) => {
306
333
  };
307
334
  var makeModuleInstanceKey = (moduleId, instanceId) => `${moduleId}::${instanceId}`;
308
335
  var makeReadQueryTopicKey = (moduleInstanceKey, selectorId) => `${moduleInstanceKey}::rq:${selectorId}`;
309
- var getRuntimeStore = (runtime) => Logix2.InternalContracts.getRuntimeStore(runtime);
310
- var getHostScheduler = (runtime) => Logix2.InternalContracts.getHostScheduler(runtime);
336
+ var getRuntimeStore = (runtime) => Logix3.InternalContracts.getRuntimeStore(runtime);
337
+ var getHostScheduler = (runtime) => Logix3.InternalContracts.getHostScheduler(runtime);
311
338
  var getOrCreateStore = (runtime, topicKey, make) => {
312
339
  const map = getStoreMapForRuntime(runtime);
313
340
  const cached = map.get(topicKey);
@@ -331,6 +358,8 @@ var makeTopicExternalStore = (args) => {
331
358
  let currentSnapshot;
332
359
  const listeners = /* @__PURE__ */ new Set();
333
360
  let unsubscribeFromRuntimeStore;
361
+ let teardownScheduled = false;
362
+ let teardownToken = 0;
334
363
  const lowPriorityDelayMs = args.options?.lowPriorityDelayMs ?? 16;
335
364
  const lowPriorityMaxDelayMs = args.options?.lowPriorityMaxDelayMs ?? 50;
336
365
  let notifyScheduled = false;
@@ -415,7 +444,38 @@ var makeTopicExternalStore = (args) => {
415
444
  currentSnapshot = next;
416
445
  return next;
417
446
  };
447
+ const cancelScheduledTeardown = () => {
448
+ if (!teardownScheduled) return;
449
+ teardownScheduled = false;
450
+ teardownToken += 1;
451
+ };
452
+ const finalizeTeardown = () => {
453
+ if (listeners.size > 0) return;
454
+ try {
455
+ args.onLastListener?.();
456
+ } catch {
457
+ }
458
+ const unsub = unsubscribeFromRuntimeStore;
459
+ unsubscribeFromRuntimeStore = void 0;
460
+ cancelLow();
461
+ try {
462
+ unsub?.();
463
+ } catch {
464
+ }
465
+ removeStore(runtime, topicKey);
466
+ };
467
+ const scheduleTeardown = () => {
468
+ if (teardownScheduled) return;
469
+ teardownScheduled = true;
470
+ const token = ++teardownToken;
471
+ hostScheduler.scheduleMicrotask(() => {
472
+ if (!teardownScheduled || token !== teardownToken) return;
473
+ teardownScheduled = false;
474
+ finalizeTeardown();
475
+ });
476
+ };
418
477
  const subscribe = (listener) => {
478
+ cancelScheduledTeardown();
419
479
  const isFirst = listeners.size === 0;
420
480
  listeners.add(listener);
421
481
  ensureSubscription();
@@ -429,18 +489,7 @@ var makeTopicExternalStore = (args) => {
429
489
  return () => {
430
490
  listeners.delete(listener);
431
491
  if (listeners.size > 0) return;
432
- try {
433
- args.onLastListener?.();
434
- } catch {
435
- }
436
- const unsub = unsubscribeFromRuntimeStore;
437
- unsubscribeFromRuntimeStore = void 0;
438
- cancelLow();
439
- try {
440
- unsub?.();
441
- } catch {
442
- }
443
- removeStore(runtime, topicKey);
492
+ scheduleTeardown();
444
493
  };
445
494
  };
446
495
  return { getSnapshot, getServerSnapshot: getSnapshot, subscribe };
@@ -507,7 +556,7 @@ function useSelector(handle, selector, equalityFn) {
507
556
  const moduleRuntime = useModuleRuntime(handle);
508
557
  const actualSelector = selector ?? ((state) => state);
509
558
  const selectorReadQuery = useMemo3(
510
- () => typeof selector === "function" ? Logix3.ReadQuery.compile(selector) : void 0,
559
+ () => typeof selector === "function" ? Logix4.ReadQuery.compile(selector) : void 0,
511
560
  [selector]
512
561
  );
513
562
  const actualEqualityFn = useMemo3(() => {
@@ -545,7 +594,7 @@ function useSelector(handle, selector, equalityFn) {
545
594
  actualEqualityFn
546
595
  );
547
596
  useEffect3(() => {
548
- if (!isDevEnv() && !Logix3.Debug.isDevtoolsEnabled()) {
597
+ if (!isDevEnv() && !Logix4.Debug.isDevtoolsEnabled()) {
549
598
  return;
550
599
  }
551
600
  const instanceId = moduleRuntime.instanceId;
@@ -561,7 +610,7 @@ function useSelector(handle, selector, equalityFn) {
561
610
  const rawDebugKey = meta.debugKey;
562
611
  selectorKey = typeof rawDebugKey === "string" && rawDebugKey.length > 0 ? rawDebugKey : typeof selector.name === "string" && selector.name.length > 0 ? selector.name : void 0;
563
612
  }
564
- const effect = Logix3.Debug.record({
613
+ const effect = Logix4.Debug.record({
565
614
  type: "trace:react-selector",
566
615
  moduleId: moduleRuntime.moduleId,
567
616
  instanceId,
@@ -585,11 +634,11 @@ function useSelector(handle, selector, equalityFn) {
585
634
 
586
635
  // src/internal/hooks/useModule.ts
587
636
  import React2 from "react";
588
- import * as Logix5 from "@logixjs/core";
589
- import { Context, Effect as Effect3, Layer as Layer2 } from "effect";
637
+ import * as Logix6 from "@logixjs/core";
638
+ import { Effect as Effect4, Layer as Layer2, ServiceMap } from "effect";
590
639
 
591
640
  // src/internal/store/resolveImportedModuleRef.ts
592
- import * as Logix4 from "@logixjs/core";
641
+ import * as Logix5 from "@logixjs/core";
593
642
  var getOrCreateWeakMap = (map, key, make) => {
594
643
  const cached = map.get(key);
595
644
  if (cached) return cached;
@@ -613,7 +662,7 @@ var resolveImportedModuleRef = (runtime, parentRuntime, module) => {
613
662
  if (cached) {
614
663
  return cached;
615
664
  }
616
- const importsScope = Logix4.InternalContracts.getImportsScope(parentRuntime);
665
+ const importsScope = Logix5.InternalContracts.getImportsScope(parentRuntime);
617
666
  const childRuntime = importsScope.get(module);
618
667
  if (childRuntime) {
619
668
  const dispatch = Object.assign(
@@ -699,8 +748,8 @@ var useStableId = () => {
699
748
 
700
749
  // src/internal/hooks/useModule.ts
701
750
  var isModuleImpl = (handle) => Boolean(handle) && typeof handle === "object" && handle._tag === "ModuleImpl";
702
- var isModule = (handle) => Logix5.Module.hasImpl(handle);
703
- var isModuleDef = (handle) => Logix5.Module.is(handle) && handle._kind === "ModuleDef";
751
+ var isModule = (handle) => Logix6.Module.hasImpl(handle);
752
+ var isModuleDef = (handle) => Logix6.Module.is(handle) && handle._kind === "ModuleDef";
704
753
  function useModule(handle, selectorOrOptions, equalityFn) {
705
754
  const runtimeBase = useRuntime();
706
755
  const runtimeContext = React2.useContext(RuntimeContext);
@@ -724,6 +773,7 @@ function useModule(handle, selectorOrOptions, equalityFn) {
724
773
  }
725
774
  }
726
775
  let runtime;
776
+ const moduleImplResolveTraceRef = React2.useRef(void 0);
727
777
  if (isModuleImpl(normalizedHandle)) {
728
778
  const cache = React2.useMemo(
729
779
  () => getModuleCache(runtimeBase, runtimeContext.reactConfigSnapshot, runtimeContext.configVersion),
@@ -752,9 +802,9 @@ function useModule(handle, selectorOrOptions, equalityFn) {
752
802
  const key = depsHash ? `${baseKey}:${depsHash}` : baseKey;
753
803
  const ownerId = moduleId;
754
804
  const baseFactory = React2.useMemo(
755
- () => (scope) => Layer2.buildWithScope(normalizedHandle.layer, scope).pipe(
756
- Effect3.map(
757
- (context) => Context.get(context, normalizedHandle.module)
805
+ () => (scope) => Layer2.buildWithScope(Layer2.fresh(normalizedHandle.layer), scope).pipe(
806
+ Effect4.map(
807
+ (context) => ServiceMap.get(context, normalizedHandle.module)
758
808
  )
759
809
  ),
760
810
  [normalizedHandle]
@@ -764,26 +814,56 @@ function useModule(handle, selectorOrOptions, equalityFn) {
764
814
  return baseFactory;
765
815
  }
766
816
  return (scope) => baseFactory(scope).pipe(
767
- Effect3.timeoutFail({
768
- duration: initTimeoutMs,
769
- onTimeout: () => new Error(`[useModule] Module "${ownerId}" initialization timed out after ${initTimeoutMs}ms`)
770
- })
817
+ Effect4.timeoutOption(initTimeoutMs),
818
+ Effect4.flatMap(
819
+ (maybe) => maybe._tag === "Some" ? Effect4.succeed(maybe.value) : Effect4.die(new Error(`[useModule] Module "${ownerId}" initialization timed out after ${initTimeoutMs}ms`))
820
+ )
771
821
  );
772
822
  }, [baseFactory, suspend, initTimeoutMs, ownerId]);
823
+ const moduleResolveStartedAt = performance.now();
773
824
  const moduleRuntime = suspend ? cache.read(key, factory, gcTime, ownerId, {
774
825
  entrypoint: "react.useModule",
775
826
  policyMode: runtimeContext.policy.mode,
776
- yield: runtimeContext.policy.yield
827
+ yield: runtimeContext.policy.yield,
828
+ optimisticSyncBudgetMs: runtimeContext.policy.syncBudgetMs
777
829
  }) : cache.readSync(key, factory, gcTime, ownerId, {
778
830
  entrypoint: "react.useModule",
779
831
  policyMode: runtimeContext.policy.mode,
780
832
  warnSyncBlockingThresholdMs: 5
781
833
  });
834
+ moduleImplResolveTraceRef.current = {
835
+ moduleId,
836
+ cacheMode: suspend ? "suspend" : "sync",
837
+ durationMs: Math.round((performance.now() - moduleResolveStartedAt) * 100) / 100
838
+ };
782
839
  React2.useEffect(() => cache.retain(key), [cache, key]);
783
840
  runtime = moduleRuntime;
784
841
  } else {
785
842
  runtime = useModuleRuntime(normalizedHandle);
786
843
  }
844
+ React2.useEffect(() => {
845
+ if (!isModuleImpl(normalizedHandle)) {
846
+ return;
847
+ }
848
+ const diagnosticsLevel = readRuntimeDiagnosticsLevel(runtimeBase);
849
+ if (diagnosticsLevel === "off") {
850
+ return;
851
+ }
852
+ const trace = moduleImplResolveTraceRef.current;
853
+ if (!trace) {
854
+ return;
855
+ }
856
+ const effect = Logix6.Debug.record({
857
+ type: "trace:react.moduleImpl.resolve",
858
+ moduleId: trace.moduleId,
859
+ instanceId: runtime.instanceId,
860
+ data: {
861
+ cacheMode: trace.cacheMode,
862
+ durationMs: trace.durationMs
863
+ }
864
+ });
865
+ emitRuntimeDebugEventBestEffort(runtimeBase, effect);
866
+ }, [runtimeBase, runtime, normalizedHandle]);
787
867
  React2.useEffect(() => {
788
868
  if (!isModuleImpl(normalizedHandle)) {
789
869
  return;
@@ -793,22 +873,22 @@ function useModule(handle, selectorOrOptions, equalityFn) {
793
873
  if (!label) {
794
874
  return;
795
875
  }
796
- const effect = Logix5.Debug.record({
876
+ const effect = Logix6.Debug.record({
797
877
  type: "trace:instanceLabel",
798
878
  moduleId: normalizedHandle.module.id,
799
879
  instanceId: runtime.instanceId,
800
880
  data: { label }
801
881
  });
802
- runtimeBase.runFork(effect);
882
+ emitRuntimeDebugEventBestEffort(runtimeBase, effect);
803
883
  }, [runtimeBase, runtime, normalizedHandle, options]);
804
884
  React2.useEffect(() => {
805
- if (!isDevEnv() && !Logix5.Debug.isDevtoolsEnabled()) {
885
+ if (!isDevEnv() && !Logix6.Debug.isDevtoolsEnabled()) {
806
886
  return;
807
887
  }
808
888
  if (!runtime.instanceId) {
809
889
  return;
810
890
  }
811
- const effect = Logix5.Debug.record({
891
+ const effect = Logix6.Debug.record({
812
892
  type: "trace:react-render",
813
893
  moduleId: runtime.moduleId,
814
894
  instanceId: runtime.instanceId,
@@ -818,7 +898,7 @@ function useModule(handle, selectorOrOptions, equalityFn) {
818
898
  }
819
899
  });
820
900
  runtimeBase.runFork(effect);
821
- }, [runtimeBase, runtime]);
901
+ });
822
902
  if (selector) {
823
903
  if (isModuleImpl(normalizedHandle)) {
824
904
  return useSelector(runtime, selector, equalityFn);
@@ -1,14 +1,14 @@
1
1
  import {
2
2
  useDispatch,
3
3
  useLocalModule
4
- } from "./chunk-WOTNVLCD.js";
4
+ } from "./chunk-6NLXTHZ7.js";
5
5
  import {
6
6
  useModule,
7
7
  useSelector
8
- } from "./chunk-G2LX7WWQ.js";
8
+ } from "./chunk-E3ZXST5F.js";
9
9
  import {
10
10
  RuntimeProvider
11
- } from "./chunk-2M6MDNVT.js";
11
+ } from "./chunk-XSGDBJXD.js";
12
12
 
13
13
  // src/ReactPlatform.ts
14
14
  import React from "react";
@@ -7,7 +7,7 @@ import { getNodeEnv, isDevEnv } from "@logixjs/core/Env";
7
7
 
8
8
  // src/internal/provider/runtimeBindings.ts
9
9
  import { useEffect, useRef, useState } from "react";
10
- import { Layer, Exit, Context, Effect, Cause, FiberRef, Scope } from "effect";
10
+ import { Cause, Effect, Exit, Layer, Logger, References, Scope } from "effect";
11
11
  import * as Logix from "@logixjs/core";
12
12
  var toErrorString = (error) => error instanceof Error ? error.stack ?? error.message : String(error);
13
13
  var debugScopeCloseFailure = (error) => {
@@ -76,14 +76,11 @@ var useLayerBinding = (runtime, layer, enabled, onError) => {
76
76
  const newScope = Effect.runSync(Scope.make());
77
77
  const buildEffect = Effect.gen(function* () {
78
78
  const context = yield* Layer.buildWithScope(layer, newScope);
79
- const applyEnv = (effect) => Effect.mapInputContext(
80
- Scope.extend(effect, newScope),
81
- (parent) => Context.merge(parent, context)
82
- );
83
- const loggers = yield* applyEnv(FiberRef.get(FiberRef.currentLoggers));
84
- const logLevel = yield* applyEnv(FiberRef.get(FiberRef.currentLogLevel));
79
+ const applyEnv = (effect) => Scope.provide(newScope)(Effect.provideServices(effect, context));
80
+ const loggers = yield* applyEnv(Effect.service(Logger.CurrentLoggers)).pipe(Effect.orDie);
81
+ const logLevel = yield* applyEnv(Effect.service(References.MinimumLogLevel)).pipe(Effect.orDie);
85
82
  const debugSinks = yield* applyEnv(
86
- FiberRef.get(Logix.Debug.internal.currentDebugSinks)
83
+ Effect.service(Logix.Debug.internal.currentDebugSinks).pipe(Effect.orDie)
87
84
  );
88
85
  return { context, loggers, logLevel, debugSinks };
89
86
  });
@@ -128,7 +125,7 @@ var useLayerBinding = (runtime, layer, enabled, onError) => {
128
125
  const cause = Cause.die(error);
129
126
  runtime.runFork(
130
127
  onError(cause, { source: "provider", phase: "provider.layer.build" }).pipe(
131
- Effect.catchAllCause(() => Effect.void)
128
+ Effect.catchCause(() => Effect.void)
132
129
  )
133
130
  );
134
131
  }
@@ -166,18 +163,11 @@ var createRuntimeAdapter = (runtime, contexts, scopes, loggerSets, logLevels, de
166
163
  if (contexts.length === 0 && scopes.length === 0 && loggerSets.length === 0 && logLevels.length === 0 && debugSinks.length === 0) {
167
164
  return runtime;
168
165
  }
169
- const applyContexts = (effect) => (
170
- // First inherit Provider scopes via scope.extend (preserving FiberRef/Logger changes),
171
- // then merge Context via mapInputContext (inner overrides outer).
172
- contexts.reduceRight(
173
- (acc, ctx) => Effect.mapInputContext(
174
- acc,
175
- (parent) => Context.merge(parent, ctx)
176
- ),
177
- scopes.reduceRight(
178
- (acc, scope) => Scope.extend(acc, scope),
179
- effect
180
- )
166
+ const applyContexts = (effect) => contexts.reduceRight(
167
+ (acc, ctx) => Effect.provideServices(acc, ctx),
168
+ scopes.reduceRight(
169
+ (acc, scope) => Scope.provide(scope)(acc),
170
+ effect
181
171
  )
182
172
  );
183
173
  const applyLoggers = (effect) => {
@@ -186,16 +176,13 @@ var createRuntimeAdapter = (runtime, contexts, scopes, loggerSets, logLevels, de
186
176
  const sinks = debugSinks.length > 0 ? debugSinks[debugSinks.length - 1] : null;
187
177
  let result = effect;
188
178
  if (last) {
189
- result = Effect.locally(FiberRef.currentLoggers, last)(result);
179
+ result = Effect.provideService(result, Logger.CurrentLoggers, last);
190
180
  }
191
181
  if (logLevel) {
192
- result = Effect.locally(FiberRef.currentLogLevel, logLevel)(result);
182
+ result = Effect.provideService(result, References.MinimumLogLevel, logLevel);
193
183
  }
194
184
  if (sinks && sinks.length > 0) {
195
- result = Effect.locally(
196
- Logix.Debug.internal.currentDebugSinks,
197
- sinks
198
- )(result);
185
+ result = Effect.provideService(result, Logix.Debug.internal.currentDebugSinks, sinks);
199
186
  }
200
187
  return result;
201
188
  };
@@ -326,9 +313,9 @@ ${message}`;
326
313
  console.debug(label, message);
327
314
  };
328
315
  var causeToUnknown = (cause) => {
329
- const failure = Option.getOrUndefined(Cause2.failureOption(cause));
316
+ const failure = Option.getOrUndefined(Cause2.findErrorOption(cause));
330
317
  if (failure !== void 0) return failure;
331
- const defect = Option.getOrUndefined(Cause2.dieOption(cause));
318
+ const defect = cause.reasons.filter(Cause2.isDieReason).map((reason) => reason.defect)[0];
332
319
  if (defect !== void 0) return defect;
333
320
  return cause;
334
321
  };
@@ -337,7 +324,7 @@ var yieldEffect = (strategy) => {
337
324
  case "none":
338
325
  return Effect3.void;
339
326
  case "microtask":
340
- return Effect3.yieldNow();
327
+ return Effect3.yieldNow;
341
328
  case "macrotask":
342
329
  return Effect3.promise(
343
330
  () => new Promise((resolve) => {
@@ -430,6 +417,67 @@ var ModuleCache = class {
430
417
  const scope = Effect3.runSync(Scope2.make());
431
418
  const workloadKey = `${options?.entrypoint ?? "unknown"}::${ownerId ?? "unknown"}`;
432
419
  const yieldDecision = decideYieldStrategy(this.runtime, workloadKey, options?.yield);
420
+ const optimisticSyncBudgetMs = options?.optimisticSyncBudgetMs ?? 0;
421
+ const shouldTryOptimisticSync = options?.policyMode === "suspend" && optimisticSyncBudgetMs > 0;
422
+ if (shouldTryOptimisticSync) {
423
+ const startedAt2 = performance.now();
424
+ try {
425
+ const value = this.runtime.runSync(factory(scope));
426
+ const durationMs = performance.now() - startedAt2;
427
+ YieldBudgetMemory.record({ runtime: this.runtime, workloadKey, durationMs });
428
+ const entry2 = {
429
+ scope,
430
+ status: "success",
431
+ promise: Promise.resolve(value),
432
+ value,
433
+ refCount: 0,
434
+ preloadRefCount: 0,
435
+ gcTime: gcTime ?? this.gcDelayMs,
436
+ ownerId,
437
+ createdBy: "read",
438
+ workloadKey,
439
+ yieldStrategy: "none"
440
+ };
441
+ this.scheduleGC(key, entry2);
442
+ this.entries.set(key, entry2);
443
+ if (isDevEnv() || Logix3.Debug.isDevtoolsEnabled()) {
444
+ void this.runtime.runPromise(
445
+ Logix3.Debug.record({
446
+ type: "trace:react.module.init",
447
+ moduleId: ownerId,
448
+ instanceId: value.instanceId,
449
+ data: {
450
+ mode: "suspend",
451
+ key,
452
+ durationMs: Math.round(durationMs * 100) / 100,
453
+ yieldStrategy: "none",
454
+ fastPath: "sync"
455
+ }
456
+ })
457
+ ).catch((error) => {
458
+ debugBestEffortFailure("[ModuleCache] Debug.record failed", error);
459
+ });
460
+ void this.runtime.runPromise(
461
+ Logix3.Debug.record({
462
+ type: "trace:react.module-instance",
463
+ moduleId: ownerId,
464
+ instanceId: value.instanceId,
465
+ data: {
466
+ event: "attach",
467
+ key,
468
+ mode: "suspend",
469
+ gcTime: entry2.gcTime,
470
+ fastPath: "sync"
471
+ }
472
+ })
473
+ ).catch((error) => {
474
+ debugBestEffortFailure("[ModuleCache] Debug.record failed", error);
475
+ });
476
+ }
477
+ return value;
478
+ } catch {
479
+ }
480
+ }
433
481
  const entry = {
434
482
  scope,
435
483
  status: "pending",
@@ -445,7 +493,7 @@ var ModuleCache = class {
445
493
  };
446
494
  this.scheduleGC(key, entry);
447
495
  const startedAt = performance.now();
448
- const buildEffect = yieldEffect(yieldDecision.strategy).pipe(Effect3.zipRight(factory(scope)));
496
+ const buildEffect = yieldEffect(yieldDecision.strategy).pipe(Effect3.flatMap(() => factory(scope)));
449
497
  const fiber = this.runtime.runFork(buildEffect);
450
498
  entry.fiber = fiber;
451
499
  const promise = this.runtime.runPromise(Fiber.await(fiber)).then((exit) => {
@@ -641,6 +689,49 @@ var ModuleCache = class {
641
689
  throw error;
642
690
  }
643
691
  }
692
+ warmSync(key, factory, gcTime, ownerId, options) {
693
+ const existing = this.entries.get(key);
694
+ if (existing) {
695
+ if (isDevEnv() && existing.ownerId !== void 0 && ownerId !== void 0 && existing.ownerId !== ownerId) {
696
+ throw new Error(
697
+ `[ModuleCache.warmSync] resource key "${key}" has already been claimed by module "${existing.ownerId}", but is now requested by module "${ownerId}".`
698
+ );
699
+ }
700
+ if (existing.status === "success") {
701
+ return existing.value;
702
+ }
703
+ return void 0;
704
+ }
705
+ const scope = this.runtime.runSync(Scope2.make());
706
+ const startedAt = performance.now();
707
+ const workloadKey = `${options?.entrypoint ?? "unknown"}::${ownerId ?? "unknown"}`;
708
+ try {
709
+ const value = this.runtime.runSync(factory(scope));
710
+ const durationMs = performance.now() - startedAt;
711
+ YieldBudgetMemory.record({ runtime: this.runtime, workloadKey, durationMs });
712
+ const entry = {
713
+ scope,
714
+ status: "success",
715
+ promise: Promise.resolve(value),
716
+ value,
717
+ refCount: 0,
718
+ preloadRefCount: 0,
719
+ gcTime: gcTime ?? this.gcDelayMs,
720
+ ownerId,
721
+ createdBy: "preload",
722
+ workloadKey,
723
+ yieldStrategy: "none"
724
+ };
725
+ this.scheduleGC(key, entry);
726
+ this.entries.set(key, entry);
727
+ return value;
728
+ } catch (error) {
729
+ void this.runtime.runPromise(Scope2.close(scope, Exit2.fail(error))).catch((closeError) => {
730
+ debugBestEffortFailure("[ModuleCache] Scope.close failed", closeError);
731
+ });
732
+ return void 0;
733
+ }
734
+ }
644
735
  preload(key, factory, options) {
645
736
  const existing = this.entries.get(key);
646
737
  if (existing) {
@@ -670,6 +761,38 @@ var ModuleCache = class {
670
761
  const gcTime = options?.gcTime ?? this.gcDelayMs;
671
762
  const workloadKey = `${options?.entrypoint ?? "unknown"}::${ownerId ?? "unknown"}`;
672
763
  const yieldDecision = decideYieldStrategy(this.runtime, workloadKey, options?.yield);
764
+ const optimisticSyncBudgetMs = options?.optimisticSyncBudgetMs ?? 0;
765
+ const shouldTryOptimisticSync = options?.policyMode === "defer" && optimisticSyncBudgetMs > 0;
766
+ if (shouldTryOptimisticSync) {
767
+ const startedAt2 = performance.now();
768
+ try {
769
+ const value = this.runtime.runSync(factory(scope));
770
+ const durationMs = performance.now() - startedAt2;
771
+ YieldBudgetMemory.record({ runtime: this.runtime, workloadKey, durationMs });
772
+ const entry2 = {
773
+ scope,
774
+ status: "success",
775
+ promise: Promise.resolve(value),
776
+ value,
777
+ refCount: 0,
778
+ preloadRefCount: 1,
779
+ gcTime,
780
+ ownerId,
781
+ createdBy: "preload",
782
+ workloadKey,
783
+ yieldStrategy: "none"
784
+ };
785
+ this.scheduleGC(key, entry2);
786
+ this.entries.set(key, entry2);
787
+ return {
788
+ promise: Promise.resolve(value),
789
+ cancel: () => {
790
+ this.cancelPreload(key, entry2);
791
+ }
792
+ };
793
+ } catch {
794
+ }
795
+ }
673
796
  const entry = {
674
797
  scope,
675
798
  status: "pending",
@@ -685,7 +808,7 @@ var ModuleCache = class {
685
808
  this.scheduleGC(key, entry);
686
809
  this.entries.set(key, entry);
687
810
  const startedAt = performance.now();
688
- const buildEffect = yieldEffect(yieldDecision.strategy).pipe(Effect3.zipRight(factory(scope)));
811
+ const buildEffect = yieldEffect(yieldDecision.strategy).pipe(Effect3.flatMap(() => factory(scope)));
689
812
  const fiber = this.runtime.runFork(buildEffect);
690
813
  entry.fiber = fiber;
691
814
  const promise = this.runtime.runPromise(Fiber.await(fiber)).then((exit) => {
@@ -24,10 +24,7 @@ var makeReactPlatform = Effect.gen(function* () {
24
24
  const resetRef = yield* Ref.make([]);
25
25
  return new ReactPlatformImpl(suspendRef, resumeRef, resetRef);
26
26
  });
27
- var ReactPlatformLayer = Layer.scoped(
28
- Platform.tag,
29
- makeReactPlatform
30
- );
27
+ var ReactPlatformLayer = Layer.effect(Platform.tag, makeReactPlatform);
31
28
 
32
29
  export {
33
30
  ReactPlatformLayer