@fluixi/core 1.0.0-alpha.91 → 1.0.0-alpha.93

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 +547 -232
  7. package/dist/src/index.mjs +547 -223
  8. package/dist/src/lib/client/chunk-QT3RFKH6.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-I66EJO2Y.mjs → router-ADTS55OP.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 +546 -231
  25. package/dist/src/lib/core.d.ts +3 -1
  26. package/dist/src/lib/core.d.ts.map +1 -1
  27. package/dist/src/lib/core.js +5 -3
  28. package/dist/src/lib/core.mjs +546 -222
  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-SKXDXBGV.mjs +0 -133
@@ -18,8 +18,10 @@ import {
18
18
  createMemo as createMemo2,
19
19
  createResource as createResource3,
20
20
  createEffect,
21
- getOwner,
21
+ createRoot,
22
+ getOwner as getOwner2,
22
23
  onCleanup,
24
+ runWithOwner,
23
25
  untrack as untrack2,
24
26
  flush
25
27
  } from "@fluixi/reactive/signal";
@@ -88,172 +90,35 @@ import {
88
90
  matchRoutes,
89
91
  matchPath
90
92
  } from "@fluixi/router";
91
- import {
92
- createMemoryHistory,
93
- createBrowserHistory as createBrowserHistory2,
94
- matchRoutes as matchRoutes2,
95
- matchPath as matchPath2
96
- } from "@fluixi/router";
97
93
 
98
94
  // src/lib/router/data.ts
99
95
  import {
100
96
  createResource,
101
97
  createSignal
102
98
  } from "@fluixi/reactive/signal";
103
-
104
- // src/lib/server/hydration.ts
105
- import { isServer as domIsServer } from "@fluixi/dom";
106
- var hydrating = false;
107
- var hydrationDataMap = /* @__PURE__ */ new Map();
108
- function isHydrating() {
109
- return hydrating;
110
- }
111
- function setHydrating(state) {
112
- hydrating = state;
113
- }
114
- function setHydrationData(key, data) {
115
- hydrationDataMap.set(key, data);
116
- }
117
- function getHydrationData(key) {
118
- return hydrationDataMap.get(key);
119
- }
120
- function clearHydrationData() {
121
- hydrationDataMap.clear();
122
- }
123
- function deserializeHydrationData() {
124
- if (typeof window === "undefined") {
125
- return false;
126
- }
127
- const script = document.getElementById("__FLUIXI_HYDRATION__");
128
- if (!script || script.getAttribute("type") !== "application/json") {
129
- return false;
130
- }
131
- try {
132
- const json = script.textContent || "{}";
133
- const data = JSON.parse(json);
134
- for (const [key, value] of Object.entries(data)) {
135
- hydrationDataMap.set(key, value);
136
- }
137
- return true;
138
- } catch (error) {
139
- console.error("Failed to deserialize hydration data:", error);
140
- return false;
141
- }
142
- }
143
- function cleanupHydration() {
144
- if (typeof window === "undefined") {
145
- return;
146
- }
147
- const script = document.getElementById("__FLUIXI_HYDRATION__");
148
- if (script) {
149
- script.remove();
150
- }
151
- const walker = document.createTreeWalker(
152
- document.body,
153
- NodeFilter.SHOW_COMMENT,
154
- null
155
- );
156
- const comments = [];
157
- let node;
158
- while (node = walker.nextNode()) {
159
- const comment = node;
160
- const text = comment.nodeValue || "";
161
- if (text.startsWith("#") || text.startsWith("/")) {
162
- comments.push(comment);
163
- }
164
- }
165
- comments.forEach((comment) => comment.remove());
166
- clearHydrationData();
167
- }
168
- if (typeof window !== "undefined") {
169
- window.addEventListener("load", () => {
170
- setTimeout(cleanupHydration, 100);
171
- });
172
- }
173
- function isServerEnvironment() {
174
- return domIsServer();
175
- }
99
+ import { currentResourceCache } from "@fluixi/reactive/signal";
176
100
 
