@fluixi/core 1.0.0-alpha.92 → 1.0.0-alpha.94

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 (113) hide show
  1. package/dist/cdn/core.global.js +2 -2
  2. package/dist/src/cdn/{chunk-MDJLC3PE.mjs → chunk-7XM7Q5VR.mjs} +271 -229
  3. package/dist/src/cdn/core.cjs +360 -253
  4. package/dist/src/cdn/core.mjs +92 -20
  5. package/dist/src/cdn/{router-JYFZP3EZ.mjs → router-VOY43YNM.mjs} +7 -3
  6. package/dist/src/index.cjs +548 -232
  7. package/dist/src/index.mjs +549 -224
  8. package/dist/src/lib/client/chunk-TSXM2PNY.mjs +53 -0
  9. package/dist/src/lib/client/index.cjs +310 -176
  10. package/dist/src/lib/client/index.d.ts.map +1 -1
  11. package/dist/src/lib/client/index.js +33 -18
  12. package/dist/src/lib/client/index.mjs +86 -15
  13. package/dist/src/lib/client/{router-PHLUYVQU.mjs → router-BDVPLLYX.mjs} +244 -99
  14. package/dist/src/lib/context/context.cjs +35 -72
  15. package/dist/src/lib/context/context.d.ts +45 -291
  16. package/dist/src/lib/context/context.d.ts.map +1 -1
  17. package/dist/src/lib/context/context.js +65 -397
  18. package/dist/src/lib/context/context.mjs +38 -73
  19. package/dist/src/lib/context/index.cjs +35 -72
  20. package/dist/src/lib/context/index.d.ts +2 -2
  21. package/dist/src/lib/context/index.d.ts.map +1 -1
  22. package/dist/src/lib/context/index.js +1 -5
  23. package/dist/src/lib/context/index.mjs +38 -73
  24. package/dist/src/lib/core.cjs +547 -231
  25. package/dist/src/lib/core.d.ts +5 -3
  26. package/dist/src/lib/core.d.ts.map +1 -1
  27. package/dist/src/lib/core.js +6 -4
  28. package/dist/src/lib/core.mjs +548 -223
  29. package/dist/src/lib/env/index.cjs +1 -17
  30. package/dist/src/lib/env/index.d.ts +2 -14
  31. package/dist/src/lib/env/index.d.ts.map +1 -1
  32. package/dist/src/lib/env/index.js +2 -26
  33. package/dist/src/lib/env/index.mjs +1 -17
  34. package/dist/src/lib/i18n/index.cjs +17 -2
  35. package/dist/src/lib/i18n/index.d.ts +10 -0
  36. package/dist/src/lib/i18n/index.d.ts.map +1 -1
  37. package/dist/src/lib/i18n/index.js +17 -1
  38. package/dist/src/lib/i18n/index.mjs +17 -2
  39. package/dist/src/lib/index.cjs +64 -99
  40. package/dist/src/lib/index.mjs +43 -76
  41. package/dist/src/lib/isomorphic.cjs +9 -4
  42. package/dist/src/lib/isomorphic.d.ts +9 -2
  43. package/dist/src/lib/isomorphic.d.ts.map +1 -1
  44. package/dist/src/lib/isomorphic.js +11 -4
  45. package/dist/src/lib/isomorphic.mjs +9 -4
  46. package/dist/src/lib/jsx-runtime/index.cjs +1 -1
  47. package/dist/src/lib/jsx-runtime/index.mjs +1 -1
  48. package/dist/src/lib/jsx-runtime/jsx-dev-runtime.cjs +1 -1
  49. package/dist/src/lib/jsx-runtime/jsx-dev-runtime.mjs +1 -1
  50. package/dist/src/lib/plugins/fluixi-routes-plugin.cjs +2 -2
  51. package/dist/src/lib/plugins/fluixi-routes-plugin.mjs +2 -2
  52. package/dist/src/lib/plugins/index.cjs +3 -3
  53. package/dist/src/lib/plugins/index.mjs +3 -3
  54. package/dist/src/lib/plugins/route-codegen.cjs +2 -2
  55. package/dist/src/lib/plugins/route-codegen.js +1 -1
  56. package/dist/src/lib/plugins/route-codegen.mjs +2 -2
  57. package/dist/src/lib/render/index.cjs +1 -1
  58. package/dist/src/lib/render/index.mjs +1 -1
  59. package/dist/src/lib/render/versions.cjs +1 -1
  60. package/dist/src/lib/render/versions.mjs +1 -1
  61. package/dist/src/lib/router/data.cjs +113 -115
  62. package/dist/src/lib/router/data.d.ts +63 -10
  63. package/dist/src/lib/router/data.d.ts.map +1 -1
  64. package/dist/src/lib/router/data.js +158 -68
  65. package/dist/src/lib/router/data.mjs +112 -112
  66. package/dist/src/lib/router/index.cjs +260 -163
  67. package/dist/src/lib/router/index.d.ts +22 -2
  68. package/dist/src/lib/router/index.d.ts.map +1 -1
  69. package/dist/src/lib/router/index.js +165 -22
  70. package/dist/src/lib/router/index.mjs +244 -143
  71. package/dist/src/lib/server/index.cjs +1 -1
  72. package/dist/src/lib/server/index.mjs +1 -1
  73. package/dist/src/lib/storage/cache.cjs +51 -0
  74. package/dist/src/lib/storage/cache.d.ts +18 -0
  75. package/dist/src/lib/storage/cache.d.ts.map +1 -0
  76. package/dist/src/lib/storage/cache.js +42 -0
  77. package/dist/src/lib/storage/cache.mjs +30 -0
  78. package/dist/src/lib/storage/index.cjs +246 -0
  79. package/dist/src/lib/storage/index.d.ts +83 -0
  80. package/dist/src/lib/storage/index.d.ts.map +1 -0
  81. package/dist/src/lib/storage/index.js +179 -0
  82. package/dist/src/lib/storage/index.mjs +231 -0
  83. package/dist/src/lib/storage/persisted.cjs +91 -0
  84. package/dist/src/lib/storage/persisted.d.ts +53 -0
  85. package/dist/src/lib/storage/persisted.d.ts.map +1 -0
  86. package/dist/src/lib/storage/persisted.js +75 -0
  87. package/dist/src/lib/storage/persisted.mjs +70 -0
  88. package/dist/src/lib/theme/color-scheme.cjs +281 -0
  89. package/dist/src/lib/theme/color-scheme.d.ts +61 -0
  90. package/dist/src/lib/theme/color-scheme.d.ts.map +1 -0
  91. package/dist/src/lib/theme/color-scheme.js +120 -0
  92. package/dist/src/lib/theme/color-scheme.mjs +265 -0
  93. package/dist/src/lib/theme/index.cjs +194 -37
  94. package/dist/src/lib/theme/index.d.ts +23 -11
  95. package/dist/src/lib/theme/index.d.ts.map +1 -1
  96. package/dist/src/lib/theme/index.js +73 -32
  97. package/dist/src/lib/theme/index.mjs +197 -35
  98. package/dist/src/lib/theme/provider.cjs +346 -0
  99. package/dist/src/lib/theme/provider.d.ts +34 -0
  100. package/dist/src/lib/theme/provider.d.ts.map +1 -0
  101. package/dist/src/lib/theme/provider.js +60 -0
  102. package/dist/src/lib/theme/provider.mjs +332 -0
  103. package/dist/src/routes.cjs +1 -1
  104. package/dist/src/routes.mjs +1 -1
  105. package/dist/src/utils.cjs +1 -1
  106. package/dist/src/utils.mjs +1 -1
  107. package/dist/src/version.generated.cjs +1 -1
  108. package/dist/src/version.generated.d.ts +1 -1
  109. package/dist/src/version.generated.js +1 -1
  110. package/dist/src/version.generated.mjs +1 -1
  111. package/dist/tsconfig.lib.tsbuildinfo +1 -1
  112. package/package.json +18 -11
  113. package/dist/src/lib/client/chunk-VB567ABU.mjs +0 -133
