lightview 2.2.2 → 2.3.4

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.
@@ -95,7 +95,10 @@
95
95
  return notFound ? notFound(ctx) : null;
96
96
  };
97
97
  const handleRequest = async (path) => {
98
+ var _a, _b;
98
99
  if (onStart) onStart(path);
100
+ const internals = (_a = globalThis.Lightview) == null ? void 0 : _a.internals;
101
+ const scrollMap = (_b = internals == null ? void 0 : internals.saveScrolls) == null ? void 0 : _b.call(internals);
99
102
  const res = await route(path);
100
103
  if (!res) return console.warn(`[Router] No route: ${path}`);
101
104
  if (res.ok && contentEl) {
@@ -106,6 +109,9 @@
106
109
  n.textContent = s.textContent;
107
110
  s.replaceWith(n);
108
111
  });
112
+ if ((internals == null ? void 0 : internals.restoreScrolls) && scrollMap) {
113
+ internals.restoreScrolls(scrollMap);
114
+ }
109
115
  const urlParts = path.split("#");
110
116
  const hash = urlParts.length > 1 ? "#" + urlParts[1] : "";
111
117
  if (hash) {
package/lightview-x.js CHANGED
@@ -1,29 +1,48 @@
1
1
  (function() {
2
2
  "use strict";
3
+ var _a, _b;
3
4
  const _LV = globalThis.__LIGHTVIEW_INTERNALS__ || (globalThis.__LIGHTVIEW_INTERNALS__ = {
4
5
  currentEffect: null,
5
6
  registry: /* @__PURE__ */ new Map(),
6
- dependencyMap: /* @__PURE__ */ new WeakMap()
7
- // Tracking signals -> subscribers
7
+ // Global name -> Signal/Proxy
8
+ localRegistries: /* @__PURE__ */ new WeakMap(),
9
+ // Object/Element -> Map(name -> Signal/Proxy)
10
+ futureSignals: /* @__PURE__ */ new Map(),
11
+ // name -> Set of (signal) => void
12
+ schemas: /* @__PURE__ */ new Map(),
13
+ // name -> Schema (Draft 7+ or Shorthand)
14
+ parents: /* @__PURE__ */ new WeakMap(),
15
+ // Proxy -> Parent (Proxy/Element)
16
+ helpers: /* @__PURE__ */ new Map(),
17
+ // name -> function (used for transforms and expressions)
18
+ hooks: {
19
+ validate: (value, schema) => true
20
+ // Hook for extensions (like JPRX) to provide full validation
21
+ }
8
22
  });
23
+ const lookup = (name, scope) => {
24
+ let current = scope;
25
+ while (current && typeof current === "object") {
26
+ const registry = _LV.localRegistries.get(current);
27
+ if (registry && registry.has(name)) return registry.get(name);
28
+ current = current.parentElement || _LV.parents.get(current);
29
+ }
30
+ return _LV.registry.get(name);
31
+ };
9
32
  const signal = (initialValue, optionsOrName) => {
10
- let name = typeof optionsOrName === "string" ? optionsOrName : optionsOrName == null ? void 0 : optionsOrName.name;
33
+ const name = typeof optionsOrName === "string" ? optionsOrName : optionsOrName == null ? void 0 : optionsOrName.name;
11
34
  const storage = optionsOrName == null ? void 0 : optionsOrName.storage;
35
+ const scope = optionsOrName == null ? void 0 : optionsOrName.scope;
12
36
  if (name && storage) {
13
37
  try {
14
38
  const stored = storage.getItem(name);
15
- if (stored !== null) {
16
- initialValue = JSON.parse(stored);
17
- }
39
+ if (stored !== null) initialValue = JSON.parse(stored);
18
40
  } catch (e) {
19
41
  }
20
42
  }
21
43
  let value = initialValue;
22
44
  const subscribers = /* @__PURE__ */ new Set();
23
- const f = (...args) => {
24
- if (args.length === 0) return f.value;
25
- f.value = args[0];
26
- };
45
+ const f = (...args) => args.length === 0 ? f.value : f.value = args[0];
27
46
  Object.defineProperty(f, "value", {
28
47
  get() {
29
48
  if (_LV.currentEffect) {
@@ -46,21 +65,39 @@
46
65
  }
47
66
  });
48
67
  if (name) {
49
- if (_LV.registry.has(name)) {
50
- if (_LV.registry.get(name) !== f) {
51
- throw new Error(`Lightview: A signal or state with the name "${name}" is already registered.`);
52
- }
53
- } else {
54
- _LV.registry.set(name, f);
68
+ const registry = scope && typeof scope === "object" ? _LV.localRegistries.get(scope) || _LV.localRegistries.set(scope, /* @__PURE__ */ new Map()).get(scope) : _LV.registry;
69
+ if (registry && registry.has(name) && registry.get(name) !== f) {
70
+ throw new Error(`Lightview: A signal or state with the name "${name}" is already registered.`);
71
+ }
72
+ if (registry) registry.set(name, f);
73
+ const futures = _LV.futureSignals.get(name);
74
+ if (futures) {
75
+ futures.forEach((resolve) => resolve(f));
55
76
  }
56
77
  }
57
78
  return f;
58
79
  };
59
- const getSignal = (name, defaultValue) => {
60
- if (!_LV.registry.has(name) && defaultValue !== void 0) {
61
- return signal(defaultValue, name);
62
- }
63
- return _LV.registry.get(name);
80
+ const getSignal = (name, defaultValueOrOptions) => {
81
+ const options = typeof defaultValueOrOptions === "object" && defaultValueOrOptions !== null ? defaultValueOrOptions : { defaultValue: defaultValueOrOptions };
82
+ const { scope, defaultValue } = options;
83
+ const existing = lookup(name, scope);
84
+ if (existing) return existing;
85
+ if (defaultValue !== void 0) return signal(defaultValue, { name, scope });
86
+ const future = signal(void 0);
87
+ const handler = (realSignal) => {
88
+ const hasValue = realSignal && (typeof realSignal === "object" || typeof realSignal === "function") && "value" in realSignal;
89
+ if (hasValue) {
90
+ future.value = realSignal.value;
91
+ effect(() => {
92
+ future.value = realSignal.value;
93
+ });
94
+ } else {
95
+ future.value = realSignal;
96
+ }
97
+ };
98
+ if (!_LV.futureSignals.has(name)) _LV.futureSignals.set(name, /* @__PURE__ */ new Set());
99
+ _LV.futureSignals.get(name).add(handler);
100
+ return future;
64
101
  };
65
102
  signal.get = getSignal;
66
103
  const effect = (fn) => {
@@ -89,9 +126,38 @@
89
126
  return execute;
90
127
  };
91
128
  const getRegistry = () => _LV.registry;
129
+ const internals = _LV;
92
130
  const stateCache = /* @__PURE__ */ new WeakMap();
93
131
  const stateSignals = /* @__PURE__ */ new WeakMap();
94
- const parents = /* @__PURE__ */ new WeakMap();
132
+ const stateSchemas = /* @__PURE__ */ new WeakMap();
133
+ const { parents, schemas, hooks } = internals;
134
+ const validate = (target, prop, value, schema) => {
135
+ var _a2, _b2;
136
+ const current = target[prop];
137
+ const type = typeof current;
138
+ const isNew = !(prop in target);
139
+ let behavior = schema;
140
+ if (typeof schema === "object" && schema !== null) behavior = schema.type;
141
+ if (behavior === "auto" && isNew) throw new Error(`Lightview: Cannot add new property "${prop}" to fixed 'auto' state.`);
142
+ if (behavior === "polymorphic" || typeof behavior === "object" && (behavior == null ? void 0 : behavior.coerce)) {
143
+ if (type === "number") return Number(value);
144
+ if (type === "boolean") return Boolean(value);
145
+ if (type === "string") return String(value);
146
+ } else if (behavior === "auto" || behavior === "dynamic") {
147
+ if (!isNew && typeof value !== type) {
148
+ throw new Error(`Lightview: Type mismatch for "${prop}". Expected ${type}, got ${typeof value}.`);
149
+ }
150
+ }
151
+ if (typeof schema === "object" && schema !== null && schema.transform) {
152
+ const trans = schema.transform;
153
+ const transformFn = typeof trans === "function" ? trans : internals.helpers.get(trans) || ((_b2 = (_a2 = globalThis.Lightview) == null ? void 0 : _a2.helpers) == null ? void 0 : _b2[trans]);
154
+ if (transformFn) value = transformFn(value);
155
+ }
156
+ if (hooks.validate(value, schema) === false) {
157
+ throw new Error(`Lightview: Validation failed for "${prop}".`);
158
+ }
159
+ return value;
160
+ };
95
161
  const protoMethods = (proto, test) => Object.getOwnPropertyNames(proto).filter((k) => typeof proto[k] === "function" && test(k));
96
162
  const DATE_TRACKING = protoMethods(Date.prototype, (k) => /^(to|get|valueOf)/.test(k));
97
163
  const DATE_MUTATING = protoMethods(Date.prototype, (k) => /^set/.test(k));
@@ -143,12 +209,14 @@
143
209
  return val;
144
210
  };
145
211
  const proxySet = (target, prop, value, receiver, signals) => {
212
+ const schema = stateSchemas.get(receiver);
213
+ const validatedValue = schema ? validate(target, prop, value, schema) : value;
146
214
  if (!signals.has(prop)) {
147
215
  signals.set(prop, signal(Reflect.get(target, prop, receiver)));
148
216
  }
149
- const success = Reflect.set(target, prop, value, receiver);
217
+ const success = Reflect.set(target, prop, validatedValue, receiver);
150
218
  const signal$1 = signals.get(prop);
151
- if (success && signal$1) signal$1.value = value;
219
+ if (success && signal$1) signal$1.value = validatedValue;
152
220
  return success;
153
221
  };
154
222
  const createSpecialProxy = (obj, monitor, trackingProps = []) => {
@@ -222,6 +290,8 @@
222
290
  if (typeof obj !== "object" || obj === null) return obj;
223
291
  const name = typeof optionsOrName === "string" ? optionsOrName : optionsOrName == null ? void 0 : optionsOrName.name;
224
292
  const storage = optionsOrName == null ? void 0 : optionsOrName.storage;
293
+ const scope = optionsOrName == null ? void 0 : optionsOrName.scope;
294
+ const schema = optionsOrName == null ? void 0 : optionsOrName.schema;
225
295
  if (name && storage) {
226
296
  try {
227
297
  const item = storage.getItem(name);
@@ -250,6 +320,7 @@
250
320
  stateCache.set(obj, proxy);
251
321
  } else return obj;
252
322
  }
323
+ if (schema) stateSchemas.set(proxy, schema);
253
324
  if (name && storage) {
254
325
  effect(() => {
255
326
  try {
@@ -259,23 +330,31 @@
259
330
  });
260
331
  }
261
332
  if (name) {
262
- const registry = getRegistry();
263
- if (registry.has(name)) {
264
- if (registry.get(name) !== proxy) {
265
- throw new Error(`Lightview: A signal or state with the name "${name}" is already registered.`);
266
- }
267
- } else {
268
- registry.set(name, proxy);
333
+ const registry = scope && typeof scope === "object" ? internals.localRegistries.get(scope) || internals.localRegistries.set(scope, /* @__PURE__ */ new Map()).get(scope) : getRegistry();
334
+ if (registry && registry.has(name) && registry.get(name) !== proxy) {
335
+ throw new Error(`Lightview: A signal or state with the name "${name}" is already registered.`);
336
+ }
337
+ if (registry) registry.set(name, proxy);
338
+ const futures = internals.futureSignals.get(name);
339
+ if (futures) {
340
+ futures.forEach((resolve) => resolve(proxy));
269
341
  }
270
342
  }
271
343
  return proxy;
272
344
  };
273
- const getState = (name, defaultValue) => {
274
- const registry = getRegistry();
275
- if (!registry.has(name) && defaultValue !== void 0) {
276
- return state(defaultValue, name);
277
- }
278
- return registry.get(name);
345
+ const getState = (name, defaultValueOrOptions) => {
346
+ const options = typeof defaultValueOrOptions === "object" && defaultValueOrOptions !== null ? defaultValueOrOptions : { defaultValue: defaultValueOrOptions };
347
+ const { scope, defaultValue } = options;
348
+ const existing = lookup(name, scope);
349
+ if (existing) return existing;
350
+ if (defaultValue !== void 0) return state(defaultValue, { name, scope });
351
+ const future = signal(void 0);
352
+ const handler = (realState) => {
353
+ future.value = realState;
354
+ };
355
+ if (!internals.futureSignals.has(name)) internals.futureSignals.set(name, /* @__PURE__ */ new Set());
356
+ internals.futureSignals.get(name).add(handler);
357
+ return future;
279
358
  };
280
359
  state.get = getState;
281
360
  const STANDARD_SRC_TAGS = ["img", "script", "iframe", "video", "audio", "source", "track", "embed", "input"];
@@ -309,7 +388,7 @@
309
388
  return keys.length === 1 && isValidTagName(keys[0]) && typeof obj[keys[0]] === "object";
310
389
  };
311
390
  const convertObjectDOM = (obj) => {
312
- var _a, _b;
391
+ var _a2, _b2;
313
392
  if (typeof obj !== "object" || obj === null) return obj;
314
393
  if (Array.isArray(obj)) return obj.map(convertObjectDOM);
315
394
  if (obj.tag) return { ...obj, children: obj.children ? convertObjectDOM(obj.children) : [] };
@@ -317,7 +396,7 @@
317
396
  const tagKey = Object.keys(obj)[0];
318
397
  const content = obj[tagKey];
319
398
  const LV = typeof window !== "undefined" ? globalThis.Lightview : typeof globalThis !== "undefined" ? globalThis.Lightview : null;
320
- const tag = ((_b = (_a = LV == null ? void 0 : LV.tags) == null ? void 0 : _a._customTags) == null ? void 0 : _b[tagKey]) || tagKey;
399
+ const tag = ((_b2 = (_a2 = LV == null ? void 0 : LV.tags) == null ? void 0 : _a2._customTags) == null ? void 0 : _b2[tagKey]) || tagKey;
321
400
  const { children, ...attributes } = content;
322
401
  return { tag, attributes, children: children ? convertObjectDOM(children) : [] };
323
402
  };
@@ -605,12 +684,12 @@
605
684
  const frag = document.createDocumentFragment();
606
685
  frag.appendChild(createMarker(markerId, false));
607
686
  elements.forEach((c) => {
608
- var _a, _b, _c;
687
+ var _a2, _b2, _c;
609
688
  if (typeof c === "string") frag.appendChild(document.createTextNode(c));
610
689
  else if (c.domEl) frag.appendChild(c.domEl);
611
690
  else if (c instanceof Node) frag.appendChild(c);
612
691
  else {
613
- const v = ((_c = (_a = globalThis.Lightview) == null ? void 0 : (_b = _a.hooks).processChild) == null ? void 0 : _c.call(_b, c)) || c;
692
+ const v = ((_c = (_a2 = globalThis.Lightview) == null ? void 0 : (_b2 = _a2.hooks).processChild) == null ? void 0 : _c.call(_b2, c)) || c;
614
693
  if (v.tag) {
615
694
  const n = element(v.tag, v.attributes || {}, v.children || []);
616
695
  if (n == null ? void 0 : n.domEl) frag.appendChild(n.domEl);
@@ -627,10 +706,10 @@
627
706
  };
628
707
  const isPath = (s) => typeof s === "string" && !isDangerousProtocol(s) && /^(https?:|\.|\/|[\w])|(\.(html|json|[vo]dom|cdomc?))$/i.test(s);
629
708
  const fetchContent = async (src) => {
630
- var _a;
709
+ var _a2;
631
710
  try {
632
711
  const LV = globalThis.Lightview;
633
- if (((_a = LV == null ? void 0 : LV.hooks) == null ? void 0 : _a.validateUrl) && !LV.hooks.validateUrl(src)) {
712
+ if (((_a2 = LV == null ? void 0 : LV.hooks) == null ? void 0 : _a2.validateUrl) && !LV.hooks.validateUrl(src)) {
634
713
  console.warn(`[LightviewX] Fetch blocked by validateUrl hook: ${src}`);
635
714
  return null;
636
715
  }
@@ -655,10 +734,10 @@
655
734
  }
656
735
  };
657
736
  const parseElements = (content, isJson, isHtml, el, element, isCdom = false, ext = "") => {
658
- var _a;
737
+ var _a2;
659
738
  if (isJson) return Array.isArray(content) ? content : [content];
660
739
  if (isCdom && ext === "cdomc") {
661
- const parser = (_a = globalThis.LightviewCDOM) == null ? void 0 : _a.parseCDOMC;
740
+ const parser = (_a2 = globalThis.LightviewCDOM) == null ? void 0 : _a2.parseCDOMC;
662
741
  if (parser) {
663
742
  try {
664
743
  const obj = parser(content);
@@ -691,11 +770,14 @@
691
770
  return null;
692
771
  }
693
772
  };
694
- const updateTargetContent = (el, elements, raw, loc, contentHash, { element, setupChildren }, targetHash = null) => {
773
+ const updateTargetContent = (el, elements, raw, loc, contentHash, options, targetHash = null) => {
774
+ var _a2;
775
+ const { element, setupChildren, saveScrolls, restoreScrolls } = { ...options, ...(_a2 = globalThis.Lightview) == null ? void 0 : _a2.internals };
695
776
  const markerId = `${loc}-${contentHash.slice(0, 8)}`;
696
777
  let track = getOrSet(insertedContentMap, el.domEl, () => ({}));
697
778
  if (track[loc]) removeInsertedContent(el.domEl, `${loc}-${track[loc].slice(0, 8)}`);
698
779
  track[loc] = contentHash;
780
+ const scrollMap = saveScrolls ? saveScrolls() : null;
699
781
  const performScroll = (root) => {
700
782
  if (!targetHash) return;
701
783
  requestAnimationFrame(() => {
@@ -709,18 +791,24 @@
709
791
  });
710
792
  });
711
793
  };
794
+ const runRestore = (root) => {
795
+ if (restoreScrolls && scrollMap) restoreScrolls(scrollMap, root);
796
+ };
712
797
  if (loc === "shadow") {
713
798
  if (!el.domEl.shadowRoot) el.domEl.attachShadow({ mode: "open" });
714
799
  setupChildren(elements, el.domEl.shadowRoot);
715
800
  executeScripts(el.domEl.shadowRoot);
716
801
  performScroll(el.domEl.shadowRoot);
802
+ runRestore(el.domEl.shadowRoot);
717
803
  } else if (loc === "innerhtml") {
718
804
  el.children = elements;
719
805
  executeScripts(el.domEl);
720
806
  performScroll(document);
807
+ runRestore(el.domEl);
721
808
  } else {
722
809
  insert(elements, el.domEl, loc, markerId, { element, setupChildren });
723
810
  performScroll(document);
811
+ runRestore(el.domEl);
724
812
  }
725
813
  };
726
814
  const handleSrcAttribute = async (el, src, tagName, { element, setupChildren }) => {
@@ -753,9 +841,9 @@
753
841
  if (root) {
754
842
  requestAnimationFrame(() => {
755
843
  requestAnimationFrame(() => {
756
- var _a;
844
+ var _a2;
757
845
  const id = targetHash.startsWith("#") ? targetHash.slice(1) : targetHash;
758
- const target = root.getElementById ? root.getElementById(id) : (_a = root.querySelector) == null ? void 0 : _a.call(root, `#${id}`);
846
+ const target = root.getElementById ? root.getElementById(id) : (_a2 = root.querySelector) == null ? void 0 : _a2.call(root, `#${id}`);
759
847
  if (target) {
760
848
  target.style.scrollMarginTop = "calc(var(--site-nav-height, 0px) + 2rem)";
761
849
  target.scrollIntoView({ behavior: "smooth", block: "start", inline: "start" });
@@ -782,7 +870,7 @@
782
870
  return { selector: targetStr, location: null };
783
871
  };
784
872
  const handleNonStandardHref = (e, { domToElement, wrapDomElement }) => {
785
- var _a;
873
+ var _a2;
786
874
  const clickedEl = e.target.closest("[href]");
787
875
  if (!clickedEl) return;
788
876
  const tagName = clickedEl.tagName.toLowerCase();
@@ -790,7 +878,7 @@
790
878
  e.preventDefault();
791
879
  const href = clickedEl.getAttribute("href");
792
880
  const LV = globalThis.Lightview;
793
- if (href && (isDangerousProtocol(href) || ((_a = LV == null ? void 0 : LV.hooks) == null ? void 0 : _a.validateUrl) && !LV.hooks.validateUrl(href))) {
881
+ if (href && (isDangerousProtocol(href) || ((_a2 = LV == null ? void 0 : LV.hooks) == null ? void 0 : _a2.validateUrl) && !LV.hooks.validateUrl(href))) {
794
882
  console.warn(`[LightviewX] Navigation or fetch blocked by security policy: ${href}`);
795
883
  return;
796
884
  }
@@ -950,9 +1038,9 @@
950
1038
  return { events, exclusions, calls };
951
1039
  };
952
1040
  const globalBeforeInterceptor = async (e) => {
953
- var _a, _b;
1041
+ var _a2, _b2;
954
1042
  if (e[BYPASS_FLAG]) return;
955
- const target = (_b = (_a = e.target).closest) == null ? void 0 : _b.call(_a, "[lv-before]");
1043
+ const target = (_b2 = (_a2 = e.target).closest) == null ? void 0 : _b2.call(_a2, "[lv-before]");
956
1044
  if (!target) return;
957
1045
  const { events, exclusions, calls } = parseBeforeAttribute(target.getAttribute("lv-before"));
958
1046
  const isExcluded = exclusions.includes(e.type);
@@ -1348,7 +1436,7 @@
1348
1436
  }).filter(Boolean);
1349
1437
  }
1350
1438
  render() {
1351
- var _a, _b;
1439
+ var _a2, _b2;
1352
1440
  const props = { useShadow: false };
1353
1441
  for (const attr of this.attributes) {
1354
1442
  const name = attr.name.replace(/-([a-z])/g, (g) => g[1].toUpperCase());
@@ -1372,7 +1460,7 @@
1372
1460
  const vdomChildren = this.parseChildrenToVDOM();
1373
1461
  const children = Object.keys(childElements).length > 0 ? vdomChildren : [{ tag: globalThis.Lightview.tags.slot }];
1374
1462
  const result = Component(props, ...children);
1375
- if (((_b = (_a = globalThis.Lightview) == null ? void 0 : _a.internals) == null ? void 0 : _b.setupChildren) && this.themeWrapper) {
1463
+ if (((_b2 = (_a2 = globalThis.Lightview) == null ? void 0 : _a2.internals) == null ? void 0 : _b2.setupChildren) && this.themeWrapper) {
1376
1464
  this.themeWrapper.innerHTML = "";
1377
1465
  globalThis.Lightview.internals.setupChildren([result], this.themeWrapper);
1378
1466
  }
@@ -1385,6 +1473,89 @@
1385
1473
  }
1386
1474
  };
1387
1475
  };
1476
+ const validateJSONSchema = (value, schema) => {
1477
+ var _a2;
1478
+ if (!schema) return true;
1479
+ const errors = [];
1480
+ const internals2 = (_a2 = globalThis.Lightview) == null ? void 0 : _a2.internals;
1481
+ const check = (val, sch, path = "") => {
1482
+ var _a3;
1483
+ if (!sch) return true;
1484
+ if (typeof sch === "string") {
1485
+ const registered = (_a3 = internals2 == null ? void 0 : internals2.schemas) == null ? void 0 : _a3.get(sch);
1486
+ if (registered) return check(val, registered, path);
1487
+ return true;
1488
+ }
1489
+ const type = sch.type;
1490
+ const getType = (v) => {
1491
+ if (v === null) return "null";
1492
+ if (Array.isArray(v)) return "array";
1493
+ return typeof v;
1494
+ };
1495
+ const currentType = getType(val);
1496
+ if (type && type !== currentType) {
1497
+ if (type === "integer" && Number.isInteger(val)) ;
1498
+ else if (!(type === "number" && typeof val === "number")) {
1499
+ errors.push({ path, message: `Expected type ${type}, got ${currentType}`, keyword: "type" });
1500
+ return false;
1501
+ }
1502
+ }
1503
+ if (currentType === "string") {
1504
+ if (sch.minLength !== void 0 && val.length < sch.minLength) errors.push({ path, keyword: "minLength" });
1505
+ if (sch.maxLength !== void 0 && val.length > sch.maxLength) errors.push({ path, keyword: "maxLength" });
1506
+ if (sch.pattern !== void 0 && !new RegExp(sch.pattern).test(val)) errors.push({ path, keyword: "pattern" });
1507
+ if (sch.format === "email" && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(val)) errors.push({ path, keyword: "format" });
1508
+ }
1509
+ if (currentType === "number") {
1510
+ if (sch.minimum !== void 0 && val < sch.minimum) errors.push({ path, keyword: "minimum" });
1511
+ if (sch.maximum !== void 0 && val > sch.maximum) errors.push({ path, keyword: "maximum" });
1512
+ if (sch.multipleOf !== void 0 && val % sch.multipleOf !== 0) errors.push({ path, keyword: "multipleOf" });
1513
+ }
1514
+ if (currentType === "object") {
1515
+ if (sch.required && Array.isArray(sch.required)) {
1516
+ for (const key of sch.required) {
1517
+ if (!(key in val)) errors.push({ path: path ? `${path}.${key}` : key, keyword: "required" });
1518
+ }
1519
+ }
1520
+ if (sch.properties) {
1521
+ for (const key in sch.properties) {
1522
+ if (key in val) check(val[key], sch.properties[key], path ? `${path}.${key}` : key);
1523
+ }
1524
+ }
1525
+ if (sch.additionalProperties === false) {
1526
+ for (const key in val) {
1527
+ if (!sch.properties || !(key in sch.properties)) errors.push({ path: path ? `${path}.${key}` : key, keyword: "additionalProperties" });
1528
+ }
1529
+ }
1530
+ }
1531
+ if (currentType === "array") {
1532
+ if (sch.minItems !== void 0 && val.length < sch.minItems) errors.push({ path, keyword: "minItems" });
1533
+ if (sch.maxItems !== void 0 && val.length > sch.maxItems) errors.push({ path, keyword: "maxItems" });
1534
+ if (sch.uniqueItems && new Set(val).size !== val.length) errors.push({ path, keyword: "uniqueItems" });
1535
+ if (sch.items) {
1536
+ val.forEach((item, i) => check(item, sch.items, `${path}[${i}]`));
1537
+ }
1538
+ }
1539
+ if (sch.const !== void 0 && val !== sch.const) errors.push({ path, keyword: "const" });
1540
+ if (sch.enum && !sch.enum.includes(val)) errors.push({ path, keyword: "enum" });
1541
+ return errors.length === 0;
1542
+ };
1543
+ const valid = check(value, schema);
1544
+ return valid || errors;
1545
+ };
1546
+ const lvInternals = globalThis.__LIGHTVIEW_INTERNALS__ || ((_a = globalThis.Lightview) == null ? void 0 : _a.internals);
1547
+ if (lvInternals) {
1548
+ const hooks2 = lvInternals.hooks || ((_b = globalThis.Lightview) == null ? void 0 : _b.hooks);
1549
+ if (hooks2) {
1550
+ hooks2.validate = (value, schema) => {
1551
+ const result = validateJSONSchema(value, schema);
1552
+ if (result === true) return true;
1553
+ const msg = result.map((e) => `${e.path || "root"}: failed ${e.keyword}${e.message ? " (" + e.message + ")" : ""}`).join(", ");
1554
+ throw new Error(`Lightview Validation Error: ${msg}`);
1555
+ };
1556
+ }
1557
+ if (globalThis.Lightview) globalThis.Lightview.validate = validateJSONSchema;
1558
+ }
1388
1559
  const LightviewX = {
1389
1560
  state,
1390
1561
  themeSignal,
@@ -1402,6 +1573,7 @@
1402
1573
  preloadComponentCSS,
1403
1574
  createCustomElement,
1404
1575
  customElementWrapper,
1576
+ validate: validateJSONSchema,
1405
1577
  internals: {
1406
1578
  handleSrcAttribute,
1407
1579
  parseElements