@logixjs/react 0.1.0 → 1.0.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 (36) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +1 -1
  3. package/dist/Hooks.cjs +462 -325
  4. package/dist/Hooks.d.cts +6 -6
  5. package/dist/Hooks.d.ts +6 -6
  6. package/dist/Hooks.js +3 -3
  7. package/dist/{ModuleRef-wZSQ3Wwo.d.cts → ModuleRef-gZmL6Zvb.d.cts} +8 -3
  8. package/dist/{ModuleRef-wZSQ3Wwo.d.ts → ModuleRef-gZmL6Zvb.d.ts} +8 -3
  9. package/dist/ModuleScope.cjs +596 -362
  10. package/dist/ModuleScope.d.cts +4 -4
  11. package/dist/ModuleScope.d.ts +4 -4
  12. package/dist/ModuleScope.js +4 -4
  13. package/dist/Platform.cjs +1 -4
  14. package/dist/Platform.d.cts +1 -2
  15. package/dist/Platform.d.ts +1 -2
  16. package/dist/Platform.js +1 -1
  17. package/dist/ReactPlatform.cjs +543 -309
  18. package/dist/ReactPlatform.d.cts +2 -2
  19. package/dist/ReactPlatform.d.ts +2 -2
  20. package/dist/ReactPlatform.js +5 -5
  21. package/dist/RuntimeProvider.cjs +276 -56
  22. package/dist/RuntimeProvider.js +2 -2
  23. package/dist/{chunk-PYWHL7TA.js → chunk-6NLXTHZ7.js} +8 -8
  24. package/dist/{chunk-UFFCJGSZ.js → chunk-E3ZXST5F.js} +256 -240
  25. package/dist/{chunk-4G7H66OY.js → chunk-KYWW4KMQ.js} +3 -3
  26. package/dist/{chunk-2WFULYPJ.js → chunk-L7KTYBXN.js} +155 -32
  27. package/dist/{chunk-ZANGOPUQ.js → chunk-NKYV44OG.js} +1 -4
  28. package/dist/{chunk-G5MRIFKK.js → chunk-SDQF3WRT.js} +7 -7
  29. package/dist/{chunk-JXAJTWSZ.js → chunk-XSGDBJXD.js} +122 -25
  30. package/dist/index.cjs +564 -333
  31. package/dist/index.d.cts +2 -2
  32. package/dist/index.d.ts +2 -2
  33. package/dist/index.js +7 -7
  34. package/dist/{useDispatch-BnzYVkRE.d.ts → useDispatch-CiDimIYZ.d.ts} +13 -15
  35. package/dist/{useDispatch-CnO5-66H.d.cts → useDispatch-DiwQQAfC.d.cts} +13 -15
  36. package/package.json +12 -4
@@ -1,14 +1,14 @@
1
1
  import {
2
2
  useDispatch,
3
3
  useLocalModule
4
- } from "./chunk-PYWHL7TA.js";
4
+ } from "./chunk-6NLXTHZ7.js";
5
5
  import {
6
6
  useModule,
7
7
  useSelector
8
- } from "./chunk-UFFCJGSZ.js";
8
+ } from "./chunk-E3ZXST5F.js";
9
9
  import {
10
10
  RuntimeProvider
11
- } from "./chunk-JXAJTWSZ.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
@@ -1,13 +1,14 @@
1
1
  import {
2
2
  useModule,
3
3
  useRuntime
4
- } from "./chunk-UFFCJGSZ.js";
4
+ } from "./chunk-E3ZXST5F.js";
5
5
  import {
6
6
  RuntimeProvider
7
- } from "./chunk-JXAJTWSZ.js";
7
+ } from "./chunk-XSGDBJXD.js";
8
8
 
9
9
  // src/ModuleScope.ts
10
10
  import React from "react";
11
+ import { Effect } from "effect";
11
12
  import * as Logix from "@logixjs/core";
