@lolyjs/core 0.1.0-alpha.9 → 0.2.0-alpha.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.
package/dist/runtime.js CHANGED
@@ -7,8 +7,7 @@ var APP_CONTAINER_ID = "__app";
7
7
 
8
8
  // modules/runtime/client/window-data.ts
9
9
  function getWindowData() {
10
- var _a;
11
- return (_a = window[WINDOW_DATA_KEY]) != null ? _a : null;
10
+ return window[WINDOW_DATA_KEY] ?? null;
12
11
  }
13
12
  function setWindowData(data) {
14
13
  window[WINDOW_DATA_KEY] = data;
@@ -21,8 +20,7 @@ function setWindowData(data) {
21
20
  }
22
21
  }
23
22
  function getCurrentTheme() {
24
- var _a, _b;
25
- return (_b = (_a = getWindowData()) == null ? void 0 : _a.theme) != null ? _b : null;
23
+ return getWindowData()?.theme ?? null;
26
24
  }
27
25
 
28
26
  // modules/runtime/client/route-matcher.ts
@@ -90,11 +88,6 @@ import { useEffect, useState, useRef } from "react";
90
88
  // modules/runtime/client/RouterView.tsx
91
89
  import { jsx } from "react/jsx-runtime";
92
90
  function RouterView({ state }) {
93
- console.log("[loly:RouterView] Rendering", {
94
- url: state.url,
95
- hasRoute: !!state.route,
96
- hasComponents: !!state.components
97
- });
98
91
  if (!state.route) {
99
92
  if (state.components === null) {
100
93
  return null;
@@ -106,11 +99,6 @@ function RouterView({ state }) {
106
99
  }
107
100
  const { Page, layouts } = state.components;
108
101
  const { params, props } = state;
109
- console.log("[loly:RouterView] Creating page element", {
110
- hasPage: !!Page,
111
- layoutsCount: layouts.length,
112
- paramsKeys: Object.keys(params)
113
- });
114
102
  let element = /* @__PURE__ */ jsx(Page, { params, ...props });
115
103
  const layoutChain = layouts.slice().reverse();
116
104
  for (const Layout of layoutChain) {
@@ -179,7 +167,7 @@ function evictOldest() {
179
167
  }
180
168
  function setCacheEntry(key, entry) {
181
169
  const existingEntry = dataCache.get(key);
182
- const wasFulfilled = (existingEntry == null ? void 0 : existingEntry.status) === "fulfilled";
170
+ const wasFulfilled = existingEntry?.status === "fulfilled";
183
171
  dataCache.set(key, entry);
184
172
  if (entry.status === "fulfilled") {
185
173
  if (!wasFulfilled) {
@@ -233,7 +221,7 @@ async function fetchRouteDataOnce(url) {
233
221
  }
234
222
  async function getRouteData(url, options) {
235
223
  const key = buildDataUrl(url);
236
- if (options == null ? void 0 : options.revalidate) {
224
+ if (options?.revalidate) {
237
225
  deleteCacheEntry(key);
238
226
  }
239
227
  const entry = dataCache.get(key);
@@ -260,7 +248,6 @@ async function getRouteData(url, options) {
260
248
 
261
249
  // modules/runtime/client/navigation.ts
262
250
  async function handleErrorRoute(nextUrl, json, errorRoute, setState) {
263
- var _a;
264
251
  try {
265
252
  const components = await errorRoute.load();
266
253
  let theme = "light";
@@ -287,7 +274,7 @@ async function handleErrorRoute(nextUrl, json, errorRoute, setState) {
287
274
  pathname: nextUrl,
288
275
  params: json.params || {},
289
276
  props: errorProps,
290
- metadata: (_a = json.metadata) != null ? _a : null,
277
+ metadata: json.metadata ?? null,
291
278
  theme,
292
279
  notFound: false,
293
280
  error: true
@@ -311,7 +298,6 @@ async function handleErrorRoute(nextUrl, json, errorRoute, setState) {
311
298
  }
312
299
  }
313
300
  async function handleNotFoundRoute(nextUrl, json, notFoundRoute, setState) {
314
- var _a, _b;
315
301
  let theme = "light";
316
302
  if (typeof document !== "undefined") {
317
303
  const cookieMatch = document.cookie.match(/theme=([^;]+)/);
@@ -327,14 +313,14 @@ async function handleNotFoundRoute(nextUrl, json, notFoundRoute, setState) {
327
313
  theme = json.theme;
328
314
  }
329
315
  const notFoundProps = {
330
- ...(_a = json.props) != null ? _a : {},
316
+ ...json.props ?? {},
331
317
  theme
332
318
  };
333
319
  const windowData = {
334
320
  pathname: nextUrl,
335
321
  params: {},
336
322
  props: notFoundProps,
337
- metadata: (_b = json.metadata) != null ? _b : null,
323
+ metadata: json.metadata ?? null,
338
324
  theme,
339
325
  notFound: true,
340
326
  error: false
@@ -360,8 +346,7 @@ async function handleNotFoundRoute(nextUrl, json, notFoundRoute, setState) {
360
346
  }
361
347
  }
362
348
  async function handleNormalRoute(nextUrl, json, routes, setState) {
363
- var _a, _b, _c;
364
- applyMetadata((_a = json.metadata) != null ? _a : null);
349
+ applyMetadata(json.metadata ?? null);
365
350
  let theme = "light";
366
351
  if (typeof document !== "undefined") {
367
352
  const cookieMatch = document.cookie.match(/theme=([^;]+)/);
@@ -379,7 +364,7 @@ async function handleNormalRoute(nextUrl, json, routes, setState) {
379
364
  theme = json.theme;
380
365
  }
381
366
  const newProps = {
382
- ...(_b = json.props) != null ? _b : {},
367
+ ...json.props ?? {},
383
368
  theme
384
369
  // Always include theme
385
370
  };
@@ -392,7 +377,7 @@ async function handleNormalRoute(nextUrl, json, routes, setState) {
392
377
  pathname: nextUrl,
393
378
  params: matched.params,
394
379
  props: newProps,
395
- metadata: (_c = json.metadata) != null ? _c : null,
380
+ metadata: json.metadata ?? null,
396
381
  theme,
397
382
  notFound: false,
398
383
  error: false
@@ -416,10 +401,9 @@ async function navigate(nextUrl, handlers, options) {
416
401
  const { setState, routes, notFoundRoute, errorRoute } = handlers;
417
402
  try {
418
403
  const { ok, json } = await getRouteData(nextUrl, {
419
- revalidate: options == null ? void 0 : options.revalidate
404
+ revalidate: options?.revalidate
420
405
  });
421
406
  if (json && json.error) {
422
- console.log("[client] Error detected in response:", json);
423
407
  if (errorRoute) {
424
408
  const handled = await handleErrorRoute(
425
409
  nextUrl,
@@ -461,50 +445,23 @@ async function navigate(nextUrl, handlers, options) {
461
445
  }
462
446
  function createClickHandler(navigate2) {
463
447
  return function handleClick(ev) {
464
- const target = ev.target;
465
- const tagName = (target == null ? void 0 : target.tagName.toLowerCase()) || "unknown";
466
- console.log("[loly:click] Click event received", {
467
- type: ev.type,
468
- tagName,
469
- target: target == null ? void 0 : target.tagName,
470
- defaultPrevented: ev.defaultPrevented,
471
- button: ev.button,
472
- clientX: ev.clientX,
473
- clientY: ev.clientY
474
- });
475
448
  try {
476
- if (ev.defaultPrevented) {
477
- console.log("[loly:click] Event already prevented, skipping");
478
- return;
479
- }
480
- if (ev.type !== "click") {
481
- console.log("[loly:click] Not a click event, skipping", { type: ev.type });
482
- return;
483
- }
484
- if (ev.button !== 0) {
485
- console.log("[loly:click] Not left button, skipping", { button: ev.button });
486
- return;
487
- }
488
- if (ev.metaKey || ev.ctrlKey || ev.shiftKey || ev.altKey) {
489
- console.log("[loly:click] Modifier keys pressed, skipping");
490
- return;
491
- }
449
+ if (ev.defaultPrevented) return;
450
+ if (ev.type !== "click") return;
451
+ if (ev.button !== 0) return;
452
+ if (ev.metaKey || ev.ctrlKey || ev.shiftKey || ev.altKey) return;
453
+ const target = ev.target;
492
454
  if (ev.clientX === 0 && ev.clientY === 0 && ev.detail === 0) {
493
455
  if (target) {
494
- const tagName3 = target.tagName.toLowerCase();
495
- if (tagName3 === "input" || tagName3 === "textarea" || tagName3 === "button" || tagName3 === "select") {
496
- console.log("[loly:click] Synthetic event on interactive element, skipping", { tagName: tagName3 });
456
+ const tagName2 = target.tagName.toLowerCase();
457
+ if (tagName2 === "input" || tagName2 === "textarea" || tagName2 === "button" || tagName2 === "select") {
497
458
  return;
498
459
  }
499
460
  }
500
461
  }
501
- if (!target) {
502
- console.log("[loly:click] No target, skipping");
503
- return;
504
- }
505
- const tagName2 = target.tagName.toLowerCase();
506
- if (tagName2 === "input" || tagName2 === "textarea" || tagName2 === "button" || tagName2 === "select" || target.isContentEditable || target.getAttribute("contenteditable") === "true") {
507
- console.log("[loly:click] Target is interactive element, skipping", { tagName: tagName2 });
462
+ if (!target) return;
463
+ const tagName = target.tagName.toLowerCase();
464
+ if (tagName === "input" || tagName === "textarea" || tagName === "button" || tagName === "select" || target.isContentEditable || target.getAttribute("contenteditable") === "true") {
508
465
  return;
509
466
  }
510
467
  const interactiveParent = target.closest("input, textarea, button, select, [contenteditable], label");
@@ -512,60 +469,29 @@ function createClickHandler(navigate2) {
512
469
  if (interactiveParent.tagName.toLowerCase() === "label") {
513
470
  const label = interactiveParent;
514
471
  if (label.control) {
515
- console.log("[loly:click] Inside label with control, skipping");
516
472
  return;
517
473
  }
518
474
  } else {
519
- console.log("[loly:click] Inside interactive parent, skipping", {
520
- parentTag: interactiveParent.tagName.toLowerCase()
521
- });
522
475
  return;
523
476
  }
524
477
  }
525
478
  const anchor = target.closest("a[href]");
526
- if (!anchor) {
527
- console.log("[loly:click] No anchor found, skipping");
528
- return;
529
- }
530
- console.log("[loly:click] Anchor found, processing navigation", {
531
- href: anchor.getAttribute("href")
532
- });
479
+ if (!anchor) return;
533
480
  const href = anchor.getAttribute("href");
534
- if (!href) {
535
- console.log("[loly:click] No href attribute, skipping");
536
- return;
537
- }
538
- if (href.startsWith("#")) {
539
- console.log("[loly:click] Hash link, skipping");
540
- return;
541
- }
481
+ if (!href) return;
482
+ if (href.startsWith("#")) return;
542
483
  const url = new URL(href, window.location.href);
543
- if (url.origin !== window.location.origin) {
544
- console.log("[loly:click] External link, skipping", { origin: url.origin });
545
- return;
546
- }
547
- if (anchor.target && anchor.target !== "_self") {
548
- console.log("[loly:click] Link has target, skipping", { target: anchor.target });
549
- return;
550
- }
484
+ if (url.origin !== window.location.origin) return;
485
+ if (anchor.target && anchor.target !== "_self") return;
551
486
  ev.preventDefault();
552
- console.log("[loly:click] Prevented default, navigating");
553
487
  const nextUrl = url.pathname + url.search;
554
488
  const currentUrl = window.location.pathname + window.location.search;
555
- if (nextUrl === currentUrl) {
556
- console.log("[loly:click] Same URL, skipping", { nextUrl });
557
- return;
558
- }
489
+ if (nextUrl === currentUrl) return;
559
490
  const shouldRevalidate = anchor.hasAttribute("data-revalidate") && anchor.getAttribute("data-revalidate") !== "false";
560
- console.log("[loly:click] Pushing state and navigating", {
561
- nextUrl,
562
- currentUrl,
563
- shouldRevalidate
564
- });
565
491
  window.history.pushState({}, "", nextUrl);
566
492
  navigate2(nextUrl, shouldRevalidate ? { revalidate: true } : void 0);
567
493
  } catch (error) {
568
- console.error("[loly:click] Error in click handler:", error);
494
+ console.error("[navigation] Error in click handler:", error);
569
495
  }
570
496
  };
571
497
  }
@@ -584,10 +510,6 @@ function AppShell({
584
510
  notFoundRoute,
585
511
  errorRoute
586
512
  }) {
587
- console.log("[loly:AppShell] Component rendering", {
588
- url: initialState.url,
589
- hasRoute: !!initialState.route
590
- });
591
513
  const [state, setState] = useState(initialState);
592
514
  const handlersRef = useRef({
593
515
  setState,
@@ -596,11 +518,6 @@ function AppShell({
596
518
  errorRoute
597
519
  });
598
520
  useEffect(() => {
599
- console.log("[loly:AppShell] Updating handlersRef", {
600
- routesCount: routes.length,
601
- hasNotFound: !!notFoundRoute,
602
- hasError: !!errorRoute
603
- });
604
521
  handlersRef.current = {
605
522
  setState,
606
523
  routes,
@@ -609,34 +526,16 @@ function AppShell({
609
526
  };
610
527
  }, [routes, notFoundRoute, errorRoute]);
611
528
  useEffect(() => {
612
- const effectId = Math.random().toString(36).substring(7);
613
- console.log("[loly:AppShell] Setting up event listeners", { effectId });
614
529
  let isMounted = true;
615
- let listenerCount = 0;
616
530
  async function handleNavigate(nextUrl, options) {
617
- if (!isMounted) {
618
- console.warn("[loly:AppShell] navigate called but component is unmounted");
619
- return;
620
- }
621
- console.log("[loly:AppShell] Navigating to", nextUrl, options);
531
+ if (!isMounted) return;
622
532
  await navigate(nextUrl, handlersRef.current, options);
623
533
  }
624
534
  const handleClick = createClickHandler(handleNavigate);
625
535
  const handlePopState = createPopStateHandler(handleNavigate);
626
536
  window.addEventListener("click", handleClick, false);
627
537
  window.addEventListener("popstate", handlePopState, false);
628
- listenerCount = 2;
629
- console.log("[loly:AppShell] Event listeners added", {
630
- clickListener: true,
631
- popStateListener: true,
632
- totalListeners: listenerCount
633
- });
634
538
  return () => {
635
- console.log("[loly:AppShell] Cleaning up event listeners", {
636
- effectId,
637
- wasMounted: isMounted,
638
- listenersToRemove: listenerCount
639
- });
640
539
  isMounted = false;
641
540
  window.removeEventListener("click", handleClick, false);
642
541
  window.removeEventListener("popstate", handlePopState, false);
@@ -651,17 +550,15 @@ function AppShell({
651
550
 
652
551
  // modules/runtime/client/bootstrap.tsx
653
552
  import { jsx as jsx3 } from "react/jsx-runtime";
654
- var __loly_hydrated = false;
655
553
  async function loadInitialRoute(initialUrl, initialData, routes, notFoundRoute, errorRoute) {
656
- var _a, _b;
657
- const isInitialNotFound = (initialData == null ? void 0 : initialData.notFound) === true;
658
- const isInitialError = (initialData == null ? void 0 : initialData.error) === true;
554
+ const isInitialNotFound = initialData?.notFound === true;
555
+ const isInitialError = initialData?.error === true;
659
556
  let initialRoute = null;
660
557
  let initialParams = {};
661
558
  let initialComponents = null;
662
559
  if (isInitialError && errorRoute) {
663
560
  initialRoute = errorRoute;
664
- initialParams = (_a = initialData == null ? void 0 : initialData.params) != null ? _a : {};
561
+ initialParams = initialData?.params ?? {};
665
562
  initialComponents = await errorRoute.load();
666
563
  } else if (isInitialNotFound && notFoundRoute) {
667
564
  initialRoute = notFoundRoute;
@@ -679,7 +576,7 @@ async function loadInitialRoute(initialUrl, initialData, routes, notFoundRoute,
679
576
  initialComponents = await notFoundRoute.load();
680
577
  } else {
681
578
  console.warn(
682
- `[client] No route match found for ${initialUrl}. Routes:`,
579
+ `[client] No route match found for ${initialUrl}. Available routes:`,
683
580
  routes.map((r) => r.pattern)
684
581
  );
685
582
  }
@@ -689,32 +586,65 @@ async function loadInitialRoute(initialUrl, initialData, routes, notFoundRoute,
689
586
  route: initialRoute,
690
587
  params: initialParams,
691
588
  components: initialComponents,
692
- props: (_b = initialData == null ? void 0 : initialData.props) != null ? _b : {}
589
+ props: initialData?.props ?? {}
693
590
  };
694
591
  }
695
- function bootstrapClient(routes, notFoundRoute, errorRoute = null) {
696
- if (__loly_hydrated) {
697
- console.warn("[loly:runtime] bootstrapClient SKIPPED (already hydrated)");
592
+ function setupHotReload() {
593
+ const nodeEnv = typeof process !== "undefined" && process?.env?.NODE_ENV || "production";
594
+ const isDev = nodeEnv !== "production";
595
+ if (!isDev) {
698
596
  return;
699
597
  }
700
- __loly_hydrated = true;
701
- console.log("[loly:runtime] bootstrapClient START");
598
+ try {
599
+ console.log("[hot-reload] Attempting to connect to /__fw/hot...");
600
+ const eventSource = new EventSource("/__fw/hot");
601
+ let reloadTimeout = null;
602
+ eventSource.addEventListener("message", (event) => {
603
+ const data = event.data;
604
+ if (data && data.startsWith("reload:")) {
605
+ const filePath = data.slice(7);
606
+ console.log(`[hot-reload] File changed: ${filePath}`);
607
+ if (reloadTimeout) {
608
+ clearTimeout(reloadTimeout);
609
+ }
610
+ reloadTimeout = setTimeout(() => {
611
+ console.log("[hot-reload] Reloading page...");
612
+ window.location.reload();
613
+ }, 500);
614
+ }
615
+ });
616
+ eventSource.addEventListener("ping", () => {
617
+ console.log("[hot-reload] \u2713 Connected to hot reload server");
618
+ });
619
+ eventSource.onopen = () => {
620
+ console.log("[hot-reload] \u2713 SSE connection opened");
621
+ };
622
+ eventSource.onerror = (error) => {
623
+ const states = ["CONNECTING", "OPEN", "CLOSED"];
624
+ const state = states[eventSource.readyState] || "UNKNOWN";
625
+ if (eventSource.readyState === EventSource.CONNECTING) {
626
+ console.log("[hot-reload] Connecting...");
627
+ } else if (eventSource.readyState === EventSource.OPEN) {
628
+ console.warn("[hot-reload] Connection error (but connection is open):", error);
629
+ } else {
630
+ console.log("[hot-reload] Connection closed (readyState:", state, ")");
631
+ }
632
+ };
633
+ } catch (error) {
634
+ console.log("[hot-reload] EventSource not supported or error:", error);
635
+ }
636
+ }
637
+ function bootstrapClient(routes, notFoundRoute, errorRoute = null) {
638
+ console.log("[client] Bootstrap starting, setting up hot reload...");
639
+ setupHotReload();
702
640
  (async function bootstrap() {
703
641
  const container = document.getElementById(APP_CONTAINER_ID);
704
642
  const initialData = getWindowData();
705
- console.log("[loly:runtime] bootstrap starting", {
706
- hasContainer: !!container,
707
- hasInitialData: !!initialData,
708
- containerId: APP_CONTAINER_ID
709
- });
710
643
  if (!container) {
711
- console.error(
712
- `[loly:runtime] Container #${APP_CONTAINER_ID} not found.`
713
- );
644
+ console.error(`Container #${APP_CONTAINER_ID} not found for hydration`);
714
645
  return;
715
646
  }
716
647
  const initialUrl = window.location.pathname + window.location.search;
717
- console.log("[loly:runtime] Loading initial route", { initialUrl });
718
648
  try {
719
649
  const initialState = await loadInitialRoute(
720
650
  initialUrl,
@@ -723,15 +653,9 @@ function bootstrapClient(routes, notFoundRoute, errorRoute = null) {
723
653
  notFoundRoute,
724
654
  errorRoute
725
655
  );
726
- console.log("[loly:runtime] Initial route loaded", {
727
- url: initialState.url,
728
- hasRoute: !!initialState.route,
729
- hasComponents: !!initialState.components
730
- });
731
- if (initialData == null ? void 0 : initialData.metadata) {
656
+ if (initialData?.metadata) {
732
657
  applyMetadata(initialData.metadata);
733
658
  }
734
- console.log("[loly:runtime] Hydrating React app");
735
659
  hydrateRoot(
736
660
  container,
737
661
  /* @__PURE__ */ jsx3(
@@ -744,9 +668,12 @@ function bootstrapClient(routes, notFoundRoute, errorRoute = null) {
744
668
  }
745
669
  )
746
670
  );
747
- console.log("[loly:runtime] Hydrated successfully");
748
671
  } catch (error) {
749
- console.error("[loly:runtime] Error during hydration:", error);
672
+ console.error(
673
+ "[client] Error loading initial route components for",
674
+ initialUrl,
675
+ error
676
+ );
750
677
  window.location.reload();
751
678
  }
752
679
  })();