177
101
  // src/lib/context/context.ts
178
102
  import { asProvider } from "@fluixi/dom";
179
- import { createContext as createReactiveContext, useContext as useReactiveContext } from "@fluixi/reactive/signal";
180
103
  import {
181
- readChildren
104
+ createContext as createReactiveContext,
105
+ getOwner,
106
+ useContext as useReactiveContext
182
107
  } from "@fluixi/reactive/signal";
183
- var _warnedEagerProvider = false;
184
- function warnEagerProvider() {
185
- if (_warnedEagerProvider) return;
186
- _warnedEagerProvider = true;
187
- if (typeof process !== "undefined" && true) return;
188
- console.warn(
189
- "[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>"
190
- );
191
- }
192
- function createProvider(a, value, childProps) {
193
- const isLazyForm = !!a?.Provider;
194
- const context = isLazyForm ? a : a.context;
195
- const rawValue = isLazyForm ? value : a.value;
196
- const debug = isLazyForm ? false : a.debug ?? false;
197
- if (!isLazyForm) warnEagerProvider();
198
- const resolvedValue = typeof rawValue === "function" ? rawValue() : rawValue;
199
- const sourceProps = isLazyForm ? childProps : a;
200
- return context.Provider({
201
- value: resolvedValue,
202
- get children() {
203
- return sourceProps?.children;
204
- },
205
- debug
206
- });
207
- }
208
- function createContextWithProvider(defaultValue) {
209
- const context = createContext(defaultValue);
210
- const provider = (props) => {
211
- return createProvider(context, props.value, props);
212
- };
213
- return [context, provider];
214
- }
215
- function composeProviders(contexts, children) {
216
- if (contexts.length === 0) {
217
- return readChildren(() => children);
218
- }
219
- const [context, value] = contexts[0];
220
- const remaining = contexts.slice(1);
221
- return createProvider(context, value, {
222
- get children() {
223
- return composeProviders(remaining, children);
224
- }
225
- });
226
- }
227
- function createConsumer(context) {
228
- return (props) => {
229
- const value = useContext(context);
230
- return props.children(value);
231
- };
232
- }
233
- function useProvide(context, factory) {
234
- const existing = useContext(context);
235
- if (existing === context.defaultValue) {
236
- return factory();
237
- }
238
- return existing;
239
- }
240
108
  function createContext(defaultValue, name) {
241
109
  const context = createReactiveContext(defaultValue, name);
242
- const Provider = (props) => {
243
- if (props.debug === true) {
244
- }
245
- return context.Provider({
110
+ const Provider = (props) => (
111
+ // Children stay a getter: the reactive Provider reads them after stamping the value on
112
+ // its owner. Reading `props.children` here would render them, and any <Outlet/> in them,
113
+ // before the context exists.
114
+ context.Provider({
246
115
  value: props.value,
247
116
  get children() {
248
117
  return props.children;
249
118
  },
250
119
  debug: props.debug
251
- });
252
- };
253
- if (typeof Symbol !== "undefined" && Symbol.for) {
254
- Provider.$$typeof = /* @__PURE__ */ Symbol.for("react.provider");
255
- }
256
- Provider.$$typeof = /* @__PURE__ */ Symbol.for("react.provider");
120
+ })
121
+ );
257
122
  return {
258
123
  id: context.id,
259
124
  Provider: asProvider(Provider),
@@ -264,55 +129,101 @@ function useContext(context) {
264
129
  const value = useReactiveContext(context);
265
130
  return value !== void 0 ? value : context.defaultValue;
266
131
  }
132
+ function createProvider(context, value, props) {
133
+ const resolved = typeof value === "function" ? value() : value;
134
+ return context.Provider({
135
+ value: resolved,
136
+ get children() {
137
+ return props.children;
138
+ }
139
+ });
140
+ }
141
+ function composeProviders(providers, props) {
142
+ const outermost = providers.reduceRight(
143
+ (inner, [context, value]) => ({
144
+ get children() {
145
+ return createProvider(context, value, inner);
146
+ }
147
+ }),
148
+ props
149
+ );
150
+ return outermost.children;
151
+ }
152
+ function requireContext(context, message) {
153
+ for (let owner = getOwner(); owner; owner = owner.parent) {
154
+ if (owner.contexts?.has(context.id)) return useContext(context);
155
+ }
156
+ throw new Error(message ?? `[fluixi] No Provider for ${context.id.description ?? "this context"} above this component.`);
157
+ }
267
158
 
268
159
  // src/lib/router/data.ts
269
- var globalCache = /* @__PURE__ */ new Map();
270
- var inflightRequests = /* @__PURE__ */ new Map();
271
- function cache(fn, name) {
272
- return async (...args) => {
160
+ var revalidationListeners = /* @__PURE__ */ new Set();
161
+ function onRevalidate(listener) {
162
+ revalidationListeners.add(listener);
163
+ return () => void revalidationListeners.delete(listener);
164
+ }
165
+ var reloadMounted = () => {
166
+ for (const listener of [...revalidationListeners]) listener();
167
+ };
168
+ function cache(fn, name, options = {}) {
169
+ const { ttl = Infinity, staleWhileRevalidate = true } = options;
170
+ return (...args) => {
171
+ const store = options.cache ?? currentResourceCache();
273
172
  const key = name + ":" + JSON.stringify(args);
274
- if (isHydrating() || typeof window !== "undefined") {
275
- const hydrated = getHydrationData(key);
276
- if (hydrated !== void 0) {
277
- return hydrated;
278
- }
279
- }
280
- if (globalCache.has(key)) {
281
- return globalCache.get(key);
282
- }
283
- if (inflightRequests.has(key)) {
284
- return inflightRequests.get(key);
285
- }
286
- const promise = Promise.resolve(fn(...args)).then(
287
- (val) => {
288
- globalCache.set(key, val);
289
- inflightRequests.delete(key);
290
- if (isServerEnvironment()) {
291
- setHydrationData(key, val);
173
+ const fetchFresh = () => {
174
+ const running = store.inflight.get(key);
175
+ if (running) return running;
176
+ const promise = Promise.resolve(fn(...args)).then(
177
+ (value) => {
178
+ store.set(key, { value, timestamp: Date.now() });
179
+ store.inflight.delete(key);
180
+ return value;
181
+ },
182
+ (error) => {
183
+ store.inflight.delete(key);
184
+ throw error;
292
185
  }
293
- return val;
294
- },
295
- (err) => {
296
- inflightRequests.delete(key);
297
- throw err;
298
- }
299
- );
300
- inflightRequests.set(key, promise);
301
- return promise;
186
+ );
187
+ store.inflight.set(key, promise);
188
+ return promise;
189
+ };
190
+ const hit = store.get(key);
191
+ if (hit && (ttl === Infinity || Date.now() - hit.timestamp <= ttl)) return Promise.resolve(hit.value);
192
+ if (hit && staleWhileRevalidate) {
193
+ fetchFresh().then(reloadMounted, () => void 0);
194
+ return Promise.resolve(hit.value);
195
+ }
196
+ return fetchFresh();
302
197
  };
303
198
  }
304
- var [revalidationVersion, setRevalidationVersion] = createSignal(0);
305
- var revalidationKeys = /* @__PURE__ */ new Set();
306
- function revalidate(key) {
307
- const predicate = typeof key === "string" ? (k) => k.startsWith(key) : key;
308
- for (const k of globalCache.keys()) {
309
- if (predicate(k)) {
310
- globalCache.delete(k);
311
- revalidationKeys.add(k);
312
- }
199
+ function revalidate(key, store) {
200
+ const names = typeof key === "string" ? [key] : Array.isArray(key) ? key : null;
201
+ drop(names ?? key, store ?? currentResourceCache());
202
+ if (names && !store) tellOtherTabs(names);
203
+ }
204
+ function drop(what, store) {
205
+ const matches = Array.isArray(what) ? (k) => what.some((n) => k === n || k.startsWith(n + ":")) : what;
206
+ for (const k of [...store.keys()]) if (matches(k)) store.delete(k);
207
+ reloadMounted();
208
+ }
209
+ var TABS_KEY = "fluixi:revalidate";
210
+ function tellOtherTabs(names) {
211
+ try {
212
+ localStorage.setItem(TABS_KEY, JSON.stringify({ names, at: Date.now() + Math.random() }));
213
+ } catch {
313
214
  }
314
- setRevalidationVersion((v) => v + 1);
315
215
  }
216
+ if (typeof window !== "undefined") {
217
+ window.addEventListener("storage", (event) => {
218
+ if (event.key !== TABS_KEY || !event.newValue) return;
219
+ try {
220
+ const names = JSON.parse(event.newValue).names;
221
+ if (Array.isArray(names) && names.every((n) => typeof n === "string")) drop(names, currentResourceCache());
222
+ } catch {
223
+ }
224
+ });
225
+ }
226
+ globalThis[/* @__PURE__ */ Symbol.for("fluixi.revalidate")] = (names) => revalidate(names);
316
227
  function createAsync(fetcher, source) {
317
228
  const [data] = createResource(source || (() => true), fetcher);
318
229
  return data;
@@ -327,19 +238,39 @@ function getSubmissionSignal(url) {
327
238
  }
328
239
  return submissionState.get(url);
329
240
  }
330
- function action(fn, name) {
241
+ var submissionsState = /* @__PURE__ */ new Map();
242
+ function getSubmissionsSignal(url) {
243
+ if (!submissionsState.has(url)) submissionsState.set(url, createSignal([]));
244
+ return submissionsState.get(url);
245
+ }
246
+ function action(fn, name, options = {}) {
331
247
  const actionUrl = name || fn.name || "action";
332
248
  const submit = async (input) => {
333
249
  const [, setSubmission] = getSubmissionSignal(actionUrl);
334
- const clear = () => setSubmission(void 0);
335
- const retry = () => submit(input);
336
- setSubmission({
250
+ const [, setSubmissions] = getSubmissionsSignal(actionUrl);
251
+ let entry;
252
+ const drop2 = () => setSubmissions((list) => list.filter((s) => s !== entry));
253
+ const settle = (next) => {
254
+ setSubmissions((list) => list.map((s) => s === entry ? next : s));
255
+ entry = next;
256
+ };
257
+ const clear = () => {
258
+ setSubmission(void 0);
259
+ drop2();
260
+ };
261
+ const retry = () => {
262
+ drop2();
263
+ return submit(input);
264
+ };
265
+ entry = {
337
266
  input,
338
267
  // Approximate type for tracking
339
268
  pending: true,
340
269
  clear,
341
270
  retry
342
- });
271
+ };
272
+ setSubmission(entry);
273
+ setSubmissions((list) => [...list, entry]);
343
274
  try {
344
275
  const result = await fn(input);
345
276
  setSubmission({
@@ -349,32 +280,49 @@ function action(fn, name) {
349
280
  clear,
350
281
  retry
351
282
  });
283
+ drop2();
284
+ const stale = typeof options.invalidates === "function" ? options.invalidates(result) : options.invalidates;
285
+ for (const key of stale ?? []) revalidate(key);
352
286
  return result;
353
287
  } catch (error) {
354
- setSubmission({
288
+ const failed = {
355
289
  input,
356
290
  error,
357
291
  pending: false,
358
292
  clear,
359
293
  retry
360
- });
294
+ };
295
+ setSubmission(failed);
296
+ settle(failed);
361
297
  throw error;
362
298
  }
363
299
  };
364
300
  submit.url = actionUrl;
365
- submit.with = (args) => action((...a) => fn(args, ...a), name);
301
+ submit.with = (args) => action((...a) => fn(args, ...a), name, options);
366
302
  return submit;
367
303
  }
368
304
  function useSubmission(actionFn) {
369
305
  const [submission] = getSubmissionSignal(actionFn.url);
370
306
  return submission;
371
307
  }
308
+ function useSubmissions(actionFn) {
309
+ const [submissions] = getSubmissionsSignal(actionFn.url);
310
+ return submissions;
311
+ }
372
312
  function useAction(actionFn) {
373
313
  return actionFn;
374
314
  }
375
315
  var RouteDataContext = createContext(void 0, "route-data");
376
316
  var RouteDataProvider = RouteDataContext.Provider;
377
317
 
318
+ // src/lib/router/index.ts
319
+ import {
320
+ createMemoryHistory,
321
+ createBrowserHistory as createBrowserHistory2,
322
+ matchRoutes as matchRoutes2,
323
+ matchPath as matchPath2
324
+ } from "@fluixi/router";
325
+
378
326
  // src/lib/render/lazy.ts
379
327
  import { createComponent } from "@fluixi/dom";
380
328
  import {
@@ -424,7 +372,7 @@ function lazy(loader, options) {
424
372
  }
425
373
 
426
374
  // src/lib/router/index.ts
427
- import { runWithOwner } from "@fluixi/reactive/signal";
375
+ import { runWithOwner as runWithOwner2 } from "@fluixi/reactive/signal";
428
376
  var RouterContext = createContext2(void 0, "router");
429
377
  var OutletContext = createContext2(void 0, "outlet");
430
378
  var LevelParamsContext = createContext2(void 0, "route-params");
@@ -437,11 +385,33 @@ function renderLayer(m, depth, base) {
437
385
  onCleanup(() => {
438
386
  cancelled = true;
439
387
  });
440
- const owner = getOwner();
441
- const [resource] = createResource3(
442
- () => loader({ params: base.params, location: base.location }, owner, () => cancelled)
443
- );
444
- data = resource;
388
+ const owner = getOwner2();
389
+ const router = useContext2(RouterContext);
390
+ let watching = false;
391
+ const trackDeps = (deps) => {
392
+ if (watching || cancelled) return;
393
+ watching = true;
394
+ runWithOwner(owner, () => {
395
+ let first = true;
396
+ createEffect(() => {
397
+ deps();
398
+ if (first) first = false;
399
+ else untrack2(() => void refetch());
400
+ });
401
+ });
402
+ };
403
+ const [resource, { refetch }] = createResource3(() => {
404
+ const result = loader({ params: base.params, location: base.location }, owner, () => cancelled, trackDeps);
405
+ return router ? router.trackLoad(result) : result;
406
+ });
407
+ onCleanup(onRevalidate(() => void refetch()));
408
+ data = () => {
409
+ const error = resource.error;
410
+ if (error !== void 0 && error !== null) throw error;
411
+ return resource();
412
+ };
413
+ Object.defineProperty(data, "loading", { get: () => resource.loading });
414
+ Object.defineProperty(data, "state", { get: () => resource.state });
445
415
  }
446
416
  const buildPage = () => RouteDataContext2.Provider({
447
417
  value: data,
@@ -459,12 +429,15 @@ function renderLayer(m, depth, base) {
459
429
  });
460
430
  }
461
431
  });
462
- if (!data) return buildPage();
432
+ if (!data) return { view: buildPage(), ready: () => true };
463
433
  const res = data;
464
- const isLoading = () => res.loading;
434
+ const isLoading = () => {
435
+ const state = res.state;
436
+ return state === void 0 ? res.loading : state === "pending" || state === "unresolved";
437
+ };
465
438
  const [ready, setReady] = createSignal2(untrack2(() => !isLoading()));
466
439
  createEffect(() => setReady(!isLoading()));
467
- return createMemo2(() => ready() ? buildPage() : null);
440
+ return { view: createMemo2(() => ready() ? buildPage() : null), ready };
468
441
  }
469
442
  function renderChild(ctx) {
470
443
  const childIdx = ctx.depth + 1;
@@ -475,17 +448,43 @@ function renderChild(ctx) {
475
448
  const c = ctx.matched()[childIdx]?.component;
476
449
  if (untrack2(childComp) !== c) setChildComp(() => c);
477
450
  });
478
- return createMemo2(() => {
451
+ const owner = getOwner2();
452
+ let shown = null;
453
+ let pending = null;
454
+ const drop2 = (layer) => layer?.dispose();
455
+ onCleanup(() => {
456
+ drop2(shown);
457
+ drop2(pending);
458
+ shown = pending = null;
459
+ });
460
+ const incoming = createMemo2(() => {
479
461
  const component = childComp();
480
- if (!component) {
462
+ if (!component) return null;
463
+ const m = untrack2(() => ctx.matched())[childIdx];
464
+ return runWithOwner(
465
+ owner,
466
+ () => createRoot((dispose) => ({
467
+ ...renderLayer(m, childIdx, { matched: ctx.matched, params: ctx.params, location: ctx.location }),
468
+ dispose
469
+ }))
470
+ );
471
+ });
472
+ return createMemo2(() => {
473
+ const next = incoming();
474
+ if (!next) {
475
+ drop2(shown);
476
+ drop2(pending);
477
+ shown = pending = null;
481
478
  return ctx.notFound ? ctx.notFound() : null;
482
479
  }
483
- const m = untrack2(() => ctx.matched())[childIdx];
484
- return renderLayer(m, childIdx, {
485
- matched: ctx.matched,
486
- params: ctx.params,
487
- location: ctx.location
488
- });
480
+ if (next === shown) return shown.view;
481
+ if (pending && pending !== next) drop2(pending);
482
+ pending = next;
483
+ if (shown && !next.ready()) return shown.view;
484
+ drop2(shown);
485
+ shown = next;
486
+ pending = null;
487
+ return shown.view;
489
488
  });
490
489
  }
491
490
  function renderNotFound(notFound) {
@@ -520,6 +519,8 @@ function Router(props) {
520
519
  history
521
520
  });
522
521
  const [state, setState] = createSignal2(router.state());
522
+ const [navigating, setNavigating] = createSignal2(false);
523
+ const [loads, setLoads] = createSignal2(0);
523
524
  let navigationId = 0;
524
525
  const takeMode = () => {
525
526
  const mode = pendingTransitionMode ?? (props.transition ?? "auto");
@@ -529,12 +530,15 @@ function Router(props) {
529
530
  const unsub = router.subscribe((next) => {
530
531
  const id = ++navigationId;
531
532
  const mode = takeMode();
533
+ setNavigating(true);
532
534
  const prepared = prepareComponents(
533
535
  (next.match?.matched ?? []).map((m) => m.component).filter(Boolean)
534
536
  );
535
537
  const commit = () => {
536
538
  setState(next);
537
- return flush();
539
+ const flushed = flush();
540
+ if (id === navigationId) setNavigating(false);
541
+ return flushed;
538
542
  };
539
543
  if (!prepared) {
540
544
  void runTransition(mode, commit);
@@ -556,7 +560,18 @@ function Router(props) {
556
560
  const ctx = {
557
561
  router,
558
562
  location: createMemo2(() => state().location),
559
- params: createMemo2(() => state().match?.params ?? {})
563
+ params: createMemo2(() => state().match?.params ?? {}),
564
+ isRouting: () => navigating() || loads() > 0,
565
+ // Counted by the promise, not by an owner: a load a newer navigation superseded still
566
+ // settles and uncounts itself, and a resource seeded from the server never calls it.
567
+ trackLoad(result) {
568
+ if (result && typeof result.finally === "function") {
569
+ setLoads((n) => n + 1);
570
+ void result.finally(() => setLoads((n) => n - 1)).catch(() => {
571
+ });
572
+ }
573
+ return result;
574
+ }
560
575
  };
561
576
  const root = {
562
577
  depth: -1,
@@ -579,6 +594,21 @@ function Outlet() {
579
594
  if (!ctx) return null;
580
595
  return renderChild(ctx);
581
596
  }
597
+ function useIsRouting(options = {}) {
598
+ const routing = useContext2(RouterContext)?.isRouting ?? (() => false);
599
+ const delay = options.delay ?? 0;
600
+ if (delay <= 0) return routing;
601
+ const [late, setLate] = createSignal2(false);
602
+ createEffect(() => {
603
+ if (!routing()) {
604
+ setLate(false);
605
+ return;
606
+ }
607
+ const timer = setTimeout(() => setLate(true), delay);
608
+ onCleanup(() => clearTimeout(timer));
609
+ });
610
+ return () => routing() && late();
611
+ }
582
612
  function Redirect(props) {
583
613
  const ctx = useContext2(RouterContext);
584
614
  if (ctx && typeof window !== "undefined") {
@@ -594,7 +624,9 @@ var _ssrRouterStub = {
594
624
  router: { navigate: () => {
595
625
  } },
596
626
  location: () => ({ pathname: "", search: "", hash: "", query: {} }),
597
- params: () => ({})
627
+ params: () => ({}),
628
+ isRouting: () => false,
629
+ trackLoad: (result) => result
598
630
  };
599
631
  function useRouter() {
600
632
  const ctx = useContext2(RouterContext);
@@ -709,18 +741,31 @@ var LINK_OWN_PROPS = /* @__PURE__ */ new Set([
709
741
  ]);
710
742
  function Link(props) {
711
743
  const ctx = useContext2(RouterContext);
712
- const navigate = () => ctx?.router.navigate(read(props.href) ?? "", { replace: read(props.replace) });
744
+ const navigate = () => ctx.router.navigate(read(props.href) ?? "", { replace: read(props.replace) });
713
745
  const a = createNativeElement("a");
714
746
  setAttribute(a, "href", read(props.href) ?? "");
715
747
  if (props.slot) setAttribute(a, "slot", read(props.slot));
716
748
  for (const k of Object.keys(props)) {
717
749
  if (LINK_OWN_PROPS.has(k)) continue;
718
750
  const v = props[k];
719
- if (v != null && typeof v !== "function") setAttribute(a, k, String(v));
751
+ if (typeof v === "function") {
752
+ if (/^on[A-Z]/.test(k)) continue;
753
+ const read2 = v;
754
+ const apply = () => {
755
+ const value = read2();
756
+ if (value == null || value === false) a.removeAttribute?.(k);
757
+ else setAttribute(a, k, String(value));
758
+ };
759
+ if (typeof a.addEventListener === "function") createEffect(apply);
760
+ else apply();
761
+ continue;
762
+ }
763
+ if (v != null) setAttribute(a, k, String(v));
720
764
  }
721
765
  if (typeof a.addEventListener === "function") {
722
766
  a.addEventListener("click", (e) => {
723
767
  if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;
768
+ if (!ctx) return;
724
769
  e.preventDefault();
725
770
  navigate();
726
771
  });
@@ -785,16 +830,11 @@ async function preloadRoutes(routes, pathname) {
785
830
  export {
786
831
  __reExport,
787
832
  lazy,
788
- createProvider,
789
- createContextWithProvider,
790
- composeProviders,
791
- createConsumer,
792
- useProvide,
793
833
  createContext,
794
834
  useContext,
795
- setHydrating,
796
- deserializeHydrationData,
797
- cleanupHydration,
835
+ createProvider,
836
+ composeProviders,
837
+ requireContext,
798
838
  supportsViewTransitions,
799
839
  prefersReducedMotion,
800
840
  cache,
@@ -802,9 +842,11 @@ export {
802
842
  createAsync,
803
843
  action,
804
844
  useSubmission,
845
+ useSubmissions,
805
846
  useAction,
806
847
  Router,
807
848
  Outlet,
849
+ useIsRouting,
808
850
  Redirect,
809
851
  useRouter,
810
852
  useParams,
@@ -826,5 +868,5 @@ export {
826
868
  createBrowserHistory2 as createBrowserHistory,
827
869
  matchRoutes2 as matchRoutes,
828
870
  matchPath2 as matchPath,
829
- runWithOwner
871
+ runWithOwner2 as runWithOwner
830
872
  };