@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
@@ -72,79 +72,20 @@ var init_lazy = __esm({
72
72
  });
73
73
 
74
74
  // src/lib/context/context.ts
75
- function warnEagerProvider() {
76
- if (_warnedEagerProvider) return;
77
- _warnedEagerProvider = true;
78
- if (typeof process !== "undefined" && true) return;
79
- console.warn(
80
- "[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>"
81
- );
82
- }
83
- function createProvider(a, value, childProps) {
84
- const isLazyForm = !!a?.Provider;
85
- const context = isLazyForm ? a : a.context;
86
- const rawValue = isLazyForm ? value : a.value;
87
- const debug = isLazyForm ? false : a.debug ?? false;
88
- if (!isLazyForm) warnEagerProvider();
89
- const resolvedValue = typeof rawValue === "function" ? rawValue() : rawValue;
90
- const sourceProps = isLazyForm ? childProps : a;
91
- return context.Provider({
92
- value: resolvedValue,
93
- get children() {
94
- return sourceProps?.children;
95
- },
96
- debug
97
- });
98
- }
99
- function createContextWithProvider(defaultValue) {
100
- const context = createContext3(defaultValue);
101
- const provider = (props) => {
102
- return createProvider(context, props.value, props);
103
- };
104
- return [context, provider];
105
- }
106
- function composeProviders(contexts, children) {
107
- if (contexts.length === 0) {
108
- return (0, import_signal8.readChildren)(() => children);
109
- }
110
- const [context, value] = contexts[0];
111
- const remaining = contexts.slice(1);
112
- return createProvider(context, value, {
113
- get children() {
114
- return composeProviders(remaining, children);
115
- }
116
- });
117
- }
118
- function createConsumer(context) {
119
- return (props) => {
120
- const value = useContext3(context);
121
- return props.children(value);
122
- };
123
- }
124
- function useProvide(context, factory) {
125
- const existing = useContext3(context);
126
- if (existing === context.defaultValue) {
127
- return factory();
128
- }
129
- return existing;
130
- }
131
75
  function createContext3(defaultValue, name) {
132
76
  const context = (0, import_signal7.createContext)(defaultValue, name);
133
- const Provider = (props) => {
134
- if (props.debug === true) {
135
- }
136
- return context.Provider({
77
+ const Provider = (props) => (
78
+ // Children stay a getter: the reactive Provider reads them after stamping the value on
79
+ // its owner. Reading `props.children` here would render them, and any <Outlet/> in them,
80
+ // before the context exists.
81
+ context.Provider({
137
82
  value: props.value,
138
83
  get children() {
139
84
  return props.children;
140
85
  },
141
86
  debug: props.debug
142
- });
143
- };
144
- if (typeof Symbol !== "undefined" && Symbol.for) {
145
- Provider.$$typeof = /* @__PURE__ */ Symbol.for("react.provider");
146
- }
147
- Provider.$$typeof = /* @__PURE__ */ Symbol.for("react.provider");
87
+ })
88
+ );
148
89
  return {
149
90
  id: context.id,
150
91
  Provider: (0, import_dom10.asProvider)(Provider),
@@ -155,93 +96,38 @@ function useContext3(context) {
155
96
  const value = (0, import_signal7.useContext)(context);
156
97
  return value !== void 0 ? value : context.defaultValue;
157
98
  }
158
- var import_dom10, import_signal7, import_signal8, _warnedEagerProvider;
159
- var init_context = __esm({
160
- "src/lib/context/context.ts"() {
161
- "use strict";
162
- import_dom10 = require("@fluixi/dom");
163
- import_signal7 = require("@fluixi/reactive/signal");
164
- import_signal8 = require("@fluixi/reactive/signal");
165
- _warnedEagerProvider = false;
166
- }
167
- });
168
-
169
- // src/lib/server/hydration.ts
170
- function isHydrating() {
171
- return hydrating;
172
- }
173
- function setHydrating(state) {
174
- hydrating = state;
175
- }
176
- function setHydrationData(key, data) {
177
- hydrationDataMap.set(key, data);
178
- }
179
- function getHydrationData(key) {
180
- return hydrationDataMap.get(key);
181
- }
182
- function clearHydrationData() {
183
- hydrationDataMap.clear();
184
- }
185
- function deserializeHydrationData() {
186
- if (typeof window === "undefined") {
187
- return false;
188
- }
189
- const script = document.getElementById("__FLUIXI_HYDRATION__");
190
- if (!script || script.getAttribute("type") !== "application/json") {
191
- return false;
192
- }
193
- try {
194
- const json = script.textContent || "{}";
195
- const data = JSON.parse(json);
196
- for (const [key, value] of Object.entries(data)) {
197
- hydrationDataMap.set(key, value);
99
+ function createProvider(context, value, props) {
100
+ const resolved = typeof value === "function" ? value() : value;
101
+ return context.Provider({
102
+ value: resolved,
103
+ get children() {
104
+ return props.children;
198
105
  }
199
- return true;
200
- } catch (error) {
201
- console.error("Failed to deserialize hydration data:", error);
202
- return false;
203
- }
106
+ });
204
107
  }
205
- function cleanupHydration() {
206
- if (typeof window === "undefined") {
207
- return;
208
- }
209
- const script = document.getElementById("__FLUIXI_HYDRATION__");
210
- if (script) {
211
- script.remove();
212
- }
213
- const walker = document.createTreeWalker(
214
- document.body,
215
- NodeFilter.SHOW_COMMENT,
216
- null
108
+ function composeProviders(providers, props) {
109
+ const outermost = providers.reduceRight(
110
+ (inner, [context, value]) => ({
111
+ get children() {
112
+ return createProvider(context, value, inner);
113
+ }
114
+ }),
115
+ props
217
116
  );
218
- const comments = [];
219
- let node;
220
- while (node = walker.nextNode()) {
221
- const comment = node;
222
- const text = comment.nodeValue || "";
223
- if (text.startsWith("#") || text.startsWith("/")) {
224
- comments.push(comment);
225
- }
226
- }
227
- comments.forEach((comment) => comment.remove());
228
- clearHydrationData();
117
+ return outermost.children;
229
118
  }
230
- function isServerEnvironment() {
231
- return (0, import_dom15.isServer)();
119
+ function requireContext(context, message) {
120
+ for (let owner = (0, import_signal7.getOwner)(); owner; owner = owner.parent) {
121
+ if (owner.contexts?.has(context.id)) return useContext3(context);
122
+ }
123
+ throw new Error(message ?? `[fluixi] No Provider for ${context.id.description ?? "this context"} above this component.`);
232
124
  }
233
- var import_dom15, hydrating, hydrationDataMap;
234
- var init_hydration = __esm({
235
- "src/lib/server/hydration.ts"() {
125
+ var import_dom10, import_signal7;
126
+ var init_context = __esm({
127
+ "src/lib/context/context.ts"() {
236
128
  "use strict";
237
- import_dom15 = require("@fluixi/dom");
238
- hydrating = false;
239
- hydrationDataMap = /* @__PURE__ */ new Map();
240
- if (typeof window !== "undefined") {
241
- window.addEventListener("load", () => {
242
- setTimeout(cleanupHydration, 100);
243
- });
244
- }
129
+ import_dom10 = require("@fluixi/dom");
130
+ import_signal7 = require("@fluixi/reactive/signal");
245
131
  }
246
132
  });
247
133
 
@@ -299,75 +185,102 @@ var init_transition = __esm({
299
185
  });
300
186
 
301
187
  // src/lib/router/data.ts
302
- function cache(fn, name) {
303
- return async (...args) => {
188
+ function onRevalidate(listener) {
189
+ revalidationListeners.add(listener);
190
+ return () => void revalidationListeners.delete(listener);
191
+ }
192
+ function cache(fn, name, options = {}) {
193
+ const { ttl = Infinity, staleWhileRevalidate = true } = options;
194
+ return (...args) => {
195
+ const store = options.cache ?? (0, import_signal13.currentResourceCache)();
304
196
  const key = name + ":" + JSON.stringify(args);
305
- if (isHydrating() || typeof window !== "undefined") {
306
- const hydrated = getHydrationData(key);
307
- if (hydrated !== void 0) {
308
- return hydrated;
309
- }
310
- }
311
- if (globalCache.has(key)) {
312
- return globalCache.get(key);
313
- }
314
- if (inflightRequests.has(key)) {
315
- return inflightRequests.get(key);
316
- }
317
- const promise = Promise.resolve(fn(...args)).then(
318
- (val) => {
319
- globalCache.set(key, val);
320
- inflightRequests.delete(key);
321
- if (isServerEnvironment()) {
322
- setHydrationData(key, val);
197
+ const fetchFresh = () => {
198
+ const running = store.inflight.get(key);
199
+ if (running) return running;
200
+ const promise = Promise.resolve(fn(...args)).then(
201
+ (value) => {
202
+ store.set(key, { value, timestamp: Date.now() });
203
+ store.inflight.delete(key);
204
+ return value;
205
+ },
206
+ (error) => {
207
+ store.inflight.delete(key);
208
+ throw error;
323
209
  }
324
- return val;
325
- },
326
- (err) => {
327
- inflightRequests.delete(key);
328
- throw err;
329
- }
330
- );
331
- inflightRequests.set(key, promise);
332
- return promise;
210
+ );
211
+ store.inflight.set(key, promise);
212
+ return promise;
213
+ };
214
+ const hit = store.get(key);
215
+ if (hit && (ttl === Infinity || Date.now() - hit.timestamp <= ttl)) return Promise.resolve(hit.value);
216
+ if (hit && staleWhileRevalidate) {
217
+ fetchFresh().then(reloadMounted, () => void 0);
218
+ return Promise.resolve(hit.value);
219
+ }
220
+ return fetchFresh();
333
221
  };
334
222
  }
335
- function revalidate(key) {
336
- const predicate = typeof key === "string" ? (k) => k.startsWith(key) : key;
337
- for (const k of globalCache.keys()) {
338
- if (predicate(k)) {
339
- globalCache.delete(k);
340
- revalidationKeys.add(k);
341
- }
223
+ function revalidate(key, store) {
224
+ const names = typeof key === "string" ? [key] : Array.isArray(key) ? key : null;
225
+ drop(names ?? key, store ?? (0, import_signal13.currentResourceCache)());
226
+ if (names && !store) tellOtherTabs(names);
227
+ }
228
+ function drop(what, store) {
229
+ const matches = Array.isArray(what) ? (k) => what.some((n) => k === n || k.startsWith(n + ":")) : what;
230
+ for (const k of [...store.keys()]) if (matches(k)) store.delete(k);
231
+ reloadMounted();
232
+ }
233
+ function tellOtherTabs(names) {
234
+ try {
235
+ localStorage.setItem(TABS_KEY, JSON.stringify({ names, at: Date.now() + Math.random() }));
236
+ } catch {
342
237
  }
343
- setRevalidationVersion((v) => v + 1);
344
238
  }
345
239
  function createAsync(fetcher, source) {
346
- const [data] = (0, import_signal13.createResource)(source || (() => true), fetcher);
240
+ const [data] = (0, import_signal12.createResource)(source || (() => true), fetcher);
347
241
  return data;
348
242
  }
349
243
  function getSubmissionSignal(url) {
350
244
  if (!submissionState.has(url)) {
351
245
  submissionState.set(
352
246
  url,
353
- (0, import_signal13.createSignal)(void 0)
247
+ (0, import_signal12.createSignal)(void 0)
354
248
  );
355
249
  }
356
250
  return submissionState.get(url);
357
251
  }
358
- function action(fn, name) {
252
+ function getSubmissionsSignal(url) {
253
+ if (!submissionsState.has(url)) submissionsState.set(url, (0, import_signal12.createSignal)([]));
254
+ return submissionsState.get(url);
255
+ }
256
+ function action(fn, name, options = {}) {
359
257
  const actionUrl = name || fn.name || "action";
360
258
  const submit = async (input) => {
361
259
  const [, setSubmission] = getSubmissionSignal(actionUrl);
362
- const clear = () => setSubmission(void 0);
363
- const retry = () => submit(input);
364
- setSubmission({
260
+ const [, setSubmissions] = getSubmissionsSignal(actionUrl);
261
+ let entry;
262
+ const drop2 = () => setSubmissions((list) => list.filter((s) => s !== entry));
263
+ const settle = (next) => {
264
+ setSubmissions((list) => list.map((s) => s === entry ? next : s));
265
+ entry = next;
266
+ };
267
+ const clear = () => {
268
+ setSubmission(void 0);
269
+ drop2();
270
+ };
271
+ const retry = () => {
272
+ drop2();
273
+ return submit(input);
274
+ };
275
+ entry = {
365
276
  input,
366
277
  // Approximate type for tracking
367
278
  pending: true,
368
279
  clear,
369
280
  retry
370
- });
281
+ };
282
+ setSubmission(entry);
283
+ setSubmissions((list) => [...list, entry]);
371
284
  try {
372
285
  const result = await fn(input);
373
286
  setSubmission({
@@ -377,41 +290,63 @@ function action(fn, name) {
377
290
  clear,
378
291
  retry
379
292
  });
293
+ drop2();
294
+ const stale = typeof options.invalidates === "function" ? options.invalidates(result) : options.invalidates;
295
+ for (const key of stale ?? []) revalidate(key);
380
296
  return result;
381
297
  } catch (error) {
382
- setSubmission({
298
+ const failed = {
383
299
  input,
384
300
  error,
385
301
  pending: false,
386
302
  clear,
387
303
  retry
388
- });
304
+ };
305
+ setSubmission(failed);
306
+ settle(failed);
389
307
  throw error;
390
308
  }
391
309
  };
392
310
  submit.url = actionUrl;
393
- submit.with = (args) => action((...a) => fn(args, ...a), name);
311
+ submit.with = (args) => action((...a) => fn(args, ...a), name, options);
394
312
  return submit;
395
313
  }
396
314
  function useSubmission(actionFn) {
397
315
  const [submission] = getSubmissionSignal(actionFn.url);
398
316
  return submission;
399
317
  }
318
+ function useSubmissions(actionFn) {
319
+ const [submissions] = getSubmissionsSignal(actionFn.url);
320
+ return submissions;
321
+ }
400
322
  function useAction(actionFn) {
401
323
  return actionFn;
402
324
  }
403
- var import_signal13, globalCache, inflightRequests, revalidationVersion, setRevalidationVersion, revalidationKeys, submissionState, RouteDataContext, RouteDataProvider;
325
+ var import_signal12, import_signal13, revalidationListeners, reloadMounted, TABS_KEY, submissionState, submissionsState, RouteDataContext, RouteDataProvider;
404
326
  var init_data = __esm({
405
327
  "src/lib/router/data.ts"() {
406
328
  "use strict";
329
+ import_signal12 = require("@fluixi/reactive/signal");
407
330
  import_signal13 = require("@fluixi/reactive/signal");
408
- init_hydration();
409
331
  init_context();
410
- globalCache = /* @__PURE__ */ new Map();
411
- inflightRequests = /* @__PURE__ */ new Map();
412
- [revalidationVersion, setRevalidationVersion] = (0, import_signal13.createSignal)(0);
413
- revalidationKeys = /* @__PURE__ */ new Set();
332
+ revalidationListeners = /* @__PURE__ */ new Set();
333
+ reloadMounted = () => {
334
+ for (const listener of [...revalidationListeners]) listener();
335
+ };
336
+ TABS_KEY = "fluixi:revalidate";
337
+ if (typeof window !== "undefined") {
338
+ window.addEventListener("storage", (event) => {
339
+ if (event.key !== TABS_KEY || !event.newValue) return;
340
+ try {
341
+ const names = JSON.parse(event.newValue).names;
342
+ if (Array.isArray(names) && names.every((n) => typeof n === "string")) drop(names, (0, import_signal13.currentResourceCache)());
343
+ } catch {
344
+ }
345
+ });
346
+ }
347
+ globalThis[/* @__PURE__ */ Symbol.for("fluixi.revalidate")] = (names) => revalidate(names);
414
348
  submissionState = /* @__PURE__ */ new Map();
349
+ submissionsState = /* @__PURE__ */ new Map();
415
350
  RouteDataContext = createContext3(void 0, "route-data");
416
351
  RouteDataProvider = RouteDataContext.Provider;
417
352
  }
@@ -441,6 +376,7 @@ __export(router_exports, {
441
376
  select: () => select,
442
377
  supportsViewTransitions: () => supportsViewTransitions,
443
378
  useAction: () => useAction,
379
+ useIsRouting: () => useIsRouting,
444
380
  useLevelParams: () => useLevelParams,
445
381
  useLinkNavigation: () => useLinkNavigation,
446
382
  useLocation: () => useLocation,
@@ -452,7 +388,8 @@ __export(router_exports, {
452
388
  useRouteData: () => useRouteData,
453
389
  useRouter: () => useRouter,
454
390
  useSearchParams: () => useSearchParams,
455
- useSubmission: () => useSubmission
391
+ useSubmission: () => useSubmission,
392
+ useSubmissions: () => useSubmissions
456
393
  });
457
394
  function renderLayer(m, depth, base) {
458
395
  let data;
@@ -463,10 +400,32 @@ function renderLayer(m, depth, base) {
463
400
  cancelled = true;
464
401
  });
465
402
  const owner = (0, import_signal14.getOwner)();
466
- const [resource] = (0, import_signal14.createResource)(
467
- () => loader({ params: base.params, location: base.location }, owner, () => cancelled)
468
- );
469
- data = resource;
403
+ const router = (0, import_signal15.useContext)(RouterContext);
404
+ let watching = false;
405
+ const trackDeps = (deps) => {
406
+ if (watching || cancelled) return;
407
+ watching = true;
408
+ (0, import_signal14.runWithOwner)(owner, () => {
409
+ let first = true;
410
+ (0, import_signal14.createEffect)(() => {
411
+ deps();
412
+ if (first) first = false;
413
+ else (0, import_signal14.untrack)(() => void refetch());
414
+ });
415
+ });
416
+ };
417
+ const [resource, { refetch }] = (0, import_signal14.createResource)(() => {
418
+ const result = loader({ params: base.params, location: base.location }, owner, () => cancelled, trackDeps);
419
+ return router ? router.trackLoad(result) : result;
420
+ });
421
+ (0, import_signal14.onCleanup)(onRevalidate(() => void refetch()));
422
+ data = () => {
423
+ const error = resource.error;
424
+ if (error !== void 0 && error !== null) throw error;
425
+ return resource();
426
+ };
427
+ Object.defineProperty(data, "loading", { get: () => resource.loading });
428
+ Object.defineProperty(data, "state", { get: () => resource.state });
470
429
  }
471
430
  const buildPage = () => RouteDataContext2.Provider({
472
431
  value: data,
@@ -484,12 +443,15 @@ function renderLayer(m, depth, base) {
484
443
  });
485
444
  }
486
445
  });
487
- if (!data) return buildPage();
446
+ if (!data) return { view: buildPage(), ready: () => true };
488
447
  const res = data;
489
- const isLoading = () => res.loading;
448
+ const isLoading = () => {
449
+ const state = res.state;
450
+ return state === void 0 ? res.loading : state === "pending" || state === "unresolved";
451
+ };
490
452
  const [ready, setReady] = (0, import_signal14.createSignal)((0, import_signal14.untrack)(() => !isLoading()));
491
453
  (0, import_signal14.createEffect)(() => setReady(!isLoading()));
492
- return (0, import_signal14.createMemo)(() => ready() ? buildPage() : null);
454
+ return { view: (0, import_signal14.createMemo)(() => ready() ? buildPage() : null), ready };
493
455
  }
494
456
  function renderChild(ctx) {
495
457
  const childIdx = ctx.depth + 1;
@@ -500,17 +462,43 @@ function renderChild(ctx) {
500
462
  const c = ctx.matched()[childIdx]?.component;
501
463
  if ((0, import_signal14.untrack)(childComp) !== c) setChildComp(() => c);
502
464
  });
503
- return (0, import_signal14.createMemo)(() => {
465
+ const owner = (0, import_signal14.getOwner)();
466
+ let shown = null;
467
+ let pending = null;
468
+ const drop2 = (layer) => layer?.dispose();
469
+ (0, import_signal14.onCleanup)(() => {
470
+ drop2(shown);
471
+ drop2(pending);
472
+ shown = pending = null;
473
+ });
474
+ const incoming = (0, import_signal14.createMemo)(() => {
504
475
  const component = childComp();
505
- if (!component) {
476
+ if (!component) return null;
477
+ const m = (0, import_signal14.untrack)(() => ctx.matched())[childIdx];
478
+ return (0, import_signal14.runWithOwner)(
479
+ owner,
480
+ () => (0, import_signal14.createRoot)((dispose) => ({
481
+ ...renderLayer(m, childIdx, { matched: ctx.matched, params: ctx.params, location: ctx.location }),
482
+ dispose
483
+ }))
484
+ );
485
+ });
486
+ return (0, import_signal14.createMemo)(() => {
487
+ const next = incoming();
488
+ if (!next) {
489
+ drop2(shown);
490
+ drop2(pending);
491
+ shown = pending = null;
506
492
  return ctx.notFound ? ctx.notFound() : null;
507
493
  }
508
- const m = (0, import_signal14.untrack)(() => ctx.matched())[childIdx];
509
- return renderLayer(m, childIdx, {
510
- matched: ctx.matched,
511
- params: ctx.params,
512
- location: ctx.location
513
- });
494
+ if (next === shown) return shown.view;
495
+ if (pending && pending !== next) drop2(pending);
496
+ pending = next;
497
+ if (shown && !next.ready()) return shown.view;
498
+ drop2(shown);
499
+ shown = next;
500
+ pending = null;
501
+ return shown.view;
514
502
  });
515
503
  }
516
504
  function renderNotFound(notFound) {
@@ -544,6 +532,8 @@ function Router(props) {
544
532
  history
545
533
  });
546
534
  const [state, setState] = (0, import_signal14.createSignal)(router.state());
535
+ const [navigating, setNavigating] = (0, import_signal14.createSignal)(false);
536
+ const [loads, setLoads] = (0, import_signal14.createSignal)(0);
547
537
  let navigationId = 0;
548
538
  const takeMode = () => {
549
539
  const mode = pendingTransitionMode ?? (props.transition ?? "auto");
@@ -553,12 +543,15 @@ function Router(props) {
553
543
  const unsub = router.subscribe((next) => {
554
544
  const id = ++navigationId;
555
545
  const mode = takeMode();
546
+ setNavigating(true);
556
547
  const prepared = prepareComponents(
557
548
  (next.match?.matched ?? []).map((m) => m.component).filter(Boolean)
558
549
  );
559
550
  const commit = () => {
560
551
  setState(next);
561
- return (0, import_signal14.flush)();
552
+ const flushed = (0, import_signal14.flush)();
553
+ if (id === navigationId) setNavigating(false);
554
+ return flushed;
562
555
  };
563
556
  if (!prepared) {
564
557
  void runTransition(mode, commit);
@@ -580,7 +573,18 @@ function Router(props) {
580
573
  const ctx = {
581
574
  router,
582
575
  location: (0, import_signal14.createMemo)(() => state().location),
583
- params: (0, import_signal14.createMemo)(() => state().match?.params ?? {})
576
+ params: (0, import_signal14.createMemo)(() => state().match?.params ?? {}),
577
+ isRouting: () => navigating() || loads() > 0,
578
+ // Counted by the promise, not by an owner: a load a newer navigation superseded still
579
+ // settles and uncounts itself, and a resource seeded from the server never calls it.
580
+ trackLoad(result) {
581
+ if (result && typeof result.finally === "function") {
582
+ setLoads((n) => n + 1);
583
+ void result.finally(() => setLoads((n) => n - 1)).catch(() => {
584
+ });
585
+ }
586
+ return result;
587
+ }
584
588
  };
585
589
  const root = {
586
590
  depth: -1,
@@ -603,6 +607,21 @@ function Outlet() {
603
607
  if (!ctx) return null;
604
608
  return renderChild(ctx);
605
609
  }
610
+ function useIsRouting(options = {}) {
611
+ const routing = (0, import_signal15.useContext)(RouterContext)?.isRouting ?? (() => false);
612
+ const delay = options.delay ?? 0;
613
+ if (delay <= 0) return routing;
614
+ const [late, setLate] = (0, import_signal14.createSignal)(false);
615
+ (0, import_signal14.createEffect)(() => {
616
+ if (!routing()) {
617
+ setLate(false);
618
+ return;
619
+ }
620
+ const timer = setTimeout(() => setLate(true), delay);
621
+ (0, import_signal14.onCleanup)(() => clearTimeout(timer));
622
+ });
623
+ return () => routing() && late();
624
+ }
606
625
  function Redirect(props) {
607
626
  const ctx = (0, import_signal15.useContext)(RouterContext);
608
627
  if (ctx && typeof window !== "undefined") {
@@ -716,18 +735,31 @@ function useRouteData() {
716
735
  }
717
736
  function Link(props) {
718
737
  const ctx = (0, import_signal15.useContext)(RouterContext);
719
- const navigate = () => ctx?.router.navigate(read(props.href) ?? "", { replace: read(props.replace) });
738
+ const navigate = () => ctx.router.navigate(read(props.href) ?? "", { replace: read(props.replace) });
720
739
  const a = (0, import_dom16.createNativeElement)("a");
721
740
  (0, import_dom16.setAttribute)(a, "href", read(props.href) ?? "");
722
741
  if (props.slot) (0, import_dom16.setAttribute)(a, "slot", read(props.slot));
723
742
  for (const k of Object.keys(props)) {
724
743
  if (LINK_OWN_PROPS.has(k)) continue;
725
744
  const v = props[k];
726
- if (v != null && typeof v !== "function") (0, import_dom16.setAttribute)(a, k, String(v));
745
+ if (typeof v === "function") {
746
+ if (/^on[A-Z]/.test(k)) continue;
747
+ const read2 = v;
748
+ const apply = () => {
749
+ const value = read2();
750
+ if (value == null || value === false) a.removeAttribute?.(k);
751
+ else (0, import_dom16.setAttribute)(a, k, String(value));
752
+ };
753
+ if (typeof a.addEventListener === "function") (0, import_signal14.createEffect)(apply);
754
+ else apply();
755
+ continue;
756
+ }
757
+ if (v != null) (0, import_dom16.setAttribute)(a, k, String(v));
727
758
  }
728
759
  if (typeof a.addEventListener === "function") {
729
760
  a.addEventListener("click", (e) => {
730
761
  if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;
762
+ if (!ctx) return;
731
763
  e.preventDefault();
732
764
  navigate();
733
765
  });
@@ -798,6 +830,7 @@ var init_router = __esm({
798
830
  import_object = require("@fluixi/utils/object");
799
831
  import_compare = require("@fluixi/utils/compare");
800
832
  import_router = require("@fluixi/router");
833
+ init_data();
801
834
  import_router2 = require("@fluixi/router");
802
835
  init_data();
803
836
  init_lazy();
@@ -811,7 +844,9 @@ var init_router = __esm({
811
844
  router: { navigate: () => {
812
845
  } },
813
846
  location: () => ({ pathname: "", search: "", hash: "", query: {} }),
814
- params: () => ({})
847
+ params: () => ({}),
848
+ isRouting: () => false,
849
+ trackLoad: (result) => result
815
850
  };
816
851
  read = (v) => typeof v === "function" ? v() : v;
817
852
  LINK_OWN_PROPS = /* @__PURE__ */ new Set([
@@ -854,33 +889,31 @@ __export(core_exports, {
854
889
  Switch: () => import_dom12.Switch,
855
890
  VERSION: () => VERSION2,
856
891
  addClass: () => import_dom14.addClass,
857
- batch: () => import_signal10.batch,
892
+ batch: () => import_signal9.batch,
858
893
  classMap: () => import_dom13.classMap,
859
894
  cleanupDelegation: () => cleanupDelegation,
860
895
  composeProviders: () => composeProviders,
861
896
  createBrowserHistory: () => import_router2.createBrowserHistory,
862
897
  createComponent: () => createComponent,
863
- createComputed: () => import_signal10.createComputed,
864
- createConsumer: () => createConsumer,
898
+ createComputed: () => import_signal9.createComputed,
865
899
  createContext: () => createContext3,
866
- createContextWithProvider: () => createContextWithProvider,
867
- createDebounce: () => import_signal11.createDebounce,
868
- createDeferred: () => import_signal10.createDeferred,
869
- createEffect: () => import_signal10.createEffect,
900
+ createDebounce: () => import_signal10.createDebounce,
901
+ createDeferred: () => import_signal9.createDeferred,
902
+ createEffect: () => import_signal9.createEffect,
870
903
  createIsomorphicState: () => createIsomorphicState,
871
- createMemo: () => import_signal10.createMemo,
904
+ createMemo: () => import_signal9.createMemo,
872
905
  createMemoryHistory: () => import_router2.createMemoryHistory,
873
906
  createProvider: () => createProvider,
874
- createRenderEffect: () => import_signal10.createRenderEffect,
875
- createResource: () => import_signal10.createResource,
876
- createRoot: () => import_signal10.createRoot,
877
- createSelector: () => import_signal11.createSelector,
878
- createSignal: () => import_signal10.createSignal,
907
+ createRenderEffect: () => import_signal9.createRenderEffect,
908
+ createResource: () => import_signal9.createResource,
909
+ createRoot: () => import_signal9.createRoot,
910
+ createSelector: () => import_signal10.createSelector,
911
+ createSignal: () => import_signal9.createSignal,
879
912
  createStore: () => import_store.createStore,
880
913
  delegateEvents: () => import_dom14.delegateEvents,
881
914
  devOnly: () => devOnly,
882
915
  getFrameworkInfo: () => getFrameworkInfo,
883
- getOwner: () => import_signal10.getOwner,
916
+ getOwner: () => import_signal9.getOwner,
884
917
  getSharedConfig: () => getSharedConfig,
885
918
  hydrate: () => import_dom14.hydrate,
886
919
  initDelegation: () => initDelegation,
@@ -893,7 +926,7 @@ __export(core_exports, {
893
926
  jsxs: () => jsx_runtime_exports.jsxs,
894
927
  lazy: () => lazy,
895
928
  mount: () => mount,
896
- onCleanup: () => import_signal10.onCleanup,
929
+ onCleanup: () => import_signal9.onCleanup,
897
930
  onMount: () => onMount,
898
931
  onUnmount: () => onUnmount,
899
932
  portal: () => portal,
@@ -901,15 +934,16 @@ __export(core_exports, {
901
934
  reduce: () => import_operators.reduceSignal,
902
935
  removeClass: () => import_dom14.removeClass,
903
936
  render: () => render,
904
- runWithOwner: () => import_signal10.runWithOwner,
937
+ requireContext: () => requireContext,
938
+ runWithOwner: () => import_signal9.runWithOwner,
905
939
  setAttribute: () => import_dom14.setAttribute,
906
- setOwner: () => import_signal10.setOwner,
940
+ setOwner: () => import_signal9.setOwner,
907
941
  setProperty: () => import_dom14.setProperty,
908
942
  spread: () => import_dom14.spread,
909
943
  startClient: () => startClient,
910
944
  styleMap: () => import_dom13.styleMap,
911
945
  toggleClass: () => import_dom14.toggleClass,
912
- untrack: () => import_signal10.untrack,
946
+ untrack: () => import_signal9.untrack,
913
947
  untrackStore: () => import_store.untrackStore,
914
948
  useCallback: () => useCallback,
915
949
  useContext: () => useContext3,
@@ -921,7 +955,6 @@ __export(core_exports, {
921
955
  useNavigate: () => useNavigate,
922
956
  useParam: () => useParam,
923
957
  useParams: () => useParams,
924
- useProvide: () => useProvide,
925
958
  useReducer: () => useReducer,
926
959
  useRef: () => useRef,
927
960
  useRouter: () => useRouter,
@@ -930,8 +963,8 @@ __export(core_exports, {
930
963
  module.exports = __toCommonJS(core_exports);
931
964
 
932
965
  // src/lib/index.ts
966
+ var import_signal9 = require("@fluixi/reactive/signal");
933
967
  var import_signal10 = require("@fluixi/reactive/signal");
934
- var import_signal11 = require("@fluixi/reactive/signal");
935
968
  var import_operators = require("@fluixi/reactive/operators");
936
969
  var import_store = require("@fluixi/reactive/store");
937
970
 
@@ -943,7 +976,7 @@ var import_signal6 = require("@fluixi/reactive/signal");
943
976
  var import_dom = require("@fluixi/dom");
944
977
 
945
978
  // src/version.generated.ts
946
- var VERSION = "1.0.0-alpha.92";
979
+ var VERSION = "1.0.0-alpha.94";
947
980
 
948
981
  // src/lib/render/versions.ts
949
982
  var import_dom2 = require("@fluixi/dom");
@@ -1246,8 +1279,10 @@ function cleanupDelegation() {
1246
1279
  init_context();
1247
1280
 
1248
1281
  // src/lib/isomorphic.ts
1249
- var import_signal9 = require("@fluixi/reactive/signal");
1282
+ var import_signal8 = require("@fluixi/reactive/signal");
1250
1283
  var import_dom11 = require("@fluixi/dom");
1284
+ var import_storage = require("@fluixi/storage");
1285
+ var import_browser = require("@fluixi/storage/browser");
1251
1286
  var REGISTRY_KEY = "__fluixi_iso_states__";
1252
1287
  var SEED_PREFIX = "__iso:";
1253
1288
  function registry() {
@@ -1261,7 +1296,7 @@ function readSeed(key) {
1261
1296
  function createIsomorphicState(key, options) {
1262
1297
  const seed = readSeed(key);
1263
1298
  const initial = seed !== void 0 ? seed : typeof window !== "undefined" && options.client ? options.client() : options.fallback;
1264
- const [value, set] = (0, import_signal9.createSignal)(initial);
1299
+ const [value, set] = (0, import_signal8.createSignal)(initial, { name: key });
1265
1300
  registry().set(key, { server: options.server, set });
1266
1301
  return { value, set, key };
1267
1302
  }
@@ -1280,7 +1315,7 @@ var jsx_runtime_exports = {};
1280
1315
  __reExport(jsx_runtime_exports, require("@fluixi/jsx/jsx-runtime"));
1281
1316
 
1282
1317
  // src/lib/index.ts
1283
- var import_signal12 = require("@fluixi/reactive/signal");
1318
+ var import_signal11 = require("@fluixi/reactive/signal");
1284
1319
  var VERSION2 = "0.0.1";
1285
1320
  var FRAMEWORK_NAME = "Fluixi";
1286
1321
  function getFrameworkInfo() {
@@ -1303,7 +1338,69 @@ function getFrameworkInfo() {
1303
1338
 
1304
1339
  // src/lib/client/index.ts
1305
1340
  var import_dom17 = require("@fluixi/dom");
1306
- init_hydration();
1341
+
1342
+ // src/lib/server/hydration.ts
1343
+ var import_dom15 = require("@fluixi/dom");
1344
+ var hydrating = false;
1345
+ var hydrationDataMap = /* @__PURE__ */ new Map();
1346
+ function setHydrating(state) {
1347
+ hydrating = state;
1348
+ }
1349
+ function clearHydrationData() {
1350
+ hydrationDataMap.clear();
1351
+ }
1352
+ function deserializeHydrationData() {
1353
+ if (typeof window === "undefined") {
1354
+ return false;
1355
+ }
1356
+ const script = document.getElementById("__FLUIXI_HYDRATION__");
1357
+ if (!script || script.getAttribute("type") !== "application/json") {
1358
+ return false;
1359
+ }
1360
+ try {
1361
+ const json = script.textContent || "{}";
1362
+ const data = JSON.parse(json);
1363
+ for (const [key, value] of Object.entries(data)) {
1364
+ hydrationDataMap.set(key, value);
1365
+ }
1366
+ return true;
1367
+ } catch (error) {
1368
+ console.error("Failed to deserialize hydration data:", error);
1369
+ return false;
1370
+ }
1371
+ }
1372
+ function cleanupHydration() {
1373
+ if (typeof window === "undefined") {
1374
+ return;
1375
+ }
1376
+ const script = document.getElementById("__FLUIXI_HYDRATION__");
1377
+ if (script) {
1378
+ script.remove();
1379
+ }
1380
+ const walker = document.createTreeWalker(
1381
+ document.body,
1382
+ NodeFilter.SHOW_COMMENT,
1383
+ null
1384
+ );
1385
+ const comments = [];
1386
+ let node;
1387
+ while (node = walker.nextNode()) {
1388
+ const comment = node;
1389
+ const text = comment.nodeValue || "";
1390
+ if (text.startsWith("#") || text.startsWith("/")) {
1391
+ comments.push(comment);
1392
+ }
1393
+ }
1394
+ comments.forEach((comment) => comment.remove());
1395
+ clearHydrationData();
1396
+ }
1397
+ if (typeof window !== "undefined") {
1398
+ window.addEventListener("load", () => {
1399
+ setTimeout(cleanupHydration, 100);
1400
+ });
1401
+ }
1402
+
1403
+ // src/lib/client/index.ts
1307
1404
  var import_signal17 = require("@fluixi/reactive/signal");
1308
1405
  var import_head = require("@fluixi/head");
1309
1406
  var import_dom18 = require("@fluixi/dom");
@@ -1327,16 +1424,26 @@ async function startClient(App, options = {}) {
1327
1424
  (0, import_head.setActiveHead)(head);
1328
1425
  (0, import_head.stripHeadMarker)(container);
1329
1426
  if (shouldHydrate) {
1330
- if (options.routes && options.routes.length) {
1331
- const { preloadRoutes: preloadRoutes2 } = await Promise.resolve().then(() => (init_router(), router_exports));
1332
- await preloadRoutes2(options.routes, window.location.pathname);
1427
+ (0, import_dom17.beginAppHydration)();
1428
+ try {
1429
+ if (options.routes && options.routes.length) {
1430
+ const { preloadRoutes: preloadRoutes2 } = await Promise.resolve().then(() => (init_router(), router_exports));
1431
+ await preloadRoutes2(options.routes, window.location.pathname);
1432
+ }
1433
+ deserializeHydrationData();
1434
+ setHydrating(true);
1435
+ (0, import_dom17.hydrate)(rootOf(App), container);
1436
+ replayEvents();
1437
+ } finally {
1438
+ (0, import_dom17.endAppHydration)();
1333
1439
  }
1334
- deserializeHydrationData();
1335
- setHydrating(true);
1336
- (0, import_dom17.hydrate)(rootOf(App), container);
1337
- replayEvents();
1338
1440
  setTimeout(() => {
1339
1441
  setHydrating(false);
1442
+ const removed = (0, import_dom17.removeUnclaimedKeyed)(container);
1443
+ if (removed && typeof process !== "undefined" && false) {
1444
+ console.warn(`[fluixi] hydration: removed ${removed} server element(s) the client did not render. Check for markup that depends on client-only state.`);
1445
+ }
1446
+ (0, import_dom17.clearHydrationKeys)();
1340
1447
  cleanupHydration();
1341
1448
  }, 100);
1342
1449
  } else {