@constela/runtime 0.19.5 → 0.19.7

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 +491 -2
  2. package/dist/index.js +1981 -23
  3. package/package.json +12 -4
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 {
@@ -1337,6 +2265,30 @@ async function executeStep(step, ctx) {
1337
2265
  case "focus":
1338
2266
  await executeFocusStep(step, ctx);
1339
2267
  break;
2268
+ case "generate":
2269
+ await executeGenerateStep(step, ctx);
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;
1340
2292
  }
1341
2293
  }
1342
2294
  async function executeSetStep(target, value, ctx) {
@@ -1834,6 +2786,158 @@ async function executeFocusStep(step, ctx) {
1834
2786
  }
1835
2787
  }
1836
2788
  }
2789
+ async function executeGenerateStep(step, ctx) {
2790
+ const { createDslGenerator } = await import("@constela/ai");
2791
+ const evalCtx = createEvalContext(ctx);
2792
+ const promptValue = evaluate(step.prompt, evalCtx);
2793
+ try {
2794
+ const generator = createDslGenerator({
2795
+ provider: step.provider
2796
+ });
2797
+ const result = await generator.generate({
2798
+ prompt: promptValue,
2799
+ output: step.output
2800
+ });
2801
+ ctx.locals[step.result] = result.dsl;
2802
+ if (!result.validated && result.errors && result.errors.length > 0) {
2803
+ ctx.locals["error"] = {
2804
+ message: `AI generated DSL validation failed: ${result.errors.join(", ")}`,
2805
+ name: "ValidationError"
2806
+ };
2807
+ if (step.onError) {
2808
+ for (const errorStep of step.onError) {
2809
+ await executeStep(errorStep, ctx);
2810
+ }
2811
+ }
2812
+ return;
2813
+ }
2814
+ if (step.onSuccess) {
2815
+ for (const successStep of step.onSuccess) {
2816
+ await executeStep(successStep, ctx);
2817
+ }
2818
+ }
2819
+ } catch (err) {
2820
+ ctx.locals["error"] = {
2821
+ message: err instanceof Error ? err.message : String(err),
2822
+ name: err instanceof Error ? err.name : "Error"
2823
+ };
2824
+ if (step.onError) {
2825
+ for (const errorStep of step.onError) {
2826
+ await executeStep(errorStep, ctx);
2827
+ }
2828
+ }
2829
+ }
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
+ }
1837
2941
 
1838
2942
  // ../../node_modules/.pnpm/marked@17.0.1/node_modules/marked/lib/marked.esm.js
1839
2943
  function L() {
@@ -14585,19 +15689,155 @@ function createApp(program, mount) {
14585
15689
  mount.removeChild(mount.firstChild);
14586
15690
  }
14587
15691
  },
14588
- setState(name, value) {
14589
- if (destroyed) return;
14590
- state.set(name, value);
14591
- },
14592
- getState(name) {
14593
- return state.get(name);
14594
- },
14595
- subscribe(name, fn) {
14596
- if (destroyed) return () => {
14597
- };
14598
- return state.subscribe(name, fn);
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
+ }
15770
+ },
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);
15796
+ }
15797
+ };
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);
14599
15815
  }
14600
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;
14601
15841
  }
14602
15842
 
14603
15843
  // src/hydrate.ts
@@ -15642,6 +16882,79 @@ function initCopyButtons(container) {
15642
16882
  });
15643
16883
  });
15644
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
+ }
15645
16958
 
15646
16959
  // src/connection/websocket.ts
15647
16960
  function createWebSocketConnection(url, handlers) {
@@ -15733,6 +17046,380 @@ function createConnectionManager() {
15733
17046
  };
15734
17047
  }
15735
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
+
15736
17423
  // src/hmr/client.ts
15737
17424
  var DEFAULT_INITIAL_RECONNECT_DELAY = 1e3;
15738
17425
  var DEFAULT_MAX_RECONNECT_DELAY = 3e4;
@@ -15906,21 +17593,27 @@ function createHMRApp(program, container, route, existingStateStore) {
15906
17593
  };
15907
17594
  }
15908
17595
  function createHMRHandler(options) {
15909
- const { container, program, route } = options;
17596
+ const { container, program, route, skipInitialRender } = options;
15910
17597
  let currentApp = null;
15911
17598
  let destroyed = false;
15912
- currentApp = createHMRApp(program, container, route);
17599
+ if (!skipInitialRender) {
17600
+ currentApp = createHMRApp(program, container, route);
17601
+ }
15913
17602
  return {
15914
17603
  handleUpdate(newProgram) {
15915
17604
  if (destroyed) {
15916
17605
  return;
15917
17606
  }
15918
- let stateSnapshot = {};
15919
- if (currentApp) {
15920
- stateSnapshot = currentApp.stateStore.serialize();
15921
- currentApp.destroy();
15922
- currentApp = null;
17607
+ if (!currentApp) {
17608
+ while (container.firstChild) {
17609
+ container.removeChild(container.firstChild);
17610
+ }
17611
+ currentApp = createHMRApp(newProgram, container, route);
17612
+ return;
15923
17613
  }
17614
+ const stateSnapshot = currentApp.stateStore.serialize();
17615
+ currentApp.destroy();
17616
+ currentApp = null;
15924
17617
  const newStateStore = createStateStore(newProgram.state);
15925
17618
  const newDefinitions = Object.entries(newProgram.state).map(
15926
17619
  ([name, def]) => ({
@@ -16022,23 +17715,288 @@ function createErrorOverlay() {
16022
17715
  }
16023
17716
  };
16024
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
+ }
16025
17968
  export {
17969
+ PREFETCH_STRATEGIES,
16026
17970
  createApp,
17971
+ createBindingManager,
16027
17972
  createComputed,
16028
17973
  createConnectionManager,
16029
17974
  createEffect,
16030
17975
  createErrorOverlay,
16031
17976
  createHMRClient,
16032
17977
  createHMRHandler,
17978
+ createIslandLoader,
17979
+ createOptimisticManager,
17980
+ createPrefetcher,
17981
+ createReconnectionManager,
17982
+ createSSEConnection,
17983
+ createSSEConnectionManager,
16033
17984
  createSignal,
16034
17985
  createStateStore,
17986
+ createThemeProvider,
16035
17987
  createTypedStateStore,
16036
17988
  createWebSocketConnection,
17989
+ detectIslandsInDOM,
16037
17990
  evaluate,
16038
17991
  evaluateStyle,
16039
17992
  executeAction,
16040
17993
  hydrateApp,
16041
- render
17994
+ hydrateAppWithIslands,
17995
+ hydrateIsland,
17996
+ isPrefetchOptions,
17997
+ prefetchIsland,
17998
+ render,
17999
+ setGlobalLoader
16042
18000
  };
16043
18001
  /*! Bundled license information:
16044
18002