@davidsouther/jiffies 2026.4.1 → 2026.24.1

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 (286) hide show
  1. package/README.md +0 -3
  2. package/lib/esm/assert.d.ts +26 -0
  3. package/lib/esm/assert.js +38 -0
  4. package/lib/esm/awaitable.js +1 -0
  5. package/lib/esm/case.d.ts +1 -0
  6. package/lib/esm/case.js +5 -0
  7. package/lib/esm/components/accordion.d.ts +5 -0
  8. package/lib/esm/components/accordion.js +9 -0
  9. package/lib/esm/components/alert.d.ts +7 -0
  10. package/lib/esm/components/alert.js +31 -0
  11. package/lib/esm/components/button_bar.d.ts +8 -0
  12. package/lib/esm/components/button_bar.js +25 -0
  13. package/lib/esm/components/card.d.ts +8 -0
  14. package/lib/esm/components/card.js +31 -0
  15. package/lib/esm/components/children.d.ts +2 -0
  16. package/lib/esm/components/children.js +7 -0
  17. package/lib/esm/components/form.d.ts +5 -0
  18. package/lib/esm/components/form.js +13 -0
  19. package/lib/esm/components/index.d.ts +9 -0
  20. package/lib/esm/components/index.js +10 -0
  21. package/lib/esm/components/inline_edit.d.ts +12 -0
  22. package/lib/esm/components/inline_edit.js +48 -0
  23. package/lib/esm/components/link.d.ts +5 -0
  24. package/lib/esm/components/link.js +11 -0
  25. package/lib/esm/components/logger.d.ts +6 -0
  26. package/lib/esm/components/logger.js +22 -0
  27. package/lib/esm/components/modal.d.ts +2 -0
  28. package/lib/esm/components/modal.js +10 -0
  29. package/lib/esm/components/nav.d.ts +11 -0
  30. package/lib/esm/components/nav.js +27 -0
  31. package/lib/esm/components/property.d.ts +9 -0
  32. package/lib/esm/components/property.js +16 -0
  33. package/lib/esm/components/select.d.ts +10 -0
  34. package/lib/esm/components/select.js +3 -0
  35. package/lib/esm/components/tabs.d.ts +20 -0
  36. package/lib/esm/components/tabs.js +45 -0
  37. package/lib/esm/components/virtual_scroll.d.ts +42 -0
  38. package/lib/esm/components/virtual_scroll.js +94 -0
  39. package/lib/esm/debounce.d.ts +1 -0
  40. package/lib/esm/debounce.js +11 -0
  41. package/lib/esm/diff.d.ts +15 -0
  42. package/lib/esm/diff.js +50 -0
  43. package/lib/esm/display.d.ts +5 -0
  44. package/lib/esm/display.js +11 -0
  45. package/lib/esm/dom/css/border.d.ts +11 -0
  46. package/lib/esm/dom/css/border.js +27 -0
  47. package/lib/esm/dom/css/constants.d.ts +31 -0
  48. package/lib/esm/dom/css/constants.js +28 -0
  49. package/lib/esm/dom/css/core.d.ts +5 -0
  50. package/lib/esm/dom/css/core.js +24 -0
  51. package/lib/esm/dom/css/fstyle.d.ts +5 -0
  52. package/lib/esm/dom/css/fstyle.js +32 -0
  53. package/lib/esm/dom/css/sizing.d.ts +5 -0
  54. package/lib/esm/dom/css/sizing.js +10 -0
  55. package/lib/esm/dom/dom.d.ts +36 -0
  56. package/lib/esm/dom/dom.js +217 -0
  57. package/lib/esm/dom/fc.d.ts +10 -0
  58. package/lib/esm/dom/fc.js +32 -0
  59. package/lib/esm/dom/form/form.app.d.ts +1 -0
  60. package/lib/esm/dom/form/form.app.js +19 -0
  61. package/lib/esm/dom/form/form.d.ts +27 -0
  62. package/lib/esm/dom/form/form.js +65 -0
  63. package/lib/esm/dom/html.d.ts +112 -0
  64. package/{src/dom/html.ts → lib/esm/dom/html.js} +2 -14
  65. package/lib/esm/dom/hydrate.d.ts +39 -0
  66. package/lib/esm/dom/hydrate.js +187 -0
  67. package/lib/esm/dom/index.js +2 -0
  68. package/lib/esm/dom/navigation/index.d.ts +76 -0
  69. package/lib/esm/dom/navigation/index.js +292 -0
  70. package/lib/esm/dom/observable.d.ts +2 -0
  71. package/lib/esm/dom/observable.js +6 -0
  72. package/lib/esm/dom/provide.d.ts +3 -0
  73. package/lib/esm/dom/provide.js +7 -0
  74. package/lib/esm/dom/render.d.ts +8 -0
  75. package/lib/esm/dom/render.js +28 -0
  76. package/lib/esm/dom/router/link.d.ts +6 -0
  77. package/lib/esm/dom/router/link.js +3 -0
  78. package/lib/esm/dom/router/router.d.ts +13 -0
  79. package/lib/esm/dom/router/router.js +52 -0
  80. package/lib/esm/dom/svg.d.ts +64 -0
  81. package/{src/dom/svg.ts → lib/esm/dom/svg.js} +2 -15
  82. package/lib/esm/dom/types/css.d.ts +6590 -0
  83. package/lib/esm/dom/types/css.js +1 -0
  84. package/lib/esm/dom/types/dom.js +1 -0
  85. package/lib/esm/dom/types/html.d.ts +614 -0
  86. package/lib/esm/dom/types/html.js +1 -0
  87. package/lib/esm/dom/xml.d.ts +1 -0
  88. package/lib/esm/dom/xml.js +4 -0
  89. package/lib/esm/equal.d.ts +11 -0
  90. package/lib/esm/equal.js +43 -0
  91. package/lib/esm/fs.d.ts +72 -0
  92. package/lib/esm/fs.js +227 -0
  93. package/lib/esm/fs_node.d.ts +15 -0
  94. package/lib/esm/fs_node.js +45 -0
  95. package/lib/esm/generator.d.ts +1 -0
  96. package/lib/esm/generator.js +10 -0
  97. package/lib/esm/lock.d.ts +1 -0
  98. package/lib/esm/lock.js +23 -0
  99. package/lib/esm/log.d.ts +69 -0
  100. package/lib/esm/log.js +211 -0
  101. package/lib/esm/observable/event.d.ts +35 -0
  102. package/lib/esm/observable/event.js +46 -0
  103. package/lib/esm/observable/observable.d.ts +134 -0
  104. package/lib/esm/observable/observable.js +349 -0
  105. package/lib/esm/range.d.ts +1 -0
  106. package/lib/esm/range.js +7 -0
  107. package/lib/esm/result.d.ts +31 -0
  108. package/lib/esm/result.js +66 -0
  109. package/lib/esm/safe.d.ts +1 -0
  110. package/lib/esm/safe.js +10 -0
  111. package/lib/esm/server/http/apps.d.ts +5 -0
  112. package/lib/esm/server/http/apps.js +23 -0
  113. package/lib/esm/server/http/css.d.ts +5 -0
  114. package/lib/esm/server/http/css.js +43 -0
  115. package/lib/esm/server/http/index.d.ts +16 -0
  116. package/lib/esm/server/http/index.js +78 -0
  117. package/lib/esm/server/http/response.d.ts +4 -0
  118. package/lib/esm/server/http/response.js +43 -0
  119. package/lib/esm/server/http/sitemap.d.ts +2 -0
  120. package/lib/esm/server/http/sitemap.js +22 -0
  121. package/lib/esm/server/http/static.d.ts +2 -0
  122. package/lib/esm/server/http/static.js +22 -0
  123. package/lib/esm/server/http/typescript.d.ts +5 -0
  124. package/lib/esm/server/http/typescript.js +40 -0
  125. package/lib/esm/server/live-reload.d.ts +46 -0
  126. package/lib/esm/server/live-reload.js +161 -0
  127. package/lib/esm/server/main.d.ts +2 -0
  128. package/lib/esm/server/main.js +23 -0
  129. package/lib/esm/server/ws/frame.d.ts +2 -0
  130. package/lib/esm/server/ws/frame.js +35 -0
  131. package/lib/esm/server/ws/handshake.d.ts +4 -0
  132. package/lib/esm/server/ws/handshake.js +32 -0
  133. package/lib/esm/server/ws/index.d.ts +14 -0
  134. package/lib/esm/server/ws/index.js +68 -0
  135. package/lib/esm/ssg/bundle.d.ts +14 -0
  136. package/lib/esm/ssg/bundle.js +73 -0
  137. package/lib/esm/ssg/copy-public.d.ts +6 -0
  138. package/lib/esm/ssg/copy-public.js +34 -0
  139. package/lib/esm/ssg/discover.d.ts +15 -0
  140. package/lib/esm/ssg/discover.js +117 -0
  141. package/lib/esm/ssg/main.d.ts +2 -0
  142. package/lib/esm/ssg/main.js +122 -0
  143. package/lib/esm/ssg/rewrite.d.ts +9 -0
  144. package/lib/esm/ssg/rewrite.js +15 -0
  145. package/lib/esm/ssg/ssg.d.ts +26 -0
  146. package/lib/esm/ssg/ssg.js +84 -0
  147. package/lib/esm/transpile.d.mts +3 -0
  148. package/lib/esm/transpile.mjs +12 -0
  149. package/package.json +19 -10
  150. package/src/404.html +0 -14
  151. package/src/assert.ts +0 -56
  152. package/src/case.ts +0 -5
  153. package/src/components/_notes +0 -33
  154. package/src/components/button_bar.ts +0 -42
  155. package/src/components/inline_edit.ts +0 -78
  156. package/src/components/logger.ts +0 -35
  157. package/src/components/select.ts +0 -22
  158. package/src/components/test.ts +0 -5
  159. package/src/components/virtual_scroll.test.ts +0 -30
  160. package/src/components/virtual_scroll.ts +0 -199
  161. package/src/context.test.ts +0 -58
  162. package/src/context.ts +0 -67
  163. package/src/debounce.ts +0 -14
  164. package/src/diff.test.ts +0 -48
  165. package/src/diff.ts +0 -82
  166. package/src/display.ts +0 -18
  167. package/src/dom/README.md +0 -102
  168. package/src/dom/css/border.ts +0 -47
  169. package/src/dom/css/constants.ts +0 -34
  170. package/src/dom/css/core.ts +0 -28
  171. package/src/dom/css/fstyle.ts +0 -42
  172. package/src/dom/css/sizing.ts +0 -11
  173. package/src/dom/dom.ts +0 -183
  174. package/src/dom/fc.test.ts +0 -43
  175. package/src/dom/fc.ts +0 -80
  176. package/src/dom/form/form.app.ts +0 -50
  177. package/src/dom/form/form.ts +0 -82
  178. package/src/dom/form/index.html +0 -15
  179. package/src/dom/html.test.ts +0 -74
  180. package/src/dom/observable.test.ts +0 -43
  181. package/src/dom/observable.ts +0 -11
  182. package/src/dom/provide.ts +0 -11
  183. package/src/dom/router/link.ts +0 -14
  184. package/src/dom/router/router.ts +0 -72
  185. package/src/dom/test.ts +0 -11
  186. package/src/dom/types/css.ts +0 -10088
  187. package/src/dom/types/dom.ts +0 -0
  188. package/src/dom/types/html.ts +0 -629
  189. package/src/dom/xml.ts +0 -11
  190. package/src/equal.test.ts +0 -23
  191. package/src/equal.ts +0 -66
  192. package/src/favicon.ico +0 -0
  193. package/src/flags.test.ts +0 -43
  194. package/src/flags.ts +0 -53
  195. package/src/fs.test.ts +0 -106
  196. package/src/fs.ts +0 -300
  197. package/src/fs_node.ts +0 -57
  198. package/src/fs_win.test.ts +0 -11
  199. package/src/generator.test.ts +0 -27
  200. package/src/generator.ts +0 -12
  201. package/src/hooks/_notes +0 -6
  202. package/src/index.html +0 -82
  203. package/src/is_browser.js +0 -1
  204. package/src/lock.test.ts +0 -17
  205. package/src/lock.ts +0 -23
  206. package/src/log.ts +0 -155
  207. package/src/observable/_notes +0 -26
  208. package/src/observable/event.ts +0 -93
  209. package/src/observable/observable.test.ts +0 -73
  210. package/src/observable/observable.ts +0 -484
  211. package/src/pico/_variables.scss +0 -66
  212. package/src/pico/components/_accordion.scss +0 -112
  213. package/src/pico/components/_button-group.scss +0 -51
  214. package/src/pico/components/_card.scss +0 -47
  215. package/src/pico/components/_dropdown.scss +0 -203
  216. package/src/pico/components/_modal.scss +0 -181
  217. package/src/pico/components/_nav.scss +0 -79
  218. package/src/pico/components/_progress.scss +0 -70
  219. package/src/pico/components/_property.scss +0 -34
  220. package/src/pico/content/_button.scss +0 -152
  221. package/src/pico/content/_code.scss +0 -63
  222. package/src/pico/content/_embedded.scss +0 -0
  223. package/src/pico/content/_form-alt.scss +0 -276
  224. package/src/pico/content/_form.scss +0 -259
  225. package/src/pico/content/_misc.scss +0 -0
  226. package/src/pico/content/_table.scss +0 -28
  227. package/src/pico/content/_toggle.scss +0 -132
  228. package/src/pico/content/_typography.scss +0 -232
  229. package/src/pico/layout/_container.scss +0 -40
  230. package/src/pico/layout/_document.scss +0 -0
  231. package/src/pico/layout/_flex.scss +0 -46
  232. package/src/pico/layout/_grid.scss +0 -24
  233. package/src/pico/layout/_scroller.scss +0 -16
  234. package/src/pico/layout/_section.scss +0 -8
  235. package/src/pico/layout/_sectioning.scss +0 -55
  236. package/src/pico/pico.scss +0 -60
  237. package/src/pico/reset/_accessibility.scss +0 -34
  238. package/src/pico/reset/_button.scss +0 -17
  239. package/src/pico/reset/_code.scss +0 -15
  240. package/src/pico/reset/_document.scss +0 -48
  241. package/src/pico/reset/_embedded.scss +0 -39
  242. package/src/pico/reset/_form.scss +0 -97
  243. package/src/pico/reset/_misc.scss +0 -23
  244. package/src/pico/reset/_nav.scss +0 -5
  245. package/src/pico/reset/_progress.scss +0 -4
  246. package/src/pico/reset/_table.scss +0 -8
  247. package/src/pico/reset/_typography.scss +0 -25
  248. package/src/pico/themes/default/_colors.scss +0 -65
  249. package/src/pico/themes/default/_dark.scss +0 -148
  250. package/src/pico/themes/default/_light.scss +0 -149
  251. package/src/pico/themes/default/_styles.scss +0 -272
  252. package/src/pico/themes/default.scss +0 -34
  253. package/src/pico/utilities/_accessibility.scss +0 -3
  254. package/src/pico/utilities/_loading.scss +0 -52
  255. package/src/pico/utilities/_reduce-motion.scss +0 -27
  256. package/src/pico/utilities/_tooltip.scss +0 -101
  257. package/src/range.ts +0 -7
  258. package/src/result.test.ts +0 -101
  259. package/src/result.ts +0 -107
  260. package/src/safe.ts +0 -12
  261. package/src/scope/describe.ts +0 -81
  262. package/src/scope/display/console.ts +0 -26
  263. package/src/scope/display/dom.ts +0 -36
  264. package/src/scope/display/junit.ts +0 -64
  265. package/src/scope/execute.ts +0 -110
  266. package/src/scope/expect.ts +0 -169
  267. package/src/scope/fix.ts +0 -30
  268. package/src/scope/index.ts +0 -11
  269. package/src/scope/scope.ts +0 -21
  270. package/src/scope/state.ts +0 -13
  271. package/src/server/http/apps.ts +0 -26
  272. package/src/server/http/css.ts +0 -49
  273. package/src/server/http/index.ts +0 -127
  274. package/src/server/http/response.ts +0 -57
  275. package/src/server/http/sitemap.ts +0 -48
  276. package/src/server/http/static.ts +0 -30
  277. package/src/server/http/typescript.ts +0 -46
  278. package/src/server/main.ts +0 -23
  279. package/src/test.mjs +0 -33
  280. package/src/test_all.ts +0 -35
  281. package/src/transpile.mjs +0 -16
  282. package/src/zip/spec.txt +0 -3260
  283. package/tsconfig.json +0 -34
  284. /package/{src/awaitable.ts → lib/esm/awaitable.d.ts} +0 -0
  285. /package/{src/dom/index.ts → lib/esm/dom/index.d.ts} +0 -0
  286. /package/{src/dom/form/form.test.ts → lib/esm/dom/types/dom.d.ts} +0 -0
