olova 2.0.64 → 2.0.68

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/core.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { E as EffectCallback, a as EffectCleanup, b as EffectMode, S as Signal, c as SignalState, d as beginHmrStateCapture, e as computed, f as derived, g as effect, h as endHmrStateCapture, s as state } from './signals-core-BdfWh1Yt.js';
2
- export { CompiledDescriptor, DangerousHtml, OlovaComponent, OlovaProps, OlovaSlots, SetupFn, createApp, dangerouslySetHtml, defineComponent, getContext, hasContext, mount, replaceComponent, setContext } from './runtime.js';
1
+ export { E as EffectCallback, a as EffectCleanup, R as RuntimeErrorHandler, S as Signal, b as SignalState, c as beginHmrStateCapture, d as computed, e as derived, f as effect, g as endHmrStateCapture, h as getReactiveProxyVersion, s as state, w as withRuntimeErrorHandler } from './signals-core-BWZ5zXK5.js';
2
+ export { CompiledDescriptor, DangerousHtml, ErrorBoundaryFallback, OlovaComponent, OlovaProps, OlovaSlots, SetupFn, Suspense, createApp, createErrorBoundary, dangerouslySetHtml, defineComponent, getContext, hasContext, mount, onCleanup, onMount, replaceComponent, setContext } from './runtime.js';
3
3
  export { global } from './global.js';
4
4
  import '@preact/signals-core';
package/dist/core.js CHANGED
@@ -1,10 +1,11 @@
1
1
  import { signal, effect as effect$1, computed as computed$1, untracked } from '@preact/signals-core';
2
2
 
3
3
  // core/signals-core.ts
4
- var rawToProxy = /* @__PURE__ */ new WeakMap();
4
+ var rawToProxyByOwner = /* @__PURE__ */ new WeakMap();
5
5
  var proxyToRaw = /* @__PURE__ */ new WeakMap();
6
6
  var proxyOwner = /* @__PURE__ */ new WeakMap();
7
7
  var hmrCaptureStack = [];
8
+ var runtimeErrorHandlerStack = [];
8
9
  function isObject(value) {
9
10
  return typeof value === "object" && value !== null;
10
11
  }
@@ -40,6 +41,9 @@ var Signal = class {
40
41
  notify() {
41
42
  this.version.value = this.version.peek() + 1;
42
43
  }
44
+ peekVersion() {
45
+ return this.version.peek();
46
+ }
43
47
  subscribe(fn) {
44
48
  return this.source.subscribe(fn);
45
49
  }
@@ -63,7 +67,8 @@ var Signal = class {
63
67
  return this.proxy;
64
68
  }
65
69
  wrapObject(value) {
66
- const existing = rawToProxy.get(value);
70
+ const ownedProxyMap = rawToProxyByOwner.get(value);
71
+ const existing = ownedProxyMap?.get(this);
67
72
  if (existing) {
68
73
  return existing;
69
74
  }
@@ -90,7 +95,11 @@ var Signal = class {
90
95
  return changed;
91
96
  }
92
97
  });
93
- rawToProxy.set(value, proxy);
98
+ const nextOwnedProxyMap = ownedProxyMap ?? /* @__PURE__ */ new WeakMap();
99
+ nextOwnedProxyMap.set(this, proxy);
100
+ if (!ownedProxyMap) {
101
+ rawToProxyByOwner.set(value, nextOwnedProxyMap);
102
+ }
94
103
  proxyToRaw.set(proxy, value);
95
104
  proxyOwner.set(proxy, this);
96
105
  return proxy;
@@ -122,15 +131,59 @@ function state(initial, hmrKey) {
122
131
  }
123
132
  return signal;
124
133
  }
