@taybart/corvid 0.1.31 → 0.2.2

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/qr.js CHANGED
@@ -1912,6 +1912,97 @@ function render(style) {
1912
1912
  Object.entries(style).forEach(([k, v])=>s += `${toKebab(k)}:${v};`);
1913
1913
  return s;
1914
1914
  }
1915
+ function splitSelectors(selector) {
1916
+ const out = [];
1917
+ let depth = 0;
1918
+ let quote = '';
1919
+ let current = '';
1920
+ for (const ch of selector){
1921
+ if (quote) {
1922
+ if (ch === quote) quote = '';
1923
+ } else if ('"' === ch || "'" === ch) quote = ch;
1924
+ else if ('(' === ch || '[' === ch) depth++;
1925
+ else if (')' === ch || ']' === ch) depth--;
1926
+ else if (',' === ch && 0 === depth) {
1927
+ if (current.trim()) out.push(current.trim());
1928
+ current = '';
1929
+ continue;
1930
+ }
1931
+ current += ch;
1932
+ }
1933
+ if (current.trim()) out.push(current.trim());
1934
+ return out;
1935
+ }
1936
+ function resolve(parents, selector) {
1937
+ const out = [];
1938
+ for (const parent of parents)for (const part of splitSelectors(selector))out.push(part.includes('&') ? part.replaceAll('&', parent) : `${parent} ${part}`);
1939
+ return out;
1940
+ }
1941
+ function compile(parents, decls, indent = '') {
1942
+ const blocks = [];
1943
+ const props = [];
1944
+ const nested = [];
1945
+ for (const [k, v] of Object.entries(decls))if (null !== v && 'object' == typeof v) nested.push([
1946
+ k,
1947
+ v
1948
+ ]);
1949
+ else props.push(`${indent} ${toKebab(k)}: ${v};`);
1950
+ if (props.length) blocks.push(`${indent}${parents.join(', ')} {\n${props.join('\n')}\n${indent}}`);
1951
+ for (const [selector, block] of nested){
1952
+ if (selector.startsWith('@')) {
1953
+ const inner = selector.startsWith('@keyframes') ? Object.entries(block).flatMap(([step, d])=>null !== d && 'object' == typeof d ? compile([
1954
+ step
1955
+ ], d, `${indent} `) : []) : compile(parents, block, `${indent} `);
1956
+ if (inner.length) blocks.push(`${indent}${selector} {\n${inner.join('\n')}\n${indent}}`);
1957
+ continue;
1958
+ }
1959
+ blocks.push(...compile(resolve(parents, selector), block, indent));
1960
+ }
1961
+ return blocks;
1962
+ }
1963
+ function css(selector, style) {
1964
+ return compile(splitSelectors(selector), style).join('\n');
1965
+ }
1966
+ function registry() {
1967
+ const doc = document;
1968
+ if (!doc.__corvidSheets) doc.__corvidSheets = new Map();
1969
+ return doc.__corvidSheets;
1970
+ }
1971
+ function adoptable() {
1972
+ return 'undefined' != typeof CSSStyleSheet && 'replaceSync' in CSSStyleSheet.prototype && 'undefined' != typeof Document && 'adoptedStyleSheets' in Document.prototype;
1973
+ }
1974
+ function styleTag(key) {
1975
+ const tagged = Array.from(document.head.querySelectorAll('style[data-corvid]'));
1976
+ for (const node of tagged)if (node.dataset.corvid === key) return node;
1977
+ const node = document.createElement('style');
1978
+ node.dataset.corvid = key;
1979
+ document.head.appendChild(node);
1980
+ return node;
1981
+ }
1982
+ function inject(selector, style, { key = selector } = {}) {
1983
+ const rules = css(selector, style);
1984
+ const sheets = registry();
1985
+ const existing = sheets.get(key);
1986
+ if (existing) {
1987
+ if ('replaceSync' in existing) existing.replaceSync(rules);
1988
+ else existing.textContent = rules;
1989
+ return rules;
1990
+ }
1991
+ if (adoptable()) {
1992
+ const sheet = new CSSStyleSheet();
1993
+ sheet.replaceSync(rules);
1994
+ document.adoptedStyleSheets = [
1995
+ ...document.adoptedStyleSheets,
1996
+ sheet
1997
+ ];
1998
+ sheets.set(key, sheet);
1999
+ return rules;
2000
+ }
2001
+ const node = styleTag(key);
2002
+ node.textContent = rules;
2003
+ sheets.set(key, node);
2004
+ return rules;
2005
+ }
1915
2006
  function utils_define_property(obj, key, value) {
1916
2007
  if (key in obj) Object.defineProperty(obj, key, {
1917
2008
  value: value,
@@ -1967,6 +2058,37 @@ class utils_logger {
1967
2058
  });
1968
2059
  }
1969
2060
  }
