@wcstack/typescript 4.0.0-rc.3 → 4.0.0-rc.5

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.esm.js CHANGED
@@ -572,7 +572,7 @@ function loadSchemaCore() {
572
572
  return cached;
573
573
  }
574
574
 
575
- var version = "4.0.0-rc.3";
575
+ var version = "4.0.0-rc.5";
576
576
  var pkg = {
577
577
  version: version};
578
578
 
@@ -766,32 +766,8 @@ var LINT_HINT = " Validate statically: npx @wcstack/lint <file>.";
766
766
  function raiseError(message, subject, candidates) {
767
767
  throw new Error(`[@wcstack/state] ${message}${hooks.explain?.(message, subject, candidates) ?? ""}`);
768
768
  }
769
- var CODES = [
770
- "",
771
- "binding-syntax",
772
- "template-syntax",
773
- "binding-path-missing",
774
- "binding-type-expectation",
775
- "filter-unknown",
776
- "filter-arity",
777
- "getter-cycle",
778
- "getter-depth-exceeded",
779
- "index-arity",
780
- "index-param-range",
781
- "recursion-unsupported",
782
- "token-misconfigured",
783
- "token-undeclared",
784
- "wildcard-rank",
785
- "spread-no-bindable",
786
- "declaration-alias",
787
- "name-alias"
788
- ];
789
- var codeOf = (id) => {
790
- const c = CODES[id / 100 | 0];
791
- return c ? `[wcs/${c}] ` : "";
792
- };
793
769
  function text(id, args = []) {
794
- return hooks.render?.(id, args) ?? `${codeOf(id)}#${id}${args.map((a) => ` ${typeof a === "string" ? JSON.stringify(a) : String(a)}`).join("")}`;
770
+ return hooks.render?.(id, args) ?? `#${id}${args.map((a) => ` ${typeof a === "string" ? JSON.stringify(a) : String(a)}`).join("")}`;
795
771
  }
