vorma 0.0.0-pre.0 → 0.83.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (225) hide show
  1. package/LICENSE +28 -0
  2. package/README.md +48 -0
  3. package/internal/framework/_typescript/client/index.ts +64 -0
  4. package/internal/framework/_typescript/client/src/asset_manager.ts +67 -0
  5. package/internal/framework/_typescript/client/src/client.ts +1201 -0
  6. package/internal/framework/_typescript/client/src/client_loaders.ts +249 -0
  7. package/internal/framework/_typescript/client/src/component_loader.ts +105 -0
  8. package/internal/framework/_typescript/client/src/error_boundary.ts +7 -0
  9. package/internal/framework/_typescript/client/src/events.ts +54 -0
  10. package/internal/framework/_typescript/client/src/global_loading_indicator/global_loading_indicator.ts +125 -0
  11. package/internal/framework/_typescript/client/src/hard_reload.ts +1 -0
  12. package/internal/framework/_typescript/client/src/head_elements/head_elements.ts +193 -0
  13. package/internal/framework/_typescript/client/src/history/history.ts +118 -0
  14. package/internal/framework/_typescript/client/src/history/npm_history_types.ts +83 -0
  15. package/internal/framework/_typescript/client/src/hmr/hmr.ts +71 -0
  16. package/internal/framework/_typescript/client/src/init_client.ts +134 -0
  17. package/internal/framework/_typescript/client/src/links.ts +218 -0
  18. package/internal/framework/_typescript/client/src/redirects/redirects.ts +203 -0
  19. package/internal/framework/_typescript/client/src/rendering.ts +135 -0
  20. package/internal/framework/_typescript/client/src/resolve_public_href.ts +15 -0
  21. package/internal/framework/_typescript/client/src/scroll_state_manager.ts +100 -0
  22. package/internal/framework/_typescript/client/src/static_route_defs/route_def_helpers.ts +22 -0
  23. package/internal/framework/_typescript/client/src/ui_lib_impl_helpers/link_components.ts +131 -0
  24. package/internal/framework/_typescript/client/src/ui_lib_impl_helpers/route_components.ts +56 -0
  25. package/internal/framework/_typescript/client/src/ui_lib_impl_helpers/typed_navigate.ts +58 -0
  26. package/internal/framework/_typescript/client/src/utils/errors.ts +10 -0
  27. package/internal/framework/_typescript/client/src/utils/logging.ts +7 -0
  28. package/internal/framework/_typescript/client/src/vorma_app_helpers/vorma_app_helpers.ts +290 -0
  29. package/internal/framework/_typescript/client/src/vorma_ctx/vorma_ctx.ts +128 -0
  30. package/internal/framework/_typescript/client/src/window_focus_revalidation/window_focus_revalidation.ts +32 -0
  31. package/internal/framework/_typescript/client/tsconfig.json +3 -0
  32. package/internal/framework/_typescript/create/main.ts +378 -0
  33. package/internal/framework/_typescript/create/package.json +33 -0
  34. package/internal/framework/_typescript/create/pnpm-lock.yaml +70 -0
  35. package/internal/framework/_typescript/create/tsconfig.json +3 -0
  36. package/internal/framework/_typescript/preact/index.tsx +10 -0
  37. package/internal/framework/_typescript/preact/src/helpers.ts +113 -0
  38. package/internal/framework/_typescript/preact/src/link.tsx +107 -0
  39. package/internal/framework/_typescript/preact/src/preact.tsx +191 -0
  40. package/internal/framework/_typescript/preact/tsconfig.json +7 -0
  41. package/internal/framework/_typescript/react/index.tsx +10 -0
  42. package/internal/framework/_typescript/react/src/helpers.ts +118 -0
  43. package/internal/framework/_typescript/react/src/link.tsx +115 -0
  44. package/internal/framework/_typescript/react/src/react.tsx +299 -0
  45. package/internal/framework/_typescript/react/tsconfig.json +6 -0
  46. package/internal/framework/_typescript/solid/index.tsx +10 -0
  47. package/internal/framework/_typescript/solid/src/helpers.ts +114 -0
  48. package/internal/framework/_typescript/solid/src/link.tsx +104 -0
  49. package/internal/framework/_typescript/solid/src/solid.tsx +204 -0
  50. package/internal/framework/_typescript/solid/tsconfig.json +7 -0
  51. package/internal/framework/_typescript/vite/tsconfig.json +3 -0
  52. package/internal/framework/_typescript/vite/vite.ts +93 -0
  53. package/internal/site/frontend/assets/vorma-banner.webp +0 -0
  54. package/kit/_typescript/converters/converters.ts +152 -0
  55. package/kit/_typescript/cookies/cookies.ts +18 -0
  56. package/kit/_typescript/csrf/csrf.ts +10 -0
  57. package/kit/_typescript/debounce/debounce.ts +17 -0
  58. package/kit/_typescript/fmt/fmt.ts +3 -0
  59. package/kit/_typescript/json/deep_equals.ts +54 -0
  60. package/kit/_typescript/json/json.ts +3 -0
  61. package/kit/_typescript/json/search_param_serializer.ts +49 -0
  62. package/kit/_typescript/json/stringify_stable.ts +43 -0
  63. package/kit/_typescript/listeners/listeners.ts +16 -0
  64. package/kit/_typescript/matcher/find_best_match.ts +205 -0
  65. package/kit/_typescript/matcher/find_nested_matches.ts +357 -0
  66. package/kit/_typescript/matcher/parse_segments.ts +30 -0
  67. package/kit/_typescript/matcher/register.ts +271 -0
  68. package/kit/_typescript/theme/theme.ts +177 -0
  69. package/kit/_typescript/tsconfig.json +3 -0
  70. package/kit/_typescript/url/url.ts +132 -0
  71. package/npm_dist/internal/framework/_typescript/client/index.d.ts +17 -0
  72. package/npm_dist/internal/framework/_typescript/client/index.d.ts.map +1 -0
  73. package/npm_dist/internal/framework/_typescript/client/index.js +2489 -0
  74. package/npm_dist/internal/framework/_typescript/client/index.js.map +7 -0
  75. package/npm_dist/internal/framework/_typescript/client/src/asset_manager.d.ts +6 -0
  76. package/npm_dist/internal/framework/_typescript/client/src/asset_manager.d.ts.map +1 -0
  77. package/npm_dist/internal/framework/_typescript/client/src/client.d.ts +119 -0
  78. package/npm_dist/internal/framework/_typescript/client/src/client.d.ts.map +1 -0
  79. package/npm_dist/internal/framework/_typescript/client/src/client_loaders.d.ts +18 -0
  80. package/npm_dist/internal/framework/_typescript/client/src/client_loaders.d.ts.map +1 -0
  81. package/npm_dist/internal/framework/_typescript/client/src/component_loader.d.ts +10 -0
  82. package/npm_dist/internal/framework/_typescript/client/src/component_loader.d.ts.map +1 -0
  83. package/npm_dist/internal/framework/_typescript/client/src/error_boundary.d.ts +3 -0
  84. package/npm_dist/internal/framework/_typescript/client/src/error_boundary.d.ts.map +1 -0
  85. package/npm_dist/internal/framework/_typescript/client/src/events.d.ts +26 -0
  86. package/npm_dist/internal/framework/_typescript/client/src/events.d.ts.map +1 -0
  87. package/npm_dist/internal/framework/_typescript/client/src/global_loading_indicator/global_loading_indicator.d.ts +12 -0
  88. package/npm_dist/internal/framework/_typescript/client/src/global_loading_indicator/global_loading_indicator.d.ts.map +1 -0
  89. package/npm_dist/internal/framework/_typescript/client/src/hard_reload.d.ts +2 -0
  90. package/npm_dist/internal/framework/_typescript/client/src/hard_reload.d.ts.map +1 -0
  91. package/npm_dist/internal/framework/_typescript/client/src/head_elements/head_elements.d.ts +7 -0
  92. package/npm_dist/internal/framework/_typescript/client/src/head_elements/head_elements.d.ts.map +1 -0
  93. package/npm_dist/internal/framework/_typescript/client/src/history/history.d.ts +14 -0
  94. package/npm_dist/internal/framework/_typescript/client/src/history/history.d.ts.map +1 -0
  95. package/npm_dist/internal/framework/_typescript/client/src/history/npm_history_types.d.ts +84 -0
  96. package/npm_dist/internal/framework/_typescript/client/src/history/npm_history_types.d.ts.map +1 -0
  97. package/npm_dist/internal/framework/_typescript/client/src/hmr/hmr.d.ts +3 -0
  98. package/npm_dist/internal/framework/_typescript/client/src/hmr/hmr.d.ts.map +1 -0
  99. package/npm_dist/internal/framework/_typescript/client/src/init_client.d.ts +9 -0
  100. package/npm_dist/internal/framework/_typescript/client/src/init_client.d.ts.map +1 -0
  101. package/npm_dist/internal/framework/_typescript/client/src/links.d.ts +33 -0
  102. package/npm_dist/internal/framework/_typescript/client/src/links.d.ts.map +1 -0
  103. package/npm_dist/internal/framework/_typescript/client/src/redirects/redirects.d.ts +26 -0
  104. package/npm_dist/internal/framework/_typescript/client/src/redirects/redirects.d.ts.map +1 -0
  105. package/npm_dist/internal/framework/_typescript/client/src/rendering.d.ts +18 -0
  106. package/npm_dist/internal/framework/_typescript/client/src/rendering.d.ts.map +1 -0
  107. package/npm_dist/internal/framework/_typescript/client/src/resolve_public_href.d.ts +2 -0
  108. package/npm_dist/internal/framework/_typescript/client/src/resolve_public_href.d.ts.map +1 -0
  109. package/npm_dist/internal/framework/_typescript/client/src/scroll_state_manager.d.ts +22 -0
  110. package/npm_dist/internal/framework/_typescript/client/src/scroll_state_manager.d.ts.map +1 -0
  111. package/npm_dist/internal/framework/_typescript/client/src/static_route_defs/route_def_helpers.d.ts +12 -0
  112. package/npm_dist/internal/framework/_typescript/client/src/static_route_defs/route_def_helpers.d.ts.map +1 -0
  113. package/npm_dist/internal/framework/_typescript/client/src/ui_lib_impl_helpers/link_components.d.ts +28 -0
  114. package/npm_dist/internal/framework/_typescript/client/src/ui_lib_impl_helpers/link_components.d.ts.map +1 -0
  115. package/npm_dist/internal/framework/_typescript/client/src/ui_lib_impl_helpers/route_components.d.ts +18 -0
  116. package/npm_dist/internal/framework/_typescript/client/src/ui_lib_impl_helpers/route_components.d.ts.map +1 -0
  117. package/npm_dist/internal/framework/_typescript/client/src/ui_lib_impl_helpers/typed_navigate.d.ts +11 -0
  118. package/npm_dist/internal/framework/_typescript/client/src/ui_lib_impl_helpers/typed_navigate.d.ts.map +1 -0
  119. package/npm_dist/internal/framework/_typescript/client/src/utils/errors.d.ts +3 -0
  120. package/npm_dist/internal/framework/_typescript/client/src/utils/errors.d.ts.map +1 -0
  121. package/npm_dist/internal/framework/_typescript/client/src/utils/logging.d.ts +3 -0
  122. package/npm_dist/internal/framework/_typescript/client/src/utils/logging.d.ts.map +1 -0
  123. package/npm_dist/internal/framework/_typescript/client/src/vorma_app_helpers/vorma_app_helpers.d.ts +119 -0
  124. package/npm_dist/internal/framework/_typescript/client/src/vorma_app_helpers/vorma_app_helpers.d.ts.map +1 -0
  125. package/npm_dist/internal/framework/_typescript/client/src/vorma_ctx/vorma_ctx.d.ts +88 -0
  126. package/npm_dist/internal/framework/_typescript/client/src/vorma_ctx/vorma_ctx.d.ts.map +1 -0
  127. package/npm_dist/internal/framework/_typescript/client/src/window_focus_revalidation/window_focus_revalidation.d.ts +10 -0
  128. package/npm_dist/internal/framework/_typescript/client/src/window_focus_revalidation/window_focus_revalidation.d.ts.map +1 -0
  129. package/npm_dist/internal/framework/_typescript/create/main.d.ts +3 -0
  130. package/npm_dist/internal/framework/_typescript/create/main.d.ts.map +1 -0
  131. package/npm_dist/internal/framework/_typescript/preact/index.d.ts +4 -0
  132. package/npm_dist/internal/framework/_typescript/preact/index.d.ts.map +1 -0
  133. package/npm_dist/internal/framework/_typescript/preact/index.js +283 -0
  134. package/npm_dist/internal/framework/_typescript/preact/index.js.map +7 -0
  135. package/npm_dist/internal/framework/_typescript/preact/src/helpers.d.ts +21 -0
  136. package/npm_dist/internal/framework/_typescript/preact/src/helpers.d.ts.map +1 -0
  137. package/npm_dist/internal/framework/_typescript/preact/src/link.d.ts +11 -0
  138. package/npm_dist/internal/framework/_typescript/preact/src/link.d.ts.map +1 -0
  139. package/npm_dist/internal/framework/_typescript/preact/src/preact.d.ts +21 -0
  140. package/npm_dist/internal/framework/_typescript/preact/src/preact.d.ts.map +1 -0
  141. package/npm_dist/internal/framework/_typescript/react/index.d.ts +4 -0
  142. package/npm_dist/internal/framework/_typescript/react/index.d.ts.map +1 -0
  143. package/npm_dist/internal/framework/_typescript/react/index.js +370 -0
  144. package/npm_dist/internal/framework/_typescript/react/index.js.map +7 -0
  145. package/npm_dist/internal/framework/_typescript/react/src/helpers.d.ts +21 -0
  146. package/npm_dist/internal/framework/_typescript/react/src/helpers.d.ts.map +1 -0
  147. package/npm_dist/internal/framework/_typescript/react/src/link.d.ts +11 -0
  148. package/npm_dist/internal/framework/_typescript/react/src/link.d.ts.map +1 -0
  149. package/npm_dist/internal/framework/_typescript/react/src/react.d.ts +20 -0
  150. package/npm_dist/internal/framework/_typescript/react/src/react.d.ts.map +1 -0
  151. package/npm_dist/internal/framework/_typescript/solid/index.d.ts +4 -0
  152. package/npm_dist/internal/framework/_typescript/solid/index.d.ts.map +1 -0
  153. package/npm_dist/internal/framework/_typescript/solid/index.js +314 -0
  154. package/npm_dist/internal/framework/_typescript/solid/index.js.map +7 -0
  155. package/npm_dist/internal/framework/_typescript/solid/src/helpers.d.ts +22 -0
  156. package/npm_dist/internal/framework/_typescript/solid/src/helpers.d.ts.map +1 -0
  157. package/npm_dist/internal/framework/_typescript/solid/src/link.d.ts +11 -0
  158. package/npm_dist/internal/framework/_typescript/solid/src/link.d.ts.map +1 -0
  159. package/npm_dist/internal/framework/_typescript/solid/src/solid.d.ts +22 -0
  160. package/npm_dist/internal/framework/_typescript/solid/src/solid.d.ts.map +1 -0
  161. package/npm_dist/internal/framework/_typescript/vite/vite.d.ts +11 -0
  162. package/npm_dist/internal/framework/_typescript/vite/vite.d.ts.map +1 -0
  163. package/npm_dist/internal/framework/_typescript/vite/vite.js +82 -0
  164. package/npm_dist/internal/framework/_typescript/vite/vite.js.map +7 -0
  165. package/npm_dist/kit/_typescript/chunk-YBAPNBS2.js +202 -0
  166. package/npm_dist/kit/_typescript/chunk-YBAPNBS2.js.map +7 -0
  167. package/npm_dist/kit/_typescript/converters/converters.d.ts +26 -0
  168. package/npm_dist/kit/_typescript/converters/converters.d.ts.map +1 -0
  169. package/npm_dist/kit/_typescript/converters/converters.js +99 -0
  170. package/npm_dist/kit/_typescript/converters/converters.js.map +7 -0
  171. package/npm_dist/kit/_typescript/cookies/cookies.d.ts +13 -0
  172. package/npm_dist/kit/_typescript/cookies/cookies.d.ts.map +1 -0
  173. package/npm_dist/kit/_typescript/cookies/cookies.js +13 -0
  174. package/npm_dist/kit/_typescript/cookies/cookies.js.map +7 -0
  175. package/npm_dist/kit/_typescript/csrf/csrf.d.ts +5 -0
  176. package/npm_dist/kit/_typescript/csrf/csrf.d.ts.map +1 -0
  177. package/npm_dist/kit/_typescript/csrf/csrf.js +11 -0
  178. package/npm_dist/kit/_typescript/csrf/csrf.js.map +7 -0
  179. package/npm_dist/kit/_typescript/debounce/debounce.d.ts +4 -0
  180. package/npm_dist/kit/_typescript/debounce/debounce.d.ts.map +1 -0
  181. package/npm_dist/kit/_typescript/debounce/debounce.js +16 -0
  182. package/npm_dist/kit/_typescript/debounce/debounce.js.map +7 -0
  183. package/npm_dist/kit/_typescript/fmt/fmt.d.ts +2 -0
  184. package/npm_dist/kit/_typescript/fmt/fmt.d.ts.map +1 -0
  185. package/npm_dist/kit/_typescript/fmt/fmt.js +8 -0
  186. package/npm_dist/kit/_typescript/fmt/fmt.js.map +7 -0
  187. package/npm_dist/kit/_typescript/json/deep_equals.d.ts +7 -0
  188. package/npm_dist/kit/_typescript/json/deep_equals.d.ts.map +1 -0
  189. package/npm_dist/kit/_typescript/json/json.d.ts +4 -0
  190. package/npm_dist/kit/_typescript/json/json.d.ts.map +1 -0
  191. package/npm_dist/kit/_typescript/json/json.js +110 -0
  192. package/npm_dist/kit/_typescript/json/json.js.map +7 -0
  193. package/npm_dist/kit/_typescript/json/search_param_serializer.d.ts +2 -0
  194. package/npm_dist/kit/_typescript/json/search_param_serializer.d.ts.map +1 -0
  195. package/npm_dist/kit/_typescript/json/stringify_stable.d.ts +7 -0
  196. package/npm_dist/kit/_typescript/json/stringify_stable.d.ts.map +1 -0
  197. package/npm_dist/kit/_typescript/listeners/listeners.d.ts +2 -0
  198. package/npm_dist/kit/_typescript/listeners/listeners.d.ts.map +1 -0
  199. package/npm_dist/kit/_typescript/listeners/listeners.js +20 -0
  200. package/npm_dist/kit/_typescript/listeners/listeners.js.map +7 -0
  201. package/npm_dist/kit/_typescript/matcher/find_best_match.d.ts +10 -0
  202. package/npm_dist/kit/_typescript/matcher/find_best_match.d.ts.map +1 -0
  203. package/npm_dist/kit/_typescript/matcher/find_best_match.js +146 -0
  204. package/npm_dist/kit/_typescript/matcher/find_best_match.js.map +7 -0
  205. package/npm_dist/kit/_typescript/matcher/find_nested_matches.d.ts +14 -0
  206. package/npm_dist/kit/_typescript/matcher/find_nested_matches.d.ts.map +1 -0
  207. package/npm_dist/kit/_typescript/matcher/find_nested_matches.js +248 -0
  208. package/npm_dist/kit/_typescript/matcher/find_nested_matches.js.map +7 -0
  209. package/npm_dist/kit/_typescript/matcher/parse_segments.d.ts +2 -0
  210. package/npm_dist/kit/_typescript/matcher/parse_segments.d.ts.map +1 -0
  211. package/npm_dist/kit/_typescript/matcher/register.d.ts +54 -0
  212. package/npm_dist/kit/_typescript/matcher/register.d.ts.map +1 -0
  213. package/npm_dist/kit/_typescript/matcher/register.js +21 -0
  214. package/npm_dist/kit/_typescript/matcher/register.js.map +7 -0
  215. package/npm_dist/kit/_typescript/theme/theme.d.ts +24 -0
  216. package/npm_dist/kit/_typescript/theme/theme.d.ts.map +1 -0
  217. package/npm_dist/kit/_typescript/theme/theme.js +133 -0
  218. package/npm_dist/kit/_typescript/theme/theme.js.map +7 -0
  219. package/npm_dist/kit/_typescript/url/url.d.ts +30 -0
  220. package/npm_dist/kit/_typescript/url/url.d.ts.map +1 -0
  221. package/npm_dist/kit/_typescript/url/url.js +100 -0
  222. package/npm_dist/kit/_typescript/url/url.js.map +7 -0
  223. package/package.json +135 -3
  224. package/tsconfig.base.json +17 -0
  225. package/index.js +0 -1