125
- function effect(fn, options) {
126
- const stop = effect$1(() => fn());
134
+ function getReactiveProxyVersion(value) {
135
+ if (!isObject(value)) {
136
+ return null;
137
+ }
138
+ const owner = proxyOwner.get(value);
139
+ return owner ? owner.peekVersion() : null;
140
+ }
141
+ function reportRuntimeError(error, handler) {
142
+ if (handler) {
143
+ handler(error);
144
+ return;
145
+ }
146
+ throw error;
147
+ }
148
+ function withRuntimeErrorHandler(handler, fn) {
149
+ if (!handler) {
150
+ return fn();
151
+ }
152
+ runtimeErrorHandlerStack.push(handler);
153
+ try {
154
+ return fn();
155
+ } finally {
156
+ runtimeErrorHandlerStack.pop();
157
+ }
158
+ }
159
+ function effect(fn) {
160
+ const handler = runtimeErrorHandlerStack[runtimeErrorHandlerStack.length - 1] ?? null;
161
+ const stop = effect$1(
162
+ () => withRuntimeErrorHandler(handler, () => {
163
+ try {
164
+ const cleanup = fn();
165
+ if (typeof cleanup !== "function") {
166
+ return;
167
+ }
168
+ return () => withRuntimeErrorHandler(handler, () => {
169
+ try {
170
+ cleanup();
171
+ } catch (error) {
172
+ reportRuntimeError(error, handler);
173
+ }
174
+ });
175
+ } catch (error) {
176
+ reportRuntimeError(error, handler);
177
+ }
178
+ })
179
+ );
127
180
  return () => stop();
128
181
  }
129
182
  function computed(getter) {
130
183
  const version = signal(0);
131
184
  const source = computed$1(() => getter());
132
185
  effect$1(() => {
133
- getter();
186
+ source.value;
134
187
  version.value = version.peek() + 1;
135
188
  });
136
189
  return new Signal(untracked(() => source.value), source, version);
@@ -162,6 +215,40 @@ function endHmrStateCapture() {
162
215
  // runtime/component.ts
163
216
  var EMPTY_SLOTS = Object.freeze({});
164
217
  var currentSetupContext = null;
218
+ var currentLifecycleHooks = null;
219
+ var ERROR_BOUNDARY = /* @__PURE__ */ Symbol("olova.error-boundary");
220
+ var SUSPENSE_BOUNDARY = /* @__PURE__ */ Symbol("olova.suspense-boundary");
221
+ function isPromiseLike(value) {
222
+ return !!value && typeof value === "object" && "then" in value;
223
+ }
224
+ function toOlovaError(error, context) {
225
+ if (error instanceof Error && error.message.startsWith("[olova]")) {
226
+ return error;
227
+ }
228
+ const original = error instanceof Error ? error : new Error(String(error));
229
+ const wrapped = new Error(`[olova] ${context}: ${original.message}`);
230
+ wrapped.cause = error;
231
+ return wrapped;
232
+ }
233
+ function getBoundaryHandler(context) {
234
+ return context?.get(ERROR_BOUNDARY) ?? null;
235
+ }
236
+ function handleRuntimeError(error, contextMessage, context) {
237
+ const normalized = toOlovaError(error, contextMessage);
238
+ const boundaryHandler = getBoundaryHandler(context);
239
+ if (boundaryHandler) {
240
+ boundaryHandler(normalized);
241
+ return;
242
+ }
243
+ throw normalized;
244
+ }
245
+ function runWithRuntimeErrorHandler(context, action, fn) {
246
+ try {
247
+ return withRuntimeErrorHandler(getBoundaryHandler(context), fn);
248
+ } catch (error) {
249
+ return handleRuntimeError(error, action, context);
250
+ }
251
+ }
165
252
  function requireSetupContext(apiName) {
166
253
  if (!currentSetupContext) {
167
254
  throw new Error(
@@ -220,12 +307,12 @@ function sanitizeHtml(html) {
220
307
  }
221
308
  for (const attr of Array.from(element.attributes)) {
222
309
  const attrName = attr.name.toLowerCase();
223
- const attrValue = attr.value.trim().toLowerCase();
310
+ const attrValue = attr.value.trim();
224
311
  if (attrName.startsWith("on")) {
225
312
  element.removeAttribute(attr.name);
226
313
  continue;
227
314
  }
228
- if ((attrName === "href" || attrName === "src" || attrName === "xlink:href") && attrValue.startsWith("javascript:")) {
315
+ if ((attrName === "href" || attrName === "src" || attrName === "xlink:href") && !isSafeUrl(attrName, attrValue)) {
229
316
  element.removeAttribute(attr.name);
230
317
  }
231
318
  }
@@ -244,7 +331,27 @@ function toHtml(value) {
244
331
  }
245
332
  return sanitizeHtml(String(value));
246
333
  }
247
- function shallowEqual(a, b) {
334
+ var SAFE_URL_PROTOCOLS = /* @__PURE__ */ new Set(["http", "https", "mailto", "tel"]);
335
+ var SAFE_IMAGE_DATA_URL_RE = /^data:image\/(?:png|gif|jpe?g|webp|avif|bmp);(?:charset=[^;,]+;)?base64,[a-z0-9+/=\s]+$/i;
336
+ function isSafeUrl(attrName, value) {
337
+ const normalized = value.replace(/[\u0000-\u001F\u007F\s]+/g, "");
338
+ if (!normalized) {
339
+ return true;
340
+ }
341
+ if (normalized.startsWith("#") || normalized.startsWith("/") || normalized.startsWith("./") || normalized.startsWith("../") || normalized.startsWith("?")) {
342
+ return true;
343
+ }
344
+ const match = normalized.match(/^([a-z0-9+.-]+):/i);
345
+ if (!match) {
346
+ return true;
347
+ }
348
+ const protocol = match[1].toLowerCase();
349
+ if (SAFE_URL_PROTOCOLS.has(protocol)) {
350
+ return true;
351
+ }
352
+ return protocol === "data" && attrName === "src" && SAFE_IMAGE_DATA_URL_RE.test(normalized);
353
+ }
354
+ function shallowEqualSlots(a, b) {
248
355
  if (a === b) {
249
356
  return true;
250
357
  }
@@ -263,20 +370,39 @@ function shallowEqual(a, b) {
263
370
  }
264
371
  return true;
265
372
  }
266
- function shallowEqualSlots(a, b) {
267
- if (a === b) {
373
+ function createComparablePropsSnapshot(props) {
374
+ return Object.fromEntries(
375
+ Object.entries(props).map(([key, value]) => [
376
+ key,
377
+ {
378
+ value,
379
+ reactiveVersion: getReactiveProxyVersion(value)
380
+ }
381
+ ])
382
+ );
383
+ }
384
+ function shallowComparablePropsEqual(snapshot, props) {
385
+ if (!snapshot && !props) {
268
386
  return true;
269
387
  }
270
- if (!a || !b) {
388
+ if (!snapshot || !props) {
271
389
  return false;
272
390
  }
273
- const aKeys = Object.keys(a);
274
- const bKeys = Object.keys(b);
275
- if (aKeys.length !== bKeys.length) {
391
+ const keys = Object.keys(snapshot);
392
+ const propKeys = Object.keys(props);
393
+ if (keys.length !== propKeys.length) {
276
394
  return false;
277
395
  }
278
- for (const key of aKeys) {
279
- if (!Object.is(a[key], b[key])) {
396
+ for (const key of keys) {
397
+ if (!(key in props)) {
398
+ return false;
399
+ }
400
+ const entry = snapshot[key];
401
+ const nextValue = props[key];
402
+ if (!Object.is(entry.value, nextValue)) {
403
+ return false;
404
+ }
405
+ if (entry.reactiveVersion !== getReactiveProxyVersion(nextValue)) {
280
406
  return false;
281
407
  }
282
408
  }
@@ -309,7 +435,7 @@ function bindJsxEventsInRange(range) {
309
435
  const listeners = [];
310
436
  const handlerIds = /* @__PURE__ */ new Set();
311
437
  const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT);
312
- let current = walker.currentNode;
438
+ let current = walker.nextNode();
313
439
  while (current) {
314
440
  let intersects = false;
315
441
  try {
@@ -429,6 +555,42 @@ function createRangeAtTokenWithinRange(hostRange, token) {
429
555
  function resolveTokenRange(target, token) {
430
556
  return target instanceof HTMLElement ? createRangeAtToken(target, token) : createRangeAtTokenWithinRange(target, token);
431
557
  }
558
+ function createLifecycleHooks() {
559
+ return {
560
+ mount: [],
561
+ cleanup: []
562
+ };
563
+ }
564
+ function flushLifecycleHooks(hooks, context) {
565
+ for (const mountHook of hooks.mount) {
566
+ const cleanup = runWithRuntimeErrorHandler(
567
+ context,
568
+ "Failed to run onMount() callback",
569
+ mountHook
570
+ );
571
+ if (typeof cleanup === "function") {
572
+ hooks.cleanup.push(cleanup);
573
+ }
574
+ }
575
+ return () => {
576
+ for (let index = hooks.cleanup.length - 1; index >= 0; index -= 1) {
577
+ runWithRuntimeErrorHandler(
578
+ context,
579
+ "Failed to run cleanup callback",
580
+ hooks.cleanup[index]
581
+ );
582
+ }
583
+ };
584
+ }
585
+ function createDescriptorEffect(context, fn) {
586
+ const stop = runWithRuntimeErrorHandler(
587
+ context,
588
+ "Failed to create reactive binding",
589
+ () => effect(fn)
590
+ );
591
+ return typeof stop === "function" ? stop : () => {
592
+ };
593
+ }
432
594
  function mountDescriptor(descriptor, target, slots, context) {
433
595
  let fragment;
434
596
  let nodes = null;
@@ -448,7 +610,7 @@ function mountDescriptor(descriptor, target, slots, context) {
448
610
  if (!textNode) {
449
611
  continue;
450
612
  }
451
- const stop = effect(() => {
613
+ const stop = createDescriptorEffect(context, () => {
452
614
  textNode.data = toText(binding.get());
453
615
  });
454
616
  cleanups.push(stop);
@@ -459,7 +621,7 @@ function mountDescriptor(descriptor, target, slots, context) {
459
621
  (element) => element.getAttribute(binding.attr) === token
460
622
  );
461
623
  for (const element of elements) {
462
- const stop = effect(() => {
624
+ const stop = createDescriptorEffect(context, () => {
463
625
  const next = binding.attr === "class" ? normalizeClassValue(binding.get()) : toAttr(binding.get());
464
626
  if (next === null) {
465
627
  element.removeAttribute(binding.attr);
@@ -478,7 +640,7 @@ function mountDescriptor(descriptor, target, slots, context) {
478
640
  }
479
641
  if (!nodes) element.removeAttribute(`data-o-on-${binding.event}`);
480
642
  let currentHandler;
481
- const stop = effect(() => {
643
+ const stop = createDescriptorEffect(context, () => {
482
644
  currentHandler = binding.get();
483
645
  });
484
646
  const listener = (event) => {
@@ -514,7 +676,7 @@ function mountDescriptor(descriptor, target, slots, context) {
514
676
  let lastHtml = "__OLOVA_INIT__";
515
677
  let cleanupJsxEvents = () => {
516
678
  };
517
- const stop = effect(() => {
679
+ const stop = createDescriptorEffect(context, () => {
518
680
  const nextHtml = toHtml(binding.get());
519
681
  if (nextHtml === lastHtml) {
520
682
  return;
@@ -550,7 +712,15 @@ function mountDescriptor(descriptor, target, slots, context) {
550
712
  range.deleteContents();
551
713
  continue;
552
714
  }
553
- const slotDescriptor = slotFactory();
715
+ const slotDescriptor = runWithRuntimeErrorHandler(
716
+ context,
717
+ `Failed to render slot "${binding.name}"`,
718
+ slotFactory
719
+ );
720
+ if (!slotDescriptor) {
721
+ range.deleteContents();
722
+ continue;
723
+ }
554
724
  const disposeSlot = mountDescriptor(slotDescriptor, range, slots, context);
555
725
  cleanups.push(disposeSlot);
556
726
  }
@@ -573,17 +743,17 @@ function mountDescriptor(descriptor, target, slots, context) {
573
743
  let lastComponent = null;
574
744
  let lastProps;
575
745
  let lastSlots;
576
- const stop = effect(() => {
746
+ const stop = createDescriptorEffect(context, () => {
577
747
  const nextComponent = binding.getComponent();
578
748
  const nextProps = binding.getProps();
579
749
  const nextSlots = binding.getSlots ? binding.getSlots() : EMPTY_SLOTS;
580
- if (lastComponent === nextComponent && shallowEqual(lastProps, nextProps) && shallowEqualSlots(lastSlots, nextSlots)) {
750
+ if (lastComponent === nextComponent && shallowComparablePropsEqual(lastProps, nextProps) && shallowEqualSlots(lastSlots, nextSlots)) {
581
751
  return;
582
752
  }
583
753
  disposeChild();
584
754
  disposeChild = mount(nextComponent, range, nextProps, nextSlots, context);
585
755
  lastComponent = nextComponent;
586
- lastProps = { ...nextProps };
756
+ lastProps = createComparablePropsSnapshot(nextProps);
587
757
  lastSlots = nextSlots;
588
758
  });
589
759
  cleanups.push(() => {
@@ -609,17 +779,39 @@ function mountDescriptor(descriptor, target, slots, context) {
609
779
  let disposeBranch = () => {
610
780
  };
611
781
  let lastCondition = void 0;
612
- const stop = effect(() => {
782
+ const stop = createDescriptorEffect(context, () => {
613
783
  const condition = !!binding.get();
614
784
  if (condition === lastCondition) {
615
785
  return;
616
786
  }
617
787
  disposeBranch();
618
788
  if (condition) {
619
- const branchDescriptor = binding.trueBranch();
789
+ const branchDescriptor = runWithRuntimeErrorHandler(
790
+ context,
791
+ "Failed to render conditional branch",
792
+ binding.trueBranch
793
+ );
794
+ if (!branchDescriptor) {
795
+ range.deleteContents();
796
+ disposeBranch = () => {
797
+ };
798
+ lastCondition = condition;
799
+ return;
800
+ }
620
801
  disposeBranch = mountDescriptor(branchDescriptor, range, slots, context);
621
802
  } else if (binding.falseBranch) {
622
- const branchDescriptor = binding.falseBranch();
803
+ const branchDescriptor = runWithRuntimeErrorHandler(
804
+ context,
805
+ "Failed to render conditional fallback branch",
806
+ binding.falseBranch
807
+ );
808
+ if (!branchDescriptor) {
809
+ range.deleteContents();
810
+ disposeBranch = () => {
811
+ };
812
+ lastCondition = condition;
813
+ return;
814
+ }
623
815
  disposeBranch = mountDescriptor(branchDescriptor, range, slots, context);
624
816
  } else {
625
817
  range.deleteContents();
@@ -655,22 +847,69 @@ function unregisterMountedInstance(instance) {
655
847
  function renderMountedInstance(instance, restoredValues) {
656
848
  const context = new Map(instance.parentContext ?? []);
657
849
  const previousContext = currentSetupContext;
850
+ const previousLifecycleHooks = currentLifecycleHooks;
851
+ const lifecycleHooks = createLifecycleHooks();
658
852
  currentSetupContext = context;
853
+ currentLifecycleHooks = lifecycleHooks;
659
854
  beginHmrStateCapture(restoredValues);
660
- try {
661
- const descriptor = instance.component.setup(instance.props, instance.slots);
662
- instance.disposeDescriptor = mountDescriptor(
855
+ const finalizeRender = (descriptor) => {
856
+ const disposeDescriptor = mountDescriptor(
663
857
  descriptor,
664
858
  instance.target,
665
859
  instance.slots,
666
860
  context
667
861
  );
862
+ const disposeLifecycle = flushLifecycleHooks(lifecycleHooks, context);
863
+ instance.disposeDescriptor = () => {
864
+ disposeLifecycle();
865
+ disposeDescriptor();
866
+ };
867
+ };
868
+ try {
869
+ const descriptor = runWithRuntimeErrorHandler(
870
+ context,
871
+ "Failed to render component setup",
872
+ () => instance.component.setup(instance.props, instance.slots)
873
+ );
874
+ if (!descriptor) {
875
+ instance.disposeDescriptor = () => {
876
+ };
877
+ instance.hmrSignals = endHmrStateCapture();
878
+ return;
879
+ }
880
+ if (isPromiseLike(descriptor)) {
881
+ const suspenseBoundary = context.get(SUSPENSE_BOUNDARY);
882
+ const settleSuspense = suspenseBoundary?.begin() ?? (() => {
883
+ });
884
+ let disposed = false;
885
+ instance.disposeDescriptor = () => {
886
+ disposed = true;
887
+ settleSuspense();
888
+ };
889
+ instance.hmrSignals = endHmrStateCapture();
890
+ descriptor.then((resolvedDescriptor) => {
891
+ if (disposed) {
892
+ return;
893
+ }
894
+ settleSuspense();
895
+ finalizeRender(resolvedDescriptor);
896
+ }).catch((error) => {
897
+ settleSuspense();
898
+ if (disposed) {
899
+ return;
900
+ }
901
+ handleRuntimeError(error, "Failed to resolve async component", context);
902
+ });
903
+ return;
904
+ }
905
+ finalizeRender(descriptor);
668
906
  instance.hmrSignals = endHmrStateCapture();
669
907
  } catch (error) {
670
908
  endHmrStateCapture();
671
909
  throw error;
672
910
  } finally {
673
911
  currentSetupContext = previousContext;
912
+ currentLifecycleHooks = previousLifecycleHooks;
674
913
  }
675
914
  }
676
915
  function collectHmrSignalValues(instance) {
@@ -714,6 +953,124 @@ function replaceComponent(current, next) {
714
953
  function defineComponent(setup, hmrId) {
715
954
  return { setup, __hmrId: hmrId };
716
955
  }
956
+ function isOlovaComponent(value) {
957
+ return typeof value === "object" && value !== null && "setup" in value;
958
+ }
959
+ function onMount(fn) {
960
+ if (!currentLifecycleHooks) {
961
+ throw new Error("[olova] onMount() can only be used during component setup.");
962
+ }
963
+ currentLifecycleHooks.mount.push(fn);
964
+ }
965
+ function onCleanup(fn) {
966
+ if (!currentLifecycleHooks) {
967
+ throw new Error("[olova] onCleanup() can only be used during component setup.");
968
+ }
969
+ currentLifecycleHooks.cleanup.push(fn);
970
+ }
971
+ function createErrorBoundary(component, fallback) {
972
+ return defineComponent((props, slots) => {
973
+ const errorState = state(null);
974
+ setContext(ERROR_BOUNDARY, (error) => {
975
+ errorState.value = error;
976
+ });
977
+ const renderFallback = () => {
978
+ const error = errorState.value ?? new Error("[olova] Unknown boundary error.");
979
+ if (isOlovaComponent(fallback)) {
980
+ return {
981
+ template: "__O_COMP_boundary_fallback__",
982
+ textBindings: [],
983
+ htmlBindings: [],
984
+ attrBindings: [],
985
+ eventBindings: [],
986
+ slotBindings: [],
987
+ componentBindings: [
988
+ {
989
+ id: "boundary_fallback",
990
+ getComponent: () => fallback,
991
+ getProps: () => ({ error })
992
+ }
993
+ ],
994
+ ifBindings: []
995
+ };
996
+ }
997
+ return fallback(error);
998
+ };
999
+ return {
1000
+ template: "__O_IF_boundary__",
1001
+ textBindings: [],
1002
+ htmlBindings: [],
1003
+ attrBindings: [],
1004
+ eventBindings: [],
1005
+ slotBindings: [],
1006
+ componentBindings: [],
1007
+ ifBindings: [
1008
+ {
1009
+ id: "boundary",
1010
+ get: () => errorState.value === null,
1011
+ trueBranch: () => ({
1012
+ template: "__O_COMP_boundary_child__",
1013
+ textBindings: [],
1014
+ htmlBindings: [],
1015
+ attrBindings: [],
1016
+ eventBindings: [],
1017
+ slotBindings: [],
1018
+ componentBindings: [
1019
+ {
1020
+ id: "boundary_child",
1021
+ getComponent: () => component,
1022
+ getProps: () => props,
1023
+ getSlots: () => slots
1024
+ }
1025
+ ],
1026
+ ifBindings: []
1027
+ }),
1028
+ falseBranch: renderFallback
1029
+ }
1030
+ ]
1031
+ };
1032
+ }, component.__hmrId ? `${component.__hmrId}:boundary` : void 0);
1033
+ }
1034
+ var Suspense = defineComponent((_props, slots) => {
1035
+ const pending = state(0);
1036
+ setContext(SUSPENSE_BOUNDARY, {
1037
+ begin() {
1038
+ pending.value += 1;
1039
+ let settled = false;
1040
+ return () => {
1041
+ if (settled) {
1042
+ return;
1043
+ }
1044
+ settled = true;
1045
+ pending.value = Math.max(0, pending.value - 1);
1046
+ };
1047
+ }
1048
+ });
1049
+ return {
1050
+ template: `<div style="__O_ATTR_default_style__">__O_SLOT_default__</div><div style="__O_ATTR_fallback_style__">__O_SLOT_fallback__</div>`,
1051
+ textBindings: [],
1052
+ htmlBindings: [],
1053
+ attrBindings: [
1054
+ {
1055
+ id: "default_style",
1056
+ attr: "style",
1057
+ get: () => pending.value === 0 ? null : "display:none"
1058
+ },
1059
+ {
1060
+ id: "fallback_style",
1061
+ attr: "style",
1062
+ get: () => pending.value > 0 ? null : "display:none"
1063
+ }
1064
+ ],
1065
+ eventBindings: [],
1066
+ slotBindings: [
1067
+ { id: "default", name: "default" },
1068
+ { id: "fallback", name: "fallback" }
1069
+ ],
1070
+ componentBindings: [],
1071
+ ifBindings: []
1072
+ };
1073
+ });
717
1074
  function dangerouslySetHtml(value) {
718
1075
  return {
719
1076
  __dangerousHtml: true,
@@ -745,7 +1102,12 @@ function mount(component, target, props = {}, slots = EMPTY_SLOTS, parentContext
745
1102
  hmrSignals: []
746
1103
  };
747
1104
  registerMountedInstance(instance);
748
- renderMountedInstance(instance);
1105
+ try {
1106
+ renderMountedInstance(instance);
1107
+ } catch (error) {
1108
+ unregisterMountedInstance(instance);
1109
+ throw toOlovaError(error, "Failed to mount component");
1110
+ }
749
1111
  return () => {
750
1112
  unregisterMountedInstance(instance);
751
1113
  instance.disposeDescriptor();
@@ -756,7 +1118,7 @@ function createApp(root) {
756
1118
  mount(target) {
757
1119
  const element = typeof target === "string" ? document.querySelector(target) : target;
758
1120
  if (!element) {
759
- throw new Error(`Target not found: ${String(target)}`);
1121
+ throw new Error(`[olova] Target not found: ${String(target)}`);
760
1122
  }
761
1123
  return mount(root, element);
762
1124
  }
@@ -819,9 +1181,6 @@ function setupPersistence(name, stateRef, target) {
819
1181
  function ensureState(name, initial, options) {
820
1182
  const existing = globalStates.get(name);
821
1183
  if (existing) {
822
- if (existing.value === void 0 && initial !== void 0) {
823
- existing.value = initial;
824
- }
825
1184
  if (options?.persist) {
826
1185
  if (existing.value === void 0) {
827
1186
  const persisted = readPersisted(name, options.persist);
@@ -831,6 +1190,9 @@ function ensureState(name, initial, options) {
831
1190
  }
832
1191
  setupPersistence(name, existing, options.persist);
833
1192
  }
1193
+ if (existing.value === void 0 && initial !== void 0) {
1194
+ existing.value = initial;
1195
+ }
834
1196
  return existing;
835
1197
  }
836
1198
  let resolvedInitial = initial;
@@ -859,6 +1221,6 @@ var global = {
859
1221
  }
860
1222
  };
861
1223
 
862
- export { Signal, beginHmrStateCapture, computed, createApp, dangerouslySetHtml, defineComponent, derived, effect, endHmrStateCapture, getContext, global, hasContext, mount, replaceComponent, setContext, state };
1224
+ export { Signal, Suspense, beginHmrStateCapture, computed, createApp, createErrorBoundary, dangerouslySetHtml, defineComponent, derived, effect, endHmrStateCapture, getContext, getReactiveProxyVersion, global, hasContext, mount, onCleanup, onMount, replaceComponent, setContext, state, withRuntimeErrorHandler };
863
1225
  //# sourceMappingURL=core.js.map
864
1226
  //# sourceMappingURL=core.js.map