12
13
  var makeModuleScope = (handle, defaults) => {
13
14
  const Context = React.createContext(null);
@@ -17,7 +18,9 @@ var makeModuleScope = (handle, defaults) => {
17
18
  };
18
19
  const getRegistryOrThrow = (runtime, where) => {
19
20
  try {
20
- const registry = runtime.runSync(Logix.ScopeRegistry.ScopeRegistryTag);
21
+ const registry = runtime.runSync(
22
+ Effect.service(Logix.ScopeRegistry.ScopeRegistryTag).pipe(Effect.orDie)
23
+ );
21
24
  if (!registry) {
22
25
  throw new Error("ScopeRegistry service is undefined");
23
26
  }
@@ -61,10 +64,7 @@ var makeModuleScope = (handle, defaults) => {
61
64
  const runtime = useRuntime();
62
65
  const registry = getRegistryOrThrow(runtime, "[ModuleScope.Bridge]");
63
66
  const scopedRuntime = registry.get(scopeId, Logix.ScopeRegistry.ScopedRuntimeTag);
64
- const moduleRuntime = registry.get(
65
- scopeId,
66
- moduleToken
67
- );
67
+ const moduleRuntime = registry.get(scopeId, moduleToken);
68
68
  if (!scopedRuntime || !moduleRuntime) {
69
69
  throw new Error(
70
70
  `[ModuleScope.Bridge] Scope "${scopeId}" is not registered (or has been disposed). Ensure you have a corresponding <ModuleScope.Provider options={{ scopeId }}> mounted.`
@@ -4,16 +4,16 @@ import {
4
4
  getModuleCache,
5
5
  isDevEnv,
6
6
  useLayerBinding
7
- } from "./chunk-2WFULYPJ.js";
7
+ } from "./chunk-L7KTYBXN.js";
8
8
 
9
9
  // src/internal/provider/RuntimeProvider.tsx
10
10
  import React2, { useContext, useEffect as useEffect2, useMemo, useState as useState2 } from "react";
11
- import { Layer as Layer2, Effect as Effect2, Cause, FiberRef, Context as Context2, Scope } from "effect";
11
+ import { Cause, Effect as Effect2, Layer as Layer2, Scope, ServiceMap as ServiceMap2 } from "effect";
12
12
  import * as Logix2 from "@logixjs/core";
13
13
 
14
14
  // src/internal/provider/config.ts
15
- import { Config, Context, Effect, Layer, Option } from "effect";
16
- var ReactRuntimeConfigTag = class extends Context.Tag("@logixjs/react/RuntimeConfig")() {
15
+ import { Config, Effect, Layer, Option, ServiceMap } from "effect";
16
+ var ReactRuntimeConfigTag = class extends ServiceMap.Service()("@logixjs/react/RuntimeConfig") {
17
17
  };
18
18
  var DEFAULT_CONFIG = {
19
19
  gcTime: 500,
@@ -394,6 +394,9 @@ var RuntimeProvider = ({
394
394
  }) => {
395
395
  const parent = useContext(RuntimeContext);
396
396
  const baseRuntime = useRuntimeResolution(runtime, parent);
397
+ const providerStartedAtRef = React2.useRef(performance.now());
398
+ const providerReadyAtRef = React2.useRef(void 0);
399
+ const didReportProviderGatingRef = React2.useRef(false);
397
400
  const resolvedPolicy = useMemo(
398
401
  () => resolveRuntimeProviderPolicy({
399
402
  policy,
@@ -403,6 +406,20 @@ var RuntimeProvider = ({
403
406
  );
404
407
  const onErrorRef = React2.useRef(onError);
405
408
  onErrorRef.current = onError;
409
+ const hasTickServices = useMemo(() => {
410
+ try {
411
+ Logix2.InternalContracts.getRuntimeStore(baseRuntime);
412
+ return true;
413
+ } catch {
414
+ return false;
415
+ }
416
+ }, [baseRuntime]);
417
+ const { binding: tickBinding } = useLayerBinding(
418
+ baseRuntime,
419
+ Logix2.InternalContracts.tickServicesLayer,
420
+ !hasTickServices,
421
+ onErrorRef.current
422
+ );
406
423
  const { binding: layerBinding } = useLayerBinding(baseRuntime, layer, Boolean(layer), onErrorRef.current);
407
424
  const onErrorSink = useMemo(() => {
408
425
  if (!onError) return null;
@@ -419,7 +436,7 @@ var RuntimeProvider = ({
419
436
  moduleId: event.moduleId,
420
437
  instanceId: event.instanceId,
421
438
  runtimeLabel: event.runtimeLabel
422
- }).pipe(Effect2.catchAllCause(() => Effect2.void));
439
+ }).pipe(Effect2.catchCause(() => Effect2.void));
423
440
  }
424
441
  if (event.type === "diagnostic" && event.severity === "error") {
425
442
  return handler(
@@ -438,7 +455,7 @@ var RuntimeProvider = ({
438
455
  instanceId: event.instanceId,
439
456
  runtimeLabel: event.runtimeLabel
440
457
  }
441
- ).pipe(Effect2.catchAllCause(() => Effect2.void));
458
+ ).pipe(Effect2.catchCause(() => Effect2.void));
442
459
  }
443
460
  return Effect2.void;
444
461
  }
@@ -453,25 +470,23 @@ var RuntimeProvider = ({
453
470
  return layerBinding.debugSinks;
454
471
  }
455
472
  try {
456
- return baseRuntime.runSync(
457
- FiberRef.get(Logix2.Debug.internal.currentDebugSinks)
458
- );
473
+ return baseRuntime.runSync(Effect2.service(Logix2.Debug.internal.currentDebugSinks).pipe(Effect2.orDie));
459
474
  } catch {
460
475
  return [];
461
476
  }
462
477
  }, [baseRuntime, layerBinding, onErrorSink]);
463
478
  const runtimeWithBindings = useMemo(
464
- () => layerBinding || onErrorSink ? createRuntimeAdapter(
479
+ () => tickBinding || layerBinding || onErrorSink ? createRuntimeAdapter(
465
480
  baseRuntime,
466
- layerBinding ? [layerBinding.context] : [],
467
- layerBinding ? [layerBinding.scope] : [],
468
- layerBinding ? [layerBinding.loggers] : [],
469
- layerBinding ? [layerBinding.logLevel] : [],
481
+ [...tickBinding ? [tickBinding.context] : [], ...layerBinding ? [layerBinding.context] : []],
482
+ [...tickBinding ? [tickBinding.scope] : [], ...layerBinding ? [layerBinding.scope] : []],
483
+ layerBinding ? [layerBinding.loggers] : tickBinding ? [tickBinding.loggers] : [],
484
+ layerBinding ? [layerBinding.logLevel] : tickBinding ? [tickBinding.logLevel] : [],
470
485
  [
471
486
  onErrorSink ? [onErrorSink, ...inheritedDebugSinks] : layerBinding ? layerBinding.debugSinks : []
472
487
  ]
473
488
  ) : baseRuntime,
474
- [baseRuntime, inheritedDebugSinks, layerBinding, onErrorSink]
489
+ [baseRuntime, inheritedDebugSinks, layerBinding, onErrorSink, tickBinding]
475
490
  );
476
491
  const didReportSyncConfigSnapshotRef = React2.useRef(false);
477
492
  const [configState, setConfigState] = useState2(() => {
@@ -576,11 +591,47 @@ var RuntimeProvider = ({
576
591
  }),
577
592
  [runtimeWithBindings, configState, resolvedPolicy]
578
593
  );
594
+ const isTickServicesReady = hasTickServices || tickBinding !== null;
579
595
  const isLayerReady = !layer || layerBinding !== null;
580
596
  const isConfigReady = configState.loaded;
581
597
  const resolveFallback = (phase) => {
582
598
  return resolveRuntimeProviderFallback({ fallback, phase, policyMode: resolvedPolicy.mode });
583
599
  };
600
+ const preloadCache = useMemo(
601
+ () => getModuleCache(runtimeWithBindings, configState.snapshot, configState.version),
602
+ [runtimeWithBindings, configState.snapshot, configState.version]
603
+ );
604
+ const syncWarmPreloadReady = useMemo(() => {
605
+ if (resolvedPolicy.mode !== "defer") return false;
606
+ if (!resolvedPolicy.preload) return true;
607
+ if (!isLayerReady || !isConfigReady) return false;
608
+ const handles = resolvedPolicy.preload.handles;
609
+ if (handles.length === 0) return true;
610
+ for (const handle of handles) {
611
+ if (handle?._tag === "ModuleImpl") {
612
+ const moduleId = handle.module?.id ?? "ModuleImpl";
613
+ const key2 = resolvedPolicy.preload.keysByModuleId.get(moduleId) ?? getPreloadKeyForModuleId(moduleId);
614
+ const factory2 = (scope) => Layer2.buildWithScope(handle.layer, scope).pipe(
615
+ Effect2.map((context) => ServiceMap2.get(context, handle.module))
616
+ );
617
+ const value2 = preloadCache.warmSync(key2, factory2, configState.snapshot.gcTime, moduleId, {
618
+ entrypoint: "react.runtime.preload.sync-warm",
619
+ policyMode: "defer"
620
+ });
621
+ if (!value2) return false;
622
+ continue;
623
+ }
624
+ const tagId = handle.id ?? "ModuleTag";
625
+ const key = resolvedPolicy.preload.keysByTagId.get(tagId) ?? getPreloadKeyForTagId(tagId);
626
+ const factory = (scope) => Scope.provide(scope)(Effect2.service(handle).pipe(Effect2.orDie));
627
+ const value = preloadCache.warmSync(key, factory, configState.snapshot.gcTime, tagId, {
628
+ entrypoint: "react.runtime.preload.sync-warm",
629
+ policyMode: "defer"
630
+ });
631
+ if (!value) return false;
632
+ }
633
+ return true;
634
+ }, [resolvedPolicy, isLayerReady, isConfigReady, preloadCache, configState.snapshot.gcTime]);
584
635
  const [deferReady, setDeferReady] = useState2(false);
585
636
  useEffect2(() => {
586
637
  if (resolvedPolicy.mode !== "defer") {
@@ -594,6 +645,10 @@ var RuntimeProvider = ({
594
645
  if (resolvedPolicy.mode !== "defer") {
595
646
  return;
596
647
  }
648
+ if (syncWarmPreloadReady) {
649
+ setDeferReady(true);
650
+ return;
651
+ }
597
652
  setDeferReady(false);
598
653
  if (!resolvedPolicy.preload) {
599
654
  setDeferReady(true);
@@ -603,7 +658,7 @@ var RuntimeProvider = ({
603
658
  return;
604
659
  }
605
660
  let cancelled = false;
606
- const cache = getModuleCache(runtimeWithBindings, configState.snapshot, configState.version);
661
+ const cache = preloadCache;
607
662
  const preloadHandles = resolvedPolicy.preload.handles;
608
663
  if (preloadHandles.length === 0) {
609
664
  setDeferReady(true);
@@ -621,13 +676,14 @@ var RuntimeProvider = ({
621
676
  const moduleId = handle.module?.id ?? "ModuleImpl";
622
677
  const key2 = resolvedPolicy.preload.keysByModuleId.get(moduleId) ?? getPreloadKeyForModuleId(moduleId);
623
678
  const factory2 = (scope) => Layer2.buildWithScope(handle.layer, scope).pipe(
624
- Effect2.map((context) => Context2.get(context, handle.module))
679
+ Effect2.map((context) => ServiceMap2.get(context, handle.module))
625
680
  );
626
681
  const op2 = cache.preload(key2, factory2, {
627
682
  ownerId: moduleId,
628
683
  yield: resolvedPolicy.preload.yield,
629
684
  entrypoint: "react.runtime.preload",
630
- policyMode: "defer"
685
+ policyMode: "defer",
686
+ optimisticSyncBudgetMs: resolvedPolicy.syncBudgetMs
631
687
  });
632
688
  allCancels.add(op2.cancel);
633
689
  await op2.promise;
@@ -652,14 +708,13 @@ var RuntimeProvider = ({
652
708
  }
653
709
  const tagId = handle.id ?? "ModuleTag";
654
710
  const key = resolvedPolicy.preload.keysByTagId.get(tagId) ?? getPreloadKeyForTagId(tagId);
655
- const factory = (scope) => handle.pipe(
656
- Scope.extend(scope)
657
- );
711
+ const factory = (scope) => Scope.provide(scope)(Effect2.service(handle).pipe(Effect2.orDie));
658
712
  const op = cache.preload(key, factory, {
659
713
  ownerId: tagId,
660
714
  yield: resolvedPolicy.preload.yield,
661
715
  entrypoint: "react.runtime.preload",
662
- policyMode: "defer"
716
+ policyMode: "defer",
717
+ optimisticSyncBudgetMs: resolvedPolicy.syncBudgetMs
663
718
  });
664
719
  allCancels.add(op.cancel);
665
720
  await op.promise;
@@ -697,7 +752,7 @@ var RuntimeProvider = ({
697
752
  if (cancelled) return;
698
753
  if (onErrorRef.current) {
699
754
  runtimeWithBindings.runFork(
700
- onErrorRef.current(Cause.die(error), { source: "provider", phase: "provider.layer.build" }).pipe(Effect2.catchAllCause(() => Effect2.void))
755
+ onErrorRef.current(Cause.die(error), { source: "provider", phase: "provider.layer.build" }).pipe(Effect2.catchCause(() => Effect2.void))
701
756
  );
702
757
  }
703
758
  setDeferReady(true);
@@ -730,12 +785,54 @@ var RuntimeProvider = ({
730
785
  release();
731
786
  };
732
787
  }, [resolvedPolicy.mode, deferReady]);
733
- const isReady = isLayerReady && isConfigReady && (resolvedPolicy.mode !== "defer" || deferReady);
788
+ const isReady = isTickServicesReady && isLayerReady && isConfigReady && (resolvedPolicy.mode !== "defer" || deferReady || syncWarmPreloadReady);
789
+ if (isReady && providerReadyAtRef.current === void 0) {
790
+ providerReadyAtRef.current = performance.now();
791
+ }
792
+ useEffect2(() => {
793
+ if (!isReady) {
794
+ return;
795
+ }
796
+ if (didReportProviderGatingRef.current) {
797
+ return;
798
+ }
799
+ let diagnosticsLevel = "off";
800
+ try {
801
+ diagnosticsLevel = runtimeWithBindings.runSync(
802
+ Effect2.service(Logix2.Debug.internal.currentDiagnosticsLevel).pipe(Effect2.orDie)
803
+ );
804
+ } catch {
805
+ diagnosticsLevel = isDevEnv() ? "light" : "off";
806
+ }
807
+ if (diagnosticsLevel === "off") {
808
+ return;
809
+ }
810
+ didReportProviderGatingRef.current = true;
811
+ const readyAt = providerReadyAtRef.current ?? performance.now();
812
+ const durationMs = Math.round((readyAt - providerStartedAtRef.current) * 100) / 100;
813
+ const effectDelayMs = Math.round((performance.now() - readyAt) * 100) / 100;
814
+ void runtimeWithBindings.runPromise(
815
+ Logix2.Debug.record({
816
+ type: "trace:react.provider.gating",
817
+ data: {
818
+ event: "ready",
819
+ policyMode: resolvedPolicy.mode,
820
+ durationMs,
821
+ effectDelayMs,
822
+ configLoadMode: configState.loadMode,
823
+ syncOverBudget: Boolean(configState.syncOverBudget),
824
+ syncDurationMs: configState.syncDurationMs !== void 0 ? Math.round(configState.syncDurationMs * 100) / 100 : void 0
825
+ }
826
+ })
827
+ ).catch(() => {
828
+ });
829
+ }, [configState.loadMode, configState.syncDurationMs, configState.syncOverBudget, isReady, resolvedPolicy.mode, runtimeWithBindings]);
734
830
  if (!isReady) {
735
831
  const blockersList = [
832
+ isTickServicesReady ? null : "tick",
736
833
  isLayerReady ? null : "layer",
737
834
  isConfigReady ? null : "config",
738
- resolvedPolicy.mode !== "defer" || deferReady ? null : "preload"
835
+ resolvedPolicy.mode !== "defer" || deferReady || syncWarmPreloadReady ? null : "preload"
739
836
  ].filter((x) => x !== null);
740
837
  const blockers = blockersList.length > 0 ? blockersList.join("+") : void 0;
741
838
  return /* @__PURE__ */ jsx2(