@@ -0,0 +1,193 @@
1
+ import { panic } from "../utils/errors.ts";
2
+ import type { HeadEl } from "../vorma_ctx/vorma_ctx.ts";
3
+
4
+ export function getStartAndEndComments(type: "meta" | "rest"): {
5
+ startComment: Comment | null;
6
+ endComment: Comment | null;
7
+ } {
8
+ const startMarker = `data-vorma="${type}-start"`;
9
+ const endMarker = `data-vorma="${type}-end"`;
10
+ const start = findComment(startMarker);
11
+ const end = findComment(endMarker);
12
+ return { startComment: start, endComment: end };
13
+ }
14
+
15
+ function findComment(matchingText: string): Comment | null {
16
+ const walker = document.createTreeWalker(
17
+ document.head,
18
+ NodeFilter.SHOW_COMMENT,
19
+ {
20
+ acceptNode(node: Comment) {
21
+ return node.nodeValue?.trim() === matchingText.trim()
22
+ ? NodeFilter.FILTER_ACCEPT
23
+ : NodeFilter.FILTER_REJECT;
24
+ },
25
+ },
26
+ );
27
+ return walker.nextNode() as Comment | null;
28
+ }
29
+
30
+ export function updateHeadEls(type: "meta" | "rest", blocks: Array<HeadEl>) {
31
+ const { startComment, endComment } = getStartAndEndComments(type);
32
+ if (!startComment || !endComment || !endComment.parentNode) {
33
+ return;
34
+ }
35
+ const parent = endComment.parentNode;
36
+
37
+ // Collect all current nodes between start and end comments
38
+ const currentNodes: Array<Node> = [];
39
+ let nodePtr = startComment.nextSibling;
40
+ while (nodePtr != null && nodePtr !== endComment) {
41
+ currentNodes.push(nodePtr);
42
+ nodePtr = nodePtr.nextSibling;
43
+ }
44
+ const currentElements = currentNodes.filter(
45
+ (node): node is Element => node.nodeType === Node.ELEMENT_NODE,
46
+ );
47
+
48
+ // Create new elements from blocks
49
+ const newElements: Array<Element> = [];
50
+ const newElementFingerprints = new Map<string, Element>();
51
+ for (const block of blocks) {
52
+ if (!block.tag) {
53
+ continue;
54
+ }
55
+ const newEl = document.createElement(block.tag);
56
+ if (block.attributesKnownSafe) {
57
+ for (const key of Object.keys(block.attributesKnownSafe)) {
58
+ const value = block.attributesKnownSafe[key];
59
+ if (value === null || value === undefined) {
60
+ panic(
61
+ `Attribute value for '${key}' in tag '${block.tag}' cannot be null or undefined.`,
62
+ );
63
+ }
64
+ newEl.setAttribute(key, value);
65
+ }
66
+ }
67
+ if (block.booleanAttributes) {
68
+ for (const key of block.booleanAttributes) {
69
+ newEl.setAttribute(key, "");
70
+ }
71
+ }
72
+ if (block.dangerousInnerHTML) {
73
+ newEl.innerHTML = block.dangerousInnerHTML;
74
+ }
75
+
76
+ const fingerprint = createElementFingerprint(newEl);
77
+ if (newElementFingerprints.has(fingerprint)) {
78
+ const elementToRemove = newElementFingerprints.get(fingerprint);
79
+ if (elementToRemove) {
80
+ const indexToRemove = newElements.indexOf(elementToRemove);
81
+ if (indexToRemove > -1) {
82
+ newElements.splice(indexToRemove, 1);
83
+ }
84
+ }
85
+ }
86
+ newElements.push(newEl);
87
+ newElementFingerprints.set(fingerprint, newEl);
88
+ }
89
+
90
+ // Build a map of current elements by fingerprint
91
+ const currentElementsMap = new Map<string, Array<Element>>();
92
+ for (const el of currentElements) {
93
+ const fingerprint = createElementFingerprint(el);
94
+ if (!currentElementsMap.has(fingerprint)) {
95
+ currentElementsMap.set(fingerprint, []);
96
+ }
97
+ currentElementsMap.get(fingerprint)?.push(el);
98
+ }
99
+
100
+ // Match new elements with existing ones when possible
101
+ const finalElements: Array<Element> = [];
102
+ const usedCurrentElements = new Set<Element>();
103
+
104
+ for (const newEl of newElements) {
105
+ const fingerprint = createElementFingerprint(newEl);
106
+ const matchingCurrentElementsList =
107
+ currentElementsMap.get(fingerprint) || [];
108
+
109
+ // Find the first matching element that hasn't been used yet
110
+ const matchingElement = matchingCurrentElementsList.find(
111
+ (el) => !usedCurrentElements.has(el),
112
+ );
113
+
114
+ if (matchingElement) {
115
+ usedCurrentElements.add(matchingElement);
116
+ finalElements.push(matchingElement);
117
+ } else {
118
+ finalElements.push(newEl);
119
+ }
120
+ }
121
+
122
+ // Create a map to track which elements are in the correct position
123
+ // and which need to be moved or added
124
+ const desiredPositions = new Map<Element, number>();
125
+ finalElements.forEach((el, index) => {
126
+ desiredPositions.set(el, index);
127
+ });
128
+
129
+ // Track elements still in the DOM
130
+ const remainingCurrentElements = new Set(currentElements);
131
+
132
+ // First pass: remove elements that are no longer needed
133
+ for (const currentElement of currentElements) {
134
+ if (!usedCurrentElements.has(currentElement)) {
135
+ parent.removeChild(currentElement);
136
+ remainingCurrentElements.delete(currentElement);
137
+ }
138
+ }
139
+
140
+ // Second pass: position elements in the correct order with minimal DOM operations
141
+ let lastProcessedElement: Element | null = null;
142
+
143
+ for (let i = 0; i < finalElements.length; i++) {
144
+ const element = finalElements[i];
145
+ if (!element) {
146
+ continue;
147
+ }
148
+ const isExistingElement = usedCurrentElements.has(element);
149
+
150
+ if (isExistingElement) {
151
+ // Check if this element is already in the correct position
152
+ const nextElementInDOM = (
153
+ lastProcessedElement
154
+ ? lastProcessedElement.nextElementSibling
155
+ : startComment.nextElementSibling
156
+ ) as Element | null;
157
+
158
+ if (nextElementInDOM !== element) {
159
+ // Element exists but is in the wrong position, move it
160
+ parent.insertBefore(element, nextElementInDOM || endComment);
161
+ }
162
+
163
+ // Mark as processed
164
+ remainingCurrentElements.delete(element);
165
+ lastProcessedElement = element;
166
+ } else {
167
+ // This is a new element, insert it
168
+ const insertBefore = lastProcessedElement
169
+ ? lastProcessedElement.nextSibling
170
+ : startComment.nextSibling;
171
+
172
+ parent.insertBefore(element, insertBefore || endComment);
173
+ lastProcessedElement = element;
174
+ }
175
+ }
176
+ }
177
+
178
+ function createElementFingerprint(element: Element): string {
179
+ const attributes: Array<string> = [];
180
+ for (let i = 0; i < element.attributes.length; i++) {
181
+ const attr = element.attributes[i];
182
+ if (!attr) {
183
+ continue;
184
+ }
185
+ const value =
186
+ element.hasAttribute(attr.name) && attr.value === ""
187
+ ? ""
188
+ : attr.value;
189
+ attributes.push(`${attr.name}="${value}"`);
190
+ }
191
+ attributes.sort();
192
+ return `${element.tagName.toUpperCase()}|${attributes.join(",")}|${(element.innerHTML || "").trim()}`;
193
+ }
@@ -0,0 +1,118 @@
1
+ import { createBrowserHistory, type Update as NPMHistoryUpdate } from "history";
2
+ import { navigationStateManager } from "../client.ts";
3
+ import { dispatchLocationEvent } from "../events.ts";
4
+ import {
5
+ __applyScrollState,
6
+ saveScrollState,
7
+ scrollStateManager,
8
+ } from "../scroll_state_manager.ts";
9
+ import { logError } from "../utils/logging.ts";
10
+ import type { historyInstance, historyListener } from "./npm_history_types.ts";
11
+
12
+ export class HistoryManager {
13
+ private static instance: historyInstance;
14
+ private static lastKnownLocation: typeof HistoryManager.instance.location;
15
+
16
+ static getInstance(): historyInstance {
17
+ if (!this.instance) {
18
+ this.instance =
19
+ createBrowserHistory() as unknown as historyInstance;
20
+ this.lastKnownLocation = this.instance.location;
21
+ }
22
+ return this.instance;
23
+ }
24
+
25
+ static getLastKnownLocation() {
26
+ return this.lastKnownLocation;
27
+ }
28
+
29
+ static updateLastKnownLocation(
30
+ location: typeof HistoryManager.instance.location,
31
+ ) {
32
+ this.lastKnownLocation = location;
33
+ }
34
+
35
+ static init(): void {
36
+ const instance = this.getInstance();
37
+ instance.listen(customHistoryListener as unknown as historyListener);
38
+ this.setManualScrollRestoration();
39
+ }
40
+
41
+ private static setManualScrollRestoration(): void {
42
+ if (
43
+ history.scrollRestoration &&
44
+ history.scrollRestoration !== "manual"
45
+ ) {
46
+ history.scrollRestoration = "manual";
47
+ }
48
+ }
49
+ }
50
+
51
+ export function initCustomHistory(): void {
52
+ HistoryManager.init();
53
+ }
54
+
55
+ export async function customHistoryListener({
56
+ action,
57
+ location,
58
+ }: NPMHistoryUpdate): Promise<void> {
59
+ const lastKnownLocation = HistoryManager.getLastKnownLocation();
60
+
61
+ if (location.key !== lastKnownLocation.key) {
62
+ dispatchLocationEvent();
63
+ }
64
+
65
+ const popWithinSameDoc =
66
+ action === "POP" &&
67
+ location.pathname === lastKnownLocation.pathname &&
68
+ location.search === lastKnownLocation.search;
69
+
70
+ const removingHash =
71
+ popWithinSameDoc && lastKnownLocation.hash && !location.hash;
72
+ const addingHash =
73
+ popWithinSameDoc && !lastKnownLocation.hash && location.hash;
74
+ const updatingHash = popWithinSameDoc && location.hash;
75
+
76
+ if (!popWithinSameDoc) {
77
+ saveScrollState();
78
+ }
79
+
80
+ let navigationSucceeded = true;
81
+
82
+ if (action === "POP") {
83
+ const newHash = location.hash.slice(1);
84
+
85
+ if (addingHash || updatingHash) {
86
+ __applyScrollState({ hash: newHash });
87
+ }
88
+
89
+ if (removingHash) {
90
+ const stored = scrollStateManager.getState(location.key);
91
+ __applyScrollState(stored ?? { x: 0, y: 0 });
92
+ }
93
+
94
+ if (!popWithinSameDoc) {
95
+ const result = await navigationStateManager.navigate({
96
+ href: window.location.href,
97
+ navigationType: "browserHistory",
98
+ scrollStateToRestore: scrollStateManager.getState(location.key),
99
+ });
100
+
101
+ if (!result.didNavigate) {
102
+ navigationSucceeded = false;
103
+ logError(
104
+ "Browser POP navigation failed, attempting hard reload of the destination.",
105
+ );
106
+
107
+ // This just reloads the current (failed) URL.
108
+ // It preserves the history stack and ensures no UI/URL mismatch,
109
+ // which could otherwise happen if a browser forward/back navigation fails
110
+ window.location.reload();
111
+ }
112
+ }
113
+ }
114
+
115
+ if (navigationSucceeded) {
116
+ HistoryManager.updateLastKnownLocation(location);
117
+ }
118
+ }
@@ -0,0 +1,83 @@
1
+ /******************************************************************************
2
+
3
+ This file is a condensed, comment-stripped, and prefixed-type version of
4
+ npm:history's index.d.ts file as of v5.3.0. npm:history is licensed under the
5
+ MIT license. It is used under the hood by vorma/client.
6
+
7
+ The npm:history repository is located at: https://github.com/remix-run/history
8
+
9
+ It's only purpose is to re-export a minimal version of the types needed by
10
+ vorma/client's "getHistoryInstance" function, which simply returns an
11
+ instance of npm:history's BrowserHistory.
12
+
13
+ Original license:
14
+
15
+ MIT License
16
+
17
+ Copyright (c) React Training 2016-2020
18
+ Copyright (c) Remix Software 2020-2021
19
+
20
+ Permission is hereby granted, free of charge, to any person obtaining a copy
21
+ of this software and associated documentation files (the "Software"), to deal
22
+ in the Software without restriction, including without limitation the rights
23
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
24
+ copies of the Software, and to permit persons to whom the Software is
25
+ furnished to do so, subject to the following conditions:
26
+
27
+ The above copyright notice and this permission notice shall be included in all
28
+ copies or substantial portions of the Software.
29
+
30
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
31
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
32
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
33
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
34
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
35
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
36
+ SOFTWARE.
37
+
38
+ ******************************************************************************/
39
+
40
+ declare enum historyAction {
41
+ Pop = "POP",
42
+ Push = "PUSH",
43
+ Replace = "REPLACE",
44
+ }
45
+ declare type historyPathname = string;
46
+ declare type historySearch = string;
47
+ declare type historyHash = string;
48
+ declare type historyKey = string;
49
+ interface historyPath {
50
+ pathname: historyPathname;
51
+ search: historySearch;
52
+ hash: historyHash;
53
+ }
54
+ export interface historyLocation extends historyPath {
55
+ state: unknown;
56
+ key: historyKey;
57
+ }
58
+ interface historyUpdate {
59
+ action: historyAction;
60
+ location: historyLocation;
61
+ }
62
+ export interface historyListener {
63
+ (update: historyUpdate): void;
64
+ }
65
+ interface historyTransition extends historyUpdate {
66
+ retry(): void;
67
+ }
68
+ interface historyBlocker {
69
+ (tx: historyTransition): void;
70
+ }
71
+ declare type historyTo = string | Partial<historyPath>;
72
+ export interface historyInstance {
73
+ readonly action: historyAction;
74
+ readonly location: historyLocation;
75
+ createHref(to: historyTo): string;
76
+ push(to: historyTo, state?: any): void;
77
+ replace(to: historyTo, state?: any): void;
78
+ go(delta: number): void;
79
+ back(): void;
80
+ forward(): void;
81
+ listen(listener: historyListener): () => void;
82
+ block(blocker: historyBlocker): () => void;
83
+ }
@@ -0,0 +1,71 @@
1
+ import { debounce } from "vorma/kit/debounce";
2
+ import { revalidate } from "../client.ts";
3
+ import { setupClientLoaders } from "../client_loaders.ts";
4
+ import { dispatchRouteChangeEvent } from "../events.ts";
5
+ import { logInfo } from "../utils/logging.ts";
6
+ import { __vormaClientGlobal } from "../vorma_ctx/vorma_ctx.ts";
7
+
8
+ let devTimeSetupClientLoadersDebounced: () => Promise<void> = () =>
9
+ Promise.resolve();
10
+
11
+ let hmrRevalidateSet: Set<string>;
12
+
13
+ export let __runClientLoadersAfterHMRUpdate: (
14
+ importMeta: ImportMeta,
15
+ pattern: string,
16
+ ) => void = () => {};
17
+
18
+ export function initHMR() {
19
+ if (import.meta.env.DEV) {
20
+ (window as any).__waveRevalidate = revalidate;
21
+
22
+ devTimeSetupClientLoadersDebounced = debounce(async () => {
23
+ await setupClientLoaders();
24
+ dispatchRouteChangeEvent({});
25
+ }, 10);
26
+
27
+ __runClientLoadersAfterHMRUpdate = (importMeta, pattern) => {
28
+ if (hmrRevalidateSet === undefined) {
29
+ hmrRevalidateSet = new Set();
30
+ }
31
+
32
+ if (import.meta.env.DEV && import.meta.hot) {
33
+ const thisURL = new URL(importMeta.url, location.href);
34
+ thisURL.search = "";
35
+ const thisPathname = thisURL.pathname;
36
+
37
+ const alreadyRegistered = hmrRevalidateSet.has(thisPathname);
38
+ if (alreadyRegistered) {
39
+ return;
40
+ }
41
+
42
+ hmrRevalidateSet.add(thisPathname);
43
+
44
+ import.meta.hot.on("vite:afterUpdate", (props) => {
45
+ for (const update of props.updates) {
46
+ if (update.type === "js-update") {
47
+ const updateURL = new URL(
48
+ update.path,
49
+ location.href,
50
+ );
51
+ updateURL.search = "";
52
+ if (updateURL.pathname === thisURL.pathname) {
53
+ if (
54
+ __vormaClientGlobal
55
+ .get("matchedPatterns")
56
+ .includes(pattern)
57
+ ) {
58
+ logInfo(
59
+ "Refreshing client loaders due to change in pattern:",
60
+ pattern,
61
+ );
62
+ devTimeSetupClientLoadersDebounced();
63
+ }
64
+ }
65
+ }
66
+ }
67
+ });
68
+ }
69
+ };
70
+ }
71
+ }
@@ -0,0 +1,134 @@
1
+ import {
2
+ createPatternRegistry,
3
+ registerPattern,
4
+ } from "vorma/kit/matcher/register";
5
+ import { setupClientLoaders } from "./client_loaders.ts";
6
+ import { ComponentLoader } from "./component_loader.ts";
7
+ import { defaultErrorBoundary } from "./error_boundary.ts";
8
+ import { VORMA_HARD_RELOAD_QUERY_PARAM } from "./hard_reload.ts";
9
+ import { HistoryManager } from "./history/history.ts";
10
+ import { initHMR } from "./hmr/hmr.ts";
11
+ import { scrollStateManager } from "./scroll_state_manager.ts";
12
+ import type { VormaAppConfig } from "./vorma_app_helpers/vorma_app_helpers.ts";
13
+ import {
14
+ __vormaClientGlobal,
15
+ type RouteErrorComponent,
16
+ type VormaClientGlobal,
17
+ } from "./vorma_ctx/vorma_ctx.ts";
18
+
19
+ export async function initClient(options: {
20
+ vormaAppConfig: VormaAppConfig;
21
+ renderFn: () => void;
22
+ defaultErrorBoundary?: RouteErrorComponent;
23
+ useViewTransitions?: boolean;
24
+ }): Promise<void> {
25
+ initHMR();
26
+
27
+ // Setup beforeunload handler for scroll restoration
28
+ window.addEventListener("beforeunload", () => {
29
+ scrollStateManager.savePageRefreshState();
30
+ });
31
+
32
+ __vormaClientGlobal.set("vormaAppConfig", options.vormaAppConfig);
33
+ const clientModuleMap: VormaClientGlobal["clientModuleMap"] = {};
34
+
35
+ // Populate client module map with initial page's modules
36
+ const initialMatchedPatterns =
37
+ __vormaClientGlobal.get("matchedPatterns") || [];
38
+ const initialImportURLs = __vormaClientGlobal.get("importURLs") || [];
39
+ const initialExportKeys = __vormaClientGlobal.get("exportKeys") || [];
40
+ const initialErrorExportKeys =
41
+ __vormaClientGlobal.get("errorExportKeys") || [];
42
+
43
+ for (let i = 0; i < initialMatchedPatterns.length; i++) {
44
+ const pattern = initialMatchedPatterns[i];
45
+ const importURL = initialImportURLs[i];
46
+ const exportKey = initialExportKeys[i];
47
+ const errorExportKey = initialErrorExportKeys[i];
48
+
49
+ if (pattern && importURL) {
50
+ clientModuleMap[pattern] = {
51
+ importURL,
52
+ exportKey: exportKey || "default",
53
+ errorExportKey: errorExportKey || "",
54
+ };
55
+ }
56
+ }
57
+ __vormaClientGlobal.set("clientModuleMap", clientModuleMap);
58
+
59
+ const patternRegistry = createPatternRegistry({
60
+ dynamicParamPrefixRune: options.vormaAppConfig.loadersDynamicRune,
61
+ splatSegmentRune: options.vormaAppConfig.loadersSplatRune,
62
+ explicitIndexSegment:
63
+ options.vormaAppConfig.loadersExplicitIndexSegment,
64
+ });
65
+ __vormaClientGlobal.set("patternRegistry", patternRegistry);
66
+
67
+ const manifestURL = __vormaClientGlobal.get("routeManifestURL");
68
+ if (manifestURL) {
69
+ fetch(manifestURL)
70
+ .then((response) => response.json())
71
+ .then((manifest) => {
72
+ __vormaClientGlobal.set("routeManifest", manifest);
73
+
74
+ // Register all patterns from manifest into the existing registry
75
+ for (const pattern of Object.keys(manifest)) {
76
+ registerPattern(patternRegistry, pattern);
77
+ }
78
+ })
79
+ .catch((error) => {
80
+ // This is no biggie -- it's a progressive enhancement
81
+ console.warn("Failed to load route manifest:", error);
82
+ });
83
+ }
84
+
85
+ // Set options
86
+ if (options.defaultErrorBoundary) {
87
+ __vormaClientGlobal.set(
88
+ "defaultErrorBoundary",
89
+ options.defaultErrorBoundary,
90
+ );
91
+ } else {
92
+ __vormaClientGlobal.set("defaultErrorBoundary", defaultErrorBoundary);
93
+ }
94
+
95
+ if (options.useViewTransitions) {
96
+ __vormaClientGlobal.set("useViewTransitions", true);
97
+ }
98
+
99
+ // Initialize history
100
+ HistoryManager.init();
101
+
102
+ // Clean URL
103
+ const url = new URL(window.location.href);
104
+ if (url.searchParams.has(VORMA_HARD_RELOAD_QUERY_PARAM)) {
105
+ url.searchParams.delete(VORMA_HARD_RELOAD_QUERY_PARAM);
106
+ HistoryManager.getInstance().replace(url.href);
107
+ }
108
+
109
+ const importURLs = __vormaClientGlobal.get("importURLs");
110
+
111
+ // Load initial components
112
+ await ComponentLoader.handleComponents(importURLs);
113
+
114
+ // Setup client loaders
115
+ await setupClientLoaders();
116
+
117
+ // Handle error boundary component (must come after setupClientLoaders)
118
+ await ComponentLoader.handleErrorBoundaryComponent(importURLs);
119
+
120
+ // Render
121
+ options.renderFn();
122
+
123
+ // Restore scroll
124
+ scrollStateManager.restorePageRefreshState();
125
+
126
+ // Touch detection
127
+ window.addEventListener(
128
+ "touchstart",
129
+ () => {
130
+ __vormaClientGlobal.set("isTouchDevice", true);
131
+ },
132
+ { once: true },
133
+ );
134
+ }