@constela/runtime 0.19.6 → 1.0.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 (3) hide show
  1. package/dist/index.d.ts +484 -2
  2. package/dist/index.js +1929 -22
  3. package/package.json +5 -5
package/dist/index.js CHANGED
@@ -436,6 +436,272 @@ var SAFE_DATE_INSTANCE_METHODS = /* @__PURE__ */ new Set([
436
436
  "getSeconds",
437
437
  "getMilliseconds"
438
438
  ]);
439
+ function getCalendarDays(year, month) {
440
+ if (typeof year !== "number" || typeof month !== "number") {
441
+ return void 0;
442
+ }
443
+ if (month < 0 || month > 11) {
444
+ return void 0;
445
+ }
446
+ const days = [];
447
+ const firstDayOfMonth = new Date(year, month, 1);
448
+ const dayOfWeek = firstDayOfMonth.getDay();
449
+ const lastDayOfMonth = new Date(year, month + 1, 0);
450
+ const daysInMonth = lastDayOfMonth.getDate();
451
+ const prevMonthDays = dayOfWeek;
452
+ const prevMonthLastDay = new Date(year, month, 0);
453
+ const prevMonthDaysCount = prevMonthLastDay.getDate();
454
+ const prevMonth = month === 0 ? 11 : month - 1;
455
+ const prevYear = month === 0 ? year - 1 : year;
456
+ for (let i = prevMonthDays - 1; i >= 0; i--) {
457
+ days.push({
458
+ date: prevMonthDaysCount - i,
459
+ month: prevMonth,
460
+ year: prevYear,
461
+ isCurrentMonth: false
462
+ });
463
+ }
464
+ for (let i = 1; i <= daysInMonth; i++) {
465
+ days.push({
466
+ date: i,
467
+ month,
468
+ year,
469
+ isCurrentMonth: true
470
+ });
471
+ }
472
+ const totalDaysSoFar = days.length;
473
+ const totalWeeksNeeded = Math.ceil(totalDaysSoFar / 7);
474
+ const targetDays = totalWeeksNeeded * 7;
475
+ const nextMonth = month === 11 ? 0 : month + 1;
476
+ const nextYear = month === 11 ? year + 1 : year;
477
+ const nextMonthDaysNeeded = targetDays - totalDaysSoFar;
478
+ for (let i = 1; i <= nextMonthDaysNeeded; i++) {
479
+ days.push({
480
+ date: i,
481
+ month: nextMonth,
482
+ year: nextYear,
483
+ isCurrentMonth: false
484
+ });
485
+ }
486
+ return days;
487
+ }
488
+ function getWeekDays(locale) {
489
+ const effectiveLocale = typeof locale === "string" ? locale : "en-US";
490
+ const baseSunday = new Date(2024, 0, 7);
491
+ const weekDays = [];
492
+ try {
493
+ const formatter = new Intl.DateTimeFormat(effectiveLocale || "en-US", { weekday: "short" });
494
+ for (let i = 0; i < 7; i++) {
495
+ const date = new Date(baseSunday);
496
+ date.setDate(baseSunday.getDate() + i);
497
+ weekDays.push(formatter.format(date));
498
+ }
499
+ return weekDays;
500
+ } catch {
501
+ const fallbackFormatter = new Intl.DateTimeFormat("en-US", { weekday: "short" });
502
+ for (let i = 0; i < 7; i++) {
503
+ const date = new Date(baseSunday);
504
+ date.setDate(baseSunday.getDate() + i);
505
+ weekDays.push(fallbackFormatter.format(date));
506
+ }
507
+ return weekDays;
508
+ }
509
+ }
510
+ function getMonthName(month, locale) {
511
+ if (typeof month !== "number" || month < 0 || month > 11) {
512
+ return void 0;
513
+ }
514
+ const effectiveLocale = typeof locale === "string" ? locale : "en-US";
515
+ try {
516
+ const date = new Date(2024, month, 1);
517
+ const formatter = new Intl.DateTimeFormat(effectiveLocale, { month: "long" });
518
+ return formatter.format(date);
519
+ } catch {
520
+ const date = new Date(2024, month, 1);
521
+ const formatter = new Intl.DateTimeFormat("en-US", { month: "long" });
522
+ return formatter.format(date);
523
+ }
524
+ }
525
+ function formatDateISO(year, month, date) {
526
+ if (typeof year !== "number" || typeof month !== "number" || typeof date !== "number") {
527
+ return void 0;
528
+ }
529
+ const y2 = String(year).padStart(4, "0");
530
+ const m2 = String(month + 1).padStart(2, "0");
531
+ const d2 = String(date).padStart(2, "0");
532
+ return `${y2}-${m2}-${d2}`;
533
+ }
534
+ function sortBy(items, key2, direction) {
535
+ if (!Array.isArray(items)) {
536
+ return void 0;
537
+ }
538
+ if (typeof key2 !== "string") {
539
+ return void 0;
540
+ }
541
+ const forbiddenKeys = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
542
+ if (forbiddenKeys.has(key2)) {
543
+ return void 0;
544
+ }
545
+ const dir = direction === "desc" ? "desc" : "asc";
546
+ const sorted = [...items];
547
+ sorted.sort((a, b2) => {
548
+ const aVal = a != null && typeof a === "object" ? a[key2] : void 0;
549
+ const bVal = b2 != null && typeof b2 === "object" ? b2[key2] : void 0;
550
+ if (aVal == null && bVal == null) return 0;
551
+ if (aVal == null) return 1;
552
+ if (bVal == null) return -1;
553
+ let comparison = 0;
554
+ if (typeof aVal === "number" && typeof bVal === "number") {
555
+ comparison = aVal - bVal;
556
+ } else {
557
+ comparison = String(aVal).localeCompare(String(bVal));
558
+ }
559
+ return dir === "desc" ? -comparison : comparison;
560
+ });
561
+ return sorted;
562
+ }
563
+ function getPaginatedItems(items, page, pageSize) {
564
+ if (!Array.isArray(items)) {
565
+ return void 0;
566
+ }
567
+ if (typeof page !== "number" || page < 0) {
568
+ return void 0;
569
+ }
570
+ if (typeof pageSize !== "number" || pageSize <= 0) {
571
+ return void 0;
572
+ }
573
+ const start = page * pageSize;
574
+ const end = start + pageSize;
575
+ return items.slice(start, end);
576
+ }
577
+ function getTotalPages(itemCount, pageSize) {
578
+ if (typeof itemCount !== "number" || typeof pageSize !== "number") {
579
+ return 0;
580
+ }
581
+ if (itemCount < 0 || pageSize <= 0) {
582
+ return 0;
583
+ }
584
+ if (itemCount === 0) {
585
+ return 0;
586
+ }
587
+ return Math.ceil(itemCount / pageSize);
588
+ }
589
+ function getPageNumbers(currentPage, totalPages, maxVisible) {
590
+ if (typeof currentPage !== "number" || typeof totalPages !== "number" || typeof maxVisible !== "number") {
591
+ return [];
592
+ }
593
+ if (totalPages <= 0) {
594
+ return [];
595
+ }
596
+ if (currentPage < 0 || currentPage >= totalPages) {
597
+ return [];
598
+ }
599
+ if (totalPages <= maxVisible) {
600
+ return Array.from({ length: totalPages }, (_2, i) => i);
601
+ }
602
+ const result = [];
603
+ const firstPage = 0;
604
+ const lastPage = totalPages - 1;
605
+ result.push(firstPage);
606
+ const middleSlots = maxVisible - 2;
607
+ let middleStart = currentPage - Math.floor((middleSlots - 1) / 2);
608
+ let middleEnd = currentPage + Math.ceil((middleSlots - 1) / 2);
609
+ if (middleStart <= 1) {
610
+ middleStart = 1;
611
+ middleEnd = Math.min(middleSlots, lastPage - 1);
612
+ }
613
+ if (middleEnd >= lastPage - 1) {
614
+ middleEnd = lastPage - 1;
615
+ middleStart = Math.max(1, lastPage - middleSlots);
616
+ }
617
+ if (middleStart > 1) {
618
+ result.push(-1);
619
+ }
620
+ for (let i = middleStart; i <= middleEnd; i++) {
621
+ if (i > firstPage && i < lastPage) {
622
+ result.push(i);
623
+ }
624
+ }
625
+ if (middleEnd < lastPage - 1) {
626
+ result.push(-1);
627
+ }
628
+ result.push(lastPage);
629
+ return result;
630
+ }
631
+ function getVisibleRange(scrollTop, itemHeight, containerHeight, overscan) {
632
+ if (typeof scrollTop !== "number" || typeof itemHeight !== "number" || typeof containerHeight !== "number" || typeof overscan !== "number") {
633
+ return void 0;
634
+ }
635
+ if (scrollTop < 0 || itemHeight <= 0 || containerHeight <= 0) {
636
+ return void 0;
637
+ }
638
+ const firstVisible = Math.floor(scrollTop / itemHeight);
639
+ const visibleCount = Math.ceil(containerHeight / itemHeight);
640
+ const start = Math.max(0, firstVisible - overscan);
641
+ const end = firstVisible + visibleCount + overscan - 1;
642
+ return { start, end };
643
+ }
644
+ function getTotalHeight(itemCount, itemHeight) {
645
+ if (typeof itemCount !== "number" || typeof itemHeight !== "number") {
646
+ return 0;
647
+ }
648
+ if (itemCount < 0 || itemHeight <= 0) {
649
+ return 0;
650
+ }
651
+ return itemCount * itemHeight;
652
+ }
653
+ function formatDate(dateStr, format, locale) {
654
+ if (typeof dateStr !== "string" || !dateStr) {
655
+ return void 0;
656
+ }
657
+ const isoDateRegex = /^\d{4}-\d{2}-\d{2}$/;
658
+ if (!isoDateRegex.test(dateStr)) {
659
+ return void 0;
660
+ }
661
+ const [yearStr, monthStr, dayStr] = dateStr.split("-");
662
+ const year = parseInt(yearStr, 10);
663
+ const month = parseInt(monthStr, 10) - 1;
664
+ const day = parseInt(dayStr, 10);
665
+ const date = new Date(Date.UTC(year, month, day));
666
+ if (isNaN(date.getTime()) || date.getUTCFullYear() !== year || date.getUTCMonth() !== month || date.getUTCDate() !== day) {
667
+ return void 0;
668
+ }
669
+ const effectiveFormat = typeof format === "string" ? format : "medium";
670
+ const effectiveLocale = typeof locale === "string" ? locale : "en-US";
671
+ if (effectiveFormat === "iso") {
672
+ return dateStr;
673
+ }
674
+ try {
675
+ let options;
676
+ switch (effectiveFormat) {
677
+ case "short":
678
+ if (effectiveLocale.startsWith("ja")) {
679
+ options = { year: "numeric", month: "2-digit", day: "2-digit", timeZone: "UTC" };
680
+ } else {
681
+ options = { year: "2-digit", month: "numeric", day: "numeric", timeZone: "UTC" };
682
+ }
683
+ break;
684
+ case "medium":
685
+ options = { year: "numeric", month: "short", day: "numeric", timeZone: "UTC" };
686
+ break;
687
+ case "long":
688
+ options = { year: "numeric", month: "long", day: "numeric", timeZone: "UTC" };
689
+ break;
690
+ default:
691
+ options = { year: "numeric", month: "short", day: "numeric", timeZone: "UTC" };
692
+ }
693
+ const formatter = new Intl.DateTimeFormat(effectiveLocale, options);
694
+ return formatter.format(date);
695
+ } catch {
696
+ const formatter = new Intl.DateTimeFormat("en-US", {
697
+ year: "numeric",
698
+ month: "short",
699
+ day: "numeric",
700
+ timeZone: "UTC"
701
+ });
702
+ return formatter.format(date);
703
+ }
704
+ }
439
705
  function createLambdaFunction(lambda, ctx) {
440
706
  return (item, index) => {
441
707
  const lambdaLocals = {
@@ -677,6 +943,50 @@ function callDateInstanceMethod(target, method) {
677
943
  return void 0;
678
944
  }
679
945
  }
946
+ var GLOBAL_FUNCTIONS = {
947
+ // Date helpers
948
+ getCalendarDays: (year, month) => getCalendarDays(year, month),
949
+ getWeekDays: (locale) => getWeekDays(locale),
950
+ getMonthName: (month, locale) => getMonthName(month, locale),
951
+ formatDate: (dateStr, format, locale) => formatDate(dateStr, format, locale),
952
+ formatDateISO: (year, month, date) => formatDateISO(year, month, date),
953
+ // DataTable helpers
954
+ sortBy: (items, key2, direction) => sortBy(items, key2, direction),
955
+ getPaginatedItems: (items, page, pageSize) => getPaginatedItems(items, page, pageSize),
956
+ getTotalPages: (itemCount, pageSize) => getTotalPages(itemCount, pageSize),
957
+ getPageNumbers: (currentPage, totalPages, maxVisible) => getPageNumbers(currentPage, totalPages, maxVisible),
958
+ // Virtual scroll helpers
959
+ getVisibleRange: (scrollTop, itemHeight, containerHeight, overscan) => getVisibleRange(scrollTop, itemHeight, containerHeight, overscan),
960
+ getTotalHeight: (itemCount, itemHeight) => getTotalHeight(itemCount, itemHeight),
961
+ // Chart helpers - Coordinate calculation
962
+ normalizeValue: (value, min, max) => normalizeValue(value, min, max),
963
+ scaleValue: (value, domainMin, domainMax, rangeMin, rangeMax) => scaleValue(value, domainMin, domainMax, rangeMin, rangeMax),
964
+ getBarDimensions: (data, index, width, height, gap, orientation) => getBarDimensions(data, index, width, height, gap, orientation),
965
+ // Chart helpers - Path generation
966
+ getLinePath: (points, curved) => getLinePath(points, curved),
967
+ getAreaPath: (points, baseline, curved) => getAreaPath(points, baseline, curved),
968
+ getArcPath: (cx, cy, radius, startAngle, endAngle) => getArcPath(cx, cy, radius, startAngle, endAngle),
969
+ // Chart helpers - Pie/Donut
970
+ getPieSlices: (data, valueKey) => getPieSlices(data, valueKey),
971
+ getDonutSlices: (data, valueKey, innerRadius) => getDonutSlices(data, valueKey, innerRadius),
972
+ // Chart helpers - Radar
973
+ getRadarPoints: (data, valueKey, cx, cy, radius, maxValue) => getRadarPoints(data, valueKey, cx, cy, radius, maxValue),
974
+ getRadarAxes: (labels, cx, cy, radius) => getRadarAxes(labels, cx, cy, radius),
975
+ // Chart helpers - Utilities
976
+ getChartBounds: (data, valueKey) => getChartBounds(data, valueKey),
977
+ generateTicks: (min, max, count) => generateTicks(min, max, count),
978
+ // Chart helpers - Data aggregation
979
+ binData: (data, valueKey, binCount) => binData(data, valueKey, binCount),
980
+ aggregateData: (data, groupKey, valueKey, aggregation) => aggregateData(data, groupKey, valueKey, aggregation),
981
+ downsample: (data, targetCount, method) => downsample(data, targetCount, method)
982
+ };
983
+ function callGlobalFunction(method, args) {
984
+ const fn = GLOBAL_FUNCTIONS[method];
985
+ if (!fn) {
986
+ return void 0;
987
+ }
988
+ return fn(...args);
989
+ }
680
990
  function evaluate(expr, ctx) {
681
991
  switch (expr.expr) {
682
992
  case "lit":
@@ -716,7 +1026,21 @@ function evaluate(expr, ctx) {
716
1026
  String,
717
1027
  Number,
718
1028
  Boolean,
719
- console
1029
+ console,
1030
+ // Date helper functions
1031
+ getCalendarDays,
1032
+ getWeekDays,
1033
+ getMonthName,
1034
+ formatDate,
1035
+ formatDateISO,
1036
+ // DataTable helper functions
1037
+ sortBy,
1038
+ getPaginatedItems,
1039
+ getTotalPages,
1040
+ getPageNumbers,
1041
+ // Virtual scroll helper functions
1042
+ getVisibleRange,
1043
+ getTotalHeight
720
1044
  };
721
1045
  value = safeGlobals[varName];
722
1046
  }
@@ -727,7 +1051,7 @@ function evaluate(expr, ctx) {
727
1051
  if (typeof value === "function" && pathParts.length > 0) {
728
1052
  let parent = ctx.locals[varName];
729
1053
  if (parent === void 0) {
730
- const safeGlobals = { JSON, Math, Date, Object, Array, String, Number, Boolean, console };
1054
+ const safeGlobals = { JSON, Math, Date, Object, Array, String, Number, Boolean, console, getCalendarDays, getWeekDays, getMonthName, formatDate, formatDateISO, sortBy, getPaginatedItems, getTotalPages, getPageNumbers, getVisibleRange, getTotalHeight };
731
1055
  parent = safeGlobals[varName];
732
1056
  }
733
1057
  for (let i = 0; i < pathParts.length - 1; i++) {
@@ -823,12 +1147,14 @@ function evaluate(expr, ctx) {
823
1147
  }
824
1148
  case "call": {
825
1149
  const callExpr = expr;
826
- const target = evaluate(callExpr.target, ctx);
827
- if (target == null) return void 0;
1150
+ const target = callExpr.target != null ? evaluate(callExpr.target, ctx) : null;
828
1151
  const args = callExpr.args?.map((arg) => {
829
1152
  if (arg.expr === "lambda") return arg;
830
1153
  return evaluate(arg, ctx);
831
1154
  }) ?? [];
1155
+ if (target === null) {
1156
+ return callGlobalFunction(callExpr.method, args);
1157
+ }
832
1158
  if (Array.isArray(target)) {
833
1159
  return callArrayMethod(target, callExpr.method, args, ctx, callExpr.args);
834
1160
  }
@@ -844,6 +1170,9 @@ function evaluate(expr, ctx) {
844
1170
  if (target instanceof Date) {
845
1171
  return callDateInstanceMethod(target, callExpr.method);
846
1172
  }
1173
+ if (typeof target === "function" && callExpr.method === "call") {
1174
+ return target(...args);
1175
+ }
847
1176
  return void 0;
848
1177
  }
849
1178
  case "lambda":
@@ -972,6 +1301,528 @@ function evaluateBinary(op, left, right, ctx) {
972
1301
  throw new Error("Unknown binary operator: " + op);
973
1302
  }
974
1303
  }
1304
+ function normalizeValue(value, min, max) {
1305
+ if (typeof value !== "number" || typeof min !== "number" || typeof max !== "number") {
1306
+ return void 0;
1307
+ }
1308
+ if (max === min) {
1309
+ return 0;
1310
+ }
1311
+ return (value - min) / (max - min);
1312
+ }
1313
+ function scaleValue(value, domainMin, domainMax, rangeMin, rangeMax) {
1314
+ if (typeof value !== "number" || typeof domainMin !== "number" || typeof domainMax !== "number" || typeof rangeMin !== "number" || typeof rangeMax !== "number") {
1315
+ return void 0;
1316
+ }
1317
+ if (domainMax === domainMin) {
1318
+ return rangeMin;
1319
+ }
1320
+ const normalized = (value - domainMin) / (domainMax - domainMin);
1321
+ return rangeMin + normalized * (rangeMax - rangeMin);
1322
+ }
1323
+ function getBarDimensions(data, index, width, height, gap, orientation) {
1324
+ if (!Array.isArray(data) || data.length === 0) {
1325
+ return void 0;
1326
+ }
1327
+ if (typeof index !== "number" || index < 0 || index >= data.length) {
1328
+ return void 0;
1329
+ }
1330
+ if (typeof width !== "number" || typeof height !== "number" || typeof gap !== "number") {
1331
+ return void 0;
1332
+ }
1333
+ const isVertical = orientation === "vertical";
1334
+ const barCount = data.length;
1335
+ const values = data.map((d2) => typeof d2 === "number" ? d2 : 0);
1336
+ const maxValue = Math.max(...values);
1337
+ if (isVertical) {
1338
+ const totalGap = gap * (barCount + 1);
1339
+ const barWidth = (width - totalGap) / barCount;
1340
+ const barX = gap + index * (barWidth + gap);
1341
+ const value = values[index] ?? 0;
1342
+ const barHeight = maxValue > 0 ? value / maxValue * height : 0;
1343
+ const barY = height - barHeight;
1344
+ return {
1345
+ x: barX,
1346
+ y: barY,
1347
+ width: barWidth,
1348
+ height: barHeight
1349
+ };
1350
+ } else {
1351
+ const totalGap = gap * barCount;
1352
+ const barHeight = (height - totalGap) / barCount;
1353
+ const barY = gap + index * (barHeight + gap);
1354
+ const value = values[index] ?? 0;
1355
+ const barWidth = maxValue > 0 ? value / maxValue * width : 0;
1356
+ return {
1357
+ x: 0,
1358
+ y: barY,
1359
+ width: barWidth,
1360
+ height: barHeight
1361
+ };
1362
+ }
1363
+ }
1364
+ function getCurvedPath(points) {
1365
+ if (points.length < 2) {
1366
+ return points.length === 1 ? `M${points[0].x},${points[0].y}` : "";
1367
+ }
1368
+ if (points.length === 2) {
1369
+ return `M${points[0].x},${points[0].y} L${points[1].x},${points[1].y}`;
1370
+ }
1371
+ const pathParts = [`M${points[0].x},${points[0].y}`];
1372
+ for (let i = 0; i < points.length - 1; i++) {
1373
+ const p0 = points[Math.max(0, i - 1)];
1374
+ const p1 = points[i];
1375
+ const p2 = points[i + 1];
1376
+ const p3 = points[Math.min(points.length - 1, i + 2)];
1377
+ const cp1x = p1.x + (p2.x - p0.x) / 6;
1378
+ const cp1y = p1.y + (p2.y - p0.y) / 6;
1379
+ const cp2x = p2.x - (p3.x - p1.x) / 6;
1380
+ const cp2y = p2.y - (p3.y - p1.y) / 6;
1381
+ pathParts.push(`C${cp1x},${cp1y} ${cp2x},${cp2y} ${p2.x},${p2.y}`);
1382
+ }
1383
+ return pathParts.join(" ");
1384
+ }
1385
+ function getLinePath(points, curved) {
1386
+ if (!Array.isArray(points)) {
1387
+ return void 0;
1388
+ }
1389
+ if (points.length === 0) {
1390
+ return "";
1391
+ }
1392
+ for (const point of points) {
1393
+ if (typeof point !== "object" || point === null || typeof point.x !== "number" || typeof point.y !== "number") {
1394
+ return void 0;
1395
+ }
1396
+ }
1397
+ const validPoints = points;
1398
+ if (validPoints.length === 1) {
1399
+ return `M${validPoints[0].x},${validPoints[0].y}`;
1400
+ }
1401
+ if (curved !== true) {
1402
+ const pathParts = validPoints.map((point, i) => {
1403
+ const command = i === 0 ? "M" : "L";
1404
+ return `${command}${point.x},${point.y}`;
1405
+ });
1406
+ return pathParts.join(" ");
1407
+ }
1408
+ return getCurvedPath(validPoints);
1409
+ }
1410
+ function getAreaPath(points, baseline, curved) {
1411
+ if (!Array.isArray(points)) {
1412
+ return void 0;
1413
+ }
1414
+ if (typeof baseline !== "number") {
1415
+ return void 0;
1416
+ }
1417
+ if (points.length === 0) {
1418
+ return "";
1419
+ }
1420
+ for (const point of points) {
1421
+ if (typeof point !== "object" || point === null || typeof point.x !== "number" || typeof point.y !== "number") {
1422
+ return void 0;
1423
+ }
1424
+ }
1425
+ const validPoints = points;
1426
+ let upperPath;
1427
+ if (curved === true && validPoints.length > 2) {
1428
+ upperPath = getCurvedPath(validPoints);
1429
+ } else {
1430
+ upperPath = validPoints.map((p2, i) => (i === 0 ? "M" : "L") + `${p2.x},${p2.y}`).join(" ");
1431
+ }
1432
+ const lastPoint = validPoints[validPoints.length - 1];
1433
+ const firstPoint = validPoints[0];
1434
+ return `${upperPath} L${lastPoint.x},${baseline} L${firstPoint.x},${baseline} Z`;
1435
+ }
1436
+ function getArcPath(cx, cy, radius, startAngle, endAngle) {
1437
+ if (typeof cx !== "number" || typeof cy !== "number" || typeof radius !== "number" || typeof startAngle !== "number" || typeof endAngle !== "number") {
1438
+ return void 0;
1439
+ }
1440
+ if (radius <= 0) {
1441
+ return void 0;
1442
+ }
1443
+ const x1 = cx + radius * Math.cos(startAngle);
1444
+ const y1 = cy + radius * Math.sin(startAngle);
1445
+ const x2 = cx + radius * Math.cos(endAngle);
1446
+ const y2 = cy + radius * Math.sin(endAngle);
1447
+ const angleDiff = endAngle - startAngle;
1448
+ const largeArcFlag = Math.abs(angleDiff) > Math.PI ? 1 : 0;
1449
+ return `M${x1},${y1} A${radius},${radius} 0 ${largeArcFlag},1 ${x2},${y2}`;
1450
+ }
1451
+ function getPieSlices(data, valueKey) {
1452
+ if (!Array.isArray(data)) {
1453
+ return void 0;
1454
+ }
1455
+ if (typeof valueKey !== "string") {
1456
+ return void 0;
1457
+ }
1458
+ if (data.length === 0) {
1459
+ return [];
1460
+ }
1461
+ const values = data.map((item) => {
1462
+ if (typeof item !== "object" || item === null) return 0;
1463
+ const val = item[valueKey];
1464
+ return typeof val === "number" ? val : 0;
1465
+ });
1466
+ const total = values.reduce((sum, val) => sum + val, 0);
1467
+ const slices = [];
1468
+ let currentAngle = 0;
1469
+ for (let i = 0; i < values.length; i++) {
1470
+ const value = values[i];
1471
+ const percentage = total > 0 ? value / total * 100 : 0;
1472
+ const angleSpan = total > 0 ? value / total * Math.PI * 2 : 0;
1473
+ slices.push({
1474
+ startAngle: currentAngle,
1475
+ endAngle: currentAngle + angleSpan,
1476
+ value,
1477
+ percentage
1478
+ });
1479
+ currentAngle += angleSpan;
1480
+ }
1481
+ return slices;
1482
+ }
1483
+ function getDonutSlices(data, valueKey, innerRadius) {
1484
+ if (typeof innerRadius !== "number" || innerRadius < 0) {
1485
+ return void 0;
1486
+ }
1487
+ const pieSlices = getPieSlices(data, valueKey);
1488
+ if (pieSlices === void 0) {
1489
+ return void 0;
1490
+ }
1491
+ const outerRadius = 100;
1492
+ return pieSlices.map((slice) => ({
1493
+ ...slice,
1494
+ outerRadius,
1495
+ innerRadius
1496
+ }));
1497
+ }
1498
+ function getRadarPoints(data, valueKey, cx, cy, radius, maxValue) {
1499
+ if (!Array.isArray(data)) {
1500
+ return void 0;
1501
+ }
1502
+ if (typeof valueKey !== "string") {
1503
+ return void 0;
1504
+ }
1505
+ if (typeof cx !== "number" || typeof cy !== "number" || typeof radius !== "number" || typeof maxValue !== "number") {
1506
+ return void 0;
1507
+ }
1508
+ if (maxValue <= 0) {
1509
+ return void 0;
1510
+ }
1511
+ if (data.length === 0) {
1512
+ return [];
1513
+ }
1514
+ const points = [];
1515
+ const angleStep = Math.PI * 2 / data.length;
1516
+ for (let i = 0; i < data.length; i++) {
1517
+ const item = data[i];
1518
+ const value = typeof item === "object" && item !== null ? item[valueKey] : 0;
1519
+ const numValue = typeof value === "number" ? value : 0;
1520
+ const scaledRadius = numValue / maxValue * radius;
1521
+ const angle = -Math.PI / 2 + i * angleStep;
1522
+ const x2 = cx + scaledRadius * Math.cos(angle);
1523
+ const y2 = cy + scaledRadius * Math.sin(angle);
1524
+ points.push({ x: x2, y: y2 });
1525
+ }
1526
+ return points;
1527
+ }
1528
+ function getRadarAxes(labels, cx, cy, radius) {
1529
+ if (!Array.isArray(labels)) {
1530
+ return void 0;
1531
+ }
1532
+ if (typeof cx !== "number" || typeof cy !== "number" || typeof radius !== "number") {
1533
+ return void 0;
1534
+ }
1535
+ if (radius < 0) {
1536
+ return void 0;
1537
+ }
1538
+ if (labels.length === 0) {
1539
+ return [];
1540
+ }
1541
+ const axes = [];
1542
+ const angleStep = Math.PI * 2 / labels.length;
1543
+ for (let i = 0; i < labels.length; i++) {
1544
+ const label = String(labels[i]);
1545
+ const angle = -Math.PI / 2 + i * angleStep;
1546
+ const x2 = cx + radius * Math.cos(angle);
1547
+ const y2 = cy + radius * Math.sin(angle);
1548
+ axes.push({
1549
+ x1: cx,
1550
+ y1: cy,
1551
+ x2,
1552
+ y2,
1553
+ label,
1554
+ angle
1555
+ });
1556
+ }
1557
+ return axes;
1558
+ }
1559
+ function getChartBounds(data, valueKey) {
1560
+ if (!Array.isArray(data) || data.length === 0) {
1561
+ return void 0;
1562
+ }
1563
+ if (typeof valueKey !== "string") {
1564
+ return void 0;
1565
+ }
1566
+ const values = [];
1567
+ for (const item of data) {
1568
+ if (typeof item !== "object" || item === null) {
1569
+ continue;
1570
+ }
1571
+ const val = item[valueKey];
1572
+ if (typeof val === "number") {
1573
+ values.push(val);
1574
+ }
1575
+ }
1576
+ if (values.length === 0) {
1577
+ return void 0;
1578
+ }
1579
+ return {
1580
+ min: Math.min(...values),
1581
+ max: Math.max(...values)
1582
+ };
1583
+ }
1584
+ function generateTicks(min, max, count) {
1585
+ if (typeof min !== "number" || typeof max !== "number" || typeof count !== "number") {
1586
+ return [];
1587
+ }
1588
+ if (count <= 0) {
1589
+ return [];
1590
+ }
1591
+ if (min === max) {
1592
+ return [min];
1593
+ }
1594
+ if (count === 1) {
1595
+ return [min];
1596
+ }
1597
+ if (count === 2) {
1598
+ return [min, max];
1599
+ }
1600
+ const range = max - min;
1601
+ const rawStep = range / (count - 1);
1602
+ const niceStep = getNiceStep(rawStep);
1603
+ const niceMin = Math.floor(min / niceStep) * niceStep;
1604
+ const ticks = [];
1605
+ for (let i = 0; i < count; i++) {
1606
+ const tick = niceMin + i * niceStep;
1607
+ ticks.push(Math.round(tick * 1e10) / 1e10);
1608
+ }
1609
+ return ticks;
1610
+ }
1611
+ function getNiceStep(rawStep) {
1612
+ const magnitude = Math.pow(10, Math.floor(Math.log10(rawStep)));
1613
+ const normalized = rawStep / magnitude;
1614
+ let niceNormalized;
1615
+ if (normalized <= 1) {
1616
+ niceNormalized = 1;
1617
+ } else if (normalized <= 2) {
1618
+ niceNormalized = 2;
1619
+ } else if (normalized <= 2.5) {
1620
+ niceNormalized = 2.5;
1621
+ } else if (normalized <= 5) {
1622
+ niceNormalized = 5;
1623
+ } else {
1624
+ niceNormalized = 10;
1625
+ }
1626
+ return niceNormalized * magnitude;
1627
+ }
1628
+ function binData(data, valueKey, binCount) {
1629
+ if (!Array.isArray(data)) {
1630
+ return void 0;
1631
+ }
1632
+ if (typeof valueKey !== "string") {
1633
+ return void 0;
1634
+ }
1635
+ if (typeof binCount !== "number" || binCount <= 0) {
1636
+ return [];
1637
+ }
1638
+ if (data.length === 0) {
1639
+ return [];
1640
+ }
1641
+ const values = [];
1642
+ for (const item of data) {
1643
+ if (typeof item !== "object" || item === null) continue;
1644
+ const val = item[valueKey];
1645
+ if (typeof val === "number") {
1646
+ values.push(val);
1647
+ }
1648
+ }
1649
+ if (values.length === 0) {
1650
+ return [];
1651
+ }
1652
+ const minVal = Math.min(...values);
1653
+ const maxVal = Math.max(...values);
1654
+ const binWidth = maxVal === minVal ? 1 : (maxVal - minVal) / binCount;
1655
+ const bins = [];
1656
+ for (let i = 0; i < binCount; i++) {
1657
+ bins.push({
1658
+ binStart: minVal + i * binWidth,
1659
+ binEnd: minVal + (i + 1) * binWidth,
1660
+ count: 0,
1661
+ values: []
1662
+ });
1663
+ }
1664
+ for (const val of values) {
1665
+ let binIndex = Math.floor((val - minVal) / binWidth);
1666
+ if (binIndex >= binCount) {
1667
+ binIndex = binCount - 1;
1668
+ }
1669
+ bins[binIndex].count++;
1670
+ bins[binIndex].values.push(val);
1671
+ }
1672
+ return bins;
1673
+ }
1674
+ function aggregateData(data, groupKey, valueKey, aggregation) {
1675
+ if (!Array.isArray(data)) {
1676
+ return void 0;
1677
+ }
1678
+ if (typeof groupKey !== "string" || typeof valueKey !== "string") {
1679
+ return void 0;
1680
+ }
1681
+ const validAggregations = /* @__PURE__ */ new Set(["sum", "avg", "min", "max", "count"]);
1682
+ if (typeof aggregation !== "string" || !validAggregations.has(aggregation)) {
1683
+ return void 0;
1684
+ }
1685
+ if (data.length === 0) {
1686
+ return [];
1687
+ }
1688
+ const groups = /* @__PURE__ */ new Map();
1689
+ for (const item of data) {
1690
+ if (typeof item !== "object" || item === null) continue;
1691
+ const obj = item;
1692
+ const group = String(obj[groupKey] ?? "");
1693
+ const val = obj[valueKey];
1694
+ const numVal = typeof val === "number" ? val : 0;
1695
+ if (!groups.has(group)) {
1696
+ groups.set(group, []);
1697
+ }
1698
+ groups.get(group).push(numVal);
1699
+ }
1700
+ const result = [];
1701
+ for (const [group, values] of groups) {
1702
+ let aggregatedValue;
1703
+ switch (aggregation) {
1704
+ case "sum":
1705
+ aggregatedValue = values.reduce((sum, v2) => sum + v2, 0);
1706
+ break;
1707
+ case "avg":
1708
+ aggregatedValue = values.reduce((sum, v2) => sum + v2, 0) / values.length;
1709
+ break;
1710
+ case "min":
1711
+ aggregatedValue = Math.min(...values);
1712
+ break;
1713
+ case "max":
1714
+ aggregatedValue = Math.max(...values);
1715
+ break;
1716
+ case "count":
1717
+ aggregatedValue = values.length;
1718
+ break;
1719
+ default:
1720
+ aggregatedValue = 0;
1721
+ }
1722
+ result.push({ group, value: aggregatedValue });
1723
+ }
1724
+ return result;
1725
+ }
1726
+ function downsample(data, targetCount, method) {
1727
+ if (!Array.isArray(data)) {
1728
+ return void 0;
1729
+ }
1730
+ if (typeof targetCount !== "number" || targetCount <= 0) {
1731
+ return void 0;
1732
+ }
1733
+ const validMethods = /* @__PURE__ */ new Set(["uniform", "lttb"]);
1734
+ if (typeof method !== "string" || !validMethods.has(method)) {
1735
+ return void 0;
1736
+ }
1737
+ if (data.length === 0) {
1738
+ return [];
1739
+ }
1740
+ if (targetCount >= data.length) {
1741
+ return data;
1742
+ }
1743
+ if (method === "uniform") {
1744
+ return downsampleUniform(data, targetCount);
1745
+ } else {
1746
+ return downsampleLTTB(data, targetCount);
1747
+ }
1748
+ }
1749
+ function downsampleUniform(data, targetCount) {
1750
+ if (targetCount === 1) {
1751
+ return [data[0]];
1752
+ }
1753
+ if (targetCount === 2) {
1754
+ return [data[0], data[data.length - 1]];
1755
+ }
1756
+ const result = [];
1757
+ const step = (data.length - 1) / (targetCount - 1);
1758
+ for (let i = 0; i < targetCount; i++) {
1759
+ const index = Math.round(i * step);
1760
+ result.push(data[index]);
1761
+ }
1762
+ return result;
1763
+ }
1764
+ function downsampleLTTB(data, targetCount) {
1765
+ if (targetCount === 1) {
1766
+ return [data[0]];
1767
+ }
1768
+ if (targetCount === 2) {
1769
+ return [data[0], data[data.length - 1]];
1770
+ }
1771
+ const getXY = (point) => {
1772
+ if (typeof point !== "object" || point === null) {
1773
+ return { x: 0, y: 0 };
1774
+ }
1775
+ const obj = point;
1776
+ const x2 = typeof obj["x"] === "number" ? obj["x"] : typeof obj["timestamp"] === "number" ? obj["timestamp"] : 0;
1777
+ const y2 = typeof obj["y"] === "number" ? obj["y"] : typeof obj["value"] === "number" ? obj["value"] : 0;
1778
+ return { x: x2, y: y2 };
1779
+ };
1780
+ const result = [];
1781
+ result.push(data[0]);
1782
+ const numBuckets = targetCount - 2;
1783
+ const middleData = data.length - 2;
1784
+ const bucketSize = middleData / numBuckets;
1785
+ let prevSelectedIndex = 0;
1786
+ for (let bucketIndex = 0; bucketIndex < numBuckets; bucketIndex++) {
1787
+ const bucketStart = Math.floor(bucketIndex * bucketSize) + 1;
1788
+ const bucketEnd = Math.floor((bucketIndex + 1) * bucketSize) + 1;
1789
+ const nextBucketStart = bucketEnd;
1790
+ const nextBucketEnd = bucketIndex < numBuckets - 1 ? Math.floor((bucketIndex + 2) * bucketSize) + 1 : data.length;
1791
+ let avgX = 0;
1792
+ let avgY = 0;
1793
+ const nextLen = nextBucketEnd - nextBucketStart;
1794
+ if (nextLen > 0) {
1795
+ for (let j2 = nextBucketStart; j2 < nextBucketEnd; j2++) {
1796
+ const point = getXY(data[j2]);
1797
+ avgX += point.x;
1798
+ avgY += point.y;
1799
+ }
1800
+ avgX /= nextLen;
1801
+ avgY /= nextLen;
1802
+ } else {
1803
+ const lastPoint = getXY(data[data.length - 1]);
1804
+ avgX = lastPoint.x;
1805
+ avgY = lastPoint.y;
1806
+ }
1807
+ const pointA = getXY(data[prevSelectedIndex]);
1808
+ let maxArea = -1;
1809
+ let maxAreaIndex = bucketStart;
1810
+ for (let j2 = bucketStart; j2 < bucketEnd; j2++) {
1811
+ const pointB = getXY(data[j2]);
1812
+ const area = Math.abs(
1813
+ pointA.x * (pointB.y - avgY) + pointB.x * (avgY - pointA.y) + avgX * (pointA.y - pointB.y)
1814
+ );
1815
+ if (area > maxArea) {
1816
+ maxArea = area;
1817
+ maxAreaIndex = j2;
1818
+ }
1819
+ }
1820
+ result.push(data[maxAreaIndex]);
1821
+ prevSelectedIndex = maxAreaIndex;
1822
+ }
1823
+ result.push(data[data.length - 1]);
1824
+ return result;
1825
+ }
975
1826
  function evaluateStyle(expr, ctx) {
976
1827
  const preset = ctx.styles?.[expr.name];
977
1828
  if (!preset) return void 0;
@@ -1003,6 +1854,83 @@ function evaluateStyle(expr, ctx) {
1003
1854
  return classes.trim();
1004
1855
  }
1005
1856
 
1857
+ // src/connection/sse.ts
1858
+ function parseMessageData(data) {
1859
+ try {
1860
+ return JSON.parse(data);
1861
+ } catch {
1862
+ return data;
1863
+ }
1864
+ }
1865
+ function createSSEConnection(url, handlers, eventTypes) {
1866
+ const eventSource = new EventSource(url, void 0);
1867
+ eventSource.onopen = () => {
1868
+ handlers.onOpen?.();
1869
+ };
1870
+ eventSource.onerror = (event) => {
1871
+ handlers.onError?.(event);
1872
+ };
1873
+ eventSource.onmessage = (event) => {
1874
+ const data = parseMessageData(event.data);
1875
+ handlers.onMessage?.(data, "message");
1876
+ };
1877
+ if (eventTypes && eventTypes.length > 0) {
1878
+ for (const eventType of eventTypes) {
1879
+ eventSource.addEventListener(eventType, (event) => {
1880
+ const data = parseMessageData(event.data);
1881
+ handlers.onMessage?.(data, eventType);
1882
+ });
1883
+ }
1884
+ }
1885
+ return {
1886
+ close() {
1887
+ eventSource.close();
1888
+ handlers.onClose?.();
1889
+ },
1890
+ getState() {
1891
+ switch (eventSource.readyState) {
1892
+ case EventSource.CONNECTING:
1893
+ return "connecting";
1894
+ case EventSource.OPEN:
1895
+ return "open";
1896
+ case EventSource.CLOSED:
1897
+ return "closed";
1898
+ default:
1899
+ return "closed";
1900
+ }
1901
+ }
1902
+ };
1903
+ }
1904
+ function createSSEConnectionManager() {
1905
+ const connections = /* @__PURE__ */ new Map();
1906
+ return {
1907
+ create(name, url, handlers, eventTypes) {
1908
+ const existing = connections.get(name);
1909
+ if (existing) {
1910
+ existing.close();
1911
+ }
1912
+ const conn = createSSEConnection(url, handlers, eventTypes);
1913
+ connections.set(name, conn);
1914
+ },
1915
+ get(name) {
1916
+ return connections.get(name);
1917
+ },
1918
+ close(name) {
1919
+ const conn = connections.get(name);
1920
+ if (conn) {
1921
+ conn.close();
1922
+ connections.delete(name);
1923
+ }
1924
+ },
1925
+ closeAll() {
1926
+ for (const conn of connections.values()) {
1927
+ conn.close();
1928
+ }
1929
+ connections.clear();
1930
+ }
1931
+ };
1932
+ }
1933
+
1006
1934
  // src/action/executor.ts
1007
1935
  function createEvalContext(ctx) {
1008
1936
  return {
@@ -1340,6 +2268,27 @@ async function executeStep(step, ctx) {
1340
2268
  case "generate":
1341
2269
  await executeGenerateStep(step, ctx);
1342
2270
  break;
2271
+ case "sseConnect":
2272
+ await executeSSEConnectStep(step, ctx);
2273
+ break;
2274
+ case "sseClose":
2275
+ await executeSSECloseStep(step, ctx);
2276
+ break;
2277
+ case "optimistic":
2278
+ await executeOptimisticStep(step, ctx);
2279
+ break;
2280
+ case "confirm":
2281
+ await executeConfirmStep(step, ctx);
2282
+ break;
2283
+ case "reject":
2284
+ await executeRejectStep(step, ctx);
2285
+ break;
2286
+ case "bind":
2287
+ await executeBindStep(step, ctx);
2288
+ break;
2289
+ case "unbind":
2290
+ await executeUnbindStep(step, ctx);
2291
+ break;
1343
2292
  }
1344
2293
  }
1345
2294
  async function executeSetStep(target, value, ctx) {
@@ -1879,6 +2828,116 @@ async function executeGenerateStep(step, ctx) {
1879
2828
  }
1880
2829
  }
1881
2830
  }
2831
+ function isSSEConnectionManager(obj) {
2832
+ return obj !== null && typeof obj === "object" && typeof obj.create === "function" && typeof obj.close === "function";
2833
+ }
2834
+ function ensureSSEManager(ctx) {
2835
+ if (!isSSEConnectionManager(ctx.sse)) {
2836
+ ctx.sse = createSSEConnectionManager();
2837
+ }
2838
+ return ctx.sse;
2839
+ }
2840
+ async function executeSSEConnectStep(step, ctx) {
2841
+ const evalCtx = createEvalContext(ctx);
2842
+ const url = String(evaluate(step.url, evalCtx));
2843
+ const sseManager = ensureSSEManager(ctx);
2844
+ const handlers = {};
2845
+ if (step.onOpen) {
2846
+ handlers.onOpen = async () => {
2847
+ for (const s of step.onOpen) {
2848
+ await executeStep(s, ctx);
2849
+ }
2850
+ };
2851
+ }
2852
+ if (step.onMessage) {
2853
+ handlers.onMessage = async (data, eventType) => {
2854
+ ctx.locals["event"] = { data, type: eventType };
2855
+ for (const s of step.onMessage) {
2856
+ await executeStep(s, ctx);
2857
+ }
2858
+ };
2859
+ }
2860
+ if (step.onError) {
2861
+ handlers.onError = async (error) => {
2862
+ ctx.locals["error"] = error;
2863
+ for (const s of step.onError) {
2864
+ await executeStep(s, ctx);
2865
+ }
2866
+ };
2867
+ }
2868
+ sseManager.create(step.connection, url, handlers, step.eventTypes);
2869
+ }
2870
+ async function executeSSECloseStep(step, ctx) {
2871
+ if (isSSEConnectionManager(ctx.sse)) {
2872
+ ctx.sse.close(step.connection);
2873
+ }
2874
+ }
2875
+ async function executeOptimisticStep(step, ctx) {
2876
+ if (!ctx.optimistic) {
2877
+ return;
2878
+ }
2879
+ const evalCtx = createEvalContext(ctx);
2880
+ const value = evaluate(step.value, evalCtx);
2881
+ const path = step.path ? evaluate(step.path, evalCtx) : void 0;
2882
+ if (step.timeout) {
2883
+ ctx.optimistic.setAutoRollbackTimeout(step.timeout);
2884
+ }
2885
+ const updateId = ctx.optimistic.apply(
2886
+ step.target,
2887
+ path,
2888
+ value,
2889
+ (target) => ctx.state.get(target),
2890
+ (target, val) => ctx.state.set(target, val)
2891
+ );
2892
+ if (step.result) {
2893
+ ctx.locals[step.result] = updateId;
2894
+ }
2895
+ }
2896
+ async function executeConfirmStep(step, ctx) {
2897
+ const evalCtx = createEvalContext(ctx);
2898
+ const id = evaluate(step.id, evalCtx);
2899
+ ctx.optimistic?.confirm(String(id));
2900
+ }
2901
+ async function executeRejectStep(step, ctx) {
2902
+ const evalCtx = createEvalContext(ctx);
2903
+ const id = evaluate(step.id, evalCtx);
2904
+ ctx.optimistic?.reject(
2905
+ String(id),
2906
+ (target) => ctx.state.get(target),
2907
+ (target, val) => ctx.state.set(target, val)
2908
+ );
2909
+ }
2910
+ async function executeBindStep(step, ctx) {
2911
+ if (!ctx.binding) {
2912
+ return;
2913
+ }
2914
+ const evalCtx = createEvalContext(ctx);
2915
+ const config = {
2916
+ connection: step.connection,
2917
+ target: step.target
2918
+ };
2919
+ if (step.eventType !== void 0) {
2920
+ config.eventType = step.eventType;
2921
+ }
2922
+ if (step.path) {
2923
+ config.path = evaluate(step.path, evalCtx);
2924
+ }
2925
+ if (step.patch !== void 0) {
2926
+ config.patch = step.patch;
2927
+ }
2928
+ ctx.binding.bind(config, (target, value, pathOrOptions) => {
2929
+ if (pathOrOptions && typeof pathOrOptions === "object" && "patch" in pathOrOptions) {
2930
+ ctx.state.set(target, value);
2931
+ } else if (Array.isArray(pathOrOptions)) {
2932
+ ctx.state.setPath(target, pathOrOptions, value);
2933
+ } else {
2934
+ ctx.state.set(target, value);
2935
+ }
2936
+ });
2937
+ }
2938
+ async function executeUnbindStep(step, ctx) {
2939
+ ctx.binding?.unbindByConnection(step.connection);
2940
+ }
1882
2941
 
1883
2942
  // ../../node_modules/.pnpm/marked@17.0.1/node_modules/marked/lib/marked.esm.js
1884
2943
  function L() {
@@ -14623,27 +15682,163 @@ function createApp(program, mount) {
14623
15682
  void executeAction(onUnmountAction, actionCtx);
14624
15683
  }
14625
15684
  }
14626
- for (const cleanup of cleanups) {
14627
- cleanup();
14628
- }
14629
- while (mount.firstChild) {
14630
- mount.removeChild(mount.firstChild);
14631
- }
14632
- },
14633
- setState(name, value) {
14634
- if (destroyed) return;
14635
- state.set(name, value);
14636
- },
14637
- getState(name) {
14638
- return state.get(name);
15685
+ for (const cleanup of cleanups) {
15686
+ cleanup();
15687
+ }
15688
+ while (mount.firstChild) {
15689
+ mount.removeChild(mount.firstChild);
15690
+ }
15691
+ },
15692
+ setState(name, value) {
15693
+ if (destroyed) return;
15694
+ state.set(name, value);
15695
+ },
15696
+ getState(name) {
15697
+ return state.get(name);
15698
+ },
15699
+ subscribe(name, fn) {
15700
+ if (destroyed) return () => {
15701
+ };
15702
+ return state.subscribe(name, fn);
15703
+ }
15704
+ };
15705
+ }
15706
+
15707
+ // src/hydrate-island.ts
15708
+ function hydrateIsland(options) {
15709
+ const { strategy } = options;
15710
+ switch (strategy) {
15711
+ case "load":
15712
+ hydrateImmediately(options);
15713
+ return () => {
15714
+ };
15715
+ case "idle":
15716
+ return hydrateOnIdle(options, options.strategyOptions?.timeout);
15717
+ case "visible":
15718
+ return hydrateOnVisible(options, options.strategyOptions);
15719
+ case "interaction":
15720
+ return hydrateOnInteraction(options);
15721
+ case "media":
15722
+ return hydrateOnMedia(options, options.strategyOptions?.media);
15723
+ case "never":
15724
+ return () => {
15725
+ };
15726
+ // SSR only, no hydration
15727
+ default:
15728
+ hydrateImmediately(options);
15729
+ return () => {
15730
+ };
15731
+ }
15732
+ }
15733
+ function hydrateImmediately(options) {
15734
+ options.element.dataset["islandHydrated"] = "true";
15735
+ }
15736
+ function hydrateOnIdle(options, timeout) {
15737
+ let cancelled = false;
15738
+ let handle2;
15739
+ if ("requestIdleCallback" in window) {
15740
+ handle2 = window.requestIdleCallback(
15741
+ () => {
15742
+ if (!cancelled) hydrateImmediately(options);
15743
+ },
15744
+ timeout ? { timeout } : void 0
15745
+ );
15746
+ return () => {
15747
+ cancelled = true;
15748
+ window.cancelIdleCallback(handle2);
15749
+ };
15750
+ } else {
15751
+ handle2 = setTimeout(() => {
15752
+ if (!cancelled) hydrateImmediately(options);
15753
+ }, timeout ?? 200);
15754
+ return () => {
15755
+ cancelled = true;
15756
+ clearTimeout(handle2);
15757
+ };
15758
+ }
15759
+ }
15760
+ function hydrateOnVisible(options, strategyOptions) {
15761
+ const observer = new IntersectionObserver(
15762
+ (entries2) => {
15763
+ for (const entry of entries2) {
15764
+ if (entry.isIntersecting) {
15765
+ hydrateImmediately(options);
15766
+ observer.disconnect();
15767
+ break;
15768
+ }
15769
+ }
14639
15770
  },
14640
- subscribe(name, fn) {
14641
- if (destroyed) return () => {
14642
- };
14643
- return state.subscribe(name, fn);
15771
+ {
15772
+ threshold: strategyOptions?.threshold ?? 0,
15773
+ rootMargin: strategyOptions?.rootMargin ?? "0px"
15774
+ }
15775
+ );
15776
+ observer.observe(options.element);
15777
+ return () => observer.disconnect();
15778
+ }
15779
+ function hydrateOnInteraction(options) {
15780
+ const events = ["click", "focusin", "mouseover"];
15781
+ let hydrated = false;
15782
+ const handler = () => {
15783
+ if (hydrated) return;
15784
+ hydrated = true;
15785
+ hydrateImmediately(options);
15786
+ for (const event of events) {
15787
+ options.element.removeEventListener(event, handler);
15788
+ }
15789
+ };
15790
+ for (const event of events) {
15791
+ options.element.addEventListener(event, handler, { passive: true });
15792
+ }
15793
+ return () => {
15794
+ for (const event of events) {
15795
+ options.element.removeEventListener(event, handler);
14644
15796
  }
14645
15797
  };
14646
15798
  }
15799
+ function hydrateOnMedia(options, media) {
15800
+ if (!media) {
15801
+ hydrateImmediately(options);
15802
+ return () => {
15803
+ };
15804
+ }
15805
+ const mql = window.matchMedia(media);
15806
+ if (mql.matches) {
15807
+ hydrateImmediately(options);
15808
+ return () => {
15809
+ };
15810
+ }
15811
+ const handler = (e) => {
15812
+ if (e.matches) {
15813
+ hydrateImmediately(options);
15814
+ mql.removeEventListener("change", handler);
15815
+ }
15816
+ };
15817
+ mql.addEventListener("change", handler);
15818
+ return () => mql.removeEventListener("change", handler);
15819
+ }
15820
+ function detectIslandsInDOM(container) {
15821
+ const islands = [];
15822
+ const elements = container.querySelectorAll("[data-island-id]");
15823
+ for (const element2 of elements) {
15824
+ const id = element2.getAttribute("data-island-id");
15825
+ const strategy = element2.getAttribute(
15826
+ "data-island-strategy"
15827
+ );
15828
+ const optionsJson = element2.getAttribute("data-island-options");
15829
+ const stateJson = element2.getAttribute("data-island-state");
15830
+ if (id && strategy) {
15831
+ islands.push({
15832
+ element: element2,
15833
+ id,
15834
+ strategy,
15835
+ strategyOptions: optionsJson ? JSON.parse(optionsJson) : void 0,
15836
+ state: stateJson ? JSON.parse(stateJson) : void 0
15837
+ });
15838
+ }
15839
+ }
15840
+ return islands;
15841
+ }
14647
15842
 
14648
15843
  // src/hydrate.ts
14649
15844
  function createReactiveLocals2(baseLocals, itemSignal, indexSignal, itemName, indexName) {
@@ -15687,6 +16882,79 @@ function initCopyButtons(container) {
15687
16882
  });
15688
16883
  });
15689
16884
  }
16885
+ function hydrateAppWithIslands(program, options) {
16886
+ const container = options?.container ?? document.getElementById("app") ?? document.body;
16887
+ const cleanupFns = [];
16888
+ const islands = detectIslandsInDOM(container);
16889
+ for (const island of islands) {
16890
+ const cleanup = hydrateIsland({
16891
+ ...island,
16892
+ program
16893
+ });
16894
+ cleanupFns.push(cleanup);
16895
+ }
16896
+ return () => {
16897
+ for (const cleanup of cleanupFns) {
16898
+ cleanup();
16899
+ }
16900
+ };
16901
+ }
16902
+
16903
+ // src/island-loader.ts
16904
+ function createIslandLoader(options = {}) {
16905
+ const basePath = options.basePath ?? "/_constela/islands/";
16906
+ const cache = /* @__PURE__ */ new Map();
16907
+ const pending = /* @__PURE__ */ new Map();
16908
+ async function load(id) {
16909
+ const cached = cache.get(id);
16910
+ if (cached) {
16911
+ return cached;
16912
+ }
16913
+ const pendingPromise = pending.get(id);
16914
+ if (pendingPromise) {
16915
+ return pendingPromise;
16916
+ }
16917
+ const loadPromise = (async () => {
16918
+ try {
16919
+ const module = await loadModule(id);
16920
+ cache.set(id, module);
16921
+ return module;
16922
+ } finally {
16923
+ pending.delete(id);
16924
+ }
16925
+ })();
16926
+ pending.set(id, loadPromise);
16927
+ return loadPromise;
16928
+ }
16929
+ async function loadModule(id) {
16930
+ const sanitizedId = id.replace(/[\/\\:*?"<>|]/g, "_");
16931
+ const modulePath = `${basePath}${sanitizedId}.js`;
16932
+ try {
16933
+ const module = await import(
16934
+ /* @vite-ignore */
16935
+ modulePath
16936
+ );
16937
+ return module;
16938
+ } catch (error) {
16939
+ const message = error instanceof Error ? error.message : String(error);
16940
+ throw new Error(`Failed to load island "${id}" from ${modulePath}: ${message}`);
16941
+ }
16942
+ }
16943
+ function preload(id) {
16944
+ if (cache.has(id)) {
16945
+ return;
16946
+ }
16947
+ if (pending.has(id)) {
16948
+ return;
16949
+ }
16950
+ load(id).catch(() => {
16951
+ });
16952
+ }
16953
+ return {
16954
+ load,
16955
+ preload
16956
+ };
16957
+ }
15690
16958
 
15691
16959
  // src/connection/websocket.ts
15692
16960
  function createWebSocketConnection(url, handlers) {
@@ -15778,6 +17046,380 @@ function createConnectionManager() {
15778
17046
  };
15779
17047
  }
15780
17048
 
17049
+ // src/connection/reconnect.ts
17050
+ function calculateDelay(strategy, attempt, baseDelay, maxDelay) {
17051
+ let delay;
17052
+ if (strategy === "exponential") {
17053
+ delay = baseDelay * Math.pow(2, attempt);
17054
+ } else if (strategy === "linear") {
17055
+ delay = baseDelay * (attempt + 1);
17056
+ } else {
17057
+ return 0;
17058
+ }
17059
+ return Math.min(delay, maxDelay);
17060
+ }
17061
+ function createReconnectionManager() {
17062
+ const managedConnections = /* @__PURE__ */ new Map();
17063
+ function shouldReconnect(policy) {
17064
+ return policy.enabled && policy.strategy !== "none";
17065
+ }
17066
+ function clearTimer(managed) {
17067
+ if (managed.timerId !== null) {
17068
+ clearTimeout(managed.timerId);
17069
+ managed.timerId = null;
17070
+ }
17071
+ }
17072
+ function scheduleReconnect(managed) {
17073
+ if (managed.isReconnecting) {
17074
+ return;
17075
+ }
17076
+ const { policy, retryCount } = managed;
17077
+ if (retryCount >= policy.maxRetries) {
17078
+ managed.isReconnecting = false;
17079
+ if (!managed.maxRetriesReached) {
17080
+ managed.maxRetriesReached = true;
17081
+ try {
17082
+ managed.onMaxRetriesReached?.();
17083
+ } catch {
17084
+ }
17085
+ }
17086
+ return;
17087
+ }
17088
+ managed.isReconnecting = true;
17089
+ const delay = calculateDelay(
17090
+ policy.strategy,
17091
+ retryCount,
17092
+ policy.baseDelay,
17093
+ policy.maxDelay
17094
+ );
17095
+ managed.timerId = setTimeout(() => {
17096
+ managed.timerId = null;
17097
+ managed.isReconnecting = false;
17098
+ attemptReconnect(managed);
17099
+ }, delay);
17100
+ }
17101
+ function attemptReconnect(managed) {
17102
+ managedConnections.delete(managed.connection);
17103
+ let newConnection;
17104
+ try {
17105
+ newConnection = managed.reconnectFn();
17106
+ } catch {
17107
+ managed.retryCount++;
17108
+ managedConnections.set(managed.connection, managed);
17109
+ scheduleReconnect(managed);
17110
+ return;
17111
+ }
17112
+ managed.retryCount++;
17113
+ managed.connection = newConnection;
17114
+ managedConnections.set(newConnection, managed);
17115
+ if (shouldReconnect(managed.policy)) {
17116
+ monitorConnection(newConnection, () => {
17117
+ handleConnectionError(managed);
17118
+ });
17119
+ }
17120
+ if (newConnection.getState() === "open") {
17121
+ managed.retryCount = 0;
17122
+ managed.maxRetriesReached = false;
17123
+ try {
17124
+ managed.onReconnect?.();
17125
+ } catch {
17126
+ }
17127
+ } else {
17128
+ scheduleReconnect(managed);
17129
+ }
17130
+ }
17131
+ function handleConnectionError(managed) {
17132
+ if (!shouldReconnect(managed.policy)) {
17133
+ return;
17134
+ }
17135
+ scheduleReconnect(managed);
17136
+ }
17137
+ function monitorConnection(connection, onError) {
17138
+ const originalGetState = connection.getState.bind(connection);
17139
+ function checkForError() {
17140
+ const currentState = originalGetState();
17141
+ if (currentState === "closed") {
17142
+ onError();
17143
+ }
17144
+ }
17145
+ const proto = Object.getPrototypeOf(connection);
17146
+ const allKeys = /* @__PURE__ */ new Set([
17147
+ ...Object.keys(connection),
17148
+ ...proto ? Object.keys(proto) : []
17149
+ ]);
17150
+ for (const key2 of allKeys) {
17151
+ const descriptor = Object.getOwnPropertyDescriptor(connection, key2) || Object.getOwnPropertyDescriptor(proto, key2);
17152
+ if (descriptor && typeof descriptor.value === "function" && key2 !== "getState" && key2 !== "close") {
17153
+ const originalMethod = connection[key2];
17154
+ connection[key2] = function(...args) {
17155
+ const result = originalMethod.apply(connection, args);
17156
+ checkForError();
17157
+ return result;
17158
+ };
17159
+ }
17160
+ }
17161
+ }
17162
+ return {
17163
+ wrap(connection, reconnectFn, policy, onReconnect, onMaxRetriesReached) {
17164
+ const managed = {
17165
+ connection,
17166
+ reconnectFn,
17167
+ policy,
17168
+ onReconnect,
17169
+ onMaxRetriesReached,
17170
+ retryCount: 0,
17171
+ timerId: null,
17172
+ isReconnecting: false,
17173
+ maxRetriesReached: false
17174
+ };
17175
+ managedConnections.set(connection, managed);
17176
+ if (shouldReconnect(policy)) {
17177
+ monitorConnection(connection, () => {
17178
+ handleConnectionError(managed);
17179
+ });
17180
+ }
17181
+ return connection;
17182
+ },
17183
+ triggerReconnect(connection) {
17184
+ const managed = managedConnections.get(connection);
17185
+ if (!managed) {
17186
+ return;
17187
+ }
17188
+ clearTimer(managed);
17189
+ managed.isReconnecting = false;
17190
+ managed.retryCount = 0;
17191
+ managed.maxRetriesReached = false;
17192
+ connection.close();
17193
+ attemptReconnect(managed);
17194
+ },
17195
+ dispose(connection) {
17196
+ const managed = managedConnections.get(connection);
17197
+ if (!managed) {
17198
+ return;
17199
+ }
17200
+ clearTimer(managed);
17201
+ managedConnections.delete(connection);
17202
+ },
17203
+ disposeAll() {
17204
+ for (const managed of managedConnections.values()) {
17205
+ clearTimer(managed);
17206
+ }
17207
+ managedConnections.clear();
17208
+ }
17209
+ };
17210
+ }
17211
+
17212
+ // src/optimistic/manager.ts
17213
+ function deepClone(value) {
17214
+ if (value === null || typeof value !== "object") {
17215
+ return value;
17216
+ }
17217
+ if (Array.isArray(value)) {
17218
+ return value.map(deepClone);
17219
+ }
17220
+ const cloned = {};
17221
+ for (const key2 in value) {
17222
+ if (Object.prototype.hasOwnProperty.call(value, key2)) {
17223
+ cloned[key2] = deepClone(value[key2]);
17224
+ }
17225
+ }
17226
+ return cloned;
17227
+ }
17228
+ function getAtPath(obj, path) {
17229
+ let current = obj;
17230
+ for (const key2 of path) {
17231
+ if (current === null || current === void 0) {
17232
+ return void 0;
17233
+ }
17234
+ current = current[key2];
17235
+ }
17236
+ return current;
17237
+ }
17238
+ function setAtPath(obj, path, value) {
17239
+ if (path.length === 0) {
17240
+ return deepClone(value);
17241
+ }
17242
+ const [head2, ...rest] = path;
17243
+ const key2 = head2;
17244
+ const isArray = Array.isArray(obj);
17245
+ const cloned = isArray ? [...obj] : { ...obj };
17246
+ if (rest.length === 0) {
17247
+ cloned[key2] = deepClone(value);
17248
+ } else {
17249
+ cloned[key2] = setAtPath(
17250
+ obj[key2],
17251
+ rest,
17252
+ value
17253
+ );
17254
+ }
17255
+ return cloned;
17256
+ }
17257
+ function createOptimisticManager() {
17258
+ const pendingUpdates = /* @__PURE__ */ new Map();
17259
+ const timers = /* @__PURE__ */ new Map();
17260
+ let autoRollbackTimeout = 0;
17261
+ const stateAccessors = /* @__PURE__ */ new Map();
17262
+ function generateId() {
17263
+ return crypto.randomUUID();
17264
+ }
17265
+ function apply2(target, path, value, getState, setState) {
17266
+ const id = generateId();
17267
+ const currentState = getState(target);
17268
+ const originalValue = path && path.length > 0 ? getAtPath(currentState, path) : currentState;
17269
+ let newState;
17270
+ if (path && path.length > 0) {
17271
+ newState = setAtPath(currentState, path, value);
17272
+ } else {
17273
+ newState = deepClone(value);
17274
+ }
17275
+ setState(target, newState);
17276
+ const pendingUpdate = {
17277
+ id,
17278
+ target,
17279
+ path: path && path.length > 0 ? path : void 0,
17280
+ originalValue,
17281
+ optimisticValue: value,
17282
+ timestamp: Date.now()
17283
+ };
17284
+ pendingUpdates.set(id, pendingUpdate);
17285
+ stateAccessors.set(id, { getState, setState });
17286
+ if (autoRollbackTimeout > 0) {
17287
+ const timer = setTimeout(() => {
17288
+ reject(id, getState, setState);
17289
+ }, autoRollbackTimeout);
17290
+ timers.set(id, timer);
17291
+ }
17292
+ return id;
17293
+ }
17294
+ function confirm(id) {
17295
+ const pending = pendingUpdates.get(id);
17296
+ if (!pending) {
17297
+ return false;
17298
+ }
17299
+ const timer = timers.get(id);
17300
+ if (timer) {
17301
+ clearTimeout(timer);
17302
+ timers.delete(id);
17303
+ }
17304
+ pendingUpdates.delete(id);
17305
+ stateAccessors.delete(id);
17306
+ return true;
17307
+ }
17308
+ function reject(id, getState, setState) {
17309
+ const pending = pendingUpdates.get(id);
17310
+ if (!pending) {
17311
+ return false;
17312
+ }
17313
+ const timer = timers.get(id);
17314
+ if (timer) {
17315
+ clearTimeout(timer);
17316
+ timers.delete(id);
17317
+ }
17318
+ const currentState = getState(pending.target);
17319
+ if (pending.path && pending.path.length > 0) {
17320
+ const restoredState = setAtPath(currentState, pending.path, pending.originalValue);
17321
+ setState(pending.target, restoredState);
17322
+ } else {
17323
+ setState(pending.target, pending.originalValue);
17324
+ }
17325
+ pendingUpdates.delete(id);
17326
+ stateAccessors.delete(id);
17327
+ return true;
17328
+ }
17329
+ function getPending(id) {
17330
+ return pendingUpdates.get(id);
17331
+ }
17332
+ function getAllPending() {
17333
+ return Array.from(pendingUpdates.values());
17334
+ }
17335
+ function setAutoRollbackTimeout(ms) {
17336
+ autoRollbackTimeout = ms;
17337
+ }
17338
+ function dispose() {
17339
+ for (const timer of timers.values()) {
17340
+ clearTimeout(timer);
17341
+ }
17342
+ timers.clear();
17343
+ pendingUpdates.clear();
17344
+ stateAccessors.clear();
17345
+ }
17346
+ return {
17347
+ apply: apply2,
17348
+ confirm,
17349
+ reject,
17350
+ getPending,
17351
+ getAllPending,
17352
+ setAutoRollbackTimeout,
17353
+ dispose
17354
+ };
17355
+ }
17356
+
17357
+ // src/binding/realtime.ts
17358
+ function createBindingManager() {
17359
+ const bindings = /* @__PURE__ */ new Map();
17360
+ function bind(config, setState) {
17361
+ const id = crypto.randomUUID();
17362
+ bindings.set(id, { id, config, setState });
17363
+ return id;
17364
+ }
17365
+ function unbind(id) {
17366
+ return bindings.delete(id);
17367
+ }
17368
+ function unbindByConnection(connection) {
17369
+ for (const [id, binding] of bindings) {
17370
+ if (binding.config.connection === connection) {
17371
+ bindings.delete(id);
17372
+ }
17373
+ }
17374
+ }
17375
+ function unbindByTarget(target) {
17376
+ for (const [id, binding] of bindings) {
17377
+ if (binding.config.target === target) {
17378
+ bindings.delete(id);
17379
+ }
17380
+ }
17381
+ }
17382
+ function handleMessage(connection, data, eventType) {
17383
+ for (const binding of bindings.values()) {
17384
+ const { config, setState } = binding;
17385
+ if (config.connection !== connection) {
17386
+ continue;
17387
+ }
17388
+ if (config.eventType !== void 0) {
17389
+ if (eventType !== config.eventType) {
17390
+ continue;
17391
+ }
17392
+ }
17393
+ let value = data;
17394
+ if (config.transform) {
17395
+ value = config.transform(data);
17396
+ }
17397
+ if (config.patch === true) {
17398
+ setState(config.target, value, { patch: true });
17399
+ } else if (config.path !== void 0) {
17400
+ setState(config.target, value, config.path);
17401
+ } else {
17402
+ setState(config.target, value);
17403
+ }
17404
+ }
17405
+ }
17406
+ function getBindings() {
17407
+ return Array.from(bindings.values()).map((b2) => ({ ...b2.config }));
17408
+ }
17409
+ function dispose() {
17410
+ bindings.clear();
17411
+ }
17412
+ return {
17413
+ bind,
17414
+ unbind,
17415
+ unbindByConnection,
17416
+ unbindByTarget,
17417
+ handleMessage,
17418
+ getBindings,
17419
+ dispose
17420
+ };
17421
+ }
17422
+
15781
17423
  // src/hmr/client.ts
15782
17424
  var DEFAULT_INITIAL_RECONNECT_DELAY = 1e3;
15783
17425
  var DEFAULT_MAX_RECONNECT_DELAY = 3e4;
@@ -16073,23 +17715,288 @@ function createErrorOverlay() {
16073
17715
  }
16074
17716
  };
16075
17717
  }
17718
+
17719
+ // src/theme/provider.ts
17720
+ var DEFAULT_COLORS = {};
17721
+ var DEFAULT_FONTS = {};
17722
+ var DEFAULT_STORAGE_KEY = "constela-theme-mode";
17723
+ function createThemeProvider(options) {
17724
+ const config = options?.config ?? {};
17725
+ const storageKey = options?.storageKey ?? DEFAULT_STORAGE_KEY;
17726
+ const useCookies = options?.useCookies ?? false;
17727
+ const defaultMode = options?.defaultMode ?? "system";
17728
+ let lightColors = { ...DEFAULT_COLORS, ...config.colors };
17729
+ let darkColors = { ...DEFAULT_COLORS, ...config.darkColors };
17730
+ const fonts = { ...DEFAULT_FONTS, ...config.fonts };
17731
+ const cssPrefix = config.cssPrefix ?? "";
17732
+ let selectedMode = loadPersistedMode() ?? config.mode ?? defaultMode;
17733
+ const subscribers = /* @__PURE__ */ new Set();
17734
+ let destroyed = false;
17735
+ const mediaQuery = typeof window !== "undefined" ? window.matchMedia("(prefers-color-scheme: dark)") : null;
17736
+ function handleMediaChange(event) {
17737
+ if (destroyed) return;
17738
+ if (selectedMode === "system") {
17739
+ applyTheme();
17740
+ notifySubscribers();
17741
+ }
17742
+ }
17743
+ if (mediaQuery) {
17744
+ mediaQuery.addEventListener("change", handleMediaChange);
17745
+ }
17746
+ function loadPersistedMode() {
17747
+ if (typeof window === "undefined") return null;
17748
+ try {
17749
+ const stored = localStorage.getItem(storageKey);
17750
+ if (stored === "light" || stored === "dark" || stored === "system") {
17751
+ return stored;
17752
+ }
17753
+ } catch {
17754
+ }
17755
+ return null;
17756
+ }
17757
+ function persistMode(mode) {
17758
+ if (typeof window === "undefined") return;
17759
+ try {
17760
+ localStorage.setItem(storageKey, mode);
17761
+ } catch {
17762
+ }
17763
+ if (useCookies && typeof document !== "undefined") {
17764
+ document.cookie = `${storageKey}=${mode}; path=/; max-age=31536000; SameSite=Lax`;
17765
+ }
17766
+ }
17767
+ function resolveMode() {
17768
+ if (selectedMode === "light" || selectedMode === "dark") {
17769
+ return selectedMode;
17770
+ }
17771
+ if (mediaQuery) {
17772
+ return mediaQuery.matches ? "dark" : "light";
17773
+ }
17774
+ return "light";
17775
+ }
17776
+ function getCurrentColors() {
17777
+ const resolved = resolveMode();
17778
+ if (resolved === "dark") {
17779
+ return Object.keys(darkColors).length > 0 ? { ...lightColors, ...darkColors } : lightColors;
17780
+ }
17781
+ return lightColors;
17782
+ }
17783
+ function applyCSSVariables() {
17784
+ if (typeof document === "undefined") return;
17785
+ const root2 = document.documentElement;
17786
+ const colors = getCurrentColors();
17787
+ const prefix = cssPrefix ? `${cssPrefix}-` : "";
17788
+ for (const [key2, value] of Object.entries(colors)) {
17789
+ if (value !== void 0) {
17790
+ root2.style.setProperty(`--${prefix}${key2}`, value);
17791
+ }
17792
+ }
17793
+ for (const [key2, value] of Object.entries(fonts)) {
17794
+ if (value !== void 0) {
17795
+ root2.style.setProperty(`--font-${key2}`, value);
17796
+ }
17797
+ }
17798
+ }
17799
+ function applyDarkClass() {
17800
+ if (typeof document === "undefined") return;
17801
+ const root2 = document.documentElement;
17802
+ if (!root2) return;
17803
+ const resolved = resolveMode();
17804
+ if (resolved === "dark") {
17805
+ root2.classList.add("dark");
17806
+ } else {
17807
+ root2.classList.remove("dark");
17808
+ }
17809
+ }
17810
+ function applyTheme() {
17811
+ applyCSSVariables();
17812
+ applyDarkClass();
17813
+ }
17814
+ function notifySubscribers() {
17815
+ if (destroyed) return;
17816
+ const theme = getTheme();
17817
+ for (const fn of subscribers) {
17818
+ fn(theme);
17819
+ }
17820
+ }
17821
+ function getTheme() {
17822
+ return {
17823
+ resolvedMode: resolveMode(),
17824
+ selectedMode,
17825
+ colors: getCurrentColors(),
17826
+ fonts
17827
+ };
17828
+ }
17829
+ function getMode() {
17830
+ return selectedMode;
17831
+ }
17832
+ function setMode(mode) {
17833
+ if (destroyed) return;
17834
+ if (mode === selectedMode) return;
17835
+ selectedMode = mode;
17836
+ persistMode(mode);
17837
+ applyTheme();
17838
+ notifySubscribers();
17839
+ }
17840
+ function subscribe(fn) {
17841
+ subscribers.add(fn);
17842
+ return () => {
17843
+ subscribers.delete(fn);
17844
+ };
17845
+ }
17846
+ function setColors(colors, mode) {
17847
+ if (destroyed) return;
17848
+ const targetMode = mode ?? "light";
17849
+ if (targetMode === "dark") {
17850
+ darkColors = { ...darkColors, ...colors };
17851
+ } else {
17852
+ lightColors = { ...lightColors, ...colors };
17853
+ }
17854
+ applyTheme();
17855
+ notifySubscribers();
17856
+ }
17857
+ function destroy() {
17858
+ destroyed = true;
17859
+ subscribers.clear();
17860
+ if (mediaQuery) {
17861
+ mediaQuery.removeEventListener("change", handleMediaChange);
17862
+ }
17863
+ }
17864
+ applyTheme();
17865
+ return {
17866
+ getTheme,
17867
+ getMode,
17868
+ setMode,
17869
+ subscribe,
17870
+ setColors,
17871
+ destroy
17872
+ };
17873
+ }
17874
+
17875
+ // src/prefetch.ts
17876
+ var PREFETCH_STRATEGIES = ["hover", "visible", "immediate"];
17877
+ function isPrefetchOptions(value) {
17878
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
17879
+ return false;
17880
+ }
17881
+ const obj = value;
17882
+ if (!PREFETCH_STRATEGIES.includes(obj["strategy"])) {
17883
+ return false;
17884
+ }
17885
+ if ("threshold" in obj && obj["threshold"] !== void 0) {
17886
+ if (typeof obj["threshold"] !== "number") {
17887
+ return false;
17888
+ }
17889
+ }
17890
+ if ("rootMargin" in obj && obj["rootMargin"] !== void 0) {
17891
+ if (typeof obj["rootMargin"] !== "string") {
17892
+ return false;
17893
+ }
17894
+ }
17895
+ return true;
17896
+ }
17897
+ var globalLoader = null;
17898
+ function setGlobalLoader(loader) {
17899
+ globalLoader = loader;
17900
+ }
17901
+ function prefetchIsland(id, options) {
17902
+ if (options !== void 0 && !isPrefetchOptions(options)) {
17903
+ return;
17904
+ }
17905
+ if (globalLoader) {
17906
+ globalLoader.preload(id);
17907
+ }
17908
+ }
17909
+ function createPrefetcher(loader) {
17910
+ const prefetchedIds = /* @__PURE__ */ new Set();
17911
+ function prefetchOnce(id) {
17912
+ if (prefetchedIds.has(id)) {
17913
+ return;
17914
+ }
17915
+ prefetchedIds.add(id);
17916
+ loader.preload(id);
17917
+ }
17918
+ function prefetchOnHover(element2, id) {
17919
+ let cleaned = false;
17920
+ const handler = () => {
17921
+ if (cleaned) return;
17922
+ prefetchOnce(id);
17923
+ };
17924
+ element2.addEventListener("mouseenter", handler, { passive: true });
17925
+ return () => {
17926
+ if (cleaned) return;
17927
+ cleaned = true;
17928
+ element2.removeEventListener("mouseenter", handler, { passive: true });
17929
+ };
17930
+ }
17931
+ function prefetchOnVisible(element2, id, options) {
17932
+ let cleaned = false;
17933
+ let observer = null;
17934
+ const observerOptions = {};
17935
+ if (options?.threshold !== void 0) {
17936
+ observerOptions.threshold = options.threshold;
17937
+ }
17938
+ if (options?.rootMargin !== void 0) {
17939
+ observerOptions.rootMargin = options.rootMargin;
17940
+ }
17941
+ const callback = (entries2) => {
17942
+ for (const entry of entries2) {
17943
+ if (entry.isIntersecting && entry.target === element2) {
17944
+ prefetchOnce(id);
17945
+ if (observer) {
17946
+ observer.unobserve(element2);
17947
+ }
17948
+ break;
17949
+ }
17950
+ }
17951
+ };
17952
+ observer = new IntersectionObserver(callback, observerOptions);
17953
+ observer.observe(element2);
17954
+ return () => {
17955
+ if (cleaned) return;
17956
+ cleaned = true;
17957
+ if (observer) {
17958
+ observer.unobserve(element2);
17959
+ observer = null;
17960
+ }
17961
+ };
17962
+ }
17963
+ return {
17964
+ prefetchOnHover,
17965
+ prefetchOnVisible
17966
+ };
17967
+ }
16076
17968
  export {
17969
+ PREFETCH_STRATEGIES,
16077
17970
  createApp,
17971
+ createBindingManager,
16078
17972
  createComputed,
16079
17973
  createConnectionManager,
16080
17974
  createEffect,
16081
17975
  createErrorOverlay,
16082
17976
  createHMRClient,
16083
17977
  createHMRHandler,
17978
+ createIslandLoader,
17979
+ createOptimisticManager,
17980
+ createPrefetcher,
17981
+ createReconnectionManager,
17982
+ createSSEConnection,
17983
+ createSSEConnectionManager,
16084
17984
  createSignal,
16085
17985
  createStateStore,
17986
+ createThemeProvider,
16086
17987
  createTypedStateStore,
16087
17988
  createWebSocketConnection,
17989
+ detectIslandsInDOM,
16088
17990
  evaluate,
16089
17991
  evaluateStyle,
16090
17992
  executeAction,
16091
17993
  hydrateApp,
16092
- render
17994
+ hydrateAppWithIslands,
17995
+ hydrateIsland,
17996
+ isPrefetchOptions,
17997
+ prefetchIsland,
17998
+ render,
17999
+ setGlobalLoader
16093
18000
  };
16094
18001
  /*! Bundled license information:
16095
18002