2061
+ function _check_private_redeclaration(obj, privateCollection) {
2062
+ if (privateCollection.has(obj)) throw new TypeError("Cannot initialize the same private elements twice on an object");
2063
+ }
2064
+ function _class_apply_descriptor_get(receiver, descriptor) {
2065
+ if (descriptor.get) return descriptor.get.call(receiver);
2066
+ return descriptor.value;
2067
+ }
2068
+ function _class_apply_descriptor_set(receiver, descriptor, value) {
2069
+ if (descriptor.set) descriptor.set.call(receiver, value);
2070
+ else {
2071
+ if (!descriptor.writable) throw new TypeError("attempted to set read only private field");
2072
+ descriptor.value = value;
2073
+ }
2074
+ }
2075
+ function _class_extract_field_descriptor(receiver, privateMap, action) {
2076
+ if (!privateMap.has(receiver)) throw new TypeError("attempted to " + action + " private field on non-instance");
2077
+ return privateMap.get(receiver);
2078
+ }
2079
+ function _class_private_field_get(receiver, privateMap) {
2080
+ var descriptor = _class_extract_field_descriptor(receiver, privateMap, "get");
2081
+ return _class_apply_descriptor_get(receiver, descriptor);
2082
+ }
2083
+ function _class_private_field_init(obj, privateMap, value) {
2084
+ _check_private_redeclaration(obj, privateMap);
2085
+ privateMap.set(obj, value);
2086
+ }
2087
+ function _class_private_field_set(receiver, privateMap, value) {
2088
+ var descriptor = _class_extract_field_descriptor(receiver, privateMap, "set");
2089
+ _class_apply_descriptor_set(receiver, descriptor, value);
2090
+ return value;
2091
+ }
1970
2092
  function dom_define_property(obj, key, value) {
1971
2093
  if (key in obj) Object.defineProperty(obj, key, {
1972
2094
  value: value,
@@ -1977,6 +2099,14 @@ function dom_define_property(obj, key, value) {
1977
2099
  else obj[key] = value;
1978
2100
  return obj;
1979
2101
  }
2102
+ function registerElement(name, ctor) {
2103
+ const existing = customElements.get(name);
2104
+ if (existing) {
2105
+ if (existing !== ctor) throw new Error(`custom element ${name} is already registered by ${existing.name}`);
2106
+ return;
2107
+ }
2108
+ customElements.define(name, ctor);
2109
+ }
1980
2110
  class dom_el {
1981
2111
  static query(query, verbose = false) {
1982
2112
  return new dom_el(query, verbose);
@@ -1999,21 +2129,29 @@ class dom_el {
1999
2129
  parent.appendChild(this.el);
2000
2130
  return this;
2001
2131
  }
2132
+ append(ch) {
2133
+ return this.child(ch);
2134
+ }
2002
2135
  appendChild(ch) {
2003
2136
  return this.child(ch);
2004
2137
  }
2005
2138
  child(ch) {
2006
2139
  if (!this.el) throw new Error(`no element from query: ${this.query}`);
2007
- if (ch instanceof dom_el) this.el.appendChild(ch.el);
2140
+ if ('string' == typeof ch) this.el.append(ch);
2141
+ else if (ch instanceof dom_el) this.el.appendChild(ch.el);
2008
2142
  else this.el.appendChild(ch);
2009
2143
  return this;
2010
2144
  }
2011
- prependChild(ch) {
2145
+ prepend(ch) {
2012
2146
  if (!this.el) throw new Error(`no element from query: ${this.query}`);
2013
- if (ch instanceof dom_el) this.el.prepend(ch.el);
2147
+ if ('string' == typeof ch) this.el.prepend(ch);
2148
+ else if (ch instanceof dom_el) this.el.prepend(ch.el);
2014
2149
  else this.el.prepend(ch);
2015
2150
  return this;
2016
2151
  }
2152
+ prependChild(ch) {
2153
+ return this.prepend(ch);
2154
+ }
2017
2155
  empty() {
2018
2156
  if (this.el) this.el.innerHTML = '';
2019
2157
  return this;
@@ -2024,17 +2162,37 @@ class dom_el {
2024
2162
  else this.el.innerHTML = content;
2025
2163
  return this;
2026
2164
  }
2165
+ html(content) {
2166
+ if (!this.el) throw new Error(`no element from query: ${this.query}`);
2167
+ this.el.innerHTML = content;
2168
+ return this;
2169
+ }
2027
2170
  src(url) {
2028
2171
  if (this.el && 'src' in this.el) this.el.src = url;
2029
2172
  return this;
2030
2173
  }
2174
+ attrs(attrs) {
2175
+ if (!this.el) throw new Error(`no element from query: ${this.query}`);
2176
+ for (const [k, v] of Object.entries(attrs))this.el.setAttribute(k, v);
2177
+ return this;
2178
+ }
2179
+ attr(key, val) {
2180
+ if (!this.el) throw new Error(`no element from query: ${this.query}`);
2181
+ this.el.setAttribute(key, val);
2182
+ return this;
2183
+ }
2184
+ removeAttr(key) {
2185
+ if (!this.el) throw new Error(`no element from query: ${this.query}`);
2186
+ this.el.removeAttribute(key);
2187
+ return this;
2188
+ }
2031
2189
  style(update, stringify = false) {
2032
2190
  if (this.el) {
2033
2191
  if ('string' == typeof update) this.el.style = update;
2034
2192
  else if ('object' == typeof update) {
2035
2193
  if (!stringify) {
2036
2194
  for (const [k, v] of Object.entries(update))this.el.style[k] = v;
2037
- return;
2195
+ return this;
2038
2196
  }
2039
2197
  const s = render(update);
2040
2198
  this.log.debug(`set style: ${this.el.style} -> ${s}`);
@@ -2059,38 +2217,23 @@ class dom_el {
2059
2217
  else for (const sc of className)this.el.classList.remove(sc);
2060
2218
  return this;
2061
2219
  }
2062
- html(content) {
2063
- if (!this.el) throw new Error(`no element from query: ${this.query}`);
2064
- this.el.innerHTML = content;
2065
- }
2066
- render(vars = {}) {
2067
- if (!this.el) throw new Error(`no element from query: ${this.query}`);
2068
- try {
2069
- return interpolate(this.el.innerHTML, vars);
2070
- } catch (e) {
2071
- throw new Error(`could not render template ${this.query}: ${e}`);
2072
- }
2073
- }
2074
- appendTemplate(template, vars) {
2075
- if (!this.el) throw new Error(`no element from query: ${this.query}`);
2076
- if (!template.el) throw new Error("template does not contain element");
2077
- const tmpl = template.render(vars);
2078
- this.el.insertAdjacentHTML('beforeend', tmpl);
2079
- }
2080
- on(event, cb) {
2220
+ on(event, cb, options) {
2081
2221
  if (!this.el) throw new Error(`no element from query: ${this.query}`);
2082
2222
  if (!this.listeners[event]) this.listeners[event] = [];
2083
- this.listeners[event].push(cb);
2084
- this.el.addEventListener(event, cb);
2223
+ this.listeners[event].push({
2224
+ cb,
2225
+ options
2226
+ });
2227
+ this.el.addEventListener(event, cb, options);
2085
2228
  return this;
2086
2229
  }
2087
- listen(event, cb) {
2088
- return this.on(event, cb);
2230
+ listen(event, cb, options) {
2231
+ return this.on(event, cb, options);
2089
2232
  }
2090
2233
  removeListeners(event) {
2091
2234
  if (!this.el) throw new Error(`no element from query: ${this.query}`);
2092
2235
  if (!this.listeners[event]) return this;
2093
- for (const cb of this.listeners[event])this.el.removeEventListener(event, cb);
2236
+ for (const { cb, options } of this.listeners[event])this.el.removeEventListener(event, cb, options);
2094
2237
  this.listeners[event] = [];
2095
2238
  return this;
2096
2239
  }
@@ -2110,7 +2253,7 @@ class dom_el {
2110
2253
  this.el = opts;
2111
2254
  return;
2112
2255
  }
2113
- const { query, element, type, class: styleClass, style, id, content, parent, attrs } = opts;
2256
+ const { query, element, tag, class: styleClass, style, id, content, parent, attrs } = opts;
2114
2257
  if (query) {
2115
2258
  this.log.debug(`using query: ${query}`);
2116
2259
  this.query = query;
@@ -2119,11 +2262,11 @@ class dom_el {
2119
2262
  } else if (element) {
2120
2263
  this.log.debug(`using existing element: ${element}`);
2121
2264
  this.el = element;
2122
- } else if (type) {
2123
- this.query = type;
2124
- this.log.debug(`creating element: ${type}`);
2125
- this.el = document.createElement(type);
2126
- } else throw new Error('no query or type provided');
2265
+ } else if (tag) {
2266
+ this.query = tag;
2267
+ this.log.debug(`creating element: ${tag}`);
2268
+ this.el = document.createElement(tag);
2269
+ } else throw new Error('no query or tag provided');
2127
2270
  if (this.el) {
2128
2271
  if (id) {
2129
2272
  this.log.debug(`setting id: ${id}`);
@@ -2138,7 +2281,12 @@ class dom_el {
2138
2281
  }
2139
2282
  if (parent) {
2140
2283
  this.log.debug("adding to parent");
2141
- parent.appendChild(this.el);
2284
+ let p = parent;
2285
+ if ('string' == typeof parent) {
2286
+ this.log.debug(`parent query: ${parent}`);
2287
+ p = document.querySelector(parent);
2288
+ }
2289
+ p.appendChild(this.el);
2142
2290
  }
2143
2291
  }
2144
2292
  if (attrs) for (const [key, val] of Object.entries(attrs)){
@@ -2147,11 +2295,51 @@ class dom_el {
2147
2295
  }
2148
2296
  }
2149
2297
  }
2150
- function interpolate(str, params) {
2151
- let names = Object.keys(params).map((k)=>`_${k}`);
2152
- let vals = Object.values(params);
2153
- return new Function(...names, `return \`${str.replace(/\$\{(\w*)\}/g, '${_$1}')}\`;`)(...vals);
2298
+ var _mounted = /*#__PURE__*/ new WeakMap(), _pendingAttrs = /*#__PURE__*/ new WeakMap();
2299
+ class component extends HTMLElement {
2300
+ static register(tag) {
2301
+ const name = tag ?? this.tag;
2302
+ if (!name) throw new Error(`${this.name}: nothing to register as, set \`static tag = 'x-...'\``);
2303
+ if (!name.includes('-')) throw new Error(`${this.name}: '${name}' is not a valid custom element name, it needs a hyphen`);
2304
+ if (this.styles) inject(name, this.styles);
2305
+ registerElement(name, this);
2306
+ }
2307
+ mount() {}
2308
+ unmount() {}
2309
+ onAttr(_name, _value, _prev) {}
2310
+ connectedCallback() {
2311
+ if (!_class_private_field_get(this, _mounted)) {
2312
+ _class_private_field_set(this, _mounted, true);
2313
+ this.mount();
2314
+ const queued = _class_private_field_get(this, _pendingAttrs);
2315
+ _class_private_field_set(this, _pendingAttrs, []);
2316
+ for (const [name, value, prev] of queued)this.onAttr(name, value, prev);
2317
+ }
2318
+ }
2319
+ disconnectedCallback() {
2320
+ this.unmount();
2321
+ }
2322
+ attributeChangedCallback(name, prev, value) {
2323
+ if (prev === value) return;
2324
+ if (!_class_private_field_get(this, _mounted)) return void _class_private_field_get(this, _pendingAttrs).push([
2325
+ name,
2326
+ value,
2327
+ prev
2328
+ ]);
2329
+ this.onAttr(name, value, prev);
2330
+ }
2331
+ constructor(...args){
2332
+ super(...args), _class_private_field_init(this, _mounted, {
2333
+ writable: true,
2334
+ value: false
2335
+ }), _class_private_field_init(this, _pendingAttrs, {
2336
+ writable: true,
2337
+ value: []
2338
+ });
2339
+ }
2154
2340
  }
2341
+ dom_define_property(component, "tag", '');
2342
+ dom_define_property(component, "styles", null);
2155
2343
  class QR {
2156
2344
  static render(config, element) {
2157
2345
  const settings = this.getDefaultSettings(config);
package/dist/style.d.ts CHANGED
@@ -3,6 +3,42 @@
3
3
  **************/
4
4
  export declare function cssVar(name: string): string;
5
5
  export declare function render(style: Object): string;
6
+ /**
7
+ * A block of css declarations. A value that is itself an object is a nested
8
+ * rule: the key is a selector (`&:focus`, `input`) or an at-rule (`@media ...`)
9
+ * rather than a property.
10
+ */
11
+ export type declarations = {
12
+ [key: string]: string | number | declarations;
13
+ };
14
+ /**
15
+ * Render a declaration block to css without touching the document.
16
+ */
17
+ export declare function css(selector: string, style: declarations): string;
18
+ /**
19
+ * Install a stylesheet for `selector`. Nested objects become nested rules, so
20
+ * a component declares its styles in one call:
21
+ *
22
+ * inject('x-search-form', {
23
+ * marginTop: '33vh',
24
+ * input: {
25
+ * border: 'none',
26
+ * '&:focus': { borderBottomColor: 'var(--ring)' },
27
+ * },
28
+ * })
29
+ *
30
+ * Injecting the same key again replaces that sheet rather than adding to it;
31
+ * `key` defaults to the selector.
32
+ * @return the generated css
33
+ */
34
+ export declare function inject(selector: string, style: declarations, { key }?: {
35
+ key?: string;
36
+ }): string;
37
+ /**
38
+ * Remove a stylesheet installed by `inject`
39
+ * @param key - the key it was injected under (the selector, unless overridden)
40
+ */
41
+ export declare function eject(key: string): void;
6
42
  /**
7
43
  * Check if the current theme is dark
8
44
  */
package/dist/style.js CHANGED
@@ -10,6 +10,105 @@ function render(style) {
10
10
  Object.entries(style).forEach(([k, v])=>s += `${toKebab(k)}:${v};`);
11
11
  return s;
12
12
  }
13
+ function splitSelectors(selector) {
14
+ const out = [];
15
+ let depth = 0;
16
+ let quote = '';
17
+ let current = '';
18
+ for (const ch of selector){
19
+ if (quote) {
20
+ if (ch === quote) quote = '';
21
+ } else if ('"' === ch || "'" === ch) quote = ch;
22
+ else if ('(' === ch || '[' === ch) depth++;
23
+ else if (')' === ch || ']' === ch) depth--;
24
+ else if (',' === ch && 0 === depth) {
25
+ if (current.trim()) out.push(current.trim());
26
+ current = '';
27
+ continue;
28
+ }
29
+ current += ch;
30
+ }
31
+ if (current.trim()) out.push(current.trim());
32
+ return out;
33
+ }
34
+ function resolve(parents, selector) {
35
+ const out = [];
36
+ for (const parent of parents)for (const part of splitSelectors(selector))out.push(part.includes('&') ? part.replaceAll('&', parent) : `${parent} ${part}`);
37
+ return out;
38
+ }
39
+ function compile(parents, decls, indent = '') {
40
+ const blocks = [];
41
+ const props = [];
42
+ const nested = [];
43
+ for (const [k, v] of Object.entries(decls))if (null !== v && 'object' == typeof v) nested.push([
44
+ k,
45
+ v
46
+ ]);
47
+ else props.push(`${indent} ${toKebab(k)}: ${v};`);
48
+ if (props.length) blocks.push(`${indent}${parents.join(', ')} {\n${props.join('\n')}\n${indent}}`);
49
+ for (const [selector, block] of nested){
50
+ if (selector.startsWith('@')) {
51
+ const inner = selector.startsWith('@keyframes') ? Object.entries(block).flatMap(([step, d])=>null !== d && 'object' == typeof d ? compile([
52
+ step
53
+ ], d, `${indent} `) : []) : compile(parents, block, `${indent} `);
54
+ if (inner.length) blocks.push(`${indent}${selector} {\n${inner.join('\n')}\n${indent}}`);
55
+ continue;
56
+ }
57
+ blocks.push(...compile(resolve(parents, selector), block, indent));
58
+ }
59
+ return blocks;
60
+ }
61
+ function css(selector, style) {
62
+ return compile(splitSelectors(selector), style).join('\n');
63
+ }
64
+ function registry() {
65
+ const doc = document;
66
+ if (!doc.__corvidSheets) doc.__corvidSheets = new Map();
67
+ return doc.__corvidSheets;
68
+ }
69
+ function adoptable() {
70
+ return 'undefined' != typeof CSSStyleSheet && 'replaceSync' in CSSStyleSheet.prototype && 'undefined' != typeof Document && 'adoptedStyleSheets' in Document.prototype;
71
+ }
72
+ function styleTag(key) {
73
+ const tagged = Array.from(document.head.querySelectorAll('style[data-corvid]'));
74
+ for (const node of tagged)if (node.dataset.corvid === key) return node;
75
+ const node = document.createElement('style');
76
+ node.dataset.corvid = key;
77
+ document.head.appendChild(node);
78
+ return node;
79
+ }
80
+ function inject(selector, style, { key = selector } = {}) {
81
+ const rules = css(selector, style);
82
+ const sheets = registry();
83
+ const existing = sheets.get(key);
84
+ if (existing) {
85
+ if ('replaceSync' in existing) existing.replaceSync(rules);
86
+ else existing.textContent = rules;
87
+ return rules;
88
+ }
89
+ if (adoptable()) {
90
+ const sheet = new CSSStyleSheet();
91
+ sheet.replaceSync(rules);
92
+ document.adoptedStyleSheets = [
93
+ ...document.adoptedStyleSheets,
94
+ sheet
95
+ ];
96
+ sheets.set(key, sheet);
97
+ return rules;
98
+ }
99
+ const node = styleTag(key);
100
+ node.textContent = rules;
101
+ sheets.set(key, node);
102
+ return rules;
103
+ }
104
+ function eject(key) {
105
+ const sheets = registry();
106
+ const sheet = sheets.get(key);
107
+ if (!sheet) return;
108
+ if ('replaceSync' in sheet) document.adoptedStyleSheets = document.adoptedStyleSheets.filter((s)=>s !== sheet);
109
+ else sheet.remove();
110
+ sheets.delete(key);
111
+ }
13
112
  function isDarkMode() {
14
113
  return window.matchMedia('(prefers-color-scheme: dark)').matches;
15
114
  }
@@ -34,4 +133,4 @@ function gradient(start, end, value) {
34
133
  const blue = Math.round(start.blue + (end.blue - start.blue) * value / 100);
35
134
  return `rgb(${red}, ${green}, ${blue})`;
36
135
  }
37
- export { cssVar, gradient, handleThemeSwitch, isDarkMode, onDarkMode, render, switchTheme };
136
+ export { css, cssVar, eject, gradient, handleThemeSwitch, inject, isDarkMode, onDarkMode, render, switchTheme };
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taybart/corvid",
3
- "version": "0.1.31",
3
+ "version": "0.2.2",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "exports": {
@@ -38,18 +38,20 @@
38
38
  "files": [
39
39
  "dist"
40
40
  ],
41
- "scripts": {
42
- "build": "rslib build",
43
- "dev": "rslib build --watch"
44
- },
45
41
  "devDependencies": {
46
- "@happy-dom/global-registrator": "^17.4.7",
47
42
  "@rslib/core": "^0.6.2",
48
43
  "@testing-library/dom": "^10.4.0",
49
44
  "@testing-library/jest-dom": "^6.6.3",
50
- "@types/bun": "^1.2.13",
51
45
  "@types/jsdom": "^21.1.7",
52
46
  "@types/node": "^22.8.1",
53
- "typescript": "^5.8.3"
47
+ "happy-dom": "^18.0.1",
48
+ "typescript": "^5.8.3",
49
+ "vitest": "^3.2.4"
50
+ },
51
+ "scripts": {
52
+ "build": "rslib build",
53
+ "dev": "rslib build --watch",
54
+ "test": "vitest run",
55
+ "test:watch": "vitest"
54
56
  }
55
- }
57
+ }