@twobirds/microcomponents 0.10.2 → 0.11.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.
@@ -619,7 +619,12 @@ function bindTemplateEvents(rootElement, instance) {
619
619
  //#endregion
620
620
  //#region src/core/mcGuards.ts
621
621
  function isMcHost(element, key) {
622
- return typeof HTMLElement !== "undefined" && element instanceof HTMLElement && "_mc" in element && (key === void 0 || key in element._mc);
622
+ if (typeof HTMLElement === "undefined" || !(element instanceof HTMLElement)) return false;
623
+ if (!("_mc" in element)) return false;
624
+ if (key === void 0) return true;
625
+ const mc = element._mc;
626
+ if (typeof mc !== "object" || mc === null) return false;
627
+ return key in mc;
623
628
  }
624
629
  //#endregion
625
630
  //#region src/core/MC.ts
@@ -627,7 +632,7 @@ function add(targetHTMLElement, key, element = {}) {
627
632
  const node = targetHTMLElement;
628
633
  if (!("_mc" in targetHTMLElement)) node._mc = new DC(targetHTMLElement);
629
634
  const _mc = node._mc;
630
- if (key.length && !_mc[key]) {
635
+ if (key.length) {
631
636
  _mc[key] = element;
632
637
  if (targetHTMLElement instanceof HTMLElement && !targetHTMLElement.getAttribute("data-mc")) targetHTMLElement.setAttribute("data-mc", "");
633
638
  }
@@ -793,7 +798,7 @@ runs once after the constructor.
793
798
  @group Micro Components
794
799
  */
795
800
  var McBase = class {
796
- _target;
801
+ #target;
797
802
  /**
798
803
  * Loaded asset cache, keyed by camelCase descriptor (`'targetHtml'` → `.target.html`).
799
804
  * Inlined by the bundler in a bundled build, filled by `loadAssets()` in dev/autoload.
@@ -822,7 +827,7 @@ var McBase = class {
822
827
  @param target - The object or element this microcomponent represents.
823
828
  */
824
829
  constructor(target) {
825
- this._target = new WeakRef(target || {});
830
+ this.#target = new WeakRef(target || {});
826
831
  const ctor = this.constructor;
827
832
  if (this instanceof DC && this.constructor !== DC) autoAttachListeners(this);
828
833
  const remaining = (ctor.assets ?? []).filter((key) => !ctor._assets[key]);
@@ -849,7 +854,7 @@ var McBase = class {
849
854
  @returns The target object.
850
855
  */
851
856
  get target() {
852
- return this._target.deref();
857
+ return this.#target.deref();
853
858
  }
854
859
  /**
855
860
  The loaded assets for this component instance.
@@ -1458,7 +1463,8 @@ data object and updates the corresponding DOM locations when notified of changes
1458
1463
  */
1459
1464
  var Placeholders = class extends DC {
1460
1465
  data = {};
1461
- oldDisplay = "";
1466
+ #oldDisplay = "";
1467
+ #restored = false;
1462
1468
  /**
1463
1469
  Creates a new `Placeholders` instance for the given HTML element.
1464
1470
 
@@ -1474,9 +1480,22 @@ var Placeholders = class extends DC {
1474
1480
  observe(this);
1475
1481
  this.#attachCallbacks();
1476
1482
  }
1483
+ /**
1484
+ Replaces every occurrence of one placeholder in a template.
1485
+ The placeholder is escaped into a global regex, so a repeated `{x}` is fully
1486
+ substituted — `String.replace` with a string pattern replaces one occurrence only.
1487
+ @param template - The template holding the placeholder.
1488
+ @param placeholder - The `{key}` token to substitute.
1489
+ @param value - The replacement value.
1490
+ @returns The template with every occurrence replaced.
1491
+ */
1492
+ #substitute(template, placeholder, value) {
1493
+ placeholder.replace(/[\{\}]/g, "");
1494
+ const escaped = placeholder.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1495
+ return template.replace(new RegExp(escaped, "g"), value);
1496
+ }
1477
1497
  #attachCallbacks() {
1478
1498
  const target = this.target;
1479
- let hide = false;
1480
1499
  (target.getAttributeNames ? target.getAttributeNames() : []).filter((attr) => target.getAttribute(attr)?.match(/\{[^\{\}]*\}/g)).forEach((attr) => {
1481
1500
  const placeholders = new Set(target.getAttribute(attr)?.match(/\{[^\{\}]*\}/g));
1482
1501
  /* v8 ignore next -- attr passed the filter above, so its value is a non-null string */
@@ -1484,32 +1503,36 @@ var Placeholders = class extends DC {
1484
1503
  this.data.subscribe(((template) => (data) => {
1485
1504
  let result = template;
1486
1505
  placeholders.forEach((placeholder) => {
1487
- result = result.replace(placeholder, String(data[placeholder.replace(/[\{\}]/g, "")]));
1506
+ result = this.#substitute(result, placeholder, String(data[placeholder.replace(/[\{\}]/g, "")]));
1488
1507
  });
1489
1508
  target.setAttribute(attr, result);
1490
1509
  })(attrTemplate));
1491
1510
  });
1492
- [...target.childNodes].filter((childNode) => {
1511
+ const textNodes = [...target.childNodes].filter((childNode) => {
1493
1512
  return childNode.nodeType === 3 && (childNode.nodeValue || "").match(/\{[^\{\}]*\}/g);
1494
- }).forEach((childNode) => {
1495
- hide = true;
1496
- /* v8 ignore next -- the filter above only admits text nodes with a non-empty
1497
- nodeValue, so the ?? fallback is unreachable */
1498
- const textTemplate = childNode.nodeValue ?? "";
1499
- const placeholders = new Set(textTemplate.match(/\{[^\{\}]*\}/g));
1500
- this.data.subscribe(((template) => (data) => {
1501
- let result = template;
1502
- placeholders.forEach((placeholder) => {
1503
- result = result.replace(placeholder, String(data[placeholder.replace(/[\{\}]/g, "")]));
1504
- });
1505
- if (this.oldDisplay) target.style = this.oldDisplay;
1506
- else target.removeAttribute("style");
1507
- delete this.oldDisplay;
1508
- childNode.nodeValue = result;
1509
- })(textTemplate));
1510
- this.oldDisplay = target.getAttribute("style") || "";
1511
- if (hide && !inputTags.includes(target.tagName) && target.tagName !== "FIELDSET") target.style.display = "none";
1512
1513
  });
1514
+ if (textNodes.length) {
1515
+ this.#oldDisplay = target.getAttribute("style") || "";
1516
+ textNodes.forEach((childNode) => {
1517
+ /* v8 ignore next -- the filter above only admits text nodes with a non-empty
1518
+ nodeValue, so the ?? fallback is unreachable */
1519
+ const textTemplate = childNode.nodeValue ?? "";
1520
+ const placeholders = new Set(textTemplate.match(/\{[^\{\}]*\}/g));
1521
+ this.data.subscribe(((template) => (data) => {
1522
+ let result = template;
1523
+ placeholders.forEach((placeholder) => {
1524
+ result = this.#substitute(result, placeholder, String(data[placeholder.replace(/[\{\}]/g, "")]));
1525
+ });
1526
+ if (!this.#restored) {
1527
+ this.#restored = true;
1528
+ if (this.#oldDisplay) target.style = this.#oldDisplay;
1529
+ else target.removeAttribute("style");
1530
+ }
1531
+ childNode.nodeValue = result;
1532
+ })(textTemplate));
1533
+ if (!inputTags.includes(target.tagName) && target.tagName !== "FIELDSET") target.style.display = "none";
1534
+ });
1535
+ }
1513
1536
  }
1514
1537
  /**
1515
1538
  Handles a `data` custom event to update the internal placeholder data.
@@ -1757,7 +1780,7 @@ function makeObservable(val) {
1757
1780
  `{count}` placeholders update on every change.
1758
1781
  */
1759
1782
  bind(target) {
1760
- if (!isBrowser()) return;
1783
+ if (!isBrowser()) return this;
1761
1784
  if (isObservableObject(this.valueOf())) {
1762
1785
  makePlaceholders(target);
1763
1786
  const placeholders = [target, ...target.querySelectorAll("[data-mc]")].filter((e) => {
@@ -1906,8 +1929,12 @@ function observe(target, propertyName) {
1906
1929
  if (key[0] === "_" || current === void 0) return;
1907
1930
  const wrappedVal = current;
1908
1931
  let o = new (getConstructor(wrappedVal))(wrappedVal);
1932
+ let notifyPending = false;
1909
1933
  const debouncedNotify = function() {
1934
+ if (notifyPending) return;
1935
+ notifyPending = true;
1910
1936
  queueMicrotask(function() {
1937
+ notifyPending = false;
1911
1938
  o.notify();
1912
1939
  });
1913
1940
  };
@@ -21,7 +21,6 @@ data object and updates the corresponding DOM locations when notified of changes
21
21
  declare class Placeholders extends DC {
22
22
  #private;
23
23
  data: Observable<Record<string, unknown>>;
24
- oldDisplay?: string;
25
24
  /**
26
25
  Creates a new `Placeholders` instance for the given HTML element.
27
26
 
@@ -1 +1 @@
1
- {"version":3,"file":"placeholders.d.ts","sourceRoot":"","sources":["../../src/state/placeholders.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;EAUE;AAEF,OAAO,EAAE,EAAE,EAAE,KAAK,QAAQ,EAAE,KAAK,MAAM,EAAE,MAAM,eAAe,CAAC;AAC/D,OAAO,EAAW,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAoD5D;;;;;;EAME;AACF,cAAM,YAAa,SAAQ,EAAE;;IAE5B,IAAI,EAAE,UAAU,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAwD;IACjG,UAAU,CAAC,EAAE,MAAM,CAAM;IAEzB;;;;;;;;MAQE;gBACU,MAAM,EAAE,WAAW;IAuF/B;;;;;;;MAOE;IACF,MAAM,CAAC,EAAE,EAAE,WAAW,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CAOzD;AAmBD;;;;;;;EAOE;AACF,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,WAAW,GAAG,gBAAgB,GAAG,UAAU,GAAG,IAAI,CAK3F;AAED;;;;EAIE;AACF,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC,WAAW,EAAE;IAAE,aAAa,CAAC,EAAE,YAAY,CAAA;CAAE,CAAC,CAAC"}
1
+ {"version":3,"file":"placeholders.d.ts","sourceRoot":"","sources":["../../src/state/placeholders.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;EAUE;AAEF,OAAO,EAAE,EAAE,EAAE,KAAK,QAAQ,EAAE,KAAK,MAAM,EAAE,MAAM,eAAe,CAAC;AAC/D,OAAO,EAAW,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAoD5D;;;;;;EAME;AACF,cAAM,YAAa,SAAQ,EAAE;;IAE5B,IAAI,EAAE,UAAU,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAwD;IAQjG;;;;;;;;MAQE;gBACU,MAAM,EAAE,WAAW;IA+G/B;;;;;;;MAOE;IACF,MAAM,CAAC,EAAE,EAAE,WAAW,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CAOzD;AAmBD;;;;;;;EAOE;AACF,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,WAAW,GAAG,gBAAgB,GAAG,UAAU,GAAG,IAAI,CAK3F;AAED;;;;EAIE;AACF,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC,WAAW,EAAE;IAAE,aAAa,CAAC,EAAE,YAAY,CAAA;CAAE,CAAC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"autoload.d.ts","sourceRoot":"","sources":["../../src/utils/autoload.ts"],"names":[],"mappings":"AAAA;;;;;;;;;EASE;AAsJF;;;;;;;;;;;;;;;;EAgBE;AACF,MAAM,WAAW,QAAQ;IACxB;;;;;;;;;;;;;IAaG;IACH,CAAC,MAAM,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC;IAC5B;;;;;IAKG;IACH,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACnB;AAED,OAAO,CAAC,MAAM,CAAC;IACd,UAAU,MAAM;QACf,QAAQ,CAAC,EAAE,QAAQ,CAAC;KACpB;CACD;AAID,wBAAgB,QAAQ,CAAC,MAAM,GAAE,OAAc,GAAG,OAAO,CAuBxD;yBAvBe,QAAQ"}
1
+ {"version":3,"file":"autoload.d.ts","sourceRoot":"","sources":["../../src/utils/autoload.ts"],"names":[],"mappings":"AAAA;;;;;;;;;EASE;AA4JF;;;;;;;;;;;;;;;;EAgBE;AACF,MAAM,WAAW,QAAQ;IACxB;;;;;;;;;;;;;IAaG;IACH,CAAC,MAAM,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC;IAC5B;;;;;IAKG;IACH,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACnB;AAED,OAAO,CAAC,MAAM,CAAC;IACd,UAAU,MAAM;QACf,QAAQ,CAAC,EAAE,QAAQ,CAAC;KACpB;CACD;AAMD,wBAAgB,QAAQ,CAAC,MAAM,GAAE,OAAc,GAAG,OAAO,CAmCxD;yBAnCe,QAAQ"}
@@ -89,6 +89,8 @@ export declare function flattenObject(source: object, doSort?: boolean): Record<
89
89
  export declare function deepEqual(a: unknown, b: unknown): boolean;
90
90
  /**
91
91
  * Retrieves a nested value from an object using a namespace path (dot-separated).
92
+ * A present falsy leaf (`0`, `''`, `false`, `null`) is a real value and is returned;
93
+ * only an absent key yields `undefined`.
92
94
  * @param ns - The namespace string or array of keys representing the path.
93
95
  * @param obj - The object to traverse.
94
96
  * @returns The value at the specified namespace path, or undefined if not found.
@@ -98,6 +100,7 @@ export declare function deepEqual(a: unknown, b: unknown): boolean;
98
100
  * const data = { user: { profile: { name: 'Alice' } } };
99
101
  * console.log(nameSpace('user.profile.name', data));
100
102
  * console.log(nameSpace(['user', 'profile'], data));
103
+ * console.log(nameSpace('count', { count: 0 })); // 0, not undefined
101
104
  * ```
102
105
  */
103
106
  export declare function nameSpace(ns: string | string[], obj?: object): unknown;
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/utils/helpers.ts"],"names":[],"mappings":"AAAA;;;;;;EAME;AAEF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAExD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,SAAS,IAAI,OAAO,CAEnC;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAE/E;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,UAAU,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC,GAAG,CAAC,CAiB1E;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,GAAE,OAAc,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAqB7F;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,OAAO,CAiCzD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,GAAG,GAAE,MAAW,GAAG,OAAO,CAe1E;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,KAAK,CAAC,CAAC,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,EAAE,EACpD,QAAQ,EAAE,CAAC,EACX,SAAS,EAAE,MAAM,EACjB,GAAG,IAAI,EAAE,OAAO,EAAE,GAChB,CAAC,CAgCH;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,UAKxC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,UAExC;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,QAAQ,CAAC,CAAC,SAAS,OAAO,EAAE,EAC3C,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,KAAK,OAAO,EAC7B,YAAY,EAAE,MAAM,GAClB,CAAC,GAAG,IAAI,EAAE,CAAC,KAAK,IAAI,CAStB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,EAAE,CAU1D;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAOvE;AAmCD,wBAAgB,SAAS,CAAC,MAAM,EAAE,QAAQ,GAAG,UAAU,EAAE,GAAG,EAAE,MAAM,GAAG,aAAa,CAanF;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CA2B1F;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAmC9E;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,kBAAkB,CACjC,WAAW,EAAE,WAAW,GAAG,UAAU,EACrC,QAAQ,EAAE,YAAY,GACpB,IAAI,CA2CN"}
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/utils/helpers.ts"],"names":[],"mappings":"AAAA;;;;;;EAME;AAEF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAExD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,SAAS,IAAI,OAAO,CAEnC;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAE/E;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,UAAU,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC,GAAG,CAAC,CAiB1E;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,GAAE,OAAc,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAqB7F;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,OAAO,CAiCzD;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,GAAG,GAAE,MAAW,GAAG,OAAO,CAoB1E;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,KAAK,CAAC,CAAC,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,EAAE,EACpD,QAAQ,EAAE,CAAC,EACX,SAAS,EAAE,MAAM,EACjB,GAAG,IAAI,EAAE,OAAO,EAAE,GAChB,CAAC,CAmCH;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,UAKxC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,UAExC;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,QAAQ,CAAC,CAAC,SAAS,OAAO,EAAE,EAC3C,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,KAAK,OAAO,EAC7B,YAAY,EAAE,MAAM,GAClB,CAAC,GAAG,IAAI,EAAE,CAAC,KAAK,IAAI,CAStB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,EAAE,CAU1D;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAOvE;AAmCD,wBAAgB,SAAS,CAAC,MAAM,EAAE,QAAQ,GAAG,UAAU,EAAE,GAAG,EAAE,MAAM,GAAG,aAAa,CAanF;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CA2B1F;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAmC9E;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,kBAAkB,CACjC,WAAW,EAAE,WAAW,GAAG,UAAU,EACrC,QAAQ,EAAE,YAAY,GACpB,IAAI,CA2CN"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@twobirds/microcomponents",
3
- "version": "0.10.2",
3
+ "version": "0.11.0",
4
4
  "description": "Micro Components",
5
5
  "keywords": [
6
6
  "PWA",
@@ -79,11 +79,12 @@
79
79
  "format": "oxfmt .",
80
80
  "lint": "oxlint \"src\"",
81
81
  "fix": "oxfmt . && oxlint \"src\" --fix",
82
- "check": "tsc --noEmit -p tsconfig.lib.json && tsc --noEmit -p tsconfig.cli.json && tsc --noEmit -p tsconfig.mcp.json && tsc -p tsconfig.test.json && tsc -p demo/web-test/tsconfig.demo.json && tsc --noEmit -p demo/shop/tsconfig.demo.json && tsc --noEmit -p demo/todo_mvc/tsconfig.demo.json && tsc --noEmit -p demo/docs/tsconfig.demo.json && tsc -p tsconfig.node.json",
82
+ "check": "tsc --noEmit -p tsconfig.lib.json && tsc --noEmit -p tsconfig.cli.json && tsc --noEmit -p tsconfig.mcp.json && tsc -p tsconfig.test.json && tsc -p demo/web-test/tsconfig.demo.json && tsc --noEmit -p demo/shop/tsconfig.demo.json && tsc --noEmit -p demo/todo_mvc/tsconfig.demo.json && tsc --noEmit -p demo/docs/tsconfig.demo.json && tsc -p tsconfig.node.json && tsc -p tsconfig.e2e.json",
83
83
  "check:pack": "publint && attw --pack . --profile esm-only",
84
84
  "test": "vitest",
85
85
  "test:run": "vitest run --reporter=verbose",
86
86
  "test:coverage": "vitest run --coverage",
87
+ "test:e2e": "pnpm run bundle && pnpm run demo:shop:build && vitest run --config vitest.e2e.config.ts",
87
88
  "demo:shop": "vite --config demo/shop/vite.config.ts",
88
89
  "demo:shop:bundled": "tsx demo/shop/vite.shop.bundle.ts",
89
90
  "demo:shop:build": "tsx demo/build-demo.ts shop && pnpm run demo:shop:bundled",
@@ -121,6 +122,7 @@
121
122
  "jsdom": "^29.1.1",
122
123
  "oxfmt": "^0.59.0",
123
124
  "oxlint": "^1.74.0",
125
+ "playwright": "1.61.0",
124
126
  "publint": "^0.3.21",
125
127
  "tsx": "^4.23.12",
126
128
  "typedoc": "^0.28.20",