@constela/runtime 0.13.0 → 0.15.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.
package/dist/index.d.ts CHANGED
@@ -275,6 +275,7 @@ interface ActionContext {
275
275
  eventPayload?: unknown;
276
276
  refs?: Record<string, Element>;
277
277
  subscriptions?: (() => void)[];
278
+ cleanups?: (() => void)[];
278
279
  route?: {
279
280
  params: Record<string, string>;
280
281
  query: Record<string, string>;
package/dist/index.js CHANGED
@@ -177,6 +177,7 @@ function createComputed(getter) {
177
177
  }
178
178
 
179
179
  // src/state/store.ts
180
+ import { isCookieInitialExpr } from "@constela/core";
180
181
  function normalizePath(path) {
181
182
  if (typeof path === "string") {
182
183
  return path.split(".").map((segment) => {
@@ -213,10 +214,33 @@ function setValueAtPath(obj, path, value) {
213
214
  );
214
215
  return clone3;
215
216
  }
217
+ function getCookieValue(key2) {
218
+ if (typeof document === "undefined") return void 0;
219
+ for (const cookie of document.cookie.split(";")) {
220
+ const eqIndex = cookie.indexOf("=");
221
+ if (eqIndex === -1) continue;
222
+ const name = cookie.slice(0, eqIndex).trim();
223
+ if (name === key2) {
224
+ const value = cookie.slice(eqIndex + 1);
225
+ try {
226
+ return decodeURIComponent(value);
227
+ } catch {
228
+ return value;
229
+ }
230
+ }
231
+ }
232
+ return void 0;
233
+ }
216
234
  function createStateStore(definitions) {
217
235
  const signals = /* @__PURE__ */ new Map();
218
236
  for (const [name, def] of Object.entries(definitions)) {
219
- let initialValue = def.initial;
237
+ let initialValue;
238
+ if (isCookieInitialExpr(def.initial)) {
239
+ const cookieValue = getCookieValue(def.initial.key);
240
+ initialValue = cookieValue !== void 0 ? cookieValue : def.initial.default;
241
+ } else {
242
+ initialValue = def.initial;
243
+ }
220
244
  if (name === "theme" && typeof window !== "undefined") {
221
245
  try {
222
246
  const stored = localStorage.getItem("theme");
@@ -245,6 +269,14 @@ function createStateStore(definitions) {
245
269
  if (!signal) {
246
270
  throw new Error(`State field "${name}" does not exist`);
247
271
  }
272
+ if (name === "theme" && typeof document !== "undefined") {
273
+ try {
274
+ const valueStr = typeof value === "string" ? value : JSON.stringify(value);
275
+ const oneYear = 365 * 24 * 60 * 60;
276
+ document.cookie = `theme=${encodeURIComponent(valueStr)}; path=/; max-age=${oneYear}; SameSite=Lax`;
277
+ } catch {
278
+ }
279
+ }
248
280
  signal.set(value);
249
281
  },
250
282
  subscribe(name, fn) {
@@ -428,6 +460,18 @@ function evaluate(expr, ctx) {
428
460
  return val == null ? "" : String(val);
429
461
  }).join("");
430
462
  }
463
+ case "validity": {
464
+ const element2 = ctx.refs?.[expr.ref];
465
+ if (!element2) return null;
466
+ const formElement = element2;
467
+ if (!formElement.validity) return null;
468
+ const validity = formElement.validity;
469
+ const property = expr.property || "valid";
470
+ if (property === "message") {
471
+ return formElement.validationMessage || "";
472
+ }
473
+ return validity[property] ?? null;
474
+ }
431
475
  default: {
432
476
  const _exhaustiveCheck = expr;
433
477
  throw new Error(`Unknown expression type: ${JSON.stringify(_exhaustiveCheck)}`);
@@ -593,15 +637,24 @@ async function executeAction(action, ctx) {
593
637
  const extAction = action;
594
638
  const isLocal = extAction._isLocalAction && extAction._localStore;
595
639
  const localStore = extAction._localStore;
640
+ const delayPromises = [];
596
641
  for (const step of action.steps) {
597
642
  if (step.do === "set" || step.do === "update" || step.do === "setPath") {
598
643
  executeStepSync(step, ctx, isLocal ? localStore : void 0);
599
644
  } else if (step.do === "if") {
600
645
  await executeIfStep(step, ctx, isLocal ? localStore : void 0);
646
+ } else if (step.do === "delay") {
647
+ const delayPromise = executeDelayStep(step, ctx);
648
+ delayPromises.push(delayPromise);
649
+ } else if (step.do === "interval") {
650
+ await executeIntervalStep(step, ctx);
601
651
  } else {
602
652
  await executeStep(step, ctx);
603
653
  }
604
654
  }
655
+ if (delayPromises.length > 0) {
656
+ await Promise.all(delayPromises);
657
+ }
605
658
  }
606
659
  function executeStepSync(step, ctx, localStore) {
607
660
  switch (step.do) {
@@ -892,6 +945,18 @@ async function executeStep(step, ctx) {
892
945
  case "close":
893
946
  await executeCloseStep(step, ctx);
894
947
  break;
948
+ case "delay":
949
+ await executeDelayStep(step, ctx);
950
+ break;
951
+ case "interval":
952
+ await executeIntervalStep(step, ctx);
953
+ break;
954
+ case "clearTimer":
955
+ await executeClearTimerStep(step, ctx);
956
+ break;
957
+ case "focus":
958
+ await executeFocusStep(step, ctx);
959
+ break;
895
960
  }
896
961
  }
897
962
  async function executeSetStep(target, value, ctx) {
@@ -1258,6 +1323,137 @@ async function executeCloseStep(step, ctx) {
1258
1323
  if (!ctx.connections) return;
1259
1324
  ctx.connections.close(step.connection);
1260
1325
  }
1326
+ async function executeDelayStep(step, ctx) {
1327
+ const evalCtx = createEvalContext(ctx);
1328
+ const msValue = evaluate(step.ms, evalCtx);
1329
+ const ms = typeof msValue === "number" ? Math.max(0, msValue) : 0;
1330
+ return new Promise((resolve) => {
1331
+ let resolved = false;
1332
+ const timeoutId = setTimeout(async () => {
1333
+ if (resolved) return;
1334
+ resolved = true;
1335
+ for (const thenStep of step.then) {
1336
+ if (thenStep.do === "set" || thenStep.do === "update" || thenStep.do === "setPath") {
1337
+ executeStepSync(thenStep, ctx);
1338
+ } else if (thenStep.do === "if") {
1339
+ await executeIfStep(thenStep, ctx);
1340
+ } else {
1341
+ await executeStep(thenStep, ctx);
1342
+ }
1343
+ }
1344
+ resolve();
1345
+ }, ms);
1346
+ const numericId = typeof timeoutId === "number" ? timeoutId : Number(timeoutId);
1347
+ if (step.result) {
1348
+ ctx.locals[step.result] = numericId;
1349
+ }
1350
+ if (!ctx.locals["_timerResolvers"]) {
1351
+ ctx.locals["_timerResolvers"] = /* @__PURE__ */ new Map();
1352
+ }
1353
+ ctx.locals["_timerResolvers"].set(numericId, () => {
1354
+ if (!resolved) {
1355
+ resolved = true;
1356
+ clearTimeout(timeoutId);
1357
+ resolve();
1358
+ }
1359
+ });
1360
+ if (ctx.cleanups) {
1361
+ ctx.cleanups.push(() => {
1362
+ if (!resolved) {
1363
+ resolved = true;
1364
+ clearTimeout(timeoutId);
1365
+ resolve();
1366
+ }
1367
+ });
1368
+ }
1369
+ });
1370
+ }
1371
+ async function executeIntervalStep(step, ctx) {
1372
+ const evalCtx = createEvalContext(ctx);
1373
+ const msValue = evaluate(step.ms, evalCtx);
1374
+ const ms = typeof msValue === "number" ? Math.max(0, msValue) : 0;
1375
+ const intervalId = setInterval(async () => {
1376
+ const action = ctx.actions[step.action];
1377
+ if (action) {
1378
+ await executeAction(action, ctx);
1379
+ }
1380
+ }, ms);
1381
+ const numericId = typeof intervalId === "number" ? intervalId : Number(intervalId);
1382
+ if (step.result) {
1383
+ ctx.locals[step.result] = numericId;
1384
+ }
1385
+ if (ctx.cleanups) {
1386
+ ctx.cleanups.push(() => clearInterval(intervalId));
1387
+ }
1388
+ }
1389
+ async function executeClearTimerStep(step, ctx) {
1390
+ const evalCtx = createEvalContext(ctx);
1391
+ const timerId = evaluate(step.target, evalCtx);
1392
+ if (timerId == null) {
1393
+ return;
1394
+ }
1395
+ const numericId = typeof timerId === "number" ? timerId : Number(timerId);
1396
+ const timerResolvers = ctx.locals["_timerResolvers"];
1397
+ if (timerResolvers?.has(numericId)) {
1398
+ const resolver = timerResolvers.get(numericId);
1399
+ if (resolver) {
1400
+ resolver();
1401
+ }
1402
+ timerResolvers.delete(numericId);
1403
+ }
1404
+ clearTimeout(timerId);
1405
+ clearInterval(timerId);
1406
+ }
1407
+ async function executeFocusStep(step, ctx) {
1408
+ const evalCtx = createEvalContext(ctx);
1409
+ const targetValue = evaluate(step.target, evalCtx);
1410
+ let element2;
1411
+ if (targetValue instanceof Element) {
1412
+ element2 = targetValue;
1413
+ } else if (typeof targetValue === "string") {
1414
+ element2 = ctx.refs?.[targetValue];
1415
+ }
1416
+ try {
1417
+ if (!element2) {
1418
+ const refName = typeof targetValue === "string" ? targetValue : "unknown";
1419
+ throw new Error(`Ref "${refName}" not found`);
1420
+ }
1421
+ switch (step.operation) {
1422
+ case "focus":
1423
+ if (typeof element2.focus === "function") {
1424
+ element2.focus();
1425
+ }
1426
+ break;
1427
+ case "blur":
1428
+ if (typeof element2.blur === "function") {
1429
+ element2.blur();
1430
+ }
1431
+ break;
1432
+ case "select":
1433
+ if (typeof element2.select === "function") {
1434
+ element2.select();
1435
+ } else {
1436
+ throw new Error(`Element does not support select operation`);
1437
+ }
1438
+ break;
1439
+ }
1440
+ if (step.onSuccess) {
1441
+ for (const successStep of step.onSuccess) {
1442
+ await executeStep(successStep, ctx);
1443
+ }
1444
+ }
1445
+ } catch (err) {
1446
+ ctx.locals["error"] = {
1447
+ message: err instanceof Error ? err.message : String(err),
1448
+ name: err instanceof Error ? err.name : "Error"
1449
+ };
1450
+ if (step.onError) {
1451
+ for (const errorStep of step.onError) {
1452
+ await executeStep(errorStep, ctx);
1453
+ }
1454
+ }
1455
+ }
1456
+ }
1261
1457
 
1262
1458
  // ../../node_modules/.pnpm/marked@17.0.1/node_modules/marked/lib/marked.esm.js
1263
1459
  function L() {
@@ -2691,14 +2887,14 @@ function createDOMPurify() {
2691
2887
  DocumentFragment,
2692
2888
  HTMLTemplateElement,
2693
2889
  Node: Node2,
2694
- Element,
2890
+ Element: Element2,
2695
2891
  NodeFilter,
2696
2892
  NamedNodeMap = window2.NamedNodeMap || window2.MozNamedAttrMap,
2697
2893
  HTMLFormElement,
2698
2894
  DOMParser,
2699
2895
  trustedTypes
2700
2896
  } = window2;
2701
- const ElementPrototype = Element.prototype;
2897
+ const ElementPrototype = Element2.prototype;
2702
2898
  const cloneNode = lookupGetter(ElementPrototype, "cloneNode");
2703
2899
  const remove = lookupGetter(ElementPrototype, "remove");
2704
2900
  const getNextSibling = lookupGetter(ElementPrototype, "nextSibling");
@@ -3152,7 +3348,7 @@ function createDOMPurify() {
3152
3348
  _forceRemove(currentNode);
3153
3349
  return true;
3154
3350
  }
3155
- if (currentNode instanceof Element && !_checkValidNamespace(currentNode)) {
3351
+ if (currentNode instanceof Element2 && !_checkValidNamespace(currentNode)) {
3156
3352
  _forceRemove(currentNode);
3157
3353
  return true;
3158
3354
  }
@@ -13248,6 +13444,188 @@ function isSvgTag(tag) {
13248
13444
  function isEventHandler(value) {
13249
13445
  return typeof value === "object" && value !== null && "event" in value && "action" in value;
13250
13446
  }
13447
+ function debounce(fn, wait, ctx) {
13448
+ let timeoutId = null;
13449
+ const debouncedFn = (event) => {
13450
+ if (timeoutId !== null) {
13451
+ clearTimeout(timeoutId);
13452
+ }
13453
+ timeoutId = setTimeout(() => {
13454
+ timeoutId = null;
13455
+ fn(event);
13456
+ }, wait);
13457
+ };
13458
+ ctx.cleanups?.push(() => {
13459
+ if (timeoutId !== null) {
13460
+ clearTimeout(timeoutId);
13461
+ timeoutId = null;
13462
+ }
13463
+ });
13464
+ return debouncedFn;
13465
+ }
13466
+ function throttle(fn, wait, ctx) {
13467
+ let lastTime = 0;
13468
+ let timeoutId = null;
13469
+ let lastEvent = null;
13470
+ const throttledFn = (event) => {
13471
+ const now = Date.now();
13472
+ const remaining = wait - (now - lastTime);
13473
+ if (remaining <= 0) {
13474
+ if (timeoutId !== null) {
13475
+ clearTimeout(timeoutId);
13476
+ timeoutId = null;
13477
+ }
13478
+ lastTime = now;
13479
+ fn(event);
13480
+ } else {
13481
+ lastEvent = event;
13482
+ if (timeoutId === null) {
13483
+ timeoutId = setTimeout(() => {
13484
+ timeoutId = null;
13485
+ lastTime = Date.now();
13486
+ if (lastEvent) {
13487
+ fn(lastEvent);
13488
+ lastEvent = null;
13489
+ }
13490
+ }, remaining);
13491
+ }
13492
+ }
13493
+ };
13494
+ ctx.cleanups?.push(() => {
13495
+ if (timeoutId !== null) {
13496
+ clearTimeout(timeoutId);
13497
+ timeoutId = null;
13498
+ }
13499
+ });
13500
+ return throttledFn;
13501
+ }
13502
+ function createEventCallback(handler, ctx) {
13503
+ return async (event) => {
13504
+ const action = ctx.actions[handler.action];
13505
+ if (!action) return;
13506
+ const eventLocals = {};
13507
+ const target = event.target;
13508
+ if (target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement || target instanceof HTMLSelectElement) {
13509
+ eventLocals["value"] = target.value;
13510
+ if (target instanceof HTMLInputElement && target.type === "checkbox") {
13511
+ eventLocals["checked"] = target.checked;
13512
+ }
13513
+ if (target instanceof HTMLInputElement && target.type === "file") {
13514
+ eventLocals["files"] = Array.from(target.files || []).map((f) => ({
13515
+ name: f.name,
13516
+ size: f.size,
13517
+ type: f.type,
13518
+ _file: f
13519
+ }));
13520
+ }
13521
+ }
13522
+ if (event instanceof KeyboardEvent) {
13523
+ eventLocals["key"] = event.key;
13524
+ eventLocals["code"] = event.code;
13525
+ eventLocals["ctrlKey"] = event.ctrlKey;
13526
+ eventLocals["shiftKey"] = event.shiftKey;
13527
+ eventLocals["altKey"] = event.altKey;
13528
+ eventLocals["metaKey"] = event.metaKey;
13529
+ }
13530
+ if (event instanceof MouseEvent) {
13531
+ eventLocals["clientX"] = event.clientX;
13532
+ eventLocals["clientY"] = event.clientY;
13533
+ eventLocals["pageX"] = event.pageX;
13534
+ eventLocals["pageY"] = event.pageY;
13535
+ eventLocals["button"] = event.button;
13536
+ }
13537
+ const touchEvent = event;
13538
+ if (touchEvent.touches && touchEvent.changedTouches) {
13539
+ eventLocals["touches"] = Array.from(touchEvent.touches).map((t) => ({
13540
+ clientX: t.clientX,
13541
+ clientY: t.clientY,
13542
+ pageX: t.pageX,
13543
+ pageY: t.pageY
13544
+ }));
13545
+ eventLocals["changedTouches"] = Array.from(touchEvent.changedTouches).map((t) => ({
13546
+ clientX: t.clientX,
13547
+ clientY: t.clientY,
13548
+ pageX: t.pageX,
13549
+ pageY: t.pageY
13550
+ }));
13551
+ }
13552
+ if (handler.event === "scroll" && event.target instanceof Element) {
13553
+ eventLocals["scrollTop"] = event.target.scrollTop;
13554
+ eventLocals["scrollLeft"] = event.target.scrollLeft;
13555
+ }
13556
+ let payload = void 0;
13557
+ if (handler.payload) {
13558
+ payload = evaluatePayload(handler.payload, {
13559
+ state: ctx.state,
13560
+ locals: { ...ctx.locals, ...eventLocals },
13561
+ ...ctx.imports && { imports: ctx.imports }
13562
+ });
13563
+ }
13564
+ const actionCtx = {
13565
+ state: ctx.state,
13566
+ actions: ctx.actions,
13567
+ locals: { ...ctx.locals, ...eventLocals, payload },
13568
+ eventPayload: payload
13569
+ };
13570
+ await executeAction(action, actionCtx);
13571
+ };
13572
+ }
13573
+ function wrapWithDebounceThrottle(callback, handler, ctx) {
13574
+ if (handler.debounce !== void 0 && handler.debounce >= 0) {
13575
+ return debounce(callback, handler.debounce, ctx);
13576
+ }
13577
+ if (handler.throttle !== void 0 && handler.throttle >= 0) {
13578
+ return throttle(callback, handler.throttle, ctx);
13579
+ }
13580
+ return callback;
13581
+ }
13582
+ function setupIntersectionObserver(el, handler, ctx) {
13583
+ const options = {};
13584
+ if (handler.options?.threshold !== void 0) {
13585
+ options.threshold = handler.options.threshold;
13586
+ }
13587
+ if (handler.options?.rootMargin !== void 0) {
13588
+ options.rootMargin = handler.options.rootMargin;
13589
+ }
13590
+ let hasTriggered = false;
13591
+ const observer = new IntersectionObserver((entries2) => {
13592
+ for (const entry of entries2) {
13593
+ if (entry.target !== el) continue;
13594
+ if (handler.options?.once && hasTriggered) {
13595
+ continue;
13596
+ }
13597
+ const action = ctx.actions[handler.action];
13598
+ if (!action) continue;
13599
+ const intersectLocals = {
13600
+ isIntersecting: entry.isIntersecting,
13601
+ intersectionRatio: entry.intersectionRatio
13602
+ };
13603
+ let payload = void 0;
13604
+ if (handler.payload) {
13605
+ payload = evaluatePayload(handler.payload, {
13606
+ state: ctx.state,
13607
+ locals: { ...ctx.locals, ...intersectLocals },
13608
+ ...ctx.imports && { imports: ctx.imports }
13609
+ });
13610
+ }
13611
+ const actionCtx = {
13612
+ state: ctx.state,
13613
+ actions: ctx.actions,
13614
+ locals: { ...ctx.locals, ...intersectLocals, payload },
13615
+ eventPayload: payload
13616
+ };
13617
+ executeAction(action, actionCtx);
13618
+ if (handler.options?.once) {
13619
+ hasTriggered = true;
13620
+ observer.unobserve(el);
13621
+ }
13622
+ }
13623
+ }, options);
13624
+ observer.observe(el);
13625
+ ctx.cleanups?.push(() => {
13626
+ observer.disconnect();
13627
+ });
13628
+ }
13251
13629
  function render(node, ctx) {
13252
13630
  switch (node.kind) {
13253
13631
  case "element":
@@ -13262,6 +13640,8 @@ function render(node, ctx) {
13262
13640
  return renderMarkdown(node, ctx);
13263
13641
  case "code":
13264
13642
  return renderCode(node, ctx);
13643
+ case "portal":
13644
+ return renderPortal(node, ctx);
13265
13645
  case "localState":
13266
13646
  return renderLocalState(node, ctx);
13267
13647
  default:
@@ -13284,34 +13664,13 @@ function renderElement(node, ctx) {
13284
13664
  if (isEventHandler(propValue)) {
13285
13665
  const handler = propValue;
13286
13666
  const eventName = handler.event;
13287
- el.addEventListener(eventName, async (event) => {
13288
- const action = ctx.actions[handler.action];
13289
- if (action) {
13290
- const eventLocals = {};
13291
- const target = event.target;
13292
- if (target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement || target instanceof HTMLSelectElement) {
13293
- eventLocals["value"] = target.value;
13294
- if (target instanceof HTMLInputElement && target.type === "checkbox") {
13295
- eventLocals["checked"] = target.checked;
13296
- }
13297
- }
13298
- let payload = void 0;
13299
- if (handler.payload) {
13300
- payload = evaluatePayload(handler.payload, {
13301
- state: ctx.state,
13302
- locals: { ...ctx.locals, ...eventLocals },
13303
- ...ctx.imports && { imports: ctx.imports }
13304
- });
13305
- }
13306
- const actionCtx = {
13307
- state: ctx.state,
13308
- actions: ctx.actions,
13309
- locals: { ...ctx.locals, ...eventLocals, payload },
13310
- eventPayload: payload
13311
- };
13312
- await executeAction(action, actionCtx);
13313
- }
13314
- });
13667
+ if (eventName === "intersect") {
13668
+ setupIntersectionObserver(el, handler, ctx);
13669
+ } else {
13670
+ const eventCallback = createEventCallback(handler, ctx);
13671
+ const wrappedCallback = wrapWithDebounceThrottle(eventCallback, handler, ctx);
13672
+ el.addEventListener(eventName, wrappedCallback);
13673
+ }
13315
13674
  } else {
13316
13675
  const cleanup = createEffect(() => {
13317
13676
  const value = evaluate(propValue, { state: ctx.state, locals: ctx.locals, ...ctx.imports && { imports: ctx.imports } });
@@ -13651,6 +14010,41 @@ function renderCode(node, ctx) {
13651
14010
  ctx.cleanups?.push(cleanup);
13652
14011
  return container;
13653
14012
  }
14013
+ function renderPortal(node, ctx) {
14014
+ let targetElement = null;
14015
+ if (node.target === "body") {
14016
+ targetElement = document.body;
14017
+ } else if (node.target === "head") {
14018
+ targetElement = document.head;
14019
+ } else {
14020
+ targetElement = document.querySelector(node.target);
14021
+ }
14022
+ if (!targetElement) {
14023
+ return document.createComment("portal:target-not-found");
14024
+ }
14025
+ const portalContainer = document.createElement("div");
14026
+ portalContainer.setAttribute("data-portal", "true");
14027
+ portalContainer.style.display = "contents";
14028
+ const portalCleanups = [];
14029
+ const portalCtx = {
14030
+ ...ctx,
14031
+ cleanups: portalCleanups
14032
+ };
14033
+ for (const child of node.children) {
14034
+ const childNode = render(child, portalCtx);
14035
+ portalContainer.appendChild(childNode);
14036
+ }
14037
+ targetElement.appendChild(portalContainer);
14038
+ ctx.cleanups?.push(() => {
14039
+ for (const cleanup of portalCleanups) {
14040
+ cleanup();
14041
+ }
14042
+ if (portalContainer.parentNode) {
14043
+ portalContainer.parentNode.removeChild(portalContainer);
14044
+ }
14045
+ });
14046
+ return document.createComment("portal");
14047
+ }
13654
14048
  function createLocalStateStore(stateDefs) {
13655
14049
  const signals = {};
13656
14050
  for (const [name, def] of Object.entries(stateDefs)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@constela/runtime",
3
- "version": "0.13.0",
3
+ "version": "0.15.0",
4
4
  "description": "Runtime DOM renderer for Constela UI framework",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -18,8 +18,8 @@
18
18
  "dompurify": "^3.3.1",
19
19
  "marked": "^17.0.1",
20
20
  "shiki": "^3.20.0",
21
- "@constela/compiler": "0.10.0",
22
- "@constela/core": "0.10.0"
21
+ "@constela/compiler": "0.11.1",
22
+ "@constela/core": "0.12.0"
23
23
  },
24
24
  "devDependencies": {
25
25
  "@types/dompurify": "^3.2.0",
@@ -29,7 +29,7 @@
29
29
  "tsup": "^8.0.0",
30
30
  "typescript": "^5.3.0",
31
31
  "vitest": "^2.0.0",
32
- "@constela/server": "6.0.0"
32
+ "@constela/server": "8.0.0"
33
33
  },
34
34
  "engines": {
35
35
  "node": ">=20.0.0"