@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,773 @@
1
+ import {
2
+ RuntimeContext,
3
+ createRuntimeAdapter,
4
+ getModuleCache,
5
+ isDevEnv,
6
+ useLayerBinding
7
+ } from "./chunk-2WFULYPJ.js";
8
+
9
+ // src/internal/provider/RuntimeProvider.tsx
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";
12
+ import * as Logix2 from "@logixjs/core";
13
+
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")() {
17
+ };
18
+ var DEFAULT_CONFIG = {
19
+ gcTime: 500,
20
+ initTimeoutMs: void 0,
21
+ lowPriorityDelayMs: 16,
22
+ lowPriorityMaxDelayMs: 50
23
+ };
24
+ var DEFAULT_CONFIG_SNAPSHOT = {
25
+ gcTime: DEFAULT_CONFIG.gcTime,
26
+ initTimeoutMs: DEFAULT_CONFIG.initTimeoutMs,
27
+ lowPriorityDelayMs: DEFAULT_CONFIG.lowPriorityDelayMs ?? 16,
28
+ lowPriorityMaxDelayMs: DEFAULT_CONFIG.lowPriorityMaxDelayMs ?? 50,
29
+ source: "default"
30
+ };
31
+ var ReactModuleConfigFromEnv = {
32
+ /**
33
+ * Default gcTime (ms) used as the idle keep-alive time for ModuleCache.
34
+ * - Applies when not explicitly specified in useModule(options.gcTime).
35
+ * - Default: 500ms (StrictMode jitter protection).
36
+ */
37
+ gcTime: Config.number("logix.react.gc_time").pipe(Config.withDefault(DEFAULT_CONFIG.gcTime)),
38
+ /**
39
+ * Default init timeout (ms), only effective when suspend:true.
40
+ * - Uses Option<number> to represent "optional config": missing means init timeout is disabled.
41
+ * - Callers can map Option.none to undefined.
42
+ */
43
+ initTimeoutMs: Config.option(Config.number("logix.react.init_timeout_ms")),
44
+ /**
45
+ * Delay (ms) for low-priority notification scheduling.
46
+ * - Roughly "defer to the next frame" cadence (default 16ms).
47
+ */
48
+ lowPriorityDelayMs: Config.number("logix.react.low_priority_delay_ms").pipe(
49
+ Config.withDefault(DEFAULT_CONFIG.lowPriorityDelayMs ?? 16)
50
+ ),
51
+ /**
52
+ * Maximum delay upper bound (ms) for low-priority notification scheduling.
53
+ * - Ensures eventual delivery, avoiding indefinite coalescing under extreme high-frequency scenarios.
54
+ * - Default 50ms.
55
+ */
56
+ lowPriorityMaxDelayMs: Config.number("logix.react.low_priority_max_delay_ms").pipe(
57
+ Config.withDefault(DEFAULT_CONFIG.lowPriorityMaxDelayMs ?? 50)
58
+ )
59
+ };
60
+ var ReactModuleConfig = {
61
+ gcTime: Effect.gen(function* () {
62
+ const override = yield* Effect.serviceOption(ReactRuntimeConfigTag);
63
+ if (Option.isSome(override)) {
64
+ return override.value.gcTime;
65
+ }
66
+ const value = yield* ReactModuleConfigFromEnv.gcTime;
67
+ return value;
68
+ }),
69
+ initTimeoutMs: Effect.gen(function* () {
70
+ const override = yield* Effect.serviceOption(ReactRuntimeConfigTag);
71
+ if (Option.isSome(override)) {
72
+ const v = override.value.initTimeoutMs;
73
+ return v === void 0 ? Option.none() : Option.some(v);
74
+ }
75
+ const opt = yield* ReactModuleConfigFromEnv.initTimeoutMs;
76
+ return opt;
77
+ }),
78
+ lowPriorityDelayMs: Effect.gen(function* () {
79
+ const override = yield* Effect.serviceOption(ReactRuntimeConfigTag);
80
+ if (Option.isSome(override)) {
81
+ return override.value.lowPriorityDelayMs ?? DEFAULT_CONFIG.lowPriorityDelayMs ?? 16;
82
+ }
83
+ const value = yield* ReactModuleConfigFromEnv.lowPriorityDelayMs;
84
+ return value;
85
+ }),
86
+ lowPriorityMaxDelayMs: Effect.gen(function* () {
87
+ const override = yield* Effect.serviceOption(ReactRuntimeConfigTag);
88
+ if (Option.isSome(override)) {
89
+ return override.value.lowPriorityMaxDelayMs ?? DEFAULT_CONFIG.lowPriorityMaxDelayMs ?? 50;
90
+ }
91
+ const value = yield* ReactModuleConfigFromEnv.lowPriorityMaxDelayMs;
92
+ return value;
93
+ })
94
+ };
95
+ var ReactRuntimeConfigSnapshot = {
96
+ load: Effect.gen(function* () {
97
+ const runtimeOverride = yield* Effect.serviceOption(ReactRuntimeConfigTag);
98
+ if (Option.isSome(runtimeOverride)) {
99
+ return {
100
+ gcTime: runtimeOverride.value.gcTime,
101
+ initTimeoutMs: runtimeOverride.value.initTimeoutMs,
102
+ lowPriorityDelayMs: runtimeOverride.value.lowPriorityDelayMs ?? DEFAULT_CONFIG.lowPriorityDelayMs ?? 16,
103
+ lowPriorityMaxDelayMs: runtimeOverride.value.lowPriorityMaxDelayMs ?? DEFAULT_CONFIG.lowPriorityMaxDelayMs ?? 50,
104
+ source: "runtime"
105
+ };
106
+ }
107
+ const envGcTime = yield* ReactModuleConfigFromEnv.gcTime;
108
+ const envInitTimeoutOpt = yield* ReactModuleConfigFromEnv.initTimeoutMs;
109
+ const envInitTimeout = Option.getOrUndefined(envInitTimeoutOpt);
110
+ const envLowPriorityDelayMs = yield* ReactModuleConfigFromEnv.lowPriorityDelayMs;
111
+ const envLowPriorityMaxDelayMs = yield* ReactModuleConfigFromEnv.lowPriorityMaxDelayMs;
112
+ const fromConfig = envGcTime !== DEFAULT_CONFIG.gcTime || Option.isSome(envInitTimeoutOpt) || envLowPriorityDelayMs !== (DEFAULT_CONFIG.lowPriorityDelayMs ?? 16) || envLowPriorityMaxDelayMs !== (DEFAULT_CONFIG.lowPriorityMaxDelayMs ?? 50);
113
+ if (fromConfig) {
114
+ return {
115
+ gcTime: envGcTime,
116
+ initTimeoutMs: envInitTimeout,
117
+ lowPriorityDelayMs: envLowPriorityDelayMs,
118
+ lowPriorityMaxDelayMs: envLowPriorityMaxDelayMs,
119
+ source: "config"
120
+ };
121
+ }
122
+ return DEFAULT_CONFIG_SNAPSHOT;
123
+ })
124
+ };
125
+
126
+ // src/internal/provider/fallback.tsx
127
+ import { useEffect, useState } from "react";
128
+ import * as Logix from "@logixjs/core";
129
+
130
+ // src/internal/provider/docs.ts
131
+ var LOGIX_DOCS_PREFIX_ENV = "LOGIX_DOCS_PREFIX";
132
+ var stripTrailingSlashes = (value) => value.replace(/\/+$/, "");
133
+ var ensureLeadingSlash = (value) => value.startsWith("/") ? value : `/${value}`;
134
+ var getProcessEnvString = (key) => {
135
+ try {
136
+ const env = globalThis?.process?.env;
137
+ const value = env?.[key];
138
+ return typeof value === "string" ? value : void 0;
139
+ } catch {
140
+ return void 0;
141
+ }
142
+ };
143
+ var getLocationOrigin = () => {
144
+ try {
145
+ const origin = globalThis?.location?.origin;
146
+ return typeof origin === "string" && origin.length > 0 ? origin : void 0;
147
+ } catch {
148
+ return void 0;
149
+ }
150
+ };
151
+ var getDocsRootPrefix = () => {
152
+ const raw = getProcessEnvString(LOGIX_DOCS_PREFIX_ENV);
153
+ const fromEnv = raw ? stripTrailingSlashes(raw.trim()) : void 0;
154
+ if (fromEnv && fromEnv.length > 0) {
155
+ if (/^https?:\/\//i.test(fromEnv)) {
156
+ return fromEnv;
157
+ }
158
+ const origin2 = getLocationOrigin();
159
+ if (origin2) {
160
+ return stripTrailingSlashes(`${origin2}${ensureLeadingSlash(fromEnv)}`);
161
+ }
162
+ return fromEnv;
163
+ }
164
+ const origin = getLocationOrigin();
165
+ return origin ? `${origin}/zh/docs` : void 0;
166
+ };
167
+ var resolveLogixDocsUrl = (docsPath) => {
168
+ const docsRoot = getDocsRootPrefix();
169
+ const path = ensureLeadingSlash(docsPath);
170
+ if (docsRoot) {
171
+ return `${stripTrailingSlashes(docsRoot)}${path}`;
172
+ }
173
+ return path;
174
+ };
175
+ var getFallbackWarningDocsUrl = () => resolveLogixDocsUrl("/guide/essentials/react-integration");
176
+ var getFallbackWarningDocsPrefixEnv = () => LOGIX_DOCS_PREFIX_ENV;
177
+
178
+ // src/internal/provider/fallback.tsx
179
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
180
+ var FALLBACK_WARNED_BY_RUNTIME = /* @__PURE__ */ new WeakMap();
181
+ var DEFAULT_FALLBACK_WARN_THRESHOLD_MS = 100;
182
+ var DEFAULT_FALLBACK_FLASH_HINT_MIN_MS = 16;
183
+ var DefaultRuntimeFallback = ({ phase, policyMode }) => {
184
+ const [elapsedMs, setElapsedMs] = useState(0);
185
+ useEffect(() => {
186
+ const start = performance.now();
187
+ const interval = setInterval(() => {
188
+ setElapsedMs(Math.round(performance.now() - start));
189
+ }, 100);
190
+ return () => {
191
+ clearInterval(interval);
192
+ };
193
+ }, []);
194
+ const message = phase === "provider.gating" ? "Logix \u8FD0\u884C\u65F6\u51C6\u5907\u4E2D\u2026" : "Logix \u6A21\u5757\u89E3\u6790\u4E2D\u2026";
195
+ return /* @__PURE__ */ jsxs(
196
+ "div",
197
+ {
198
+ style: {
199
+ padding: 12,
200
+ fontSize: 13,
201
+ lineHeight: 1.4,
202
+ opacity: 0.85,
203
+ fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace'
204
+ },
205
+ children: [
206
+ /* @__PURE__ */ jsxs("div", { children: [
207
+ message,
208
+ " ",
209
+ elapsedMs,
210
+ "ms"
211
+ ] }),
212
+ /* @__PURE__ */ jsxs("div", { style: { marginTop: 6, opacity: 0.8 }, children: [
213
+ "policy.mode=",
214
+ policyMode,
215
+ "\uFF08\u60F3\u201C\u65E0\u7B49\u5F85\u201D\u2192 sync\uFF1B\u60F3\u201C\u5C11\u95EA\u70C1\u201D\u2192 defer+preload\uFF09"
216
+ ] })
217
+ ]
218
+ }
219
+ );
220
+ };
221
+ var resolveRuntimeProviderFallback = (args) => {
222
+ if (args.fallback !== void 0) {
223
+ return args.fallback;
224
+ }
225
+ if (!isDevEnv()) {
226
+ return null;
227
+ }
228
+ return /* @__PURE__ */ jsx(DefaultRuntimeFallback, { phase: args.phase, policyMode: args.policyMode });
229
+ };
230
+ var recordFallbackDuration = (args) => {
231
+ if (!isDevEnv() && !Logix.Debug.isDevtoolsEnabled()) {
232
+ return;
233
+ }
234
+ void args.runtime.runPromise(
235
+ Logix.Debug.record({
236
+ type: "trace:react.fallback.duration",
237
+ data: {
238
+ phase: args.phase,
239
+ policyMode: args.policyMode,
240
+ durationMs: Math.round(args.durationMs * 100) / 100,
241
+ blockers: args.blockers
242
+ }
243
+ })
244
+ ).catch(() => {
245
+ });
246
+ };
247
+ var warnFallbackDuration = (args) => {
248
+ if (!isDevEnv()) {
249
+ return;
250
+ }
251
+ const warnThresholdMs = DEFAULT_FALLBACK_WARN_THRESHOLD_MS;
252
+ const isVisibleLong = args.durationMs >= warnThresholdMs;
253
+ const isFlashHint = args.phase === "react.suspense" && args.policyMode !== "sync" && args.durationMs >= DEFAULT_FALLBACK_FLASH_HINT_MIN_MS && args.durationMs < warnThresholdMs;
254
+ if (!isVisibleLong && !isFlashHint) {
255
+ return;
256
+ }
257
+ const runtimeKey = args.runtime;
258
+ const warned = FALLBACK_WARNED_BY_RUNTIME.get(runtimeKey) ?? /* @__PURE__ */ new Set();
259
+ FALLBACK_WARNED_BY_RUNTIME.set(runtimeKey, warned);
260
+ const kind = isVisibleLong ? "visible" : "flash";
261
+ const dedupeKey = `${kind}::${args.phase}::${args.policyMode}::${args.blockers ?? "none"}`;
262
+ if (warned.has(dedupeKey)) {
263
+ return;
264
+ }
265
+ warned.add(dedupeKey);
266
+ const docs = `Docs: ${getFallbackWarningDocsUrl()} (override via ${getFallbackWarningDocsPrefixEnv()})`;
267
+ const header = isVisibleLong ? "[Logix][React] Fallback visible" : "[Logix][React] Suspense fallback resolved quickly";
268
+ const hint = (() => {
269
+ if (isVisibleLong) {
270
+ if (args.phase === "provider.gating") {
271
+ return args.policyMode === "sync" ? 'Hint: You are using policy.mode="sync", but the provider is still waiting for dependencies (layer/config). Consider using "suspend"/"defer" (default UX), and/or make your layer/config ready earlier.' : "Hint: RuntimeProvider is still waiting for dependencies (layer/config/preload). Provide a fallback, use defer+preload, or switch to sync (deterministic, but render-blocking).";
272
+ }
273
+ return 'Hint: Suspense fallback is active (module resolve). If you prefer fewer fallbacks, use defer+preload; if you prefer \u201Cno waiting\u201D, switch to policy.mode="sync" (moves the cost to render-time sync blocking).';
274
+ }
275
+ return 'Hint: This fallback is short. If you want to eliminate the flicker, consider policy.mode="sync" (but it may introduce render-time sync blocking).';
276
+ })();
277
+ const example = (() => {
278
+ if (isFlashHint) {
279
+ return 'Example: <RuntimeProvider policy={{ mode: "sync" }} fallback={<Loading />}>\u2026</RuntimeProvider>';
280
+ }
281
+ return args.phase === "provider.gating" ? 'Example: <RuntimeProvider policy={{ mode: "defer", preload: [MyImpl] }} fallback={<Loading />}>\u2026</RuntimeProvider>' : 'Example: <RuntimeProvider policy={{ mode: "sync" }} fallback={<Loading />}>\u2026</RuntimeProvider>';
282
+ })();
283
+ const log = isVisibleLong ? console.warn : console.info;
284
+ log(
285
+ header,
286
+ `(${Math.round(args.durationMs)}ms)`,
287
+ "\n",
288
+ `phase=${args.phase}`,
289
+ "\n",
290
+ `policy.mode=${args.policyMode}`,
291
+ args.blockers ? `
292
+ blockers=${args.blockers}` : "",
293
+ "\n",
294
+ hint,
295
+ "\n",
296
+ example,
297
+ "\n",
298
+ docs
299
+ );
300
+ };
301
+ var FallbackProbeEnabled = ({ runtime, phase, policyMode, blockers, children }) => {
302
+ useEffect(() => {
303
+ const startedAt = performance.now();
304
+ return () => {
305
+ const durationMs = performance.now() - startedAt;
306
+ recordFallbackDuration({ runtime, phase, policyMode, durationMs, blockers });
307
+ warnFallbackDuration({ runtime, phase, policyMode, durationMs, blockers });
308
+ };
309
+ }, [runtime, phase, policyMode, blockers]);
310
+ return /* @__PURE__ */ jsx(Fragment, { children });
311
+ };
312
+ var FallbackProbeNoop = ({ children }) => /* @__PURE__ */ jsx(Fragment, { children });
313
+ var FallbackProbe = (props) => {
314
+ const enabled = isDevEnv() || Logix.Debug.isDevtoolsEnabled();
315
+ const Impl = enabled ? FallbackProbeEnabled : FallbackProbeNoop;
316
+ return /* @__PURE__ */ jsx(Impl, { ...props });
317
+ };
318
+
319
+ // src/internal/provider/policy.ts
320
+ var DEFAULT_PRELOAD_CONCURRENCY = 5;
321
+ var DEFAULT_SYNC_BUDGET_MS = 5;
322
+ var DEFAULT_YIELD_STRATEGY = "microtask";
323
+ var isModuleImpl = (handle) => Boolean(handle) && typeof handle === "object" && handle._tag === "ModuleImpl";
324
+ var isModuleTag = (handle) => {
325
+ if (!handle || typeof handle !== "object" && typeof handle !== "function") {
326
+ return false;
327
+ }
328
+ const candidate = handle;
329
+ return candidate._kind === "ModuleTag";
330
+ };
331
+ var getPreloadKeyForModuleId = (moduleId) => `preload:impl:${moduleId}`;
332
+ var getPreloadKeyForTagId = (tagId) => `preload:tag:${tagId}`;
333
+ var normalizeYieldPolicy = (policy) => ({
334
+ strategy: policy?.strategy ?? DEFAULT_YIELD_STRATEGY,
335
+ onlyWhenOverBudgetMs: policy?.onlyWhenOverBudgetMs
336
+ });
337
+ var normalizePreload = (preload) => {
338
+ if (!preload) return null;
339
+ const policy = Array.isArray(preload) ? { handles: preload } : preload;
340
+ const handles = [];
341
+ for (const handle of policy.handles ?? []) {
342
+ if (isModuleImpl(handle) || isModuleTag(handle)) {
343
+ handles.push(handle);
344
+ }
345
+ }
346
+ return {
347
+ handles,
348
+ concurrency: Math.max(1, policy.concurrency ?? DEFAULT_PRELOAD_CONCURRENCY),
349
+ yield: normalizeYieldPolicy(policy.yield)
350
+ };
351
+ };
352
+ var resolveRuntimeProviderPolicy = (args) => {
353
+ const inheritedMode = args.parentPolicy?.mode;
354
+ const mode = args.policy?.mode ?? (inheritedMode !== void 0 ? inheritedMode : "suspend");
355
+ const moduleResolveMode = mode === "sync" ? "sync" : "suspend";
356
+ const preload = mode === "defer" ? normalizePreload(args.policy?.preload) : null;
357
+ const keysByModuleId = /* @__PURE__ */ new Map();
358
+ const keysByTagId = /* @__PURE__ */ new Map();
359
+ if (preload) {
360
+ for (const handle of preload.handles) {
361
+ if (isModuleImpl(handle)) {
362
+ const moduleId = handle.module.id ?? "ModuleImpl";
363
+ keysByModuleId.set(moduleId, getPreloadKeyForModuleId(moduleId));
364
+ } else if (isModuleTag(handle)) {
365
+ const tagId = handle.id ?? "ModuleTag";
366
+ keysByTagId.set(tagId, getPreloadKeyForTagId(tagId));
367
+ }
368
+ }
369
+ }
370
+ return {
371
+ mode,
372
+ moduleImplMode: moduleResolveMode,
373
+ moduleTagMode: moduleResolveMode,
374
+ syncBudgetMs: Math.max(0, args.policy?.syncBudgetMs ?? args.parentPolicy?.syncBudgetMs ?? DEFAULT_SYNC_BUDGET_MS),
375
+ yield: normalizeYieldPolicy(args.policy?.yield ?? args.parentPolicy?.yield),
376
+ preload: preload ? {
377
+ ...preload,
378
+ keysByModuleId,
379
+ keysByTagId
380
+ } : null
381
+ };
382
+ };
383
+
384
+ // src/internal/provider/RuntimeProvider.tsx
385
+ import { jsx as jsx2 } from "react/jsx-runtime";
386
+ var SYNC_CONFIG_OVER_BUDGET_BY_RUNTIME = /* @__PURE__ */ new WeakMap();
387
+ var RuntimeProvider = ({
388
+ layer,
389
+ runtime,
390
+ children,
391
+ fallback,
392
+ policy,
393
+ onError
394
+ }) => {
395
+ const parent = useContext(RuntimeContext);
396
+ const baseRuntime = useRuntimeResolution(runtime, parent);
397
+ const resolvedPolicy = useMemo(
398
+ () => resolveRuntimeProviderPolicy({
399
+ policy,
400
+ parentPolicy: parent?.policy ?? null
401
+ }),
402
+ [policy, parent?.policy]
403
+ );
404
+ const onErrorRef = React2.useRef(onError);
405
+ onErrorRef.current = onError;
406
+ const { binding: layerBinding } = useLayerBinding(baseRuntime, layer, Boolean(layer), onErrorRef.current);
407
+ const onErrorSink = useMemo(() => {
408
+ if (!onError) return null;
409
+ const sink = {
410
+ record: (event) => {
411
+ const handler = onErrorRef.current;
412
+ if (!handler) {
413
+ return Effect2.void;
414
+ }
415
+ if (event.type === "lifecycle:error") {
416
+ return handler(event.cause, {
417
+ source: "provider",
418
+ phase: "debug.lifecycle_error",
419
+ moduleId: event.moduleId,
420
+ instanceId: event.instanceId,
421
+ runtimeLabel: event.runtimeLabel
422
+ }).pipe(Effect2.catchAllCause(() => Effect2.void));
423
+ }
424
+ if (event.type === "diagnostic" && event.severity === "error") {
425
+ return handler(
426
+ Cause.fail({
427
+ code: event.code,
428
+ message: event.message,
429
+ hint: event.hint
430
+ }),
431
+ {
432
+ source: "provider",
433
+ phase: "debug.diagnostic_error",
434
+ code: event.code,
435
+ message: event.message,
436
+ hint: event.hint,
437
+ moduleId: event.moduleId,
438
+ instanceId: event.instanceId,
439
+ runtimeLabel: event.runtimeLabel
440
+ }
441
+ ).pipe(Effect2.catchAllCause(() => Effect2.void));
442
+ }
443
+ return Effect2.void;
444
+ }
445
+ };
446
+ return sink;
447
+ }, [Boolean(onError)]);
448
+ const inheritedDebugSinks = useMemo(() => {
449
+ if (!onErrorSink) {
450
+ return [];
451
+ }
452
+ if (layerBinding) {
453
+ return layerBinding.debugSinks;
454
+ }
455
+ try {
456
+ return baseRuntime.runSync(
457
+ FiberRef.get(Logix2.Debug.internal.currentDebugSinks)
458
+ );
459
+ } catch {
460
+ return [];
461
+ }
462
+ }, [baseRuntime, layerBinding, onErrorSink]);
463
+ const runtimeWithBindings = useMemo(
464
+ () => layerBinding || onErrorSink ? createRuntimeAdapter(
465
+ baseRuntime,
466
+ layerBinding ? [layerBinding.context] : [],
467
+ layerBinding ? [layerBinding.scope] : [],
468
+ layerBinding ? [layerBinding.loggers] : [],
469
+ layerBinding ? [layerBinding.logLevel] : [],
470
+ [
471
+ onErrorSink ? [onErrorSink, ...inheritedDebugSinks] : layerBinding ? layerBinding.debugSinks : []
472
+ ]
473
+ ) : baseRuntime,
474
+ [baseRuntime, inheritedDebugSinks, layerBinding, onErrorSink]
475
+ );
476
+ const didReportSyncConfigSnapshotRef = React2.useRef(false);
477
+ const [configState, setConfigState] = useState2(() => {
478
+ const budgetMs = resolvedPolicy.syncBudgetMs;
479
+ if (budgetMs <= 0) {
480
+ return { snapshot: DEFAULT_CONFIG_SNAPSHOT, version: 1, loaded: false, loadMode: "none" };
481
+ }
482
+ if (SYNC_CONFIG_OVER_BUDGET_BY_RUNTIME.has(baseRuntime)) {
483
+ return { snapshot: DEFAULT_CONFIG_SNAPSHOT, version: 1, loaded: false, loadMode: "none" };
484
+ }
485
+ const startedAt = performance.now();
486
+ try {
487
+ const snapshot = runtimeWithBindings.runSync(
488
+ ReactRuntimeConfigSnapshot.load
489
+ );
490
+ const durationMs = performance.now() - startedAt;
491
+ const overBudget = durationMs > budgetMs;
492
+ if (overBudget) {
493
+ SYNC_CONFIG_OVER_BUDGET_BY_RUNTIME.set(baseRuntime, true);
494
+ }
495
+ return {
496
+ snapshot,
497
+ version: 1,
498
+ loaded: !overBudget,
499
+ loadMode: "sync",
500
+ syncDurationMs: durationMs,
501
+ syncOverBudget: overBudget
502
+ };
503
+ } catch {
504
+ return { snapshot: DEFAULT_CONFIG_SNAPSHOT, version: 1, loaded: false, loadMode: "none" };
505
+ }
506
+ });
507
+ useEffect2(() => {
508
+ if (configState.loadMode !== "sync") {
509
+ return;
510
+ }
511
+ if (didReportSyncConfigSnapshotRef.current) {
512
+ return;
513
+ }
514
+ didReportSyncConfigSnapshotRef.current = true;
515
+ void runtimeWithBindings.runPromise(
516
+ Logix2.Debug.record({
517
+ type: "trace:react.runtime.config.snapshot",
518
+ data: {
519
+ source: configState.snapshot.source,
520
+ mode: "sync",
521
+ durationMs: configState.syncDurationMs !== void 0 ? Math.round(configState.syncDurationMs * 100) / 100 : void 0,
522
+ syncBudgetMs: resolvedPolicy.syncBudgetMs,
523
+ overBudget: Boolean(configState.syncOverBudget)
524
+ }
525
+ })
526
+ ).catch(() => {
527
+ });
528
+ }, [configState, resolvedPolicy.syncBudgetMs, runtimeWithBindings]);
529
+ useEffect2(() => {
530
+ let cancelled = false;
531
+ runtimeWithBindings.runPromise(ReactRuntimeConfigSnapshot.load).then((snapshot) => {
532
+ if (cancelled) return;
533
+ setConfigState((prev) => {
534
+ const sameSnapshot = prev.snapshot.gcTime === snapshot.gcTime && prev.snapshot.initTimeoutMs === snapshot.initTimeoutMs && prev.snapshot.lowPriorityDelayMs === snapshot.lowPriorityDelayMs && prev.snapshot.lowPriorityMaxDelayMs === snapshot.lowPriorityMaxDelayMs && prev.snapshot.source === snapshot.source;
535
+ if (sameSnapshot && prev.loaded) {
536
+ return prev;
537
+ }
538
+ const cacheCriticalChanged = prev.snapshot.gcTime !== snapshot.gcTime;
539
+ return {
540
+ snapshot,
541
+ version: cacheCriticalChanged ? prev.version + 1 : prev.version,
542
+ loaded: true,
543
+ loadMode: "async"
544
+ };
545
+ });
546
+ void runtimeWithBindings.runPromise(
547
+ Logix2.Debug.record({
548
+ type: "trace:react.runtime.config.snapshot",
549
+ data: {
550
+ source: snapshot.source,
551
+ mode: "async"
552
+ }
553
+ })
554
+ ).catch(() => {
555
+ });
556
+ }).catch((error) => {
557
+ if (cancelled) return;
558
+ console.debug("[RuntimeProvider] Failed to load React runtime config snapshot, fallback to default.", error);
559
+ setConfigState((prev) => ({
560
+ snapshot: DEFAULT_CONFIG_SNAPSHOT,
561
+ version: prev.version,
562
+ loaded: true,
563
+ loadMode: "async"
564
+ }));
565
+ });
566
+ return () => {
567
+ cancelled = true;
568
+ };
569
+ }, [runtimeWithBindings]);
570
+ const contextValue = useMemo(
571
+ () => ({
572
+ runtime: runtimeWithBindings,
573
+ reactConfigSnapshot: configState.snapshot,
574
+ configVersion: configState.version,
575
+ policy: resolvedPolicy
576
+ }),
577
+ [runtimeWithBindings, configState, resolvedPolicy]
578
+ );
579
+ const isLayerReady = !layer || layerBinding !== null;
580
+ const isConfigReady = configState.loaded;
581
+ const resolveFallback = (phase) => {
582
+ return resolveRuntimeProviderFallback({ fallback, phase, policyMode: resolvedPolicy.mode });
583
+ };
584
+ const [deferReady, setDeferReady] = useState2(false);
585
+ useEffect2(() => {
586
+ if (resolvedPolicy.mode !== "defer") {
587
+ setDeferReady(false);
588
+ return;
589
+ }
590
+ setDeferReady(false);
591
+ }, [resolvedPolicy.mode]);
592
+ const preloadCancelsRef = React2.useRef(null);
593
+ useEffect2(() => {
594
+ if (resolvedPolicy.mode !== "defer") {
595
+ return;
596
+ }
597
+ setDeferReady(false);
598
+ if (!resolvedPolicy.preload) {
599
+ setDeferReady(true);
600
+ return;
601
+ }
602
+ if (!isLayerReady || !isConfigReady) {
603
+ return;
604
+ }
605
+ let cancelled = false;
606
+ const cache = getModuleCache(runtimeWithBindings, configState.snapshot, configState.version);
607
+ const preloadHandles = resolvedPolicy.preload.handles;
608
+ if (preloadHandles.length === 0) {
609
+ setDeferReady(true);
610
+ return;
611
+ }
612
+ const concurrency = Math.max(1, resolvedPolicy.preload.concurrency ?? DEFAULT_PRELOAD_CONCURRENCY);
613
+ const allCancels = /* @__PURE__ */ new Set();
614
+ preloadCancelsRef.current = allCancels;
615
+ const run = async () => {
616
+ const queue = preloadHandles.slice();
617
+ const runOne = async (handle) => {
618
+ if (cancelled) return;
619
+ const startedAt = performance.now();
620
+ if (handle?._tag === "ModuleImpl") {
621
+ const moduleId = handle.module?.id ?? "ModuleImpl";
622
+ const key2 = resolvedPolicy.preload.keysByModuleId.get(moduleId) ?? getPreloadKeyForModuleId(moduleId);
623
+ const factory2 = (scope) => Layer2.buildWithScope(handle.layer, scope).pipe(
624
+ Effect2.map((context) => Context2.get(context, handle.module))
625
+ );
626
+ const op2 = cache.preload(key2, factory2, {
627
+ ownerId: moduleId,
628
+ yield: resolvedPolicy.preload.yield,
629
+ entrypoint: "react.runtime.preload",
630
+ policyMode: "defer"
631
+ });
632
+ allCancels.add(op2.cancel);
633
+ await op2.promise;
634
+ const durationMs2 = performance.now() - startedAt;
635
+ void runtimeWithBindings.runPromise(
636
+ Logix2.Debug.record({
637
+ type: "trace:react.module.preload",
638
+ moduleId,
639
+ data: {
640
+ mode: "defer",
641
+ handleKind: "ModuleImpl",
642
+ key: key2,
643
+ durationMs: Math.round(durationMs2 * 100) / 100,
644
+ concurrency,
645
+ yieldStrategy: resolvedPolicy.preload.yield.strategy,
646
+ yieldOnlyWhenOverBudgetMs: resolvedPolicy.preload.yield.onlyWhenOverBudgetMs
647
+ }
648
+ })
649
+ ).catch(() => {
650
+ });
651
+ return;
652
+ }
653
+ const tagId = handle.id ?? "ModuleTag";
654
+ const key = resolvedPolicy.preload.keysByTagId.get(tagId) ?? getPreloadKeyForTagId(tagId);
655
+ const factory = (scope) => handle.pipe(
656
+ Scope.extend(scope)
657
+ );
658
+ const op = cache.preload(key, factory, {
659
+ ownerId: tagId,
660
+ yield: resolvedPolicy.preload.yield,
661
+ entrypoint: "react.runtime.preload",
662
+ policyMode: "defer"
663
+ });
664
+ allCancels.add(op.cancel);
665
+ await op.promise;
666
+ const durationMs = performance.now() - startedAt;
667
+ void runtimeWithBindings.runPromise(
668
+ Logix2.Debug.record({
669
+ type: "trace:react.module.preload",
670
+ data: {
671
+ mode: "defer",
672
+ handleKind: "ModuleTag",
673
+ tokenId: tagId,
674
+ key,
675
+ durationMs: Math.round(durationMs * 100) / 100,
676
+ concurrency,
677
+ yieldStrategy: resolvedPolicy.preload.yield.strategy,
678
+ yieldOnlyWhenOverBudgetMs: resolvedPolicy.preload.yield.onlyWhenOverBudgetMs
679
+ }
680
+ })
681
+ ).catch(() => {
682
+ });
683
+ };
684
+ const workers = Array.from({ length: Math.min(concurrency, queue.length) }, async () => {
685
+ while (!cancelled) {
686
+ const next = queue.shift();
687
+ if (!next) return;
688
+ await runOne(next);
689
+ }
690
+ });
691
+ await Promise.all(workers);
692
+ };
693
+ run().then(() => {
694
+ if (cancelled) return;
695
+ setDeferReady(true);
696
+ }).catch((error) => {
697
+ if (cancelled) return;
698
+ if (onErrorRef.current) {
699
+ runtimeWithBindings.runFork(
700
+ onErrorRef.current(Cause.die(error), { source: "provider", phase: "provider.layer.build" }).pipe(Effect2.catchAllCause(() => Effect2.void))
701
+ );
702
+ }
703
+ setDeferReady(true);
704
+ });
705
+ return () => {
706
+ cancelled = true;
707
+ preloadCancelsRef.current = null;
708
+ for (const cancel of allCancels) cancel();
709
+ };
710
+ }, [resolvedPolicy, isLayerReady, isConfigReady, runtimeWithBindings, configState, onErrorRef]);
711
+ useEffect2(() => {
712
+ if (resolvedPolicy.mode !== "defer" || !deferReady) {
713
+ return;
714
+ }
715
+ const cancels = preloadCancelsRef.current;
716
+ if (!cancels || cancels.size === 0) {
717
+ return;
718
+ }
719
+ preloadCancelsRef.current = null;
720
+ const list = Array.from(cancels);
721
+ let released = false;
722
+ const release = () => {
723
+ if (released) return;
724
+ released = true;
725
+ for (const cancel of list) cancel();
726
+ };
727
+ const timeout = setTimeout(release, 0);
728
+ return () => {
729
+ clearTimeout(timeout);
730
+ release();
731
+ };
732
+ }, [resolvedPolicy.mode, deferReady]);
733
+ const isReady = isLayerReady && isConfigReady && (resolvedPolicy.mode !== "defer" || deferReady);
734
+ if (!isReady) {
735
+ const blockersList = [
736
+ isLayerReady ? null : "layer",
737
+ isConfigReady ? null : "config",
738
+ resolvedPolicy.mode !== "defer" || deferReady ? null : "preload"
739
+ ].filter((x) => x !== null);
740
+ const blockers = blockersList.length > 0 ? blockersList.join("+") : void 0;
741
+ return /* @__PURE__ */ jsx2(
742
+ FallbackProbe,
743
+ {
744
+ runtime: runtimeWithBindings,
745
+ phase: "provider.gating",
746
+ policyMode: resolvedPolicy.mode,
747
+ blockers,
748
+ children: resolveFallback("provider.gating")
749
+ }
750
+ );
751
+ }
752
+ const content = resolvedPolicy.mode === "sync" ? children : /* @__PURE__ */ jsx2(
753
+ React2.Suspense,
754
+ {
755
+ fallback: /* @__PURE__ */ jsx2(FallbackProbe, { runtime: runtimeWithBindings, phase: "react.suspense", policyMode: resolvedPolicy.mode, children: resolveFallback("react.suspense") }),
756
+ children
757
+ }
758
+ );
759
+ return React2.createElement(RuntimeContext.Provider, { value: contextValue }, content);
760
+ };
761
+ var useRuntimeResolution = (runtimeProp, parent) => {
762
+ const baseRuntime = runtimeProp ?? parent?.runtime;
763
+ if (!baseRuntime) {
764
+ throw new Error(
765
+ "[RuntimeProvider] Missing runtime.\n\nFix:\n- Provide `runtime` prop: <RuntimeProvider runtime={runtime}>...\n- Or nest under an ancestor RuntimeProvider that provides `runtime`.\n"
766
+ );
767
+ }
768
+ return baseRuntime;
769
+ };
770
+
771
+ export {
772
+ RuntimeProvider
773
+ };