@@ -0,0 +1,292 @@
1
+ "use client"; // The navigation runtime drives same-document transitions client side.
2
+ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
3
+ if (typeof path === "string" && /^\.\.?\//.test(path)) {
4
+ return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
5
+ return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
6
+ });
7
+ }
8
+ return path;
9
+ };
10
+ import { start } from "../hydrate.js";
11
+ /**
12
+ * Registered `onNavigate` hooks, fired in registration order after every
13
+ * completed navigation. Module-level state only: this module installs no
14
+ * listeners, touches no DOM, and never hydrates or calls `start()` at import
15
+ * time (M1 plan Step 1 invariant). The static `../hydrate.ts` import does pull
16
+ * in `../dom.ts`, whose jsdom bootstrap runs only in windowless Node and is
17
+ * skipped under a browser or jsdom where `window` already exists — so importing
18
+ * `./index.ts` is side-effect-free in those environments.
19
+ */
20
+ const navigateCallbacks = [];
21
+ /**
22
+ * Register `cb` to run after every in-app navigation completes (body swapped and
23
+ * hydration scheduled). Invariant: callbacks fire exactly once per navigation,
24
+ * in registration order, with the navigation's `NavigationContext`. Used by
25
+ * shell code such as a GA `page_view`.
26
+ */
27
+ export function onNavigate(cb) {
28
+ navigateCallbacks.push(cb);
29
+ }
30
+ /** Hooks awaiting the first load, fired once during bootstrap in registration order. */
31
+ const firstLoadCallbacks = [];
32
+ /**
33
+ * The first-load context, set by `bootstrap()` once the initial page hydrates.
34
+ * `undefined` until then. Retained so an `onFirstLoad` registered AFTER first load
35
+ * (e.g. a shell module that imports the runtime lazily) still receives the event.
36
+ */
37
+ let firstLoadContext;
38
+ /**
39
+ * Register `cb` to run once, when the initial page hydrates on first document load.
40
+ * Invariant: if registered BEFORE bootstrap, `cb` is queued and fired during
41
+ * bootstrap with the first-load context (`type: "first"`). If registered AFTER first
42
+ * load, `cb` is invoked immediately with the retained context, so a late
43
+ * registration never drops the initial event (design §1). Registering installs no
44
+ * listeners and touches no DOM (import-side-effect-free invariant).
45
+ */
46
+ export function onFirstLoad(cb) {
47
+ if (firstLoadContext) {
48
+ cb(firstLoadContext);
49
+ return;
50
+ }
51
+ firstLoadCallbacks.push(cb);
52
+ }
53
+ /**
54
+ * Attribute marking a one-time "shell" node in `<head>` (a theme bootstrap, an
55
+ * analytics tag): the build emits it, and the head reconciler preserves any node
56
+ * carrying it by identity across navigations so its inline script never re-runs.
57
+ */
58
+ const SHELL_ATTR = "data-shell";
59
+ /** True for a head node the reconciler must preserve in place across navigations. */
60
+ function isShell(node) {
61
+ return node instanceof Element && node.hasAttribute(SHELL_ATTR);
62
+ }
63
+ /**
64
+ * Abandon the same-document path for an ordinary full document load of `url`
65
+ * (design "Failure modes"). Returns `null` so a fetch caller can `return fullLoad(url)`
66
+ * to both trigger the load and signal "stop here" — the destination becomes a normal
67
+ * navigation, never a broken intermediate state.
68
+ */
69
+ function fullLoad(url) {
70
+ window.location.assign(url.href);
71
+ return null;
72
+ }
73
+ /**
74
+ * Fetch `url` and parse its body text into a detached Document via the global
75
+ * DOMParser. Returns the parsed document, or `null` when the same-document path
76
+ * must be abandoned for a full load: a non-2xx response or a network error falls
77
+ * back to `fullLoad(url)` so the caller aborts before reconciling. Invariant: a
78
+ * returned document is detached — its nodes must be adopted with
79
+ * `document.importNode` before insertion into the live document.
80
+ */
81
+ async function fetchDocument(url) {
82
+ let response;
83
+ try {
84
+ response = await fetch(url);
85
+ }
86
+ catch {
87
+ return fullLoad(url); // network error
88
+ }
89
+ if (!response.ok) {
90
+ return fullLoad(url); // non-2xx
91
+ }
92
+ const html = await response.text();
93
+ return new DOMParser().parseFromString(html, "text/html");
94
+ }
95
+ /**
96
+ * Reconcile the live `<head>` against `destHead`. Preserves every existing
97
+ * `[data-shell]` node by identity; replaces all non-shell live nodes with
98
+ * `destHead`'s non-shell nodes (adopted into the live document). Applies the
99
+ * destination `<title>` via `document.title` and mirrors `<html lang>`. Leaves
100
+ * the destination `#__hydration` payload in place for the subsequent `start()`.
101
+ */
102
+ function reconcileHead(destHead) {
103
+ const liveHead = window.document.head;
104
+ // Drop every live per-page node, leaving the [data-shell] nodes untouched —
105
+ // preserved by identity so their inline scripts (theme, analytics) never re-run.
106
+ for (const node of [...liveHead.childNodes]) {
107
+ if (!isShell(node))
108
+ node.remove();
109
+ }
110
+ // Adopt the destination's per-page nodes (title, metadata, the #__hydration
111
+ // payload) and append them. The destination's own shell nodes are dropped —
112
+ // the live ones already cover them.
113
+ for (const node of [...destHead.childNodes]) {
114
+ if (isShell(node))
115
+ continue;
116
+ liveHead.appendChild(window.document.importNode(node, true));
117
+ }
118
+ // Apply <title> through document.title so it takes effect immediately, and
119
+ // mirror <html lang> from the destination when it differs.
120
+ const destRoot = destHead.ownerDocument.documentElement;
121
+ window.document.title = destHead.ownerDocument.title;
122
+ if (destRoot.lang && destRoot.lang !== window.document.documentElement.lang) {
123
+ window.document.documentElement.lang = destRoot.lang;
124
+ }
125
+ }
126
+ /**
127
+ * Replace the live `<body>` children with `destBody`'s children, adopted via
128
+ * `document.importNode`. New element instances replace the old ones — a child
129
+ * replacement, not an in-place patch — so the destination's island is a fresh
130
+ * node. Any `<script type="module">` rides along inert: a script inserted via
131
+ * the DOM never executes on its own, which is why `importPageModules` imports it
132
+ * explicitly. (`destBody` is typed `HTMLElement` because `Document.body` is.)
133
+ */
134
+ function swapBody(destBody) {
135
+ const adopted = [...destBody.childNodes].map((node) => window.document.importNode(node, true));
136
+ window.document.body.replaceChildren(...adopted);
137
+ }
138
+ /** Matches each inline `import "<spec>";` statement, capturing the specifier. */
139
+ const IMPORT_STATEMENT = /import\s+["']([^"']+)["']\s*;?/g;
140
+ /**
141
+ * Extract every `import "<spec>";` specifier from `root`'s
142
+ * `<script type="module">` elements and dynamically import each, awaiting all.
143
+ * The build emits inline `import` statements (not `src` attributes, matching
144
+ * src/ssg/rewrite.ts), and a script node inserted via the DOM never executes on
145
+ * its own, so the runtime imports the specifiers itself. The ES module cache
146
+ * dedupes chunks already loaded this session.
147
+ */
148
+ async function importPageModules(root) {
149
+ const specifiers = [];
150
+ for (const script of root.querySelectorAll('script[type="module"]')) {
151
+ for (const match of (script.textContent ?? "").matchAll(IMPORT_STATEMENT)) {
152
+ specifiers.push(match[1]);
153
+ }
154
+ }
155
+ await Promise.all(specifiers.map((spec) => import(__rewriteRelativeImportExtension(spec))));
156
+ }
157
+ /**
158
+ * The shared same-document core the Navigation API interceptor funnels into
159
+ * (design §2). Fetches the destination's built HTML, reconciles `<head>`, swaps
160
+ * `<body>` — inside a View Transition when one is available — imports the
161
+ * destination's page modules, hydrates, then fires `onNavigate`. Resolves when
162
+ * hydration has been scheduled and hooks have fired. If `fetchDocument` fell back
163
+ * to a full load (non-2xx or network error), it returns `null` and this aborts
164
+ * without reconciling, swapping, or firing hooks.
165
+ */
166
+ export async function navigate(url, options = {}) {
167
+ const target = new URL(url, window.location.href);
168
+ const destination = await fetchDocument(target);
169
+ if (destination === null)
170
+ return; // fell back to a full document load
171
+ reconcileHead(destination.head);
172
+ // Swap the body inside a same-document View Transition when the browser
173
+ // supports one AND has not already animated this navigation itself (design §2
174
+ // step 4). startViewTransition snapshots the live DOM, runs the swap callback
175
+ // to mutate it, then animates the before/after states; awaiting
176
+ // updateCallbackDone resumes once the swap has applied (the DOM is updated),
177
+ // before modules import and hydration run. Where startViewTransition is absent
178
+ // or the UA already transitioned (hasUAVisualTransition), the swap is applied
179
+ // directly — degraded to an abrupt replacement, never broken (design "Failure
180
+ // modes").
181
+ const doc = window.document;
182
+ const applySwap = () => swapBody(destination.body);
183
+ if (typeof doc.startViewTransition === "function" &&
184
+ !options.hasUAVisualTransition) {
185
+ await doc.startViewTransition(applySwap).updateCallbackDone;
186
+ }
187
+ else {
188
+ applySwap();
189
+ }
190
+ await importPageModules(window.document.body);
191
+ // Hydrate the swapped-in body: start() reads the destination #__hydration
192
+ // payload (placed by reconcileHead) and schedules each island's update().
193
+ start(window.document.body);
194
+ // Report the completed navigation. title reflects the reconciled <head>; url
195
+ // is the absolute target; type mirrors the navigation's entry kind (a "reload"
196
+ // or a direct navigate() with no event reports "push"). Hooks fire once each,
197
+ // in registration order.
198
+ const { navigationType } = options;
199
+ const context = {
200
+ url: target,
201
+ title: window.document.title,
202
+ type: navigationType === "push" ||
203
+ navigationType === "replace" ||
204
+ navigationType === "traverse"
205
+ ? navigationType
206
+ : "push",
207
+ };
208
+ for (const cb of navigateCallbacks)
209
+ cb(context);
210
+ }
211
+ /**
212
+ * Install the navigation interceptor. The Navigation API is the sole interception
213
+ * mechanism: it delivers one `navigate` event for every same-document candidate —
214
+ * link click, programmatic navigation, and back/forward — so a single listener
215
+ * replaces a click handler plus a `popstate` listener, and the API owns history
216
+ * and scroll restoration (so the core stays `history.pushState`-free).
217
+ *
218
+ * The Navigation API is Baseline as of Jan 2026; this project targets evergreen
219
+ * browsers. Where it is absent there is NO interception: links perform normal
220
+ * full-document navigations — degraded (no shared-runtime hydration) but never
221
+ * broken. That minimal alternative is the entire fallback.
222
+ *
223
+ * Called by `bootstrap()` after `start()` and before firing `onFirstLoad`. The
224
+ * listener is added only here, never at module top level, so importing this module
225
+ * stays side-effect-free. Types come from `@types/dom-navigation` (folded into
226
+ * lib.dom as of TS 6.0); see the tracking task in `docs/developer/TASKS.md`.
227
+ */
228
+ function installInterceptor() {
229
+ if (!("navigation" in window))
230
+ return;
231
+ window.navigation.addEventListener("navigate", (event) => {
232
+ // Decline (let the browser navigate natively) when the API cannot intercept
233
+ // — cross-origin, etc. — or for a hash-only, download, or non-GET (form)
234
+ // navigation. Form submissions are out of scope (design §2, Summary).
235
+ if (!event.canIntercept)
236
+ return;
237
+ if (event.hashChange)
238
+ return;
239
+ if (event.downloadRequest !== null)
240
+ return;
241
+ if (event.formData !== null)
242
+ return;
243
+ // Claim the navigation: run the shared core as the same-document transition.
244
+ // navigate() fetches, reconciles <head>, swaps <body> (inside a View
245
+ // Transition when the browser offers one and did not already animate this
246
+ // navigation), imports the page module, hydrates, and fires onNavigate. The
247
+ // API commits the history entry. hasUAVisualTransition and navigationType are
248
+ // threaded through because only this interception path sees the event.
249
+ const { url } = event.destination;
250
+ event.intercept({
251
+ handler: () => navigate(url, {
252
+ hasUAVisualTransition: event.hasUAVisualTransition,
253
+ navigationType: event.navigationType,
254
+ }),
255
+ });
256
+ });
257
+ }
258
+ /**
259
+ * Bootstrap route hydration for the initial document. Explicit entry — NOT run at
260
+ * import time (the module stays side-effect-free; tests and the M3 injected entry
261
+ * decide when this runs). On call it:
262
+ * 1. `start(window.document.body)` — hydrate the server-rendered initial islands
263
+ * in place (reads the initial `#__hydration` payload already in <head>).
264
+ * 2. `installInterceptor()` — register the Navigation API `navigate` listener to
265
+ * capture subsequent in-app navigations, between `start()` and `onFirstLoad`.
266
+ * 3. Build the first-load `NavigationContext`: `url` from `window.location.href`,
267
+ * `title` from `document.title`, `type: "first"`. Store it in `firstLoadContext`.
268
+ * 4. Fire every queued `onFirstLoad` callback once, in registration order.
269
+ * Invariant: `onFirstLoad` fires exactly once per bootstrap.
270
+ */
271
+ export async function bootstrap() {
272
+ // 1. Hydrate the server-rendered initial islands in place. start() reads the
273
+ // initial #__hydration payload already in <head> and schedules each update().
274
+ start(window.document.body);
275
+ // 2. Register the Navigation API interceptor so subsequent in-app navigations
276
+ // are captured. Between start() and firing onFirstLoad, per design §1.
277
+ installInterceptor();
278
+ // 3. Build and retain the first-load context (design §1). url is the initial
279
+ // location, title the current document title, type "first".
280
+ firstLoadContext = {
281
+ url: new URL(window.location.href),
282
+ title: window.document.title,
283
+ type: "first",
284
+ };
285
+ // 4. Fire every queued onFirstLoad callback once, in registration order, then
286
+ // clear the queue so the event fires exactly once per bootstrap. A callback
287
+ // registered AFTER this point fires immediately against firstLoadContext (see
288
+ // onFirstLoad), so a late registration never drops the initial event.
289
+ for (const cb of firstLoadCallbacks)
290
+ cb(firstLoadContext);
291
+ firstLoadCallbacks.length = 0;
292
+ }
@@ -0,0 +1,2 @@
1
+ import type { Observable } from "../observable/observable.ts";
2
+ export declare function O<E extends Element>(element: E, observable: Observable<Parameters<E["update"]>>): E;
@@ -0,0 +1,6 @@
1
+ export function O(element, observable) {
2
+ observable.subscribe((t) => {
3
+ element.update(...t);
4
+ });
5
+ return element;
6
+ }
@@ -0,0 +1,3 @@
1
+ import type { Option } from "../result.ts";
2
+ export declare function provide(items: Record<string, unknown>): void;
3
+ export declare function retrieve<T>(key: string): Option<T>;
@@ -0,0 +1,7 @@
1
+ let registry = {};
2
+ export function provide(items) {
3
+ registry = { ...registry, ...items };
4
+ }
5
+ export function retrieve(key) {
6
+ return registry[key];
7
+ }
@@ -0,0 +1,8 @@
1
+ export interface DocumentOptions {
2
+ body: Node | Node[];
3
+ head?: Node | Node[];
4
+ lang?: string;
5
+ doctype?: string;
6
+ }
7
+ export declare function renderToString(node: Node | Node[]): string;
8
+ export declare function renderDocument({ body, head, lang, doctype, }: DocumentOptions): string;
@@ -0,0 +1,28 @@
1
+ import { XHTML_NAMESPACE_URI } from "./dom.js";
2
+ export function renderToString(node) {
3
+ if (Array.isArray(node))
4
+ return node.map(renderToString).join("");
5
+ if (node.nodeType === 1) {
6
+ const el = node;
7
+ const html = el.outerHTML;
8
+ const ns = el.namespaceURI;
9
+ if (ns && ns !== XHTML_NAMESPACE_URI && !html.includes("xmlns=")) {
10
+ return html.replace(/^<([^\s>]+)/, `<$1 xmlns="${ns}"`);
11
+ }
12
+ return html;
13
+ }
14
+ if (node.nodeType === 11)
15
+ return Array.from(node.childNodes).map(renderToString).join("");
16
+ if (node.nodeType === 3) {
17
+ return (node.textContent ?? "")
18
+ .replace(/&/g, "&amp;")
19
+ .replace(/</g, "&lt;")
20
+ .replace(/>/g, "&gt;");
21
+ }
22
+ return "";
23
+ }
24
+ export function renderDocument({ body, head, lang = "en", doctype = "<!doctype html>", }) {
25
+ const headStr = head != null ? renderToString(head) : "";
26
+ const bodyStr = renderToString(body);
27
+ return `${doctype}<html lang="${lang}"><head>${headStr}</head><body>${bodyStr}</body></html>`;
28
+ }
@@ -0,0 +1,6 @@
1
+ export interface Link {
2
+ href: string;
3
+ link: string;
4
+ target: () => Node;
5
+ }
6
+ export declare const link: ({ href, link }: Link) => HTMLAnchorElement;
@@ -0,0 +1,3 @@
1
+ import { a } from "../html.js";
2
+ import { Router } from "./router.js";
3
+ export const link = ({ href, link }) => a({ href: Router.href(href), events: { click: Router.navigate(href) } }, link);
@@ -0,0 +1,13 @@
1
+ import type { DOMElement } from "../dom.ts";
2
+ import type { Link } from "./link.ts";
3
+ export interface Router {
4
+ current?: string;
5
+ navigate: (url: string) => (event: Event) => void;
6
+ (target: DOMElement): DOMElement;
7
+ }
8
+ export declare const Router: {
9
+ local(links: Link[], index: string): Router;
10
+ for(links: Link[], index: string, setGlobalRouter?: boolean): Router;
11
+ href(link: string): string;
12
+ navigate(href: string): (event: Event) => void;
13
+ };
@@ -0,0 +1,52 @@
1
+ const baseURI = `${window.document.baseURI}`;
2
+ const normalizeHref = () => {
3
+ return `${location.href}/` === baseURI ? baseURI : location.href;
4
+ };
5
+ let globalRouter;
6
+ export const Router = {
7
+ local(links, index) {
8
+ return Router.for(links, index, false);
9
+ },
10
+ for(links, index, setGlobalRouter = true) {
11
+ let target;
12
+ const partialRouter = (t) => {
13
+ target = t;
14
+ const href = normalizeHref();
15
+ const route = href === baseURI ? baseURI + index : href;
16
+ doNavigate(route);
17
+ window.addEventListener("popstate", () => {
18
+ doNavigate(location.href);
19
+ });
20
+ return target;
21
+ };
22
+ const doNavigate = (link) => {
23
+ link = link.replace(baseURI, "") || index;
24
+ if (link === partialRouter.current) {
25
+ return false;
26
+ }
27
+ partialRouter.current = link;
28
+ target.update((links.find(({ href }) => link.endsWith(href))?.target ??
29
+ (() => undefined))());
30
+ return true;
31
+ };
32
+ const navigate = (url) => {
33
+ return (event) => {
34
+ event.preventDefault();
35
+ if (doNavigate(url || index)) {
36
+ history.pushState(null, "", url);
37
+ }
38
+ };
39
+ };
40
+ partialRouter.navigate = navigate;
41
+ if (setGlobalRouter) {
42
+ globalRouter = partialRouter;
43
+ }
44
+ return partialRouter;
45
+ },
46
+ href(link) {
47
+ return `${baseURI}${link.replace(/^\//, "")}`;
48
+ },
49
+ navigate(href) {
50
+ return globalRouter?.navigate(href);
51
+ },
52
+ };
@@ -0,0 +1,64 @@
1
+ import { type DenormAttrs, type DenormChildren } from "./dom.ts";
2
+ export declare const a: (attrs?: DenormAttrs<SVGAElement>, ...children: DenormChildren[]) => Element;
3
+ export declare const animate: (attrs?: DenormAttrs<SVGAnimateElement>, ...children: DenormChildren[]) => Element;
4
+ export declare const animateMotion: (attrs?: DenormAttrs<SVGAnimateMotionElement>, ...children: DenormChildren[]) => Element;
5
+ export declare const animateTransform: (attrs?: DenormAttrs<SVGAnimateTransformElement>, ...children: DenormChildren[]) => Element;
6
+ export declare const circle: (attrs?: DenormAttrs<SVGCircleElement>, ...children: DenormChildren[]) => Element;
7
+ export declare const clipPath: (attrs?: DenormAttrs<SVGClipPathElement>, ...children: DenormChildren[]) => Element;
8
+ export declare const defs: (attrs?: DenormAttrs<SVGDefsElement>, ...children: DenormChildren[]) => Element;
9
+ export declare const desc: (attrs?: DenormAttrs<SVGDescElement>, ...children: DenormChildren[]) => Element;
10
+ export declare const ellipse: (attrs?: DenormAttrs<SVGEllipseElement>, ...children: DenormChildren[]) => Element;
11
+ export declare const feBlend: (attrs?: DenormAttrs<SVGFEBlendElement>, ...children: DenormChildren[]) => Element;
12
+ export declare const feColorMatrix: (attrs?: DenormAttrs<SVGFEColorMatrixElement>, ...children: DenormChildren[]) => Element;
13
+ export declare const feComponentTransfer: (attrs?: DenormAttrs<SVGFEComponentTransferElement>, ...children: DenormChildren[]) => Element;
14
+ export declare const feComposite: (attrs?: DenormAttrs<SVGFECompositeElement>, ...children: DenormChildren[]) => Element;
15
+ export declare const feConvolveMatrix: (attrs?: DenormAttrs<SVGFEConvolveMatrixElement>, ...children: DenormChildren[]) => Element;
16
+ export declare const feDiffuseLighting: (attrs?: DenormAttrs<SVGFEDiffuseLightingElement>, ...children: DenormChildren[]) => Element;
17
+ export declare const feDisplacementMap: (attrs?: DenormAttrs<SVGFEDisplacementMapElement>, ...children: DenormChildren[]) => Element;
18
+ export declare const feDistantLight: (attrs?: DenormAttrs<SVGFEDistantLightElement>, ...children: DenormChildren[]) => Element;
19
+ export declare const feDropShadow: (attrs?: DenormAttrs<SVGFEDropShadowElement>, ...children: DenormChildren[]) => Element;
20
+ export declare const feFlood: (attrs?: DenormAttrs<SVGFEFloodElement>, ...children: DenormChildren[]) => Element;
21
+ export declare const feFuncA: (attrs?: DenormAttrs<SVGFEFuncAElement>, ...children: DenormChildren[]) => Element;
22
+ export declare const feFuncB: (attrs?: DenormAttrs<SVGFEFuncBElement>, ...children: DenormChildren[]) => Element;
23
+ export declare const feFuncG: (attrs?: DenormAttrs<SVGFEFuncGElement>, ...children: DenormChildren[]) => Element;
24
+ export declare const feFuncR: (attrs?: DenormAttrs<SVGFEFuncRElement>, ...children: DenormChildren[]) => Element;
25
+ export declare const feGaussianBlur: (attrs?: DenormAttrs<SVGFEGaussianBlurElement>, ...children: DenormChildren[]) => Element;
26
+ export declare const feImage: (attrs?: DenormAttrs<SVGFEImageElement>, ...children: DenormChildren[]) => Element;
27
+ export declare const feMerge: (attrs?: DenormAttrs<SVGFEMergeElement>, ...children: DenormChildren[]) => Element;
28
+ export declare const feMergeNode: (attrs?: DenormAttrs<SVGFEMergeNodeElement>, ...children: DenormChildren[]) => Element;
29
+ export declare const feMorphology: (attrs?: DenormAttrs<SVGFEMorphologyElement>, ...children: DenormChildren[]) => Element;
30
+ export declare const feOffset: (attrs?: DenormAttrs<SVGFEOffsetElement>, ...children: DenormChildren[]) => Element;
31
+ export declare const fePointLight: (attrs?: DenormAttrs<SVGFEPointLightElement>, ...children: DenormChildren[]) => Element;
32
+ export declare const feSpecularLighting: (attrs?: DenormAttrs<SVGFESpecularLightingElement>, ...children: DenormChildren[]) => Element;
33
+ export declare const feSpotLight: (attrs?: DenormAttrs<SVGFESpotLightElement>, ...children: DenormChildren[]) => Element;
34
+ export declare const feTile: (attrs?: DenormAttrs<SVGFETileElement>, ...children: DenormChildren[]) => Element;
35
+ export declare const feTurbulence: (attrs?: DenormAttrs<SVGFETurbulenceElement>, ...children: DenormChildren[]) => Element;
36
+ export declare const filter: (attrs?: DenormAttrs<SVGFilterElement>, ...children: DenormChildren[]) => Element;
37
+ export declare const foreignObject: (attrs?: DenormAttrs<SVGForeignObjectElement>, ...children: DenormChildren[]) => Element;
38
+ export declare const g: (attrs?: DenormAttrs<SVGGElement>, ...children: DenormChildren[]) => Element;
39
+ export declare const image: (attrs?: DenormAttrs<SVGImageElement>, ...children: DenormChildren[]) => Element;
40
+ export declare const line: (attrs?: DenormAttrs<SVGLineElement>, ...children: DenormChildren[]) => Element;
41
+ export declare const linearGradient: (attrs?: DenormAttrs<SVGLinearGradientElement>, ...children: DenormChildren[]) => Element;
42
+ export declare const marker: (attrs?: DenormAttrs<SVGMarkerElement>, ...children: DenormChildren[]) => Element;
43
+ export declare const mask: (attrs?: DenormAttrs<SVGMaskElement>, ...children: DenormChildren[]) => Element;
44
+ export declare const metadata: (attrs?: DenormAttrs<SVGMetadataElement>, ...children: DenormChildren[]) => Element;
45
+ export declare const mpath: (attrs?: DenormAttrs<SVGMPathElement>, ...children: DenormChildren[]) => Element;
46
+ export declare const path: (attrs?: DenormAttrs<SVGPathElement>, ...children: DenormChildren[]) => Element;
47
+ export declare const pattern: (attrs?: DenormAttrs<SVGPatternElement>, ...children: DenormChildren[]) => Element;
48
+ export declare const polygon: (attrs?: DenormAttrs<SVGPolygonElement>, ...children: DenormChildren[]) => Element;
49
+ export declare const polyline: (attrs?: DenormAttrs<SVGPolylineElement>, ...children: DenormChildren[]) => Element;
50
+ export declare const radialGradient: (attrs?: DenormAttrs<SVGRadialGradientElement>, ...children: DenormChildren[]) => Element;
51
+ export declare const rect: (attrs?: DenormAttrs<SVGRectElement>, ...children: DenormChildren[]) => Element;
52
+ export declare const script: (attrs?: DenormAttrs<SVGScriptElement>, ...children: DenormChildren[]) => Element;
53
+ export declare const set: (attrs?: DenormAttrs<SVGSetElement>, ...children: DenormChildren[]) => Element;
54
+ export declare const stop: (attrs?: DenormAttrs<SVGStopElement>, ...children: DenormChildren[]) => Element;
55
+ export declare const style: (attrs?: DenormAttrs<SVGStyleElement>, ...children: DenormChildren[]) => Element;
56
+ export declare const svg: (attrs?: DenormAttrs<SVGSVGElement>, ...children: DenormChildren[]) => Element;
57
+ export declare const svgswitch: (attrs?: DenormAttrs<SVGSwitchElement>, ...children: DenormChildren[]) => Element;
58
+ export declare const symbol: (attrs?: DenormAttrs<SVGSymbolElement>, ...children: DenormChildren[]) => Element;
59
+ export declare const text: (attrs?: DenormAttrs<SVGTextElement>, ...children: DenormChildren[]) => Element;
60
+ export declare const textPath: (attrs?: DenormAttrs<SVGTextPathElement>, ...children: DenormChildren[]) => Element;
61
+ export declare const title: (attrs?: DenormAttrs<SVGTitleElement>, ...children: DenormChildren[]) => Element;
62
+ export declare const tspan: (attrs?: DenormAttrs<SVGTSpanElement>, ...children: DenormChildren[]) => Element;
63
+ export declare const use: (attrs?: DenormAttrs<SVGUseElement>, ...children: DenormChildren[]) => Element;
64
+ export declare const view: (attrs?: DenormAttrs<SVGViewElement>, ...children: DenormChildren[]) => Element;
@@ -1,18 +1,5 @@
1
- import {
2
- type DenormAttrs,
3
- type DenormChildren,
4
- SVG_NAMESPACE_URI,
5
- up,
6
- } from "./dom.js";
7
-
8
- const makeSVGElement =
9
- <K extends keyof SVGElementTagNameMap>(name: K) =>
10
- (
11
- attrs?: DenormAttrs<SVGElementTagNameMap[K]>,
12
- ...children: DenormChildren[]
13
- ) =>
14
- up(document.createElementNS(SVG_NAMESPACE_URI, name), attrs, ...children);
15
-
1
+ import { SVG_NAMESPACE_URI, up, } from "./dom.js";
2
+ const makeSVGElement = (name) => (attrs, ...children) => up(window.document.createElementNS(SVG_NAMESPACE_URI, name), attrs, ...children);
16
3
  export const a = makeSVGElement("a");
17
4
  export const animate = makeSVGElement("animate");
18
5
  export const animateMotion = makeSVGElement("animateMotion");