796
772
  function raise(id, args, subject, candidates) {
797
773
  raiseError(text(id, args), subject, candidates);
@@ -819,7 +795,21 @@ var FORMATS_FILTER_NAMES = [
819
795
  "time",
820
796
  "datetime",
821
797
  "ymd",
822
- "hms"
798
+ "hms",
799
+ "add",
800
+ "sub",
801
+ "mul",
802
+ "div",
803
+ "mod",
804
+ "abs",
805
+ "clamp",
806
+ "int",
807
+ "float",
808
+ "defaults",
809
+ "coalesce",
810
+ "number",
811
+ "string",
812
+ "nullIfEmpty"
823
813
  ];
824
814
  var definitions = /* @__PURE__ */ new Map();
825
815
  function hasFilter(name) {
@@ -1011,6 +1001,30 @@ function parseStatePart(statePart) {
1011
1001
  }
1012
1002
  var MAX_DRAIN_PASSES = 32;
1013
1003
  var MAX_RENDER_CHAIN = 100;
1004
+ var CODES = [
1005
+ "",
1006
+ "binding-syntax",
1007
+ "template-syntax",
1008
+ "binding-path-missing",
1009
+ "binding-type-expectation",
1010
+ "filter-unknown",
1011
+ "filter-arity",
1012
+ "getter-cycle",
1013
+ "getter-depth-exceeded",
1014
+ "index-arity",
1015
+ "index-param-range",
1016
+ "recursion-unsupported",
1017
+ "token-misconfigured",
1018
+ "token-undeclared",
1019
+ "wildcard-rank",
1020
+ "spread-no-bindable",
1021
+ "declaration-alias",
1022
+ "name-alias"
1023
+ ];
1024
+ var codeOf = (id) => {
1025
+ const c = CODES[id / 100 | 0];
1026
+ return c ? `[wcs/${c}] ` : "";
1027
+ };
1014
1028
  var MOVED = ["enableMustache", "sameValueGuard", "enableDirectionalInitialSync"];
1015
1029
  var element = (tag, at2) => `<${tag}${at2.map((a, i) => i % 2 ? `="${a}"` : ` ${a}`).join("")}>`;
1016
1030
  var CSP_GUIDE = "https://github.com/wcstack/wcstack/blob/main/docs/csp.md";
@@ -1363,10 +1377,11 @@ var SENTENCES = {
1363
1377
  1201
1364
1378
  /* CommandRightSide */
1365
1379
  ]: (prop, p) => `"${prop}: ${p}": the right-hand side must be $command.<name>`,
1380
+ // a native element's command (no hyphen: never a custom element) is the native-commands add-on's
1366
1381
  [
1367
1382
  1202
1368
1383
  /* NoBindable */
1369
- ]: (tag, what) => `<${tag}> declares no static wcBindable (${what}).`,
1384
+ ]: (tag, what) => `<${tag}> declares no static wcBindable (${what}).${!tag.includes("-") && what.startsWith("command.") ? " A native element's command needs the add-on @wcstack/state/features/native-commands." : ""}`,
1370
1385
  [
1371
1386
  1203
1372
1387
  /* NoCommand */
@@ -1375,6 +1390,10 @@ var SENTENCES = {
1375
1390
  1204
1376
1391
  /* NoProperty */
1377
1392
  ]: (tag, prop) => `<${tag}> declares no property "${prop}".`,
1393
+ [
1394
+ 1205
1395
+ /* NativeNoCommand */
1396
+ ]: (tag, method, allowed) => `<${tag}> has no command "${method}" (a native <${tag}>'s commands: ${allowed}).`,
1378
1397
  [
1379
1398
  1301
1380
1399
  /* EventTokenUndeclared */
@@ -1690,26 +1709,26 @@ function findStartTagRegions(html) {
1690
1709
  let lower2 = null;
1691
1710
  let i = 0;
1692
1711
  while (i < html.length) {
1693
- const lt = html.indexOf("<", i);
1694
- if (lt === -1) break;
1695
- const next = html[lt + 1];
1696
- if (html.startsWith("<!--", lt)) {
1697
- const close = html.indexOf("-->", lt + 4);
1712
+ const lt2 = html.indexOf("<", i);
1713
+ if (lt2 === -1) break;
1714
+ const next = html[lt2 + 1];
1715
+ if (html.startsWith("<!--", lt2)) {
1716
+ const close = html.indexOf("-->", lt2 + 4);
1698
1717
  i = close === -1 ? html.length : close + 3;
1699
1718
  continue;
1700
1719
  }
1701
1720
  if (next === "!" || next === "?" || next === "/") {
1702
- const close = html.indexOf(">", lt + 1);
1721
+ const close = html.indexOf(">", lt2 + 1);
1703
1722
  i = close === -1 ? html.length : close + 1;
1704
1723
  continue;
1705
1724
  }
1706
1725
  if (next === void 0 || !/[A-Za-z]/.test(next)) {
1707
- i = lt + 1;
1726
+ i = lt2 + 1;
1708
1727
  continue;
1709
1728
  }
1710
- let n = lt + 1;
1729
+ let n = lt2 + 1;
1711
1730
  while (n < html.length && /[^\s/>]/.test(html[n])) n++;
1712
- const tagName = asciiLowerCase(html.slice(lt + 1, n));
1731
+ const tagName = asciiLowerCase(html.slice(lt2 + 1, n));
1713
1732
  let j = n;
1714
1733
  let quote = null;
1715
1734
  while (j < html.length) {
@@ -1723,10 +1742,10 @@ function findStartTagRegions(html) {
1723
1742
  }
1724
1743
  j++;
1725
1744
  }
1726
- const gt = j < html.length ? j : html.length;
1727
- const selfClosing = html[gt - 1] === "/";
1728
- out.push({ tagName, start: n, end: selfClosing ? gt - 1 : gt });
1729
- i = gt + 1;
1745
+ const gt2 = j < html.length ? j : html.length;
1746
+ const selfClosing = html[gt2 - 1] === "/";
1747
+ out.push({ tagName, start: n, end: selfClosing ? gt2 - 1 : gt2 });
1748
+ i = gt2 + 1;
1730
1749
  if (tagName === "script" || tagName === "style") {
1731
1750
  const close = (lower2 ??= asciiLowerCase(html)).indexOf(`</${tagName}`, i);
1732
1751
  i = close === -1 ? html.length : close;
@@ -1770,8 +1789,8 @@ function matchRawTextElement(html, lower2, pos) {
1770
1789
  return null;
1771
1790
  }
1772
1791
  function afterCloseTag(html, closeStart) {
1773
- const gt = html.indexOf(">", closeStart);
1774
- return gt === -1 ? html.length : gt + 1;
1792
+ const gt2 = html.indexOf(">", closeStart);
1793
+ return gt2 === -1 ? html.length : gt2 + 1;
1775
1794
  }
1776
1795
  function parseWcsStateElements(html, stateTagName = "wcs-state") {
1777
1796
  const elements = [];
@@ -1877,22 +1896,22 @@ function collectTemplateTags(html) {
1877
1896
  const tags = [];
1878
1897
  let pos = 0;
1879
1898
  while (pos < html.length) {
1880
- const lt = html.indexOf("<", pos);
1881
- if (lt === -1) break;
1882
- if (html.startsWith("<!--", lt)) {
1883
- const commentEnd = html.indexOf("-->", lt + 4);
1899
+ const lt2 = html.indexOf("<", pos);
1900
+ if (lt2 === -1) break;
1901
+ if (html.startsWith("<!--", lt2)) {
1902
+ const commentEnd = html.indexOf("-->", lt2 + 4);
1884
1903
  if (commentEnd === -1) break;
1885
1904
  pos = commentEnd + 3;
1886
1905
  continue;
1887
1906
  }
1888
- const raw = matchRawTextElement(html, lower2, lt);
1907
+ const raw = matchRawTextElement(html, lower2, lt2);
1889
1908
  if (raw !== null) {
1890
1909
  pos = raw.closed ? afterCloseTag(html, raw.contentEnd) : html.length;
1891
1910
  continue;
1892
1911
  }
1893
- const tag = /^<(\/?)template(?=[\s/>])/i.exec(html.slice(lt, lt + 11));
1894
- if (tag !== null) tags.push({ at: lt, close: tag[1] === "/" });
1895
- pos = lt + 1;
1912
+ const tag = /^<(\/?)template(?=[\s/>])/i.exec(html.slice(lt2, lt2 + 11));
1913
+ if (tag !== null) tags.push({ at: lt2, close: tag[1] === "/" });
1914
+ pos = lt2 + 1;
1896
1915
  }
1897
1916
  return tags;
1898
1917
  }
@@ -2036,32 +2055,8 @@ var hooks2 = {};
2036
2055
  function raiseError2(message, subject, candidates) {
2037
2056
  throw new Error(`[@wcstack/state] ${message}${hooks2.explain?.(message, subject, candidates) ?? ""}`);
2038
2057
  }
2039
- var CODES2 = [
2040
- "",
2041
- "binding-syntax",
2042
- "template-syntax",
2043
- "binding-path-missing",
2044
- "binding-type-expectation",
2045
- "filter-unknown",
2046
- "filter-arity",
2047
- "getter-cycle",
2048
- "getter-depth-exceeded",
2049
- "index-arity",
2050
- "index-param-range",
2051
- "recursion-unsupported",
2052
- "token-misconfigured",
2053
- "token-undeclared",
2054
- "wildcard-rank",
2055
- "spread-no-bindable",
2056
- "declaration-alias",
2057
- "name-alias"
2058
- ];
2059
- var codeOf2 = (id) => {
2060
- const c = CODES2[id / 100 | 0];
2061
- return c ? `[wcs/${c}] ` : "";
2062
- };
2063
2058
  function text2(id, args = []) {
2064
- return hooks2.render?.(id, args) ?? `${codeOf2(id)}#${id}${args.map((a) => ` ${typeof a === "string" ? JSON.stringify(a) : String(a)}`).join("")}`;
2059
+ return hooks2.render?.(id, args) ?? `#${id}${args.map((a) => ` ${typeof a === "string" ? JSON.stringify(a) : String(a)}`).join("")}`;
2065
2060
  }
2066
2061
  function raise2(id, args, subject, candidates) {
2067
2062
  raiseError2(text2(id, args), subject, candidates);
@@ -2076,7 +2071,17 @@ var config = {
2076
2071
  locale: typeof document !== "undefined" ? document.documentElement?.lang || "en" : "en",
2077
2072
  enableContractAnalyzer: false
2078
2073
  };
2079
- var FEATURE_NAMES = ["formats", "diagnostics", "temporal", "list-keys", "scopes", "recursion", "ssr", "devtools"];
2074
+ var FEATURE_NAMES = ["formats", "diagnostics", "temporal", "list-keys", "scopes", "recursion", "ssr", "devtools", "native-commands"];
2075
+ var NATIVE_COMMANDS = {
2076
+ "*": ["focus", "blur", "click", "scrollIntoView", "showPopover", "hidePopover", "togglePopover"],
2077
+ "dialog": ["show", "showModal", "close", "requestClose"],
2078
+ "form": ["requestSubmit", "checkValidity", "reportValidity"],
2079
+ "input": ["select", "setSelectionRange", "showPicker", "setCustomValidity", "checkValidity", "reportValidity"],
2080
+ "textarea": ["select", "setSelectionRange", "setCustomValidity", "checkValidity", "reportValidity"],
2081
+ "select": ["showPicker", "setCustomValidity", "checkValidity", "reportValidity"],
2082
+ "audio": ["play", "pause", "load"],
2083
+ "video": ["play", "pause", "load"]
2084
+ };
2080
2085
  var WILDCARD2 = "*";
2081
2086
  function optionsRequired(fnName) {
2082
2087
  raise2(26, [fnName]);
@@ -2165,42 +2170,6 @@ var numeric = (name, op) => ({
2165
2170
  var truthy = () => (value) => !!value;
2166
2171
  var falsy = () => (value) => !value;
2167
2172
  var boolean = () => (value) => Boolean(value);
2168
- var abs = () => (value) => {
2169
- if (typeof value !== "number") {
2170
- valueMustBeNumber("abs");
2171
- }
2172
- return Math.abs(value);
2173
- };
2174
- var clamp = (options) => {
2175
- const min = requiredNumberOption(options, 0, "clamp");
2176
- const max = requiredNumberOption(options, 1, "clamp");
2177
- return (value) => {
2178
- if (typeof value !== "number") {
2179
- valueMustBeNumber("clamp");
2180
- }
2181
- return Math.min(Math.max(value, min), max);
2182
- };
2183
- };
2184
- var number = () => (value) => Number(value);
2185
- var string = () => (value) => String(value);
2186
- var int = () => (value) => parseInt(String(value), 10);
2187
- var float = () => (value) => parseFloat(String(value));
2188
- var defaults = (options, literals) => {
2189
- const opt = options?.[0] ?? optionsRequired("defaults");
2190
- const fallback = firstLiteral(opt, literals);
2191
- return (value) => {
2192
- if (!value) {
2193
- return fallback;
2194
- }
2195
- return value;
2196
- };
2197
- };
2198
- var coalesce = (options, literals) => {
2199
- const opt = options?.[0] ?? optionsRequired("coalesce");
2200
- const fallback = firstLiteral(opt, literals);
2201
- return (value) => value ?? fallback;
2202
- };
2203
- var nullIfEmpty = () => (value) => value === "" ? null : value;
2204
2173
  var coreFilters = {
2205
2174
  eq: { factory: eq, arity: [1, 1] },
2206
2175
  ne: { factory: ne, arity: [1, 1] },
@@ -2209,23 +2178,9 @@ var coreFilters = {
2209
2178
  le: numeric("le", (value, opt) => value <= opt),
2210
2179
  gt: numeric("gt", (value, opt) => value > opt),
2211
2180
  ge: numeric("ge", (value, opt) => value >= opt),
2212
- add: numeric("add", (value, opt) => value + opt),
2213
- sub: numeric("sub", (value, opt) => value - opt),
2214
- mul: numeric("mul", (value, opt) => value * opt),
2215
- div: numeric("div", (value, opt) => value / opt),
2216
- mod: numeric("mod", (value, opt) => value % opt),
2217
- abs: { factory: abs, arity: [0, 0] },
2218
- clamp: { factory: clamp, arity: [2, 2] },
2219
- int: { factory: int, arity: [0, 0] },
2220
- float: { factory: float, arity: [0, 0] },
2221
2181
  falsy: { factory: falsy, arity: [0, 0] },
2222
2182
  truthy: { factory: truthy, arity: [0, 0] },
2223
- defaults: { factory: defaults, arity: [1, 1] },
2224
- coalesce: { factory: coalesce, arity: [1, 1] },
2225
- boolean: { factory: boolean, arity: [0, 0] },
2226
- number: { factory: number, arity: [0, 0] },
2227
- string: { factory: string, arity: [0, 0] },
2228
- nullIfEmpty: { factory: nullIfEmpty, arity: [0, 0] }
2183
+ boolean: { factory: boolean, arity: [0, 0] }
2229
2184
  };
2230
2185
  var nullishPassthrough = (factory) => (options) => {
2231
2186
  const filterFn = factory(options);
@@ -2410,7 +2365,44 @@ var hms = (options) => {
2410
2365
  return `${hours}${opt}${minutes}${opt}${seconds}`;
2411
2366
  };
2412
2367
  };
2413
- var formatFilters = {
2368
+ var abs = () => (value) => {
2369
+ if (typeof value !== "number") {
2370
+ valueMustBeNumber("abs");
2371
+ }
2372
+ return Math.abs(value);
2373
+ };
2374
+ var clamp = (options) => {
2375
+ const min = requiredNumberOption(options, 0, "clamp");
2376
+ const max = requiredNumberOption(options, 1, "clamp");
2377
+ return (value) => {
2378
+ if (typeof value !== "number") {
2379
+ valueMustBeNumber("clamp");
2380
+ }
2381
+ return Math.min(Math.max(value, min), max);
2382
+ };
2383
+ };
2384
+ var number = () => (value) => Number(value);
2385
+ var string = () => (value) => String(value);
2386
+ var int = () => (value) => parseInt(String(value), 10);
2387
+ var float = () => (value) => parseFloat(String(value));
2388
+ var defaults = (options, literals) => {
2389
+ const opt = options?.[0] ?? optionsRequired("defaults");
2390
+ const fallback = firstLiteral(opt, literals);
2391
+ return (value) => {
2392
+ if (!value) {
2393
+ return fallback;
2394
+ }
2395
+ return value;
2396
+ };
2397
+ };
2398
+ var coalesce = (options, literals) => {
2399
+ const opt = options?.[0] ?? optionsRequired("coalesce");
2400
+ const fallback = firstLiteral(opt, literals);
2401
+ return (value) => value ?? fallback;
2402
+ };
2403
+ var nullIfEmpty = () => (value) => value === "" ? null : value;
2404
+ var { eq: eq2, ne: ne2, not: not2, lt, le, gt, ge, falsy: falsy2, truthy: truthy2, boolean: boolean2 } = coreFilters;
2405
+ var registered = {
2414
2406
  toFixed: { factory: toFixed, arity: [0, 1] },
2415
2407
  locale: { factory: locale, arity: [0, 1] },
2416
2408
  // The `String(value)` family passes an absent value through (B8 — see nullishPassthrough)
@@ -2436,8 +2428,36 @@ var formatFilters = {
2436
2428
  time: { factory: time, arity: [0, 1] },
2437
2429
  datetime: { factory: datetime, arity: [0, 1] },
2438
2430
  ymd: { factory: ymd, arity: [0, 1] },
2439
- hms: { factory: hms, arity: [0, 1] }
2431
+ hms: { factory: hms, arity: [0, 1] },
2432
+ // the core set of 4.0.0-rc.3 (the core's own: eq ne not lt le gt ge falsy truthy boolean)
2433
+ eq: eq2,
2434
+ ne: ne2,
2435
+ not: not2,
2436
+ lt,
2437
+ le,
2438
+ gt,
2439
+ ge,
2440
+ add: numeric("add", (value, opt) => value + opt),
2441
+ sub: numeric("sub", (value, opt) => value - opt),
2442
+ mul: numeric("mul", (value, opt) => value * opt),
2443
+ div: numeric("div", (value, opt) => value / opt),
2444
+ mod: numeric("mod", (value, opt) => value % opt),
2445
+ abs: { factory: abs, arity: [0, 0] },
2446
+ clamp: { factory: clamp, arity: [2, 2] },
2447
+ int: { factory: int, arity: [0, 0] },
2448
+ float: { factory: float, arity: [0, 0] },
2449
+ falsy: falsy2,
2450
+ truthy: truthy2,
2451
+ defaults: { factory: defaults, arity: [1, 1] },
2452
+ coalesce: { factory: coalesce, arity: [1, 1] },
2453
+ boolean: boolean2,
2454
+ number: { factory: number, arity: [0, 0] },
2455
+ string: { factory: string, arity: [0, 0] },
2456
+ nullIfEmpty: { factory: nullIfEmpty, arity: [0, 0] }
2440
2457
  };
2458
+ var formatFilters = /* @__PURE__ */ Object.fromEntries(
2459
+ /* @__PURE__ */ Object.entries(registered).filter(([name]) => !(name in coreFilters))
2460
+ );
2441
2461
  var STRUCTURAL_BINDING_TYPE_SET2 = /* @__PURE__ */ new Set([
2442
2462
  "if",
2443
2463
  "elseif",
@@ -2573,10 +2593,18 @@ function getWcsManifest() {
2573
2593
  ],
2574
2594
  // every option is a boolean, true when left out (engine.ts `loadTarget`: `typeof … === "boolean"`, `?? true`)
2575
2595
  behaviorOptions: Object.fromEntries(BEHAVIOR_OPTION_KEYS.map((key) => [key, { type: "boolean", default: true }])),
2576
- features: [...FEATURE_NAMES]
2596
+ features: [...FEATURE_NAMES],
2597
+ nativeCommands: Object.fromEntries(Object.entries(NATIVE_COMMANDS).map(([tag, methods]) => [tag, [...methods]]))
2577
2598
  };
2578
2599
  }
2579
2600
 
2601
+ // src/service/wcsManifest.ts
2602
+ var NATIVE_COMMANDS2 = getWcsManifest().nativeCommands ?? null;
2603
+ function nativeCommandsOf(tag) {
2604
+ if (NATIVE_COMMANDS2 === null) return null;
2605
+ return [...NATIVE_COMMANDS2["*"] ?? [], ...Object.hasOwn(NATIVE_COMMANDS2, tag) ? NATIVE_COMMANDS2[tag] : []];
2606
+ }
2607
+
2580
2608
  // src/service/completionData.ts
2581
2609
  var BUILTIN_FILTERS = Object.entries(builtinFilterMeta).map(
2582
2610
  ([name, meta]) => ({ name, ...meta })
@@ -2592,6 +2620,41 @@ var STRUCTURAL_DIRECTIVES = [...STRUCTURAL_BINDING_TYPE_SET2].map((name) => ({
2592
2620
  ...STRUCTURAL_DIRECTIVE_INFO[name]
2593
2621
  }));
2594
2622
 
2623
+ // src/service/suggestion.ts
2624
+ function suggestion(input, candidates, msgs) {
2625
+ let best = null;
2626
+ let bestDistance = 3;
2627
+ for (const c of candidates) {
2628
+ const d = editDistance2(input.toLowerCase(), c.toLowerCase(), bestDistance);
2629
+ if (d < bestDistance) {
2630
+ best = c;
2631
+ bestDistance = d;
2632
+ }
2633
+ }
2634
+ return best !== null ? msgs.didYouMean(best) : "";
2635
+ }
2636
+ function editDistance2(a, b, bound) {
2637
+ if (Math.abs(a.length - b.length) >= bound) return bound;
2638
+ const prev = new Array(b.length + 1);
2639
+ const curr = new Array(b.length + 1);
2640
+ for (let j = 0; j <= b.length; j++) prev[j] = j;
2641
+ for (let i = 1; i <= a.length; i++) {
2642
+ curr[0] = i;
2643
+ let rowMin = curr[0];
2644
+ for (let j = 1; j <= b.length; j++) {
2645
+ curr[j] = Math.min(
2646
+ prev[j] + 1,
2647
+ curr[j - 1] + 1,
2648
+ prev[j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1)
2649
+ );
2650
+ if (curr[j] < rowMin) rowMin = curr[j];
2651
+ }
2652
+ if (rowMin >= bound) return bound;
2653
+ for (let j = 0; j <= b.length; j++) prev[j] = curr[j];
2654
+ }
2655
+ return Math.min(prev[b.length], bound);
2656
+ }
2657
+
2595
2658
  // src/service/recursionPaths.ts
2596
2659
  var RECURSION_WILDCARD2 = "**";
2597
2660
  var RECURSION_KEY = "$recursion";
@@ -10319,6 +10382,7 @@ var ja = {
10319
10382
  featuresAttrNotRoot: () => `features \u5C5E\u6027\u3092\u8AAD\u3080\u306E\u306F\u6587\u66F8\u306E root \u306E <wcs-state>\uFF08mount \u3068 bind-component \u3092\u6301\u305F\u306A\u3044\u6700\u521D\u306E\u3082\u306E\uFF09\u3060\u3051\u3067\u3059\u3002\u3053\u306E\u8981\u7D20\u306E features \u306F\u7121\u8996\u3055\u308C\u307E\u3059 \u2014 root \u306E <wcs-state> \u306B\u66F8\u3044\u3066\u304F\u3060\u3055\u3044`,
10320
10383
  onPrefixedMember: (member, tag, modifiers) => `"${member}" \u306F <${tag}> \u306E\u30E1\u30F3\u30D0\u30FC\u3067\u3059\u304C\u3001"on" \u3067\u59CB\u307E\u308B\u540D\u524D\u306F\u30A4\u30D9\u30F3\u30C8\u675F\u7E1B\u306B\u306A\u308A\uFF08"${member.slice(2)}" \u30A4\u30D9\u30F3\u30C8\u3092\u5F85\u3064\uFF09\u3001\u5024\u306F\u5C4A\u304D\u307E\u305B\u3093\u3002\u30D7\u30ED\u30D1\u30C6\u30A3\u3068\u3057\u3066\u675F\u7E1B\u3059\u308B\u306B\u306F ".${member}${modifiers ? `#${modifiers}` : ""}:" \u3068\u66F8\u3044\u3066\u304F\u3060\u3055\u3044\uFF08@wcstack/state 3.1\uFF09`,
10321
10384
  tagCommandUnknown: (name, tag, declared) => `"${name}" \u306F <${tag}> \u306E command \u3067\u306F\u3042\u308A\u307E\u305B\u3093\uFF08\u5BA3\u8A00\u6E08\u307F: ${declared}\uFF09`,
10385
+ nativeCommandUnknown: (method, tag, allowed) => `"${method}" \u306F\u30CD\u30A4\u30C6\u30A3\u30D6\u306E <${tag}> \u306B command \u3067\u547C\u3079\u308B\u30E1\u30BD\u30C3\u30C9\u3067\u306F\u3042\u308A\u307E\u305B\u3093\uFF08\u547C\u3079\u308B\u306E\u306F ${allowed}\u3002\u30E9\u30F3\u30BF\u30A4\u30E0\u306F\u521D\u671F\u5316\u3067 wcs/token-misconfigured \u3092\u6295\u3052\u307E\u3059\uFF09`,
10322
10386
  spreadNoBindable: (tag) => `'...'\uFF08spread\uFF09\u306F <${tag}> \u306B\u6709\u52B9\u306A wcBindable \u5BA3\u8A00\u304C\u5FC5\u8981\u3067\u3059 \u2014 \u3053\u306E\u30BF\u30B0\u306F\u5BA3\u8A00\u3092\u6301\u305F\u306A\u3044\u305F\u3081\u3001\u30E9\u30F3\u30BF\u30A4\u30E0\u306F\u30A8\u30E9\u30FC\u3092\u9001\u51FA\u3057\u307E\u3059`,
10323
10387
  tagEventTokenKeyUnknown: (name, tag, declared) => `eventToken \u306E\u30AD\u30FC "${name}" \u306F <${tag}> \u306E wcBindable \u30D7\u30ED\u30D1\u30C6\u30A3\u3067\u306F\u3042\u308A\u307E\u305B\u3093\u3002\u751F DOM \u30A4\u30D9\u30F3\u30C8\u540D\u306F\u767A\u706B\u3057\u307E\u305B\u3093 \u2014 \u30D7\u30ED\u30D1\u30C6\u30A3\u540D\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\uFF08\u5BA3\u8A00\u6E08\u307F: ${declared}\uFF09`,
10324
10388
  ariaAttrUnknown: (name) => `"${name}" \u306F WAI-ARIA \u306E\u5C5E\u6027\u3067\u306F\u3042\u308A\u307E\u305B\u3093\u3002setAttribute \u306F\u305D\u306E\u307E\u307E\u66F8\u304D\u8FBC\u307F\u307E\u3059\u304C\u3001\u652F\u63F4\u6280\u8853\u306B\u306F\u9ED9\u3063\u3066\u7121\u8996\u3055\u308C\u307E\u3059`,
@@ -10504,6 +10568,7 @@ var en = {
10504
10568
  featuresAttrNotRoot: () => `The features attribute is read only on the document's root <wcs-state> (the first one without mount and bind-component); it is ignored here \u2014 put it on the root <wcs-state>`,
10505
10569
  onPrefixedMember: (member, tag, modifiers) => `"${member}" is a member of <${tag}>, but a name starting with "on" makes an event binding (it listens for a "${member.slice(2)}" event) and the value never arrives. Write ".${member}${modifiers ? `#${modifiers}` : ""}:" to bind the property (@wcstack/state 3.1)`,
10506
10570
  tagCommandUnknown: (name, tag, declared) => `"${name}" is not a command of <${tag}> (declared: ${declared})`,
10571
+ nativeCommandUnknown: (method, tag, allowed) => `"${method}" is not a command of a native <${tag}> (its commands: ${allowed}; the runtime throws wcs/token-misconfigured at initialization)`,
10507
10572
  spreadNoBindable: (tag) => `'...' (spread) requires <${tag}> to expose a valid wcBindable declaration \u2014 this tag declares none, so the runtime raises an error`,
10508
10573
  tagEventTokenKeyUnknown: (name, tag, declared) => `eventToken key "${name}" is not a wcBindable property of <${tag}>. Raw DOM event names never fire \u2014 use the property name (declared: ${declared})`,
10509
10574
  ariaAttrUnknown: (name) => `"${name}" is not a WAI-ARIA attribute. setAttribute writes it anyway, and assistive technology silently ignores it`,
@@ -10894,6 +10959,18 @@ function validateBindings(html, attrName, stateTagName = "wcs-state", locale2, f
10894
10959
  scopedPaths.filter((p) => p.kind === "command").map((p) => p.path)
10895
10960
  );
10896
10961
  if (propNoMod.startsWith("command.")) {
10962
+ const allowed = attr.tagName !== void 0 && !attr.tagName.includes("-") ? nativeCommandsOf(attr.tagName) : null;
10963
+ const method = propNoMod.slice("command.".length);
10964
+ if (allowed !== null && !allowed.includes(method)) {
10965
+ const methodStart = bindingStart + binding.indexOf("command.") + "command.".length;
10966
+ diagnostics.push({
10967
+ code: WcsDiagnosticCode.TokenMisconfigured,
10968
+ start: methodStart,
10969
+ end: methodStart + method.length,
10970
+ message: msgs.nativeCommandUnknown(method, attr.tagName, allowed.join(", ")) + suggestion(method, allowed, msgs),
10971
+ severity: "error"
10972
+ });
10973
+ }
10897
10974
  const tokenPath = parsed.path?.trim() ?? "";
10898
10975
  if (tokenPath) {
10899
10976
  const pathOffset = binding.indexOf(parsed.path);
@@ -13504,39 +13581,6 @@ function normalizeSeed(raw) {
13504
13581
  const compact = raw.replace(/\s+/g, "");
13505
13582
  return compact === "" ? raw : compact;
13506
13583
  }
13507
- function suggestion(input, candidates, msgs) {
13508
- let best = null;
13509
- let bestDistance = 3;
13510
- for (const c of candidates) {
13511
- const d = editDistance2(input.toLowerCase(), c.toLowerCase(), bestDistance);
13512
- if (d < bestDistance) {
13513
- best = c;
13514
- bestDistance = d;
13515
- }
13516
- }
13517
- return best !== null ? msgs.didYouMean(best) : "";
13518
- }
13519
- function editDistance2(a, b, bound) {
13520
- if (Math.abs(a.length - b.length) >= bound) return bound;
13521
- const prev = new Array(b.length + 1);
13522
- const curr = new Array(b.length + 1);
13523
- for (let j = 0; j <= b.length; j++) prev[j] = j;
13524
- for (let i = 1; i <= a.length; i++) {
13525
- curr[0] = i;
13526
- let rowMin = curr[0];
13527
- for (let j = 1; j <= b.length; j++) {
13528
- curr[j] = Math.min(
13529
- prev[j] + 1,
13530
- curr[j - 1] + 1,
13531
- prev[j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1)
13532
- );
13533
- if (curr[j] < rowMin) rowMin = curr[j];
13534
- }
13535
- if (rowMin >= bound) return bound;
13536
- for (let j = 0; j <= b.length; j++) prev[j] = curr[j];
13537
- }
13538
- return Math.min(prev[b.length], bound);
13539
- }
13540
13584
  function findBuiltinTagOccurrences(html) {
13541
13585
  const out = [];
13542
13586
  const regex = /<(wcs-[a-z0-9-]+)((?:"[^"]*"|'[^']*'|[^>"'])*)>/gi;
package/dist/tsc-core.cjs CHANGED
@@ -64,8 +64,8 @@ function matchRawTextElement(html, lower2, pos) {
64
64
  return null;
65
65
  }
66
66
  function afterCloseTag(html, closeStart) {
67
- const gt = html.indexOf(">", closeStart);
68
- return gt === -1 ? html.length : gt + 1;
67
+ const gt2 = html.indexOf(">", closeStart);
68
+ return gt2 === -1 ? html.length : gt2 + 1;
69
69
  }
70
70
  function parseWcsStateElements(html, stateTagName = "wcs-state") {
71
71
  const elements = [];
@@ -221,32 +221,8 @@ var hooks = {};
221
221
  function raiseError(message, subject, candidates) {
222
222
  throw new Error(`[@wcstack/state] ${message}${hooks.explain?.(message, subject, candidates) ?? ""}`);
223
223
  }
224
- var CODES = [
225
- "",
226
- "binding-syntax",
227
- "template-syntax",
228
- "binding-path-missing",
229
- "binding-type-expectation",
230
- "filter-unknown",
231
- "filter-arity",
232
- "getter-cycle",
233
- "getter-depth-exceeded",
234
- "index-arity",
235
- "index-param-range",
236
- "recursion-unsupported",
237
- "token-misconfigured",
238
- "token-undeclared",
239
- "wildcard-rank",
240
- "spread-no-bindable",
241
- "declaration-alias",
242
- "name-alias"
243
- ];
244
- var codeOf = (id) => {
245
- const c = CODES[id / 100 | 0];
246
- return c ? `[wcs/${c}] ` : "";
247
- };
248
224
  function text(id, args = []) {
249
- return hooks.render?.(id, args) ?? `${codeOf(id)}#${id}${args.map((a) => ` ${typeof a === "string" ? JSON.stringify(a) : String(a)}`).join("")}`;
225
+ return hooks.render?.(id, args) ?? `#${id}${args.map((a) => ` ${typeof a === "string" ? JSON.stringify(a) : String(a)}`).join("")}`;
250
226
  }
251
227
  function raise(id, args, subject, candidates) {
252
228
  raiseError(text(id, args), subject, candidates);
@@ -261,7 +237,17 @@ var config = {
261
237
  locale: typeof document !== "undefined" ? document.documentElement?.lang || "en" : "en",
262
238
  enableContractAnalyzer: false
263
239
  };
264
- var FEATURE_NAMES = ["formats", "diagnostics", "temporal", "list-keys", "scopes", "recursion", "ssr", "devtools"];
240
+ var FEATURE_NAMES = ["formats", "diagnostics", "temporal", "list-keys", "scopes", "recursion", "ssr", "devtools", "native-commands"];
241
+ var NATIVE_COMMANDS = {
242
+ "*": ["focus", "blur", "click", "scrollIntoView", "showPopover", "hidePopover", "togglePopover"],
243
+ "dialog": ["show", "showModal", "close", "requestClose"],
244
+ "form": ["requestSubmit", "checkValidity", "reportValidity"],
245
+ "input": ["select", "setSelectionRange", "showPicker", "setCustomValidity", "checkValidity", "reportValidity"],
246
+ "textarea": ["select", "setSelectionRange", "setCustomValidity", "checkValidity", "reportValidity"],
247
+ "select": ["showPicker", "setCustomValidity", "checkValidity", "reportValidity"],
248
+ "audio": ["play", "pause", "load"],
249
+ "video": ["play", "pause", "load"]
250
+ };
265
251
  var WILDCARD = "*";
266
252
  function optionsRequired(fnName) {
267
253
  raise(26, [fnName]);
@@ -350,42 +336,6 @@ var numeric = (name, op) => ({
350
336
  var truthy = () => (value) => !!value;
351
337
  var falsy = () => (value) => !value;
352
338
  var boolean = () => (value) => Boolean(value);
353
- var abs = () => (value) => {
354
- if (typeof value !== "number") {
355
- valueMustBeNumber("abs");
356
- }
357
- return Math.abs(value);
358
- };
359
- var clamp = (options) => {
360
- const min = requiredNumberOption(options, 0, "clamp");
361
- const max = requiredNumberOption(options, 1, "clamp");
362
- return (value) => {
363
- if (typeof value !== "number") {
364
- valueMustBeNumber("clamp");
365
- }
366
- return Math.min(Math.max(value, min), max);
367
- };
368
- };
369
- var number = () => (value) => Number(value);
370
- var string = () => (value) => String(value);
371
- var int = () => (value) => parseInt(String(value), 10);
372
- var float = () => (value) => parseFloat(String(value));
373
- var defaults = (options, literals) => {
374
- const opt = options?.[0] ?? optionsRequired("defaults");
375
- const fallback = firstLiteral(opt, literals);
376
- return (value) => {
377
- if (!value) {
378
- return fallback;
379
- }
380
- return value;
381
- };
382
- };
383
- var coalesce = (options, literals) => {
384
- const opt = options?.[0] ?? optionsRequired("coalesce");
385
- const fallback = firstLiteral(opt, literals);
386
- return (value) => value ?? fallback;
387
- };
388
- var nullIfEmpty = () => (value) => value === "" ? null : value;
389
339
  var coreFilters = {
390
340
  eq: { factory: eq, arity: [1, 1] },
391
341
  ne: { factory: ne, arity: [1, 1] },
@@ -394,23 +344,9 @@ var coreFilters = {
394
344
  le: numeric("le", (value, opt) => value <= opt),
395
345
  gt: numeric("gt", (value, opt) => value > opt),
396
346
  ge: numeric("ge", (value, opt) => value >= opt),
397
- add: numeric("add", (value, opt) => value + opt),
398
- sub: numeric("sub", (value, opt) => value - opt),
399
- mul: numeric("mul", (value, opt) => value * opt),
400
- div: numeric("div", (value, opt) => value / opt),
401
- mod: numeric("mod", (value, opt) => value % opt),
402
- abs: { factory: abs, arity: [0, 0] },
403
- clamp: { factory: clamp, arity: [2, 2] },
404
- int: { factory: int, arity: [0, 0] },
405
- float: { factory: float, arity: [0, 0] },
406
347
  falsy: { factory: falsy, arity: [0, 0] },
407
348
  truthy: { factory: truthy, arity: [0, 0] },
408
- defaults: { factory: defaults, arity: [1, 1] },
409
- coalesce: { factory: coalesce, arity: [1, 1] },
410
- boolean: { factory: boolean, arity: [0, 0] },
411
- number: { factory: number, arity: [0, 0] },
412
- string: { factory: string, arity: [0, 0] },
413
- nullIfEmpty: { factory: nullIfEmpty, arity: [0, 0] }
349
+ boolean: { factory: boolean, arity: [0, 0] }
414
350
  };
415
351
  var nullishPassthrough = (factory) => (options) => {
416
352
  const filterFn = factory(options);
@@ -595,7 +531,44 @@ var hms = (options) => {
595
531
  return `${hours}${opt}${minutes}${opt}${seconds}`;
596
532
  };
597
533
  };
598
- var formatFilters = {
534
+ var abs = () => (value) => {
535
+ if (typeof value !== "number") {
536
+ valueMustBeNumber("abs");
537
+ }
538
+ return Math.abs(value);
539
+ };
540
+ var clamp = (options) => {
541
+ const min = requiredNumberOption(options, 0, "clamp");
542
+ const max = requiredNumberOption(options, 1, "clamp");
543
+ return (value) => {
544
+ if (typeof value !== "number") {
545
+ valueMustBeNumber("clamp");
546
+ }
547
+ return Math.min(Math.max(value, min), max);
548
+ };
549
+ };
550
+ var number = () => (value) => Number(value);
551
+ var string = () => (value) => String(value);
552
+ var int = () => (value) => parseInt(String(value), 10);
553
+ var float = () => (value) => parseFloat(String(value));
554
+ var defaults = (options, literals) => {
555
+ const opt = options?.[0] ?? optionsRequired("defaults");
556
+ const fallback = firstLiteral(opt, literals);
557
+ return (value) => {
558
+ if (!value) {
559
+ return fallback;
560
+ }
561
+ return value;
562
+ };
563
+ };
564
+ var coalesce = (options, literals) => {
565
+ const opt = options?.[0] ?? optionsRequired("coalesce");
566
+ const fallback = firstLiteral(opt, literals);
567
+ return (value) => value ?? fallback;
568
+ };
569
+ var nullIfEmpty = () => (value) => value === "" ? null : value;
570
+ var { eq: eq2, ne: ne2, not: not2, lt, le, gt, ge, falsy: falsy2, truthy: truthy2, boolean: boolean2 } = coreFilters;
571
+ var registered = {
599
572
  toFixed: { factory: toFixed, arity: [0, 1] },
600
573
  locale: { factory: locale, arity: [0, 1] },
601
574
  // The `String(value)` family passes an absent value through (B8 — see nullishPassthrough)
@@ -621,8 +594,36 @@ var formatFilters = {
621
594
  time: { factory: time, arity: [0, 1] },
622
595
  datetime: { factory: datetime, arity: [0, 1] },
623
596
  ymd: { factory: ymd, arity: [0, 1] },
624
- hms: { factory: hms, arity: [0, 1] }
597
+ hms: { factory: hms, arity: [0, 1] },
598
+ // the core set of 4.0.0-rc.3 (the core's own: eq ne not lt le gt ge falsy truthy boolean)
599
+ eq: eq2,
600
+ ne: ne2,
601
+ not: not2,
602
+ lt,
603
+ le,
604
+ gt,
605
+ ge,
606
+ add: numeric("add", (value, opt) => value + opt),
607
+ sub: numeric("sub", (value, opt) => value - opt),
608
+ mul: numeric("mul", (value, opt) => value * opt),
609
+ div: numeric("div", (value, opt) => value / opt),
610
+ mod: numeric("mod", (value, opt) => value % opt),
611
+ abs: { factory: abs, arity: [0, 0] },
612
+ clamp: { factory: clamp, arity: [2, 2] },
613
+ int: { factory: int, arity: [0, 0] },
614
+ float: { factory: float, arity: [0, 0] },
615
+ falsy: falsy2,
616
+ truthy: truthy2,
617
+ defaults: { factory: defaults, arity: [1, 1] },
618
+ coalesce: { factory: coalesce, arity: [1, 1] },
619
+ boolean: boolean2,
620
+ number: { factory: number, arity: [0, 0] },
621
+ string: { factory: string, arity: [0, 0] },
622
+ nullIfEmpty: { factory: nullIfEmpty, arity: [0, 0] }
625
623
  };
624
+ var formatFilters = /* @__PURE__ */ Object.fromEntries(
625
+ /* @__PURE__ */ Object.entries(registered).filter(([name]) => !(name in coreFilters))
626
+ );
626
627
  var STRUCTURAL_BINDING_TYPE_SET = /* @__PURE__ */ new Set([
627
628
  "if",
628
629
  "elseif",
@@ -758,10 +759,14 @@ function getWcsManifest() {
758
759
  ],
759
760
  // every option is a boolean, true when left out (engine.ts `loadTarget`: `typeof … === "boolean"`, `?? true`)
760
761
  behaviorOptions: Object.fromEntries(BEHAVIOR_OPTION_KEYS.map((key) => [key, { type: "boolean", default: true }])),
761
- features: [...FEATURE_NAMES]
762
+ features: [...FEATURE_NAMES],
763
+ nativeCommands: Object.fromEntries(Object.entries(NATIVE_COMMANDS).map(([tag, methods]) => [tag, [...methods]]))
762
764
  };
763
765
  }
764
766
 
767
+ // src/service/wcsManifest.ts
768
+ var NATIVE_COMMANDS2 = getWcsManifest().nativeCommands ?? null;
769
+
765
770
  // src/language/preamble.ts
766
771
  var manifest = getWcsManifest();
767
772
  var BEHAVIOR_TYPE = `{ ${Object.entries(manifest.behaviorOptions).map(([key, option]) => `${key}?: ${option.type}`).join("; ")} }`;
@@ -573,7 +573,7 @@ function loadSchemaCore() {
573
573
  return cached;
574
574
  }
575
575
 
576
- var version = "4.0.0-rc.3";
576
+ var version = "4.0.0-rc.5";
577
577
  var pkg = {
578
578
  version: version};
579
579
 
package/dist/wcs-tsc.mjs CHANGED
@@ -5,7 +5,7 @@ import { tmpdir } from 'node:os';
5
5
  import { dirname, join, isAbsolute, resolve, basename } from 'node:path';
6
6
  import { fileURLToPath } from 'node:url';
7
7
 
8
- var version = "4.0.0-rc.3";
8
+ var version = "4.0.0-rc.5";
9
9
  var pkg = {
10
10
  version: version};
11
11
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wcstack/typescript",
3
- "version": "4.0.0-rc.3",
3
+ "version": "4.0.0-rc.5",
4
4
  "description": "TypeScript tooling for wcstack apps: wcs-schema generates the sidecar stateSchema from a typed state file, so wcs-validate checks data-wcs paths against real types in CI and every editor.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.esm.js",