@@ -23,6 +23,7 @@ __export(core_exports, {
23
23
  $component: () => $component,
24
24
  Await: () => Await,
25
25
  ClientOnly: () => import_dom16.ClientOnly,
26
+ ColorSchemeProvider: () => ColorSchemeProvider,
26
27
  Dynamic: () => import_dom16.Dynamic,
27
28
  ErrorBoundary: () => import_dom16.ErrorBoundary,
28
29
  FRAMEWORK_NAME: () => FRAMEWORK_NAME,
@@ -39,46 +40,46 @@ __export(core_exports, {
39
40
  Suspense: () => Suspense,
40
41
  SuspenseList: () => SuspenseList,
41
42
  Switch: () => import_dom16.Switch,
43
+ ThemeProvider: () => ThemeProvider,
42
44
  VERSION: () => VERSION2,
43
45
  addClass: () => import_dom18.addClass,
44
46
  appendChildren: () => import_dom18.appendChildren,
45
- batch: () => import_signal15.batch,
47
+ batch: () => import_signal18.batch,
46
48
  classMap: () => import_dom19.classMap,
47
49
  cleanupDelegation: () => cleanupDelegation,
48
50
  clientOnly: () => clientOnly,
49
51
  closest: () => import_dom18.closest,
50
52
  composeProviders: () => composeProviders,
53
+ createColorScheme: () => createColorScheme,
51
54
  createComponent: () => createComponent,
52
- createComputed: () => import_signal15.createComputed,
53
- createConsumer: () => createConsumer,
55
+ createComputed: () => import_signal18.createComputed,
54
56
  createContext: () => createContext3,
55
- createContextWithProvider: () => createContextWithProvider,
56
- createDebounce: () => import_signal16.createDebounce,
57
- createDebouncedResource: () => import_signal16.createDebouncedResource,
58
- createDebouncedSignal: () => import_signal16.createDebouncedSignal,
59
- createDeferred: () => import_signal15.createDeferred,
60
- createEffect: () => import_signal15.createEffect,
57
+ createDebounce: () => import_signal19.createDebounce,
58
+ createDebouncedResource: () => import_signal19.createDebouncedResource,
59
+ createDebouncedSignal: () => import_signal19.createDebouncedSignal,
60
+ createDeferred: () => import_signal18.createDeferred,
61
+ createEffect: () => import_signal18.createEffect,
61
62
  createIsomorphicState: () => createIsomorphicState,
62
63
  createIsomorphicStore: () => createIsomorphicStore,
63
- createMemo: () => import_signal15.createMemo,
64
- createMemoryStorage: () => createMemoryStorage,
64
+ createMemo: () => import_signal18.createMemo,
65
65
  createProvider: () => createProvider,
66
- createRenderEffect: () => import_signal15.createRenderEffect,
67
- createResource: () => import_signal15.createResource,
68
- createRoot: () => import_signal15.createRoot,
69
- createSelector: () => import_signal16.createSelector,
70
- createSignal: () => import_signal15.createSignal,
66
+ createRenderEffect: () => import_signal18.createRenderEffect,
67
+ createResource: () => import_signal18.createResource,
68
+ createRoot: () => import_signal18.createRoot,
69
+ createSelector: () => import_signal19.createSelector,
70
+ createSignal: () => import_signal18.createSignal,
71
71
  createStore: () => import_store.createStore,
72
72
  createTheme: () => createTheme,
73
+ css: () => import_dom17.css,
73
74
  default: () => core_default,
74
75
  deferred: () => deferred,
75
76
  delegate: () => import_dom18.delegate,
76
77
  domCreateElement: () => import_dom18.createElement,
77
- effect: () => import_signal15.effect,
78
+ effect: () => import_signal18.effect,
78
79
  env: () => env,
79
80
  getAttribute: () => import_dom18.getAttribute,
80
81
  getOffset: () => import_dom18.getOffset,
81
- getOwner: () => import_signal15.getOwner,
82
+ getOwner: () => import_signal18.getOwner,
82
83
  getStyle: () => import_dom18.getStyle,
83
84
  hasClass: () => import_dom18.hasClass,
84
85
  html: () => import_dom17.html,
@@ -92,11 +93,11 @@ __export(core_exports, {
92
93
  isServer: () => import_dom12.isServer,
93
94
  isVisible: () => import_dom18.isVisible,
94
95
  lazy: () => lazy,
95
- memo: () => import_signal15.memo,
96
+ memo: () => import_signal18.memo,
96
97
  mergeProps: () => import_dom19.mergeProps,
97
98
  observeIntersection: () => import_dom18.observeIntersection,
98
99
  observeResize: () => import_dom18.observeResize,
99
- onCleanup: () => import_signal15.onCleanup,
100
+ onCleanup: () => import_signal18.onCleanup,
100
101
  onMount: () => onMount,
101
102
  onUnmount: () => onUnmount,
102
103
  portal: () => portal,
@@ -108,22 +109,23 @@ __export(core_exports, {
108
109
  removeChildren: () => import_dom18.removeChildren,
109
110
  removeClass: () => import_dom18.removeClass,
110
111
  render: () => render,
111
- resource: () => import_signal15.resource,
112
- runWithOwner: () => import_signal15.runWithOwner,
112
+ requireContext: () => requireContext,
113
+ resource: () => import_signal18.resource,
114
+ runWithOwner: () => import_signal18.runWithOwner,
113
115
  scrollIntoView: () => import_dom18.scrollIntoView,
114
116
  setAttribute: () => import_dom18.setAttribute,
115
- setOwner: () => import_signal15.setOwner,
117
+ setOwner: () => import_signal18.setOwner,
116
118
  setStyles: () => import_dom18.setStyles,
117
- signal: () => import_signal15.signal,
119
+ signal: () => import_signal18.signal,
118
120
  splitProps: () => import_dom19.splitProps,
119
- storage: () => storage,
120
121
  styleMap: () => import_dom19.styleMap,
121
122
  svg: () => import_dom17.svg,
122
123
  toggleClass: () => import_dom18.toggleClass,
123
- untrack: () => import_signal15.untrack,
124
+ untrack: () => import_signal18.untrack,
124
125
  untrackStore: () => import_store.untrackStore,
125
126
  unwrap: () => import_store.unwrap,
126
127
  useCallback: () => useCallback,
128
+ useColorScheme: () => useColorScheme,
127
129
  useContext: () => useContext3,
128
130
  useEffect: () => useEffect,
129
131
  useEventListener: () => useEventListener,
@@ -131,15 +133,15 @@ __export(core_exports, {
131
133
  useMemo: () => useMemo,
132
134
  useNavigate: () => useNavigate,
133
135
  useParams: () => useParams,
134
- useProvide: () => useProvide,
135
136
  useReducer: () => useReducer,
136
137
  useRef: () => useRef,
137
138
  useRouter: () => useRouter,
139
+ useTheme: () => useTheme,
138
140
  waitForElement: () => import_dom18.waitForElement
139
141
  });
140
142
  module.exports = __toCommonJS(core_exports);
141
- var import_signal15 = require("@fluixi/reactive/signal");
142
- var import_signal16 = require("@fluixi/reactive/signal");
143
+ var import_signal18 = require("@fluixi/reactive/signal");
144
+ var import_signal19 = require("@fluixi/reactive/signal");
143
145
  var import_operators = require("@fluixi/reactive/operators");
144
146
 
145
147
  // src/lib/render/index.ts
@@ -150,7 +152,7 @@ var import_signal6 = require("@fluixi/reactive/signal");
150
152
  var import_dom = require("@fluixi/dom");
151
153
 
152
154
  // src/version.generated.ts
153
- var VERSION = "1.0.0-alpha.92";
155
+ var VERSION = "1.0.0-alpha.94";
154
156
 
155
157
  // src/lib/render/versions.ts
156
158
  var import_dom2 = require("@fluixi/dom");
@@ -646,81 +648,20 @@ function cleanupDelegation() {
646
648
  // src/lib/context/context.ts
647
649
  var import_dom10 = require("@fluixi/dom");
648
650
  var import_signal7 = require("@fluixi/reactive/signal");
649
- var import_signal8 = require("@fluixi/reactive/signal");
650
- var _warnedEagerProvider = false;
651
- function warnEagerProvider() {
652
- if (_warnedEagerProvider) return;
653
- _warnedEagerProvider = true;
654
- if (typeof process !== "undefined" && true) return;
655
- console.warn(
656
- "[fluixi] createProvider({ context, value, children }) reads `children` eagerly: the object literal materialises `children` BEFORE the context is stamped, so a routed <Outlet/> that has siblings inside the provider renders detached and its descendants see no context under SSR. Use the lazy form instead:\n createProvider(Context, value, props) // children stays a getter\n // or JSX: <Context.Provider value={...}>{props.children}</Context.Provider>"
657
- );
658
- }
659
- function createProvider(a, value, childProps) {
660
- const isLazyForm = !!a?.Provider;
661
- const context = isLazyForm ? a : a.context;
662
- const rawValue = isLazyForm ? value : a.value;
663
- const debug = isLazyForm ? false : a.debug ?? false;
664
- if (!isLazyForm) warnEagerProvider();
665
- const resolvedValue = typeof rawValue === "function" ? rawValue() : rawValue;
666
- const sourceProps = isLazyForm ? childProps : a;
667
- return context.Provider({
668
- value: resolvedValue,
669
- get children() {
670
- return sourceProps?.children;
671
- },
672
- debug
673
- });
674
- }
675
- function createContextWithProvider(defaultValue) {
676
- const context = createContext3(defaultValue);
677
- const provider = (props) => {
678
- return createProvider(context, props.value, props);
679
- };
680
- return [context, provider];
681
- }
682
- function composeProviders(contexts, children) {
683
- if (contexts.length === 0) {
684
- return (0, import_signal8.readChildren)(() => children);
685
- }
686
- const [context, value] = contexts[0];
687
- const remaining = contexts.slice(1);
688
- return createProvider(context, value, {
689
- get children() {
690
- return composeProviders(remaining, children);
691
- }
692
- });
693
- }
694
- function createConsumer(context) {
695
- return (props) => {
696
- const value = useContext3(context);
697
- return props.children(value);
698
- };
699
- }
700
- function useProvide(context, factory) {
701
- const existing = useContext3(context);
702
- if (existing === context.defaultValue) {
703
- return factory();
704
- }
705
- return existing;
706
- }
707
651
  function createContext3(defaultValue, name) {
708
652
  const context = (0, import_signal7.createContext)(defaultValue, name);
709
- const Provider = (props) => {
710
- if (props.debug === true) {
711
- }
712
- return context.Provider({
653
+ const Provider = (props) => (
654
+ // Children stay a getter: the reactive Provider reads them after stamping the value on
655
+ // its owner. Reading `props.children` here would render them, and any <Outlet/> in them,
656
+ // before the context exists.
657
+ context.Provider({
713
658
  value: props.value,
714
659
  get children() {
715
660
  return props.children;
716
661
  },
717
662
  debug: props.debug
718
- });
719
- };
720
- if (typeof Symbol !== "undefined" && Symbol.for) {
721
- Provider.$$typeof = /* @__PURE__ */ Symbol.for("react.provider");
722
- }
723
- Provider.$$typeof = /* @__PURE__ */ Symbol.for("react.provider");
663
+ })
664
+ );
724
665
  return {
725
666
  id: context.id,
726
667
  Provider: (0, import_dom10.asProvider)(Provider),
@@ -731,10 +672,38 @@ function useContext3(context) {
731
672
  const value = (0, import_signal7.useContext)(context);
732
673
  return value !== void 0 ? value : context.defaultValue;
733
674
  }
675
+ function createProvider(context, value, props) {
676
+ const resolved = typeof value === "function" ? value() : value;
677
+ return context.Provider({
678
+ value: resolved,
679
+ get children() {
680
+ return props.children;
681
+ }
682
+ });
683
+ }
684
+ function composeProviders(providers, props) {
685
+ const outermost = providers.reduceRight(
686
+ (inner, [context, value]) => ({
687
+ get children() {
688
+ return createProvider(context, value, inner);
689
+ }
690
+ }),
691
+ props
692
+ );
693
+ return outermost.children;
694
+ }
695
+ function requireContext(context, message) {
696
+ for (let owner = (0, import_signal7.getOwner)(); owner; owner = owner.parent) {
697
+ if (owner.contexts?.has(context.id)) return useContext3(context);
698
+ }
699
+ throw new Error(message ?? `[fluixi] No Provider for ${context.id.description ?? "this context"} above this component.`);
700
+ }
734
701
 
735
702
  // src/lib/isomorphic.ts
736
- var import_signal9 = require("@fluixi/reactive/signal");
703
+ var import_signal8 = require("@fluixi/reactive/signal");
737
704
  var import_dom11 = require("@fluixi/dom");
705
+ var import_storage = require("@fluixi/storage");
706
+ var import_browser = require("@fluixi/storage/browser");
738
707
  var REGISTRY_KEY = "__fluixi_iso_states__";
739
708
  var SEED_PREFIX = "__iso:";
740
709
  function registry() {
@@ -748,7 +717,7 @@ function readSeed(key) {
748
717
  function createIsomorphicState(key, options) {
749
718
  const seed = readSeed(key);
750
719
  const initial = seed !== void 0 ? seed : typeof window !== "undefined" && options.client ? options.client() : options.fallback;
751
- const [value, set] = (0, import_signal9.createSignal)(initial);
720
+ const [value, set] = (0, import_signal8.createSignal)(initial, { name: key });
752
721
  registry().set(key, { server: options.server, set });
753
722
  return { value, set, key };
754
723
  }
@@ -760,9 +729,12 @@ function createIsomorphicStore(key, options) {
760
729
  });
761
730
  const value = () => base.value() ?? options.fallback;
762
731
  const persistWrite = (v) => {
763
- if (!options.persist || typeof localStorage === "undefined") return;
732
+ const target = options.persist;
733
+ if (!target) return;
734
+ if (typeof target === "string" && typeof window === "undefined") return;
764
735
  try {
765
- localStorage.setItem(options.persist, JSON.stringify(v));
736
+ const { storage, key: key2 } = typeof target === "string" ? { storage: (0, import_storage.createSyncStorage)({ adapter: (0, import_browser.localStorageAdapter)() }), key: target } : target;
737
+ storage.set(key2, v);
766
738
  } catch {
767
739
  }
768
740
  };
@@ -775,7 +747,7 @@ function createIsomorphicStore(key, options) {
775
747
  };
776
748
  const fields = {};
777
749
  for (const k of Object.keys(options.fallback)) {
778
- fields[k] = (0, import_signal9.createMemo)(() => value()[k]);
750
+ fields[k] = (0, import_signal8.createMemo)(() => value()[k], void 0, { name: `${key}.${String(k)}` });
779
751
  }
780
752
  let initPromise = null;
781
753
  const ensureInit = () => {
@@ -799,20 +771,6 @@ var import_dom13 = require("@fluixi/dom");
799
771
  function isClient() {
800
772
  return !(0, import_dom13.isServer)();
801
773
  }
802
- function createMemoryStorage() {
803
- const mem = /* @__PURE__ */ new Map();
804
- return {
805
- getItem: (k) => mem.has(k) ? mem.get(k) : null,
806
- setItem: (k, v) => void mem.set(k, String(v)),
807
- removeItem: (k) => void mem.delete(k),
808
- clear: () => mem.clear(),
809
- key: (i) => Array.from(mem.keys())[i] ?? null,
810
- get length() {
811
- return mem.size;
812
- }
813
- };
814
- }
815
- var storage = typeof localStorage !== "undefined" ? localStorage : createMemoryStorage();
816
774
  var env = {
817
775
  get window() {
818
776
  return typeof window !== "undefined" ? window : void 0;
@@ -826,36 +784,205 @@ function clientOnly(fn, fallback) {
826
784
  }
827
785
 
828
786
  // src/lib/theme/index.ts
787
+ var import_head = require("@fluixi/head");
788
+ var import_signal11 = require("@fluixi/reactive/signal");
789
+ var import_storage3 = require("@fluixi/storage");
790
+
791
+ // src/lib/storage/persisted.ts
792
+ var import_signal9 = require("@fluixi/reactive/signal");
793
+ var isAsync = (storage) => "encryption" in storage;
794
+ function persisted(storage, key, options = {}) {
795
+ const fallback = options.fallback ?? null;
796
+ const name = options.name ?? `storage:${storage.keyPrefix}${key}`;
797
+ const state = (0, import_signal9.signal)(isAsync(storage) ? fallback : storage.get(key) ?? fallback, { name });
798
+ const put = (value) => state.set(() => value);
799
+ const resolve = (next) => typeof next === "function" ? next((0, import_signal9.untrack)(state)) : next;
800
+ const writeOptions = (extra) => ({
801
+ ...options.ttl !== void 0 ? { ttl: options.ttl } : {},
802
+ ...options.encrypt ? { encrypt: true } : {},
803
+ ...extra
804
+ });
805
+ if (!isAsync(storage)) {
806
+ const off2 = storage.subscribe(key, (event) => {
807
+ if (event.type !== "set") return put(fallback);
808
+ if (event.value !== void 0) put(event.value);
809
+ });
810
+ if ((0, import_signal9.getOwner)()) (0, import_signal9.onCleanup)(off2);
811
+ return Object.assign(() => state(), {
812
+ set(next, extra) {
813
+ const value = resolve(next);
814
+ storage.set(key, value, writeOptions(extra));
815
+ put(value);
816
+ },
817
+ remove() {
818
+ storage.remove(key);
819
+ put(fallback);
820
+ }
821
+ });
822
+ }
823
+ let written = false;
824
+ const reread = () => storage.get(key).then(
825
+ (value) => put(value ?? fallback),
826
+ () => void 0
827
+ );
828
+ const off = storage.subscribe(key, (event) => {
829
+ if (event.type !== "set") return put(fallback);
830
+ if (event.value !== void 0) put(event.value);
831
+ else void reread();
832
+ });
833
+ if ((0, import_signal9.getOwner)()) (0, import_signal9.onCleanup)(off);
834
+ const ready = storage.get(key).then((value) => {
835
+ if (!written) put(value ?? fallback);
836
+ });
837
+ return Object.assign(() => state(), {
838
+ ready,
839
+ async set(next, extra) {
840
+ const previous = (0, import_signal9.untrack)(state);
841
+ const value = resolve(next);
842
+ written = true;
843
+ put(value);
844
+ try {
845
+ await storage.set(key, value, writeOptions(extra));
846
+ } catch (error) {
847
+ put(previous);
848
+ throw error;
849
+ }
850
+ },
851
+ async remove() {
852
+ written = true;
853
+ await storage.remove(key);
854
+ put(fallback);
855
+ }
856
+ });
857
+ }
858
+
859
+ // src/lib/storage/index.ts
860
+ var import_dom14 = require("@fluixi/dom");
829
861
  var import_signal10 = require("@fluixi/reactive/signal");
862
+ var import_storage2 = require("@fluixi/storage");
863
+ var import_browser2 = require("@fluixi/storage/browser");
864
+ var import_server = require("@fluixi/storage/server");
865
+ var StorageContext = (0, import_signal10.createContext)(void 0, "storage");
866
+ var AsyncStorageContext = (0, import_signal10.createContext)(void 0, "asyncStorage");
867
+ var isBrowser = () => typeof window !== "undefined" && typeof document !== "undefined";
868
+ var perRequest = /* @__PURE__ */ new WeakMap();
869
+ var perPage = /* @__PURE__ */ new Map();
870
+ function cached(scope, id, make) {
871
+ let adapter = scope.get(id);
872
+ if (!adapter) scope.set(id, adapter = make());
873
+ return adapter;
874
+ }
875
+ function requestRequired(scope) {
876
+ throw new import_storage2.StorageUnavailableError(
877
+ `useStorage({ scope: '${scope}' }) on the server needs a request: call it during a render, a loader or a server function, or provide a storage with <StorageProvider>.`
878
+ );
879
+ }
880
+ function adapterFor(scope, cookie = {}) {
881
+ const ctx = (0, import_dom14.getRequestContext)();
882
+ const options = { httpOnly: false, ...cookie };
883
+ switch (scope) {
884
+ case "local":
885
+ return (0, import_browser2.localStorageAdapter)();
886
+ case "session":
887
+ return (0, import_browser2.sessionStorageAdapter)();
888
+ case "request":
889
+ if (isBrowser()) return cached(perPage, "request", import_storage2.memoryStorage);
890
+ if (!ctx) requestRequired(scope);
891
+ return cached(perRequest.get(ctx) ?? perRequest.set(ctx, /* @__PURE__ */ new Map()).get(ctx), "request", import_server.requestStorage);
892
+ case "cookie": {
893
+ const id = `cookie:${JSON.stringify(options)}`;
894
+ if (isBrowser()) {
895
+ const { httpOnly: _ignored, ...browser } = options;
896
+ return cached(perPage, id, () => (0, import_browser2.documentCookieAdapter)(browser));
897
+ }
898
+ if (!ctx) requestRequired(scope);
899
+ const raw = ctx.event.request;
900
+ const target = raw && typeof raw === "object" ? raw : void 0;
901
+ return cached(
902
+ perRequest.get(ctx) ?? perRequest.set(ctx, /* @__PURE__ */ new Map()).get(ctx),
903
+ id,
904
+ () => (0, import_server.cookieStorage)({
905
+ ...options,
906
+ request: raw && typeof raw === "object" && ("headers" in raw || "getHeader" in raw) ? (0, import_server.toStorageRequest)(raw) : void 0,
907
+ response: {
908
+ appendHeader(name, value) {
909
+ if (!(0, import_dom14.appendResponseHeader)(name, value, target)) {
910
+ throw new import_storage2.StorageUnavailableError("The response headers were already sent, so this cookie cannot reach the browser. Write it before the page head goes out: in middleware, a loader, a server function or the render before its first chunk.");
911
+ }
912
+ }
913
+ }
914
+ })
915
+ );
916
+ }
917
+ }
918
+ }
919
+ function useStorage(options = {}) {
920
+ const { scope = "auto", cookie, ...storageOptions } = options;
921
+ if (scope === "auto") {
922
+ const provided = (0, import_signal10.useContext)(StorageContext);
923
+ if (provided) return options.namespace ? provided.namespace(options.namespace) : provided;
924
+ }
925
+ return (0, import_storage2.createSyncStorage)({ ...storageOptions, adapter: resolveAdapter(scope, cookie) });
926
+ }
927
+ function resolveAdapter(scope, cookie) {
928
+ if (scope !== "auto") return adapterFor(scope, cookie);
929
+ if (isBrowser()) return adapterFor("local");
930
+ if (!(0, import_dom14.getRequestContext)()) requestRequired(scope);
931
+ return adapterFor("request");
932
+ }
933
+
934
+ // src/lib/theme/index.ts
830
935
  function systemMap(system) {
831
936
  if (!system) return null;
832
937
  if (system === true) return { dark: "dark", light: "light" };
833
938
  return system;
834
939
  }
940
+ var YEAR = 60 * 60 * 24 * 365;
941
+ function defaultStorage() {
942
+ try {
943
+ return useStorage({ scope: "cookie", cookie: { maxAge: YEAR, sameSite: "lax" } });
944
+ } catch (error) {
945
+ if (error instanceof import_storage3.StorageUnavailableError) return null;
946
+ throw error;
947
+ }
948
+ }
949
+ function storedLookup(storage, key) {
950
+ if (!storage) return null;
951
+ const full = storage.keyPrefix + key;
952
+ if (storage.scope === "cookie") {
953
+ return { from: "cookie", name: encodeURIComponent(full).replace(/[()]/g, (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`) };
954
+ }
955
+ if (storage.scope === "local") return { from: "local", name: full };
956
+ return null;
957
+ }
835
958
  function createTheme(options) {
836
959
  const storageKey = options.storageKey ?? "theme";
837
960
  const attribute = options.attribute ?? "data-theme";
838
961
  const sys = systemMap(options.system);
839
- const detectInitial = () => {
840
- const stored = storage.getItem(storageKey);
841
- if (stored && options.themes.includes(stored)) return stored;
842
- if (sys && env.window?.matchMedia?.("(prefers-color-scheme: dark)")?.matches) return sys.dark;
843
- if (sys) return sys.light;
844
- return options.default;
962
+ const storage = options.storage === void 0 ? defaultStorage() : options.storage;
963
+ const valid = (t) => typeof t === "string" && options.themes.includes(t);
964
+ const fallback = sys && env.window?.matchMedia?.("(prefers-color-scheme: dark)")?.matches ? sys.dark : sys ? sys.light : options.default;
965
+ const stored = storage ? persisted(storage, storageKey) : null;
966
+ const [unsaved, setUnsaved] = (0, import_signal11.createSignal)(null);
967
+ const value = () => {
968
+ const local = unsaved();
969
+ if (local !== null) return local;
970
+ const saved = stored?.();
971
+ return valid(saved) ? saved : fallback;
845
972
  };
846
- const [value, setValue] = (0, import_signal10.createSignal)(detectInitial());
847
- const [isLoading, setLoading] = (0, import_signal10.createSignal)(false);
973
+ const [isLoading, setLoading] = (0, import_signal11.createSignal)(false);
848
974
  const apply = (t) => {
849
975
  env.document?.documentElement.setAttribute(attribute, t);
850
- try {
851
- storage.setItem(storageKey, t);
852
- } catch {
853
- }
854
976
  };
855
977
  const set = (t) => {
856
978
  if (!options.themes.includes(t)) return;
857
979
  const prev = value();
858
- setValue(() => t);
980
+ try {
981
+ stored?.set(t);
982
+ setUnsaved(stored ? null : t);
983
+ } catch {
984
+ setUnsaved(() => t);
985
+ }
859
986
  apply(t);
860
987
  options.onChange?.(t, prev);
861
988
  if (options.load) {
@@ -872,20 +999,130 @@ function createTheme(options) {
872
999
  set(options.themes[(i + 1) % options.themes.length]);
873
1000
  };
874
1001
  const init = () => apply(value());
875
- const headScript = () => {
876
- const cfg = JSON.stringify({
877
- k: storageKey,
878
- a: attribute,
879
- d: options.default,
880
- sys
881
- });
882
- return `<script>(function(){try{var c=${cfg},t=localStorage.getItem(c.k);if(!t&&c.sys)t=matchMedia('(prefers-color-scheme: dark)').matches?c.sys.dark:c.sys.light;if(!t)t=c.d;document.documentElement.setAttribute(c.a,t);}catch(e){}})();<\/script>`;
1002
+ const scriptBody = () => {
1003
+ const cfg = JSON.stringify({ s: storedLookup(storage, storageKey), a: attribute, d: options.default, sys, ts: options.themes }).replace(/</g, "\\u003c");
1004
+ return `(function(){try{var c=${cfg},t=null,s=c.s,r=null;if(s&&s.from==='local')r=localStorage.getItem(s.name);if(s&&s.from==='cookie')document.cookie.split('; ').forEach(function(p){var i=p.indexOf('=');if(p.slice(0,i)===s.name)r=decodeURIComponent(p.slice(i+1));});if(r){var e=JSON.parse(r);t=e&&e.v===1?e.value:e;}if(c.ts.indexOf(t)<0)t=null;if(!t&&c.sys)t=matchMedia('(prefers-color-scheme: dark)').matches?c.sys.dark:c.sys.light;if(!t)t=c.d;document.documentElement.setAttribute(c.a,t);}catch(e){}})();`;
883
1005
  };
1006
+ const headScript = () => `<script>${scriptBody()}<\/script>`;
1007
+ if ((0, import_head.getActiveHead)()) {
1008
+ (0, import_head.useHead)({
1009
+ htmlAttrs: { [attribute]: () => value() },
1010
+ ...options.script === false ? {} : { script: [{ children: scriptBody() }] }
1011
+ });
1012
+ }
884
1013
  return { value, isLoading, set, toggle, init, headScript };
885
1014
  }
886
1015
 
887
- // src/lib/router/index.ts
1016
+ // src/lib/theme/color-scheme.ts
1017
+ var import_head2 = require("@fluixi/head");
888
1018
  var import_signal12 = require("@fluixi/reactive/signal");
1019
+ var CHOICES = ["system", "light", "dark"];
1020
+ var DARK = "(prefers-color-scheme: dark)";
1021
+ function createColorScheme(options = {}) {
1022
+ const storageKey = options.storageKey ?? "color-scheme";
1023
+ const attribute = options.attribute ?? "data-theme";
1024
+ const systemAs = options.systemAs ?? "resolved";
1025
+ const storage = options.storage === void 0 ? defaultStorage() : options.storage;
1026
+ const saved = storage ? persisted(storage, storageKey) : null;
1027
+ const [unsaved, setUnsaved] = (0, import_signal12.createSignal)(null);
1028
+ const value = () => {
1029
+ const local = unsaved();
1030
+ if (local) return local;
1031
+ const stored = saved?.();
1032
+ return CHOICES.includes(stored) ? stored : "system";
1033
+ };
1034
+ const media = env.window?.matchMedia?.(DARK);
1035
+ const [osDark, setOsDark] = (0, import_signal12.createSignal)(media?.matches ?? false);
1036
+ if (media) {
1037
+ const onChange = () => setOsDark(media.matches);
1038
+ media.addEventListener?.("change", onChange);
1039
+ if ((0, import_signal12.getOwner)()) (0, import_signal12.onCleanup)(() => media.removeEventListener?.("change", onChange));
1040
+ }
1041
+ const resolved = () => {
1042
+ const choice = value();
1043
+ return choice === "system" ? osDark() ? "dark" : "light" : choice;
1044
+ };
1045
+ const shown = () => systemAs === "system" && value() === "system" ? "system" : resolved();
1046
+ const apply = () => {
1047
+ env.document?.documentElement.setAttribute(attribute, shown());
1048
+ };
1049
+ const set = (choice) => {
1050
+ if (!CHOICES.includes(choice)) return;
1051
+ if (!saved) setUnsaved(choice);
1052
+ else {
1053
+ try {
1054
+ saved.set(choice);
1055
+ setUnsaved(null);
1056
+ } catch {
1057
+ setUnsaved(choice);
1058
+ }
1059
+ }
1060
+ apply();
1061
+ options.onChange?.(choice);
1062
+ };
1063
+ const scriptBody = () => {
1064
+ const cfg = JSON.stringify({ s: storedLookup(storage, storageKey), a: attribute, sys: systemAs === "system", tc: options.themeColor ?? null, cs: options.colorSchemeMeta !== false }).replace(/</g, "\\u003c");
1065
+ return `(function(){try{var c=${cfg},t=null,s=c.s,r=null;if(s&&s.from==='local')r=localStorage.getItem(s.name);if(s&&s.from==='cookie')document.cookie.split('; ').forEach(function(p){var i=p.indexOf('=');if(p.slice(0,i)===s.name)r=decodeURIComponent(p.slice(i+1));});if(r){var e=JSON.parse(r);t=e&&e.v===1?e.value:e;}var chosen=t==='light'||t==='dark'?t:null;if(!chosen)t=c.sys?'system':(matchMedia('${DARK}').matches?'dark':'light');document.documentElement.setAttribute(c.a,t);if(chosen&&c.tc)document.querySelectorAll('meta[name="theme-color"]').forEach(function(m){m.setAttribute('content',c.tc[chosen]);m.removeAttribute('media');});if(chosen&&c.cs){var m=document.querySelector('meta[name="color-scheme"]');if(m)m.setAttribute('content',chosen);}}catch(e){}})();`;
1066
+ };
1067
+ if ((0, import_head2.getActiveHead)()) {
1068
+ const colors = options.themeColor;
1069
+ (0, import_head2.useHead)({
1070
+ htmlAttrs: { [attribute]: shown },
1071
+ ...colors ? {
1072
+ themeColor: () => value() === "system" ? [
1073
+ { color: colors.light, media: "(prefers-color-scheme: light)" },
1074
+ { color: colors.dark, media: DARK }
1075
+ ] : colors[resolved()]
1076
+ } : {},
1077
+ ...options.colorSchemeMeta === false ? {} : { colorScheme: () => value() === "system" ? "light dark" : resolved() },
1078
+ ...options.script === false ? {} : { script: [{ children: scriptBody() }] }
1079
+ });
1080
+ }
1081
+ if (env.document && (0, import_signal12.getOwner)()) (0, import_signal12.createEffect)(apply);
1082
+ return {
1083
+ value,
1084
+ resolved,
1085
+ set,
1086
+ toggle: () => set(resolved() === "dark" ? "light" : "dark"),
1087
+ init: apply,
1088
+ headScript: () => `<script>${scriptBody()}<\/script>`
1089
+ };
1090
+ }
1091
+
1092
+ // src/lib/theme/provider.ts
1093
+ var ThemesContext = createContext3(void 0, "themes");
1094
+ function ThemeProvider(props) {
1095
+ const outer = useContext3(ThemesContext);
1096
+ return ThemesContext.Provider({
1097
+ // A nested provider adds to the axes above it rather than hiding them.
1098
+ value: outer ? { ...outer, ...props.themes } : props.themes,
1099
+ get children() {
1100
+ return props.children;
1101
+ }
1102
+ });
1103
+ }
1104
+ function useTheme(name) {
1105
+ const themes = useContext3(ThemesContext);
1106
+ if (!themes || !(name in themes)) {
1107
+ throw new Error(`[fluixi] No theme named "${name}" above this component: provide it with <ThemeProvider> or <ColorSchemeProvider>.`);
1108
+ }
1109
+ return themes[name];
1110
+ }
1111
+ function ColorSchemeProvider(props) {
1112
+ const scheme = createColorScheme(props);
1113
+ return ThemeProvider({
1114
+ themes: { scheme },
1115
+ get children() {
1116
+ return props.children;
1117
+ }
1118
+ });
1119
+ }
1120
+ function useColorScheme() {
1121
+ return useTheme("scheme");
1122
+ }
1123
+
1124
+ // src/lib/router/index.ts
1125
+ var import_signal15 = require("@fluixi/reactive/signal");
889
1126
 
890
1127
  // src/lib/router/transition.ts
891
1128
  function supportsViewTransitions() {
@@ -937,76 +1174,95 @@ function runTransition(mode, commit) {
937
1174
 
938
1175
  // src/lib/router/index.ts
939
1176
  var import_dom15 = require("@fluixi/dom");
940
- var import_signal13 = require("@fluixi/reactive/signal");
1177
+ var import_signal16 = require("@fluixi/reactive/signal");
941
1178
  var import_object = require("@fluixi/utils/object");
942
1179
  var import_compare = require("@fluixi/utils/compare");
943
1180
  var import_router = require("@fluixi/router");
944
- var import_router2 = require("@fluixi/router");
945
1181
 
946
1182
  // src/lib/router/data.ts
947
- var import_signal11 = require("@fluixi/reactive/signal");
948
-
949
- // src/lib/server/hydration.ts
950
- var import_dom14 = require("@fluixi/dom");
951
- var hydrationDataMap = /* @__PURE__ */ new Map();
952
- function clearHydrationData() {
953
- hydrationDataMap.clear();
1183
+ var import_signal13 = require("@fluixi/reactive/signal");
1184
+ var import_signal14 = require("@fluixi/reactive/signal");
1185
+ var revalidationListeners = /* @__PURE__ */ new Set();
1186
+ function onRevalidate(listener) {
1187
+ revalidationListeners.add(listener);
1188
+ return () => void revalidationListeners.delete(listener);
954
1189
  }
955
- function cleanupHydration() {
956
- if (typeof window === "undefined") {
957
- return;
958
- }
959
- const script = document.getElementById("__FLUIXI_HYDRATION__");
960
- if (script) {
961
- script.remove();
962
- }
963
- const walker = document.createTreeWalker(
964
- document.body,
965
- NodeFilter.SHOW_COMMENT,
966
- null
967
- );
968
- const comments = [];
969
- let node;
970
- while (node = walker.nextNode()) {
971
- const comment = node;
972
- const text = comment.nodeValue || "";
973
- if (text.startsWith("#") || text.startsWith("/")) {
974
- comments.push(comment);
975
- }
1190
+ var reloadMounted = () => {
1191
+ for (const listener of [...revalidationListeners]) listener();
1192
+ };
1193
+ function revalidate(key, store) {
1194
+ const names = typeof key === "string" ? [key] : Array.isArray(key) ? key : null;
1195
+ drop(names ?? key, store ?? (0, import_signal14.currentResourceCache)());
1196
+ if (names && !store) tellOtherTabs(names);
1197
+ }
1198
+ function drop(what, store) {
1199
+ const matches = Array.isArray(what) ? (k) => what.some((n) => k === n || k.startsWith(n + ":")) : what;
1200
+ for (const k of [...store.keys()]) if (matches(k)) store.delete(k);
1201
+ reloadMounted();
1202
+ }
1203
+ var TABS_KEY = "fluixi:revalidate";
1204
+ function tellOtherTabs(names) {
1205
+ try {
1206
+ localStorage.setItem(TABS_KEY, JSON.stringify({ names, at: Date.now() + Math.random() }));
1207
+ } catch {
976
1208
  }
977
- comments.forEach((comment) => comment.remove());
978
- clearHydrationData();
979
1209
  }
980
1210
  if (typeof window !== "undefined") {
981
- window.addEventListener("load", () => {
982
- setTimeout(cleanupHydration, 100);
1211
+ window.addEventListener("storage", (event) => {
1212
+ if (event.key !== TABS_KEY || !event.newValue) return;
1213
+ try {
1214
+ const names = JSON.parse(event.newValue).names;
1215
+ if (Array.isArray(names) && names.every((n) => typeof n === "string")) drop(names, (0, import_signal14.currentResourceCache)());
1216
+ } catch {
1217
+ }
983
1218
  });
984
1219
  }
985
-
986
- // src/lib/router/data.ts
987
- var [revalidationVersion, setRevalidationVersion] = (0, import_signal11.createSignal)(0);
1220
+ globalThis[/* @__PURE__ */ Symbol.for("fluixi.revalidate")] = (names) => revalidate(names);
988
1221
  var RouteDataContext = createContext3(void 0, "route-data");
989
1222
  var RouteDataProvider = RouteDataContext.Provider;
990
1223
 
991
1224
  // src/lib/router/index.ts
992
- var import_signal14 = require("@fluixi/reactive/signal");
993
- var RouterContext = (0, import_signal13.createContext)(void 0, "router");
994
- var OutletContext = (0, import_signal13.createContext)(void 0, "outlet");
995
- var LevelParamsContext = (0, import_signal13.createContext)(void 0, "route-params");
996
- var RouteDataContext2 = (0, import_signal13.createContext)(void 0, "route-data");
1225
+ var import_router2 = require("@fluixi/router");
1226
+ var import_signal17 = require("@fluixi/reactive/signal");
1227
+ var RouterContext = (0, import_signal16.createContext)(void 0, "router");
1228
+ var OutletContext = (0, import_signal16.createContext)(void 0, "outlet");
1229
+ var LevelParamsContext = (0, import_signal16.createContext)(void 0, "route-params");
1230
+ var RouteDataContext2 = (0, import_signal16.createContext)(void 0, "route-data");
997
1231
  function renderLayer(m, depth, base) {
998
1232
  let data;
999
1233
  const loader = m.route.data;
1000
1234
  if (typeof loader === "function") {
1001
1235
  let cancelled = false;
1002
- (0, import_signal12.onCleanup)(() => {
1236
+ (0, import_signal15.onCleanup)(() => {
1003
1237
  cancelled = true;
1004
1238
  });
1005
- const owner = (0, import_signal12.getOwner)();
1006
- const [resource2] = (0, import_signal12.createResource)(
1007
- () => loader({ params: base.params, location: base.location }, owner, () => cancelled)
1008
- );
1009
- data = resource2;
1239
+ const owner = (0, import_signal15.getOwner)();
1240
+ const router = (0, import_signal16.useContext)(RouterContext);
1241
+ let watching = false;
1242
+ const trackDeps = (deps) => {
1243
+ if (watching || cancelled) return;
1244
+ watching = true;
1245
+ (0, import_signal15.runWithOwner)(owner, () => {
1246
+ let first = true;
1247
+ (0, import_signal15.createEffect)(() => {
1248
+ deps();
1249
+ if (first) first = false;
1250
+ else (0, import_signal15.untrack)(() => void refetch());
1251
+ });
1252
+ });
1253
+ };
1254
+ const [resource2, { refetch }] = (0, import_signal15.createResource)(() => {
1255
+ const result = loader({ params: base.params, location: base.location }, owner, () => cancelled, trackDeps);
1256
+ return router ? router.trackLoad(result) : result;
1257
+ });
1258
+ (0, import_signal15.onCleanup)(onRevalidate(() => void refetch()));
1259
+ data = () => {
1260
+ const error = resource2.error;
1261
+ if (error !== void 0 && error !== null) throw error;
1262
+ return resource2();
1263
+ };
1264
+ Object.defineProperty(data, "loading", { get: () => resource2.loading });
1265
+ Object.defineProperty(data, "state", { get: () => resource2.state });
1010
1266
  }
1011
1267
  const buildPage = () => RouteDataContext2.Provider({
1012
1268
  value: data,
@@ -1024,33 +1280,62 @@ function renderLayer(m, depth, base) {
1024
1280
  });
1025
1281
  }
1026
1282
  });
1027
- if (!data) return buildPage();
1283
+ if (!data) return { view: buildPage(), ready: () => true };
1028
1284
  const res = data;
1029
- const isLoading = () => res.loading;
1030
- const [ready, setReady] = (0, import_signal12.createSignal)((0, import_signal12.untrack)(() => !isLoading()));
1031
- (0, import_signal12.createEffect)(() => setReady(!isLoading()));
1032
- return (0, import_signal12.createMemo)(() => ready() ? buildPage() : null);
1285
+ const isLoading = () => {
1286
+ const state = res.state;
1287
+ return state === void 0 ? res.loading : state === "pending" || state === "unresolved";
1288
+ };
1289
+ const [ready, setReady] = (0, import_signal15.createSignal)((0, import_signal15.untrack)(() => !isLoading()));
1290
+ (0, import_signal15.createEffect)(() => setReady(!isLoading()));
1291
+ return { view: (0, import_signal15.createMemo)(() => ready() ? buildPage() : null), ready };
1033
1292
  }
1034
1293
  function renderChild(ctx) {
1035
1294
  const childIdx = ctx.depth + 1;
1036
- const [childComp, setChildComp] = (0, import_signal12.createSignal)(
1037
- (0, import_signal12.untrack)(() => ctx.matched()[childIdx]?.component)
1295
+ const [childComp, setChildComp] = (0, import_signal15.createSignal)(
1296
+ (0, import_signal15.untrack)(() => ctx.matched()[childIdx]?.component)
1038
1297
  );
1039
- (0, import_signal12.createEffect)(() => {
1298
+ (0, import_signal15.createEffect)(() => {
1040
1299
  const c = ctx.matched()[childIdx]?.component;
1041
- if ((0, import_signal12.untrack)(childComp) !== c) setChildComp(() => c);
1300
+ if ((0, import_signal15.untrack)(childComp) !== c) setChildComp(() => c);
1042
1301
  });
1043
- return (0, import_signal12.createMemo)(() => {
1302
+ const owner = (0, import_signal15.getOwner)();
1303
+ let shown = null;
1304
+ let pending = null;
1305
+ const drop2 = (layer) => layer?.dispose();
1306
+ (0, import_signal15.onCleanup)(() => {
1307
+ drop2(shown);
1308
+ drop2(pending);
1309
+ shown = pending = null;
1310
+ });
1311
+ const incoming = (0, import_signal15.createMemo)(() => {
1044
1312
  const component = childComp();
1045
- if (!component) {
1313
+ if (!component) return null;
1314
+ const m = (0, import_signal15.untrack)(() => ctx.matched())[childIdx];
1315
+ return (0, import_signal15.runWithOwner)(
1316
+ owner,
1317
+ () => (0, import_signal15.createRoot)((dispose) => ({
1318
+ ...renderLayer(m, childIdx, { matched: ctx.matched, params: ctx.params, location: ctx.location }),
1319
+ dispose
1320
+ }))
1321
+ );
1322
+ });
1323
+ return (0, import_signal15.createMemo)(() => {
1324
+ const next = incoming();
1325
+ if (!next) {
1326
+ drop2(shown);
1327
+ drop2(pending);
1328
+ shown = pending = null;
1046
1329
  return ctx.notFound ? ctx.notFound() : null;
1047
1330
  }
1048
- const m = (0, import_signal12.untrack)(() => ctx.matched())[childIdx];
1049
- return renderLayer(m, childIdx, {
1050
- matched: ctx.matched,
1051
- params: ctx.params,
1052
- location: ctx.location
1053
- });
1331
+ if (next === shown) return shown.view;
1332
+ if (pending && pending !== next) drop2(pending);
1333
+ pending = next;
1334
+ if (shown && !next.ready()) return shown.view;
1335
+ drop2(shown);
1336
+ shown = next;
1337
+ pending = null;
1338
+ return shown.view;
1054
1339
  });
1055
1340
  }
1056
1341
  function renderNotFound(notFound) {
@@ -1084,7 +1369,9 @@ function Router(props) {
1084
1369
  base: props.base,
1085
1370
  history
1086
1371
  });
1087
- const [state, setState] = (0, import_signal12.createSignal)(router.state());
1372
+ const [state, setState] = (0, import_signal15.createSignal)(router.state());
1373
+ const [navigating, setNavigating] = (0, import_signal15.createSignal)(false);
1374
+ const [loads, setLoads] = (0, import_signal15.createSignal)(0);
1088
1375
  let navigationId = 0;
1089
1376
  const takeMode = () => {
1090
1377
  const mode = pendingTransitionMode ?? (props.transition ?? "auto");
@@ -1094,12 +1381,15 @@ function Router(props) {
1094
1381
  const unsub = router.subscribe((next) => {
1095
1382
  const id = ++navigationId;
1096
1383
  const mode = takeMode();
1384
+ setNavigating(true);
1097
1385
  const prepared = prepareComponents(
1098
1386
  (next.match?.matched ?? []).map((m) => m.component).filter(Boolean)
1099
1387
  );
1100
1388
  const commit = () => {
1101
1389
  setState(next);
1102
- return (0, import_signal12.flush)();
1390
+ const flushed = (0, import_signal15.flush)();
1391
+ if (id === navigationId) setNavigating(false);
1392
+ return flushed;
1103
1393
  };
1104
1394
  if (!prepared) {
1105
1395
  void runTransition(mode, commit);
@@ -1110,18 +1400,29 @@ function Router(props) {
1110
1400
  return runTransition(mode, commit);
1111
1401
  });
1112
1402
  });
1113
- (0, import_signal12.onCleanup)(() => {
1403
+ (0, import_signal15.onCleanup)(() => {
1114
1404
  unsub();
1115
1405
  router.destroy();
1116
1406
  navigationId++;
1117
1407
  });
1118
- const matched = (0, import_signal12.createMemo)(
1408
+ const matched = (0, import_signal15.createMemo)(
1119
1409
  () => (state().match?.matched ?? []).filter((m) => m.component)
1120
1410
  );
1121
1411
  const ctx = {
1122
1412
  router,
1123
- location: (0, import_signal12.createMemo)(() => state().location),
1124
- params: (0, import_signal12.createMemo)(() => state().match?.params ?? {})
1413
+ location: (0, import_signal15.createMemo)(() => state().location),
1414
+ params: (0, import_signal15.createMemo)(() => state().match?.params ?? {}),
1415
+ isRouting: () => navigating() || loads() > 0,
1416
+ // Counted by the promise, not by an owner: a load a newer navigation superseded still
1417
+ // settles and uncounts itself, and a resource seeded from the server never calls it.
1418
+ trackLoad(result) {
1419
+ if (result && typeof result.finally === "function") {
1420
+ setLoads((n) => n + 1);
1421
+ void result.finally(() => setLoads((n) => n - 1)).catch(() => {
1422
+ });
1423
+ }
1424
+ return result;
1425
+ }
1125
1426
  };
1126
1427
  const root = {
1127
1428
  depth: -1,
@@ -1143,17 +1444,19 @@ var _ssrRouterStub = {
1143
1444
  router: { navigate: () => {
1144
1445
  } },
1145
1446
  location: () => ({ pathname: "", search: "", hash: "", query: {} }),
1146
- params: () => ({})
1447
+ params: () => ({}),
1448
+ isRouting: () => false,
1449
+ trackLoad: (result) => result
1147
1450
  };
1148
1451
  function useRouter() {
1149
- const ctx = (0, import_signal13.useContext)(RouterContext);
1452
+ const ctx = (0, import_signal16.useContext)(RouterContext);
1150
1453
  if (ctx) return ctx;
1151
1454
  if (typeof window === "undefined") return _ssrRouterStub;
1152
1455
  throw new Error("[router] useRouter must be used within <Router>");
1153
1456
  }
1154
1457
  function useParams() {
1155
1458
  const params = useRouter().params;
1156
- return (0, import_signal12.createMemo)(() => params(), {}, {
1459
+ return (0, import_signal15.createMemo)(() => params(), {}, {
1157
1460
  equals: (a, b) => JSON.stringify(a) === JSON.stringify(b)
1158
1461
  });
1159
1462
  }
@@ -1185,19 +1488,32 @@ var LINK_OWN_PROPS = /* @__PURE__ */ new Set([
1185
1488
  "children"
1186
1489
  ]);
1187
1490
  function Link(props) {
1188
- const ctx = (0, import_signal13.useContext)(RouterContext);
1189
- const navigate = () => ctx?.router.navigate(read(props.href) ?? "", { replace: read(props.replace) });
1491
+ const ctx = (0, import_signal16.useContext)(RouterContext);
1492
+ const navigate = () => ctx.router.navigate(read(props.href) ?? "", { replace: read(props.replace) });
1190
1493
  const a = (0, import_dom15.createNativeElement)("a");
1191
1494
  (0, import_dom15.setAttribute)(a, "href", read(props.href) ?? "");
1192
1495
  if (props.slot) (0, import_dom15.setAttribute)(a, "slot", read(props.slot));
1193
1496
  for (const k of Object.keys(props)) {
1194
1497
  if (LINK_OWN_PROPS.has(k)) continue;
1195
1498
  const v = props[k];
1196
- if (v != null && typeof v !== "function") (0, import_dom15.setAttribute)(a, k, String(v));
1499
+ if (typeof v === "function") {
1500
+ if (/^on[A-Z]/.test(k)) continue;
1501
+ const read3 = v;
1502
+ const apply = () => {
1503
+ const value = read3();
1504
+ if (value == null || value === false) a.removeAttribute?.(k);
1505
+ else (0, import_dom15.setAttribute)(a, k, String(value));
1506
+ };
1507
+ if (typeof a.addEventListener === "function") (0, import_signal15.createEffect)(apply);
1508
+ else apply();
1509
+ continue;
1510
+ }
1511
+ if (v != null) (0, import_dom15.setAttribute)(a, k, String(v));
1197
1512
  }
1198
1513
  if (typeof a.addEventListener === "function") {
1199
1514
  a.addEventListener("click", (e) => {
1200
1515
  if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;
1516
+ if (!ctx) return;
1201
1517
  e.preventDefault();
1202
1518
  navigate();
1203
1519
  });
@@ -1206,7 +1522,7 @@ function Link(props) {
1206
1522
  if (base) (0, import_dom15.setAttribute)(a, "class", base);
1207
1523
  }
1208
1524
  if (typeof a.addEventListener === "function") {
1209
- (0, import_signal12.createEffect)(() => {
1525
+ (0, import_signal15.createEffect)(() => {
1210
1526
  const href = read(props.href) ?? "";
1211
1527
  a.setAttribute("href", href);
1212
1528
  const base = read(props.className ?? props.class) ?? "";
@@ -1238,22 +1554,22 @@ var core_default = {
1238
1554
  // render,
1239
1555
  onMount,
1240
1556
  onUnmount,
1241
- createEffect: import_signal15.createEffect,
1242
- createMemo: import_signal15.createMemo,
1243
- createSignal: import_signal15.createSignal,
1557
+ createEffect: import_signal18.createEffect,
1558
+ createMemo: import_signal18.createMemo,
1559
+ createSignal: import_signal18.createSignal,
1244
1560
  createContext: createContext3,
1245
- createRoot: import_signal15.createRoot,
1246
- createDeferred: import_signal15.createDeferred,
1247
- createComputed: import_signal15.createComputed,
1248
- createRenderEffect: import_signal15.createRenderEffect,
1249
- createSelector: import_signal16.createSelector,
1250
- onCleanup: import_signal15.onCleanup,
1251
- getOwner: import_signal15.getOwner,
1252
- runWithOwner: import_signal15.runWithOwner,
1253
- batch: import_signal15.batch,
1254
- untrack: import_signal15.untrack,
1561
+ createRoot: import_signal18.createRoot,
1562
+ createDeferred: import_signal18.createDeferred,
1563
+ createComputed: import_signal18.createComputed,
1564
+ createRenderEffect: import_signal18.createRenderEffect,
1565
+ createSelector: import_signal19.createSelector,
1566
+ onCleanup: import_signal18.onCleanup,
1567
+ getOwner: import_signal18.getOwner,
1568
+ runWithOwner: import_signal18.runWithOwner,
1569
+ batch: import_signal18.batch,
1570
+ untrack: import_signal18.untrack,
1255
1571
  useContext: useContext3,
1256
- setOwner: import_signal15.setOwner,
1572
+ setOwner: import_signal18.setOwner,
1257
1573
  // Control Flow
1258
1574
  // Show,
1259
1575
  // For,