@fluixi/core 1.0.0-alpha.100

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 (253) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +334 -0
  3. package/dist/cdn/core.global.js +2 -0
  4. package/dist/cli/generate.cjs +5639 -0
  5. package/dist/cli/generate.d.ts +7 -0
  6. package/dist/cli/generate.d.ts.map +1 -0
  7. package/dist/cli/generate.js +2580 -0
  8. package/dist/cli/generate.mjs +5644 -0
  9. package/dist/cli/index.cjs +5639 -0
  10. package/dist/cli/index.d.ts +7 -0
  11. package/dist/cli/index.d.ts.map +1 -0
  12. package/dist/cli/index.js +2580 -0
  13. package/dist/cli/index.mjs +5644 -0
  14. package/dist/cli/run.cjs +64 -0
  15. package/dist/cli/run.d.ts +3 -0
  16. package/dist/cli/run.d.ts.map +1 -0
  17. package/dist/cli/run.js +62 -0
  18. package/dist/cli/run.mjs +40 -0
  19. package/dist/fluixi.d.ts +5 -0
  20. package/dist/src/cdn/chunk-RKBFEFZP.mjs +987 -0
  21. package/dist/src/cdn/core.cjs +1592 -0
  22. package/dist/src/cdn/core.d.ts +16 -0
  23. package/dist/src/cdn/core.d.ts.map +1 -0
  24. package/dist/src/cdn/core.js +15 -0
  25. package/dist/src/cdn/core.mjs +714 -0
  26. package/dist/src/cdn/router-V2THK27H.mjs +74 -0
  27. package/dist/src/index.cjs +1711 -0
  28. package/dist/src/index.d.ts +4 -0
  29. package/dist/src/index.d.ts.map +1 -0
  30. package/dist/src/index.js +11 -0
  31. package/dist/src/index.mjs +1833 -0
  32. package/dist/src/jsx-dev-runtime.cjs +26 -0
  33. package/dist/src/jsx-dev-runtime.d.ts +2 -0
  34. package/dist/src/jsx-dev-runtime.d.ts.map +1 -0
  35. package/dist/src/jsx-dev-runtime.js +1 -0
  36. package/dist/src/jsx-dev-runtime.mjs +24 -0
  37. package/dist/src/jsx-runtime.cjs +26 -0
  38. package/dist/src/jsx-runtime.d.ts +7 -0
  39. package/dist/src/jsx-runtime.d.ts.map +1 -0
  40. package/dist/src/jsx-runtime.js +8 -0
  41. package/dist/src/jsx-runtime.mjs +24 -0
  42. package/dist/src/lib/client/chunk-FDOPDB6Z.mjs +53 -0
  43. package/dist/src/lib/client/index.cjs +1210 -0
  44. package/dist/src/lib/client/index.d.ts +51 -0
  45. package/dist/src/lib/client/index.d.ts.map +1 -0
  46. package/dist/src/lib/client/index.js +174 -0
  47. package/dist/src/lib/client/index.mjs +293 -0
  48. package/dist/src/lib/client/router-4PTBPZLQ.mjs +891 -0
  49. package/dist/src/lib/context/context.cjs +81 -0
  50. package/dist/src/lib/context/context.d.ts +73 -0
  51. package/dist/src/lib/context/context.d.ts.map +1 -0
  52. package/dist/src/lib/context/context.js +93 -0
  53. package/dist/src/lib/context/context.mjs +64 -0
  54. package/dist/src/lib/context/index.cjs +83 -0
  55. package/dist/src/lib/context/index.d.ts +3 -0
  56. package/dist/src/lib/context/index.d.ts.map +1 -0
  57. package/dist/src/lib/context/index.js +1 -0
  58. package/dist/src/lib/context/index.mjs +64 -0
  59. package/dist/src/lib/control-flow.cjs +35 -0
  60. package/dist/src/lib/control-flow.d.ts +4 -0
  61. package/dist/src/lib/control-flow.d.ts.map +1 -0
  62. package/dist/src/lib/control-flow.js +1 -0
  63. package/dist/src/lib/control-flow.mjs +25 -0
  64. package/dist/src/lib/core.cjs +1710 -0
  65. package/dist/src/lib/core.d.ts +91 -0
  66. package/dist/src/lib/core.d.ts.map +1 -0
  67. package/dist/src/lib/core.js +158 -0
  68. package/dist/src/lib/core.mjs +1833 -0
  69. package/dist/src/lib/env/index.cjs +44 -0
  70. package/dist/src/lib/env/index.d.ts +26 -0
  71. package/dist/src/lib/env/index.d.ts.map +1 -0
  72. package/dist/src/lib/env/index.js +31 -0
  73. package/dist/src/lib/env/index.mjs +23 -0
  74. package/dist/src/lib/i18n/index.cjs +123 -0
  75. package/dist/src/lib/i18n/index.d.ts +110 -0
  76. package/dist/src/lib/i18n/index.d.ts.map +1 -0
  77. package/dist/src/lib/i18n/index.js +164 -0
  78. package/dist/src/lib/i18n/index.mjs +103 -0
  79. package/dist/src/lib/index.cjs +598 -0
  80. package/dist/src/lib/index.d.ts +81 -0
  81. package/dist/src/lib/index.d.ts.map +1 -0
  82. package/dist/src/lib/index.js +130 -0
  83. package/dist/src/lib/index.mjs +688 -0
  84. package/dist/src/lib/isomorphic.cjs +90 -0
  85. package/dist/src/lib/isomorphic.d.ts +114 -0
  86. package/dist/src/lib/isomorphic.d.ts.map +1 -0
  87. package/dist/src/lib/isomorphic.js +132 -0
  88. package/dist/src/lib/isomorphic.mjs +69 -0
  89. package/dist/src/lib/jsx-runtime/index.cjs +21 -0
  90. package/dist/src/lib/jsx-runtime/index.d.ts +13 -0
  91. package/dist/src/lib/jsx-runtime/index.d.ts.map +1 -0
  92. package/dist/src/lib/jsx-runtime/index.js +13 -0
  93. package/dist/src/lib/jsx-runtime/index.mjs +4 -0
  94. package/dist/src/lib/jsx-runtime/jsx-dev-runtime.cjs +21 -0
  95. package/dist/src/lib/jsx-runtime/jsx-dev-runtime.d.ts +2 -0
  96. package/dist/src/lib/jsx-runtime/jsx-dev-runtime.d.ts.map +1 -0
  97. package/dist/src/lib/jsx-runtime/jsx-dev-runtime.js +1 -0
  98. package/dist/src/lib/jsx-runtime/jsx-dev-runtime.mjs +4 -0
  99. package/dist/src/lib/plugins/fluixi-routes-plugin.cjs +302 -0
  100. package/dist/src/lib/plugins/fluixi-routes-plugin.d.ts +35 -0
  101. package/dist/src/lib/plugins/fluixi-routes-plugin.d.ts.map +1 -0
  102. package/dist/src/lib/plugins/fluixi-routes-plugin.js +118 -0
  103. package/dist/src/lib/plugins/fluixi-routes-plugin.mjs +271 -0
  104. package/dist/src/lib/plugins/index.cjs +311 -0
  105. package/dist/src/lib/plugins/index.d.ts +6 -0
  106. package/dist/src/lib/plugins/index.d.ts.map +1 -0
  107. package/dist/src/lib/plugins/index.js +7 -0
  108. package/dist/src/lib/plugins/index.mjs +279 -0
  109. package/dist/src/lib/plugins/route-codegen.cjs +73 -0
  110. package/dist/src/lib/plugins/route-codegen.d.ts +12 -0
  111. package/dist/src/lib/plugins/route-codegen.d.ts.map +1 -0
  112. package/dist/src/lib/plugins/route-codegen.js +80 -0
  113. package/dist/src/lib/plugins/route-codegen.mjs +52 -0
  114. package/dist/src/lib/plugins/route-scanner.cjs +189 -0
  115. package/dist/src/lib/plugins/route-scanner.d.ts +38 -0
  116. package/dist/src/lib/plugins/route-scanner.d.ts.map +1 -0
  117. package/dist/src/lib/plugins/route-scanner.js +215 -0
  118. package/dist/src/lib/plugins/route-scanner.mjs +158 -0
  119. package/dist/src/lib/plugins/vite.cjs +27 -0
  120. package/dist/src/lib/plugins/vite.d.ts +3 -0
  121. package/dist/src/lib/plugins/vite.d.ts.map +1 -0
  122. package/dist/src/lib/plugins/vite.js +1 -0
  123. package/dist/src/lib/plugins/vite.mjs +6 -0
  124. package/dist/src/lib/render/await.cjs +38 -0
  125. package/dist/src/lib/render/await.d.ts +26 -0
  126. package/dist/src/lib/render/await.d.ts.map +1 -0
  127. package/dist/src/lib/render/await.js +45 -0
  128. package/dist/src/lib/render/await.mjs +17 -0
  129. package/dist/src/lib/render/component.cjs +221 -0
  130. package/dist/src/lib/render/component.d.ts +296 -0
  131. package/dist/src/lib/render/component.d.ts.map +1 -0
  132. package/dist/src/lib/render/component.js +514 -0
  133. package/dist/src/lib/render/component.mjs +214 -0
  134. package/dist/src/lib/render/deferred.cjs +175 -0
  135. package/dist/src/lib/render/deferred.d.ts +36 -0
  136. package/dist/src/lib/render/deferred.d.ts.map +1 -0
  137. package/dist/src/lib/render/deferred.js +141 -0
  138. package/dist/src/lib/render/deferred.mjs +158 -0
  139. package/dist/src/lib/render/index.cjs +573 -0
  140. package/dist/src/lib/render/index.d.ts +98 -0
  141. package/dist/src/lib/render/index.d.ts.map +1 -0
  142. package/dist/src/lib/render/index.js +180 -0
  143. package/dist/src/lib/render/index.mjs +590 -0
  144. package/dist/src/lib/render/lazy.cjs +67 -0
  145. package/dist/src/lib/render/lazy.d.ts +49 -0
  146. package/dist/src/lib/render/lazy.d.ts.map +1 -0
  147. package/dist/src/lib/render/lazy.js +105 -0
  148. package/dist/src/lib/render/lazy.mjs +50 -0
  149. package/dist/src/lib/render/suspense.cjs +141 -0
  150. package/dist/src/lib/render/suspense.d.ts +65 -0
  151. package/dist/src/lib/render/suspense.d.ts.map +1 -0
  152. package/dist/src/lib/render/suspense.js +196 -0
  153. package/dist/src/lib/render/suspense.mjs +136 -0
  154. package/dist/src/lib/render/versions.cjs +35 -0
  155. package/dist/src/lib/render/versions.d.ts +2 -0
  156. package/dist/src/lib/render/versions.d.ts.map +1 -0
  157. package/dist/src/lib/render/versions.js +21 -0
  158. package/dist/src/lib/render/versions.mjs +14 -0
  159. package/dist/src/lib/router/data.cjs +228 -0
  160. package/dist/src/lib/router/data.d.ts +142 -0
  161. package/dist/src/lib/router/data.d.ts.map +1 -0
  162. package/dist/src/lib/router/data.js +258 -0
  163. package/dist/src/lib/router/data.mjs +214 -0
  164. package/dist/src/lib/router/index.cjs +921 -0
  165. package/dist/src/lib/router/index.d.ts +274 -0
  166. package/dist/src/lib/router/index.d.ts.map +1 -0
  167. package/dist/src/lib/router/index.js +904 -0
  168. package/dist/src/lib/router/index.mjs +938 -0
  169. package/dist/src/lib/router/scroll.cjs +79 -0
  170. package/dist/src/lib/router/scroll.d.ts +19 -0
  171. package/dist/src/lib/router/scroll.d.ts.map +1 -0
  172. package/dist/src/lib/router/scroll.js +68 -0
  173. package/dist/src/lib/router/scroll.mjs +58 -0
  174. package/dist/src/lib/router/transition.cjs +75 -0
  175. package/dist/src/lib/router/transition.d.ts +81 -0
  176. package/dist/src/lib/router/transition.d.ts.map +1 -0
  177. package/dist/src/lib/router/transition.js +136 -0
  178. package/dist/src/lib/router/transition.mjs +54 -0
  179. package/dist/src/lib/server/hydration.cjs +169 -0
  180. package/dist/src/lib/server/hydration.d.ts +198 -0
  181. package/dist/src/lib/server/hydration.d.ts.map +1 -0
  182. package/dist/src/lib/server/hydration.js +345 -0
  183. package/dist/src/lib/server/hydration.mjs +148 -0
  184. package/dist/src/lib/server/index.cjs +406 -0
  185. package/dist/src/lib/server/index.d.ts +115 -0
  186. package/dist/src/lib/server/index.d.ts.map +1 -0
  187. package/dist/src/lib/server/index.js +164 -0
  188. package/dist/src/lib/server/index.mjs +384 -0
  189. package/dist/src/lib/server/reactive.cjs +197 -0
  190. package/dist/src/lib/server/reactive.d.ts +239 -0
  191. package/dist/src/lib/server/reactive.d.ts.map +1 -0
  192. package/dist/src/lib/server/reactive.js +413 -0
  193. package/dist/src/lib/server/reactive.mjs +176 -0
  194. package/dist/src/lib/storage/cache.cjs +51 -0
  195. package/dist/src/lib/storage/cache.d.ts +18 -0
  196. package/dist/src/lib/storage/cache.d.ts.map +1 -0
  197. package/dist/src/lib/storage/cache.js +42 -0
  198. package/dist/src/lib/storage/cache.mjs +30 -0
  199. package/dist/src/lib/storage/index.cjs +246 -0
  200. package/dist/src/lib/storage/index.d.ts +83 -0
  201. package/dist/src/lib/storage/index.d.ts.map +1 -0
  202. package/dist/src/lib/storage/index.js +179 -0
  203. package/dist/src/lib/storage/index.mjs +231 -0
  204. package/dist/src/lib/storage/persisted.cjs +91 -0
  205. package/dist/src/lib/storage/persisted.d.ts +53 -0
  206. package/dist/src/lib/storage/persisted.d.ts.map +1 -0
  207. package/dist/src/lib/storage/persisted.js +75 -0
  208. package/dist/src/lib/storage/persisted.mjs +70 -0
  209. package/dist/src/lib/theme/color-scheme.cjs +281 -0
  210. package/dist/src/lib/theme/color-scheme.d.ts +61 -0
  211. package/dist/src/lib/theme/color-scheme.d.ts.map +1 -0
  212. package/dist/src/lib/theme/color-scheme.js +120 -0
  213. package/dist/src/lib/theme/color-scheme.mjs +265 -0
  214. package/dist/src/lib/theme/index.cjs +267 -0
  215. package/dist/src/lib/theme/index.d.ts +78 -0
  216. package/dist/src/lib/theme/index.d.ts.map +1 -0
  217. package/dist/src/lib/theme/index.js +135 -0
  218. package/dist/src/lib/theme/index.mjs +251 -0
  219. package/dist/src/lib/theme/provider.cjs +346 -0
  220. package/dist/src/lib/theme/provider.d.ts +34 -0
  221. package/dist/src/lib/theme/provider.d.ts.map +1 -0
  222. package/dist/src/lib/theme/provider.js +60 -0
  223. package/dist/src/lib/theme/provider.mjs +332 -0
  224. package/dist/src/lib/utils/index.cjs +209 -0
  225. package/dist/src/lib/utils/index.d.ts +286 -0
  226. package/dist/src/lib/utils/index.d.ts.map +1 -0
  227. package/dist/src/lib/utils/index.js +434 -0
  228. package/dist/src/lib/utils/index.mjs +188 -0
  229. package/dist/src/routes.cjs +37 -0
  230. package/dist/src/routes.d.ts +22 -0
  231. package/dist/src/routes.d.ts.map +1 -0
  232. package/dist/src/routes.js +20 -0
  233. package/dist/src/routes.mjs +17 -0
  234. package/dist/src/server.cjs +405 -0
  235. package/dist/src/server.d.ts +63 -0
  236. package/dist/src/server.d.ts.map +1 -0
  237. package/dist/src/server.js +62 -0
  238. package/dist/src/server.mjs +382 -0
  239. package/dist/src/utils.cjs +21 -0
  240. package/dist/src/utils.d.ts +10 -0
  241. package/dist/src/utils.d.ts.map +1 -0
  242. package/dist/src/utils.js +9 -0
  243. package/dist/src/utils.mjs +4 -0
  244. package/dist/src/version.generated.cjs +26 -0
  245. package/dist/src/version.generated.d.ts +3 -0
  246. package/dist/src/version.generated.d.ts.map +1 -0
  247. package/dist/src/version.generated.js +3 -0
  248. package/dist/src/version.generated.mjs +5 -0
  249. package/dist/tsconfig.lib.tsbuildinfo +1 -0
  250. package/package.json +164 -0
  251. package/src/index.ts +19 -0
  252. package/types/fluixi.d.ts +5 -0
  253. package/types/vinxi.d.ts +50 -0
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Lazy-load a component module.
3
+ *
4
+ * SolidJS-aligned pattern:
5
+ * 1. `comp` signal is shared across all instances of this lazy component (module-level).
6
+ * 2. `createResource` at the component level registers the load promise with the
7
+ * nearest Suspense boundary (trackPromise / resource.loading).
8
+ * 3. A pure `createMemo` reads `comp()`: undefined while loading (Suspense shows
9
+ * fallback), the rendered component once loaded.
10
+ * 4. `preload()` is exposed so routes / link hover handlers can start the download
11
+ * before the component is rendered.
12
+ */
13
+ import { createComponent } from '@fluixi/dom';
14
+ import { untrack, createMemo, createResource, } from '@fluixi/reactive/signal';
15
+ /**
16
+ * Say that this came out of a chunk of its own, so devtools can tell a routed component apart
17
+ * from one that shipped in the first bundle. Nothing but a watcher reads it.
18
+ */
19
+ function fromChunk(value) {
20
+ if (value != null && (typeof value === 'object' || typeof value === 'function')) {
21
+ try {
22
+ value['$lazy'] = true;
23
+ }
24
+ catch {
25
+ /* frozen or exotic */
26
+ }
27
+ }
28
+ return value;
29
+ }
30
+ // ─── Implementation ──────────────────────────────────────────────────────────
31
+ /**
32
+ * Defer a component to its own chunk, loaded the first time it renders.
33
+ *
34
+ * `loader` is a dynamic import, and its `default` export is the component. The returned
35
+ * function stands in for it and carries `preload()`, which starts the import without
36
+ * rendering anything. File routing uses that: `startClient` preloads the matched route
37
+ * before hydrating.
38
+ *
39
+ * Preloading is what keeps hydration intact. If the module has already resolved by the
40
+ * time this renders, the component renders synchronously on the first pass, so it adopts
41
+ * the server's DOM in place. On a cold client navigation it goes through a resource
42
+ * instead, suspends, and a `Suspense` boundary above it shows the fallback.
43
+ *
44
+ * @example
45
+ * ```ts
46
+ * const Settings = lazy(() => import('./Settings.js'));
47
+ *
48
+ * // somewhere that knows the route is next
49
+ * void Settings.preload();
50
+ * ```
51
+ */
52
+ export function lazy(loader, options) {
53
+ // The resolved component, set once the module loads. The whole hydration story turns on
54
+ // this: when it's already set when LazyWrapper renders, because the route was preloaded
55
+ // before hydration (see startClient -> preloadRoutes): we render it SYNCHRONOUSLY on the
56
+ // first pass. No resource, no Suspense fallback, no late re-render. So the lazy file-route
57
+ // adopts the server DOM in place instead of resolving a tick later and building a duplicate
58
+ // next to it. The async path below is only for a cold client navigation.
59
+ let resolved;
60
+ // Single in-flight / completed import. Idempotent: repeat calls reuse the same promise.
61
+ let promise;
62
+ // Suspense-tracked resource, created lazily on the cold (not-preloaded) path only.
63
+ let component;
64
+ function load() {
65
+ if (!promise) {
66
+ promise = loader().then((mod) => (resolved = mod.default));
67
+ }
68
+ return promise;
69
+ }
70
+ if (options?.preload)
71
+ load();
72
+ const LazyWrapper = ((props) => {
73
+ // Fast path: module already loaded (eager preload, or preloaded before hydration).
74
+ // Render straight through: under hydration this consumes the cursor in place.
75
+ if (resolved) {
76
+ const C = resolved;
77
+ return fromChunk(untrack(() => C(props)));
78
+ }
79
+ // Cold path: not loaded yet (client-side navigation to a route whose chunk hasn't been
80
+ // fetched). Register the import with the nearest Suspense via createResource so the
81
+ // fallback shows while it downloads, then swap in the component once it resolves.
82
+ if (!component) {
83
+ // transport:false, the value here is the component function, which never
84
+ // travelled to the client, and the id it used to consume made the page's own
85
+ // resource ids depend on whether this module was already loaded in the
86
+ // process. The client re-imports the chunk itself either way.
87
+ const [resource] = createResource(() => load(), { transport: false });
88
+ component = resource;
89
+ if (resource.error)
90
+ throw resource.error;
91
+ }
92
+ // Left unnamed: this memo is the routed component's wrapper, so it should end up carrying
93
+ // that component's name rather than a label of its own.
94
+ return createMemo(() => {
95
+ const C = component();
96
+ return C ? fromChunk(createComponent(C, props)) : undefined;
97
+ });
98
+ });
99
+ // Callers (link hover, router prefetch, the hydration preload pass) start the download
100
+ // before the component enters the view: <a onMouseEnter={() => LazyFoo.preload()}>.
101
+ LazyWrapper.preload = () => load().then(() => undefined);
102
+ LazyWrapper.$lazy = true;
103
+ LazyWrapper.loader = loader;
104
+ return LazyWrapper;
105
+ }
@@ -0,0 +1,50 @@
1
+ // src/lib/render/lazy.ts
2
+ import { createComponent } from "@fluixi/dom";
3
+ import {
4
+ untrack,
5
+ createMemo,
6
+ createResource
7
+ } from "@fluixi/reactive/signal";
8
+ function fromChunk(value) {
9
+ if (value != null && (typeof value === "object" || typeof value === "function")) {
10
+ try {
11
+ value["$lazy"] = true;
12
+ } catch {
13
+ }
14
+ }
15
+ return value;
16
+ }
17
+ function lazy(loader, options) {
18
+ let resolved;
19
+ let promise;
20
+ let component;
21
+ function load() {
22
+ if (!promise) {
23
+ promise = loader().then((mod) => resolved = mod.default);
24
+ }
25
+ return promise;
26
+ }
27
+ if (options?.preload) load();
28
+ const LazyWrapper = ((props) => {
29
+ if (resolved) {
30
+ const C = resolved;
31
+ return fromChunk(untrack(() => C(props)));
32
+ }
33
+ if (!component) {
34
+ const [resource] = createResource(() => load(), { transport: false });
35
+ component = resource;
36
+ if (resource.error) throw resource.error;
37
+ }
38
+ return createMemo(() => {
39
+ const C = component();
40
+ return C ? fromChunk(createComponent(C, props)) : void 0;
41
+ });
42
+ });
43
+ LazyWrapper.preload = () => load().then(() => void 0);
44
+ LazyWrapper.$lazy = true;
45
+ LazyWrapper.loader = loader;
46
+ return LazyWrapper;
47
+ }
48
+ export {
49
+ lazy
50
+ };
@@ -0,0 +1,141 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/lib/render/suspense.ts
21
+ var suspense_exports = {};
22
+ __export(suspense_exports, {
23
+ $SUSPENSE: () => $SUSPENSE,
24
+ Suspense: () => Suspense,
25
+ SuspenseList: () => SuspenseList,
26
+ isJSXElement: () => isJSXElement,
27
+ isLitResult: () => isLitResult,
28
+ isNode: () => isNode,
29
+ isPromise: () => isPromise
30
+ });
31
+ module.exports = __toCommonJS(suspense_exports);
32
+ var import_signal = require("@fluixi/reactive/signal");
33
+ var import_dom = require("@fluixi/dom");
34
+ var $SUSPENSE = /* @__PURE__ */ Symbol.for("suspense");
35
+ function isNode(value) {
36
+ if (typeof window === "undefined") return false;
37
+ if (typeof Node === "function" && value instanceof Node) return true;
38
+ return !!(value && typeof value === "object" && typeof value.nodeType === "number");
39
+ }
40
+ function isLitResult(value) {
41
+ if (!value || typeof value !== "object") return false;
42
+ return !!(typeof value["_$litType$"] !== "undefined" || typeof value["_$litDirective$"] !== "undefined" || Array.isArray(value.strings) && Array.isArray(value.values));
43
+ }
44
+ function isPromise(value) {
45
+ return !!(value && typeof value === "object" && typeof value.then === "function" || value instanceof Promise);
46
+ }
47
+ function isJSXElement(value) {
48
+ return !!(value && typeof value === "object" && "type" in value && "props" in value);
49
+ }
50
+ var SuspenseListContext = (0, import_signal.createContext)(void 0, "suspense-list");
51
+ function Suspense(props) {
52
+ return (0, import_dom.createComponent)(SuspenseImpl, props);
53
+ }
54
+ function SuspenseImpl(props) {
55
+ const boundary = (0, import_signal.createSuspenseBoundary)();
56
+ const listCtx = (0, import_signal.useContext)(SuspenseListContext);
57
+ const listState = listCtx ? listCtx.register(boundary.inFallback) : void 0;
58
+ function instantiateOnce(val) {
59
+ if (val == null || typeof val === "boolean") return null;
60
+ if (typeof val === "function" && (0, import_dom.isComponent)(val) && !(0, import_signal.isSignal)(val)) return val();
61
+ return val;
62
+ }
63
+ const fallback = (0, import_dom.runWithoutHydration)(() => instantiateOnce(props.fallback ?? null));
64
+ const children = (0, import_signal.provideSuspense)(boundary, () => (0, import_signal.readChildren)(() => props.children));
65
+ const track = (value, depth) => {
66
+ if (depth > 8) return;
67
+ if (Array.isArray(value)) {
68
+ for (const x of value) track(x, depth + 1);
69
+ } else if (typeof value === "function" && (0, import_signal.isSignal)(value)) {
70
+ track(value(), depth + 1);
71
+ }
72
+ };
73
+ (0, import_signal.provideSuspense)(boundary, () => {
74
+ (0, import_signal.createRenderEffect)(() => track(children, 0));
75
+ });
76
+ const [resolved, setResolved] = (0, import_signal.createSignal)(void 0);
77
+ const childIsPromise = isPromise(children);
78
+ if (childIsPromise) {
79
+ boundary.increment();
80
+ children.then(
81
+ (v) => {
82
+ setResolved(() => v);
83
+ boundary.decrement();
84
+ },
85
+ () => boundary.decrement()
86
+ );
87
+ }
88
+ return (0, import_signal.createMemo)(() => {
89
+ const showFallback = listState ? listState.showFallback() : boundary.inFallback();
90
+ if (showFallback) return fallback;
91
+ if (childIsPromise) return resolved();
92
+ return children;
93
+ });
94
+ }
95
+ function SuspenseList(props) {
96
+ return (0, import_dom.createComponent)(SuspenseListImpl, props);
97
+ }
98
+ function SuspenseListImpl(props) {
99
+ const { revealOrder = "together", tail } = props;
100
+ const registrations = [];
101
+ function coordinateReveal() {
102
+ if (revealOrder === "together") {
103
+ const allResolved = registrations.every((r) => !r.inFallback());
104
+ registrations.forEach((r) => {
105
+ r.setShowContent(allResolved);
106
+ r.setShowFallback(!allResolved);
107
+ });
108
+ } else {
109
+ const items = revealOrder === "backwards" ? [...registrations].reverse() : registrations;
110
+ let blocked = false;
111
+ for (const r of items) {
112
+ if (blocked || r.inFallback()) {
113
+ blocked = true;
114
+ r.setShowContent(false);
115
+ r.setShowFallback(tail !== "hidden");
116
+ } else {
117
+ r.setShowContent(true);
118
+ r.setShowFallback(false);
119
+ }
120
+ }
121
+ }
122
+ }
123
+ const coordinator = {
124
+ register: (inFallback) => {
125
+ const [showContent, setShowContent] = (0, import_signal.createSignal)(true);
126
+ const [showFallback, setShowFallback] = (0, import_signal.createSignal)(false);
127
+ registrations.push({ inFallback, setShowContent, setShowFallback });
128
+ (0, import_signal.createEffect)(() => {
129
+ inFallback();
130
+ coordinateReveal();
131
+ });
132
+ return { showContent, showFallback };
133
+ }
134
+ };
135
+ return SuspenseListContext.Provider({
136
+ value: coordinator,
137
+ children: props.children
138
+ });
139
+ }
140
+ (0, import_dom.asSuspense)(Suspense);
141
+ Object.defineProperty(Suspense, "__suspense", { value: true, enumerable: true });
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Suspense on the TC39 boundary. A createResource read throws its promise while
3
+ * pending; the effect running it reports that to the nearest boundary (stamped as
4
+ * SuspenseContext on this component's owner), which flips inFallback. The output
5
+ * memo swaps between fallback and children on that signal.
6
+ */
7
+ export interface SuspenseProps {
8
+ fallback?: any;
9
+ children?: any;
10
+ id?: string;
11
+ }
12
+ export interface SuspenseListProps {
13
+ children: any;
14
+ revealOrder?: 'forwards' | 'backwards' | 'together';
15
+ tail?: 'collapsed' | 'hidden';
16
+ }
17
+ export declare const $SUSPENSE: unique symbol;
18
+ declare function isNode(value: any): value is Node;
19
+ declare function isLitResult(value: any): boolean;
20
+ declare function isPromise<T = any>(value: any): value is Promise<T>;
21
+ declare function isJSXElement(value: any): boolean;
22
+ /**
23
+ * Show `fallback` while anything below is still pending.
24
+ *
25
+ * A boundary is a counter on the owner tree. A read that suspends, which is a resource
26
+ * throwing its pending promise, bubbles to the effect that was reading, and the runtime
27
+ * hands it to the nearest boundary above. The promise settling decrements the counter,
28
+ * and the content replaces the fallback through ordinary reactivity.
29
+ *
30
+ * Nothing needs to be wired by hand. Anything that suspends reports here on its own,
31
+ * including a `lazy` component on a cold navigation and a `createResource` still loading.
32
+ *
33
+ * @example
34
+ * ```ts
35
+ * html`
36
+ * <${Suspense} fallback=${html`<p>loading...</p>`}>
37
+ * <${Profile} />
38
+ * </${Suspense}>`;
39
+ * ```
40
+ */
41
+ export declare function Suspense(props: SuspenseProps): any;
42
+ /**
43
+ * Order how a group of sibling `Suspense` boundaries reveal themselves.
44
+ *
45
+ * Without it, each boundary swaps its fallback for content the moment its own work
46
+ * finishes, so a list of independently-loading rows appears in whatever order the
47
+ * network happened to return. `revealOrder` imposes one instead.
48
+ *
49
+ * `forwards` and `backwards` hold a boundary in its fallback until the ones before it in
50
+ * that direction have resolved. `together`, the default, holds all of them until every
51
+ * one has. `tail` decides what the unrevealed ones show: `collapsed` keeps only the next
52
+ * fallback, `hidden` shows none.
53
+ *
54
+ * @example
55
+ * ```ts
56
+ * html`
57
+ * <${SuspenseList} revealOrder="forwards" tail="collapsed">
58
+ * <${Suspense} fallback=${skeleton}><${Row} id=${1} /></${Suspense}>
59
+ * <${Suspense} fallback=${skeleton}><${Row} id=${2} /></${Suspense}>
60
+ * </${SuspenseList}>`;
61
+ * ```
62
+ */
63
+ export declare function SuspenseList(props: SuspenseListProps): any;
64
+ export { isNode, isLitResult, isPromise, isJSXElement };
65
+ //# sourceMappingURL=suspense.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"suspense.d.ts","sourceRoot":"","sources":["../../../../src/lib/render/suspense.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAuBH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,GAAG,CAAC;IACf,QAAQ,CAAC,EAAE,GAAG,CAAC;IACf,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,GAAG,CAAC;IACd,WAAW,CAAC,EAAE,UAAU,GAAG,WAAW,GAAG,UAAU,CAAC;IACpD,IAAI,CAAC,EAAE,WAAW,GAAG,QAAQ,CAAC;CAC/B;AAED,eAAO,MAAM,SAAS,eAAyB,CAAC;AAEhD,iBAAS,MAAM,CAAC,KAAK,EAAE,GAAG,GAAG,KAAK,IAAI,IAAI,CAIzC;AAED,iBAAS,WAAW,CAAC,KAAK,EAAE,GAAG,GAAG,OAAO,CAOxC;AAED,iBAAS,SAAS,CAAC,CAAC,GAAG,GAAG,EAAE,KAAK,EAAE,GAAG,GAAG,KAAK,IAAI,OAAO,CAAC,CAAC,CAAC,CAK3D;AAED,iBAAS,YAAY,CAAC,KAAK,EAAE,GAAG,GAAG,OAAO,CAEzC;AAYD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,GAAG,CAElD;AA+ED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,GAAG,CAE1D;AAwDD,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC"}
@@ -0,0 +1,196 @@
1
+ /**
2
+ * Suspense on the TC39 boundary. A createResource read throws its promise while
3
+ * pending; the effect running it reports that to the nearest boundary (stamped as
4
+ * SuspenseContext on this component's owner), which flips inFallback. The output
5
+ * memo swaps between fallback and children on that signal.
6
+ */
7
+ import { createSignal, createMemo, createEffect, createRenderEffect, isSignal, readChildren, useContext, createContext, createSuspenseBoundary, provideSuspense, } from '@fluixi/reactive/signal';
8
+ import { asSuspense, isComponent, createComponent, runWithoutHydration, } from '@fluixi/dom';
9
+ export const $SUSPENSE = Symbol.for('suspense');
10
+ function isNode(value) {
11
+ if (typeof window === 'undefined')
12
+ return false;
13
+ if (typeof Node === 'function' && value instanceof Node)
14
+ return true;
15
+ return !!(value && typeof value === 'object' && typeof value.nodeType === 'number');
16
+ }
17
+ function isLitResult(value) {
18
+ if (!value || typeof value !== 'object')
19
+ return false;
20
+ return !!(typeof value['_$litType$'] !== 'undefined' ||
21
+ typeof value['_$litDirective$'] !== 'undefined' ||
22
+ (Array.isArray(value.strings) && Array.isArray(value.values)));
23
+ }
24
+ function isPromise(value) {
25
+ return !!((value && typeof value === 'object' && typeof value.then === 'function') ||
26
+ value instanceof Promise);
27
+ }
28
+ function isJSXElement(value) {
29
+ return !!(value && typeof value === 'object' && 'type' in value && 'props' in value);
30
+ }
31
+ const SuspenseListContext = createContext(undefined, 'suspense-list');
32
+ /**
33
+ * Show `fallback` while anything below is still pending.
34
+ *
35
+ * A boundary is a counter on the owner tree. A read that suspends, which is a resource
36
+ * throwing its pending promise, bubbles to the effect that was reading, and the runtime
37
+ * hands it to the nearest boundary above. The promise settling decrements the counter,
38
+ * and the content replaces the fallback through ordinary reactivity.
39
+ *
40
+ * Nothing needs to be wired by hand. Anything that suspends reports here on its own,
41
+ * including a `lazy` component on a cold navigation and a `createResource` still loading.
42
+ *
43
+ * @example
44
+ * ```ts
45
+ * html`
46
+ * <${Suspense} fallback=${html`<p>loading...</p>`}>
47
+ * <${Profile} />
48
+ * </${Suspense}>`;
49
+ * ```
50
+ */
51
+ export function Suspense(props) {
52
+ return createComponent(SuspenseImpl, props);
53
+ }
54
+ function SuspenseImpl(props) {
55
+ const boundary = createSuspenseBoundary();
56
+ // SuspenseList coordination, if any.
57
+ const listCtx = useContext(SuspenseListContext);
58
+ const listState = listCtx ? listCtx.register(boundary.inFallback) : undefined;
59
+ function instantiateOnce(val) {
60
+ if (val == null || typeof val === 'boolean')
61
+ return null;
62
+ if (typeof val === 'function' && isComponent(val) && !isSignal(val))
63
+ return val();
64
+ return val;
65
+ }
66
+ // Build the fallback off the hydration cursor, it isn't shown when the server
67
+ // already rendered content, so it must not adopt (and duplicate) server nodes.
68
+ const fallback = runWithoutHydration(() => instantiateOnce(props.fallback ?? null));
69
+ // Read the children under the boundary so the memos they create capture its owner;
70
+ // then a suspended resource read inside them throws to this boundary, not the parent.
71
+ //
72
+ // `readChildren`, not a bare `props.children` read: a component child arrives as a THUNK
73
+ // that instantiates when called, so reading the prop alone hands back a function and the
74
+ // component never runs here. It would then be instantiated later, downstream of the output
75
+ // memo, where a suspend has no boundary that can re-run it, the failure that produced an
76
+ // empty node on a client navigation into a not-yet-loaded lazy route: no fallback, no
77
+ // content, no error. `readChildren` unwraps the thunks (so the lazy component runs under
78
+ // this boundary), stops at signals so dynamic children stay reactive, and memoises the
79
+ // result so the tracking effect below and the output render the same instantiation.
80
+ const children = provideSuspense(boundary, () => readChildren(() => props.children));
81
+ // Instantiate the children now, in their own effect under the boundary. A suspended
82
+ // resource read inside them throws here synchronously, a sibling of the output memo
83
+ // below, so the increment reschedules the output cleanly (matching the canary). If we
84
+ // only rendered children through the output's return, the resource read would be nested
85
+ // inside the output's own insert effect, which then never re-runs to show the fallback.
86
+ //
87
+ // Follow NESTED accessors, not just the outer one: the compiler wraps a dynamic child in
88
+ // its own memo, and `readChildren` stops at signals on purpose so they stay reactive. Stop
89
+ // at the outer read and a lazy component behind a condition is never instantiated here
90
+ // it runs later, downstream, where its suspend reaches no boundary. Reading a memo is
91
+ // idempotent (it caches, and the output reads the same one), so following the chain costs
92
+ // nothing and cannot duplicate the subtree.
93
+ const track = (value, depth) => {
94
+ if (depth > 8)
95
+ return;
96
+ if (Array.isArray(value)) {
97
+ for (const x of value)
98
+ track(x, depth + 1);
99
+ }
100
+ else if (typeof value === 'function' && isSignal(value)) {
101
+ track(value(), depth + 1);
102
+ }
103
+ };
104
+ provideSuspense(boundary, () => {
105
+ createRenderEffect(() => track(children, 0));
106
+ });
107
+ // A raw promise child: track it directly on the boundary.
108
+ const [resolved, setResolved] = createSignal(undefined);
109
+ const childIsPromise = isPromise(children);
110
+ if (childIsPromise) {
111
+ boundary.increment();
112
+ children.then((v) => {
113
+ setResolved(() => v);
114
+ boundary.decrement();
115
+ }, () => boundary.decrement());
116
+ }
117
+ return createMemo(() => {
118
+ const showFallback = listState ? listState.showFallback() : boundary.inFallback();
119
+ if (showFallback)
120
+ return fallback;
121
+ if (childIsPromise)
122
+ return resolved();
123
+ return children;
124
+ });
125
+ }
126
+ /**
127
+ * Order how a group of sibling `Suspense` boundaries reveal themselves.
128
+ *
129
+ * Without it, each boundary swaps its fallback for content the moment its own work
130
+ * finishes, so a list of independently-loading rows appears in whatever order the
131
+ * network happened to return. `revealOrder` imposes one instead.
132
+ *
133
+ * `forwards` and `backwards` hold a boundary in its fallback until the ones before it in
134
+ * that direction have resolved. `together`, the default, holds all of them until every
135
+ * one has. `tail` decides what the unrevealed ones show: `collapsed` keeps only the next
136
+ * fallback, `hidden` shows none.
137
+ *
138
+ * @example
139
+ * ```ts
140
+ * html`
141
+ * <${SuspenseList} revealOrder="forwards" tail="collapsed">
142
+ * <${Suspense} fallback=${skeleton}><${Row} id=${1} /></${Suspense}>
143
+ * <${Suspense} fallback=${skeleton}><${Row} id=${2} /></${Suspense}>
144
+ * </${SuspenseList}>`;
145
+ * ```
146
+ */
147
+ export function SuspenseList(props) {
148
+ return createComponent(SuspenseListImpl, props);
149
+ }
150
+ function SuspenseListImpl(props) {
151
+ const { revealOrder = 'together', tail } = props;
152
+ const registrations = [];
153
+ function coordinateReveal() {
154
+ if (revealOrder === 'together') {
155
+ const allResolved = registrations.every((r) => !r.inFallback());
156
+ registrations.forEach((r) => {
157
+ r.setShowContent(allResolved);
158
+ r.setShowFallback(!allResolved);
159
+ });
160
+ }
161
+ else {
162
+ const items = revealOrder === 'backwards' ? [...registrations].reverse() : registrations;
163
+ let blocked = false;
164
+ for (const r of items) {
165
+ if (blocked || r.inFallback()) {
166
+ blocked = true;
167
+ r.setShowContent(false);
168
+ r.setShowFallback(tail !== 'hidden');
169
+ }
170
+ else {
171
+ r.setShowContent(true);
172
+ r.setShowFallback(false);
173
+ }
174
+ }
175
+ }
176
+ }
177
+ const coordinator = {
178
+ register: (inFallback) => {
179
+ const [showContent, setShowContent] = createSignal(true);
180
+ const [showFallback, setShowFallback] = createSignal(false);
181
+ registrations.push({ inFallback, setShowContent, setShowFallback });
182
+ createEffect(() => {
183
+ inFallback();
184
+ coordinateReveal();
185
+ });
186
+ return { showContent, showFallback };
187
+ },
188
+ };
189
+ return SuspenseListContext.Provider({
190
+ value: coordinator,
191
+ children: props.children,
192
+ });
193
+ }
194
+ asSuspense(Suspense);
195
+ Object.defineProperty(Suspense, '__suspense', { value: true, enumerable: true });
196
+ export { isNode, isLitResult, isPromise, isJSXElement };