@wcstack/typescript 3.3.0 → 3.4.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.
package/dist/index.esm.js CHANGED
@@ -566,7 +566,7 @@ function loadSchemaCore() {
566
566
  return cached;
567
567
  }
568
568
 
569
- var version = "3.3.0";
569
+ var version = "3.4.0";
570
570
  var pkg = {
571
571
  version: version};
572
572
 
@@ -3657,7 +3657,8 @@ var ja = {
3657
3657
  patternPathOutsideFor: (p) => `\u30D1\u30BF\u30FC\u30F3\u30D1\u30B9 "${p}" \u306F <template for> \u306E\u5916\u5074\u3067\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093`,
3658
3658
  omittedPathOutsideFor: (p) => `\u7701\u7565\u30D1\u30B9 "${p}" \u306F <template for> \u306E\u5916\u5074\u3067\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093`,
3659
3659
  loopIndexOutsideFor: (p) => `\u30EB\u30FC\u30D7\u30A4\u30F3\u30C7\u30C3\u30AF\u30B9 "${p}" \u306F <template for> \u306E\u5916\u5074\u3067\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093`,
3660
- resolvedPathInUi: (p) => `\u89E3\u6C7A\u6E08\u307F\u30D1\u30B9 "${p}" \u306F UI \u30D0\u30A4\u30F3\u30C7\u30A3\u30F3\u30B0\u3067\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093\u3002\u30D1\u30BF\u30FC\u30F3\u30D1\u30B9\u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044`,
3660
+ resolvedPathInUi: (p) => `\u30D1\u30B9 "${p}" \u306F\u884C\u3067\u306F\u306A\u304F\u7D20\u306E\u30D1\u30B9\u3068\u3057\u3066\u8AAD\u307E\u308C\u308B\u305F\u3081\u3001\u6DFB\u5B57\u3092\u901A\u3057\u305F\u66F8\u304D\u8FBC\u307F\u304C\u3053\u306E\u30D0\u30A4\u30F3\u30C7\u30A3\u30F3\u30B0\u306B\u5C4A\u304D\u307E\u305B\u3093\uFF08\u6700\u521D\u306E\u5024\u306E\u307E\u307E\u6B62\u307E\u308B\u3053\u3068\u304C\u3042\u308A\u307E\u3059\uFF09\u3002\u6307\u3059\u884C\u306B\u8FFD\u5F93\u3059\u308B\u306E\u306F\u3001\u6570\u5024\u306E\u6DFB\u5B57\u304C\u3061\u3087\u3046\u3069 1 \u3064\u3067 "*" \u3092\u542B\u307E\u306A\u3044\u30D1\u30B9\uFF08items.0.name\uFF09\u3060\u3051\u3067\u3059\u3002<template for> \u306E\u4E2D\u3067\u30D1\u30BF\u30FC\u30F3\u30D1\u30B9\u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044`,
3661
+ indexPathInFor: (p) => `for: "${p}" \u306F\u6570\u5024\u306E\u6DFB\u5B57\u3092\u542B\u3080\u30EA\u30B9\u30C8\u3067\u3059\u3002\u884C\u306F "${p}.*.\u2026" \u3068\u3057\u3066\u89E3\u6C7A\u3055\u308C\u3001\u884C\u3078\u306E\u53CC\u65B9\u5411\u675F\u7E1B\uFF08value: .v\uFF09\u3084\u6DFB\u5B57\u306E\u30D1\u30B9\u306E\u8AAD\u307F\u66F8\u304D\u306F\u5B9F\u884C\u6642\u306B\u4F8B\u5916\u306B\u306A\u308A\u307E\u3059\uFF08@wcstack/state #363\uFF09\u3002\u30EA\u30B9\u30C8\u306E\u6BB5\u3054\u3068\u306B for: \u3092\u5165\u308C\u5B50\u306B\u3057\u3066\u304F\u3060\u3055\u3044\uFF08\u4F8B: for: groups \u306E\u4E2D\u306B for: .items\uFF09`,
3661
3662
  indexArity: (api, p, req, wc, actual) => `${api}("${p}") \u306E\u6DFB\u5B57\u306F${req === "exact" ? `\u3061\u3087\u3046\u3069 ${wc} \u500B` : `${wc} \u500B\u4EE5\u4E0B`}\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\uFF08\u30D1\u30B9\u4E2D\u306E "*" \u306F ${wc} \u500B\uFF09\u3002${actual} \u500B\u6307\u5B9A\u3055\u308C\u3066\u3044\u307E\u3059`,
3662
3663
  wildcardRank: (subject, needed, available) => `${subject} \u306F ${needed} \u6BB5\u306E\u30EB\u30FC\u30D7\u304C\u5FC5\u8981\u3067\u3059\u304C\u3001\u73FE\u5728\u306E\u30B9\u30B3\u30FC\u30D7\u306F ${available} \u6BB5\u3067\u3059`,
3663
3664
  getterCycle: (cycle) => `\u30D1\u30B9 getter \u304C\u5FAA\u74B0\u53C2\u7167\u3057\u3066\u3044\u307E\u3059: ${cycle}`,
@@ -3679,6 +3680,7 @@ var ja = {
3679
3680
  watchKeyEmptySegment: (k) => `$watch \u306E\u30AD\u30FC "${k}" \u306B\u7A7A\u306E\u30D1\u30B9\u30BB\u30B0\u30E1\u30F3\u30C8\u304C\u3042\u308A\u307E\u3059`,
3680
3681
  watchHandlerNotFunction: (k) => `$watch \u306E\u30A8\u30F3\u30C8\u30EA "${k}" \u306E\u5024\u306F\u95A2\u6570\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059`,
3681
3682
  watchPathMissing: (k) => `$watch \u306E\u30AD\u30FC "${k}" \u306F\u72B6\u614B\u5B9A\u7FA9\u306B\u5B58\u5728\u3057\u307E\u305B\u3093\uFF08\u4E00\u5EA6\u3082\u767A\u706B\u3057\u307E\u305B\u3093\uFF09`,
3683
+ watchIndexKey: (k) => `$watch \u306E\u30AD\u30FC "${k}" \u306F\u6570\u5024\u306E\u6DFB\u5B57\u3092\u542B\u307F\u307E\u3059\u3002\u30EA\u30B9\u30C8\u304C\u4E38\u3054\u3068\u7F6E\u304D\u63DB\u308F\u308B\u3068\u767A\u706B\u3057\u307E\u3059\u304C\u3001\u6DFB\u5B57\u3092\u901A\u3057\u305F\u66F8\u304D\u8FBC\u307F\uFF08this["${k}"] = \u2026\uFF09\u3067\u306F\u767A\u706B\u3057\u307E\u305B\u3093\uFF08\u540C\u3058\u30D1\u30B9\u3092\u30DE\u30FC\u30AF\u30A2\u30C3\u30D7\u3067\u675F\u7E1B\u3057\u3066\u3044\u308C\u3070\u767A\u706B\u3057\u307E\u3059\uFF09`,
3682
3684
  scanNotObject: () => `$scan \u306F\u300C\u51FA\u529B\u540D \u2192 { from | on, initial, fold, resetOn? }\u300D\u306E\u30AA\u30D6\u30B8\u30A7\u30AF\u30C8\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\uFF08\u3053\u306E\u5F62\u306F\u30E9\u30F3\u30BF\u30A4\u30E0\u304C\u8AAD\u307F\u8FBC\u307F\u6642\u306B throw \u3057\u307E\u3059\uFF09`,
3683
3685
  scanOutputInvalid: (n) => `$scan \u306E\u51FA\u529B\u540D "${n}" \u306F\u5E73\u5766\u306A\u30D7\u30ED\u30D1\u30C6\u30A3\u540D\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\uFF08"."\u30FB"*"\u30FB\u5148\u982D\u306E "$" \u306F\u4F7F\u3048\u307E\u305B\u3093\uFF09`,
3684
3686
  scanOutputReserved: (n) => `$scan \u306E\u51FA\u529B\u540D "${n}" \u306F Object.prototype \u304B\u3089\u7D99\u627F\u3055\u308C\u308B\u540D\u524D\u3067\u3059\uFF08"constructor" \u306A\u3069\uFF09`,
@@ -3853,7 +3855,8 @@ var en = {
3853
3855
  patternPathOutsideFor: (p) => `Pattern path "${p}" cannot be used outside a <template for>`,
3854
3856
  omittedPathOutsideFor: (p) => `Shorthand path "${p}" cannot be used outside a <template for>`,
3855
3857
  loopIndexOutsideFor: (p) => `Loop index "${p}" cannot be used outside a <template for>`,
3856
- resolvedPathInUi: (p) => `Resolved path "${p}" cannot be used in a UI binding. Use a pattern path instead`,
3858
+ resolvedPathInUi: (p) => `Path "${p}" is read as a plain path, not as a row, so a write through its index does not reach this binding (it can keep its first value). Only a path with exactly one numeric index and no "*" (items.0.name) follows the row it points at. Use a pattern path inside a <template for> instead`,
3859
+ indexPathInFor: (p) => `for: "${p}" is a list reached through a numeric index. Its rows resolve as "${p}.*.\u2026", and a two-way binding in a row (value: .v) or a read or write through an index path throws at runtime (@wcstack/state #363). Nest one for: per list level instead (e.g. for: groups, with for: .items inside)`,
3857
3860
  indexArity: (api, p, req, wc, actual) => `${api}("${p}") requires ${req === "exact" ? "exactly" : "at most"} ${wc} index(es) ("*" appears ${wc} time(s) in the path) but got ${actual}`,
3858
3861
  wildcardRank: (subject, needed, available) => `${subject} needs ${needed} enclosing loop level(s) but the current scope provides ${available}`,
3859
3862
  getterCycle: (cycle) => `Path getters form a dependency cycle: ${cycle}`,
@@ -3875,6 +3878,7 @@ var en = {
3875
3878
  watchKeyEmptySegment: (k) => `$watch key "${k}" has an empty path segment`,
3876
3879
  watchHandlerNotFunction: (k) => `The value of $watch entry "${k}" must be a function`,
3877
3880
  watchPathMissing: (k) => `$watch key "${k}" does not exist in the state definition (it will never fire)`,
3881
+ watchIndexKey: (k) => `$watch key "${k}" has a numeric index: it fires when the list is replaced, but not on a write through the index (this["${k}"] = \u2026) unless the same path is also bound in the markup`,
3878
3882
  scanNotObject: () => `$scan must be an object mapping output names to { from | on, initial, fold, resetOn? } (the runtime throws on this shape at load time)`,
3879
3883
  scanOutputInvalid: (n) => `$scan output name "${n}" must be a flat property name ("." and "*" and a leading "$" are not allowed)`,
3880
3884
  scanOutputReserved: (n) => `$scan output name "${n}" is a property name inherited from Object.prototype (e.g. "constructor")`,
@@ -4033,6 +4037,49 @@ function getMessages(locale2) {
4033
4037
  return CATALOGS[resolveLocale(locale2)];
4034
4038
  }
4035
4039
 
4040
+ // src/service/indexPath.ts
4041
+ var ARRAY_INDEX_KEY = /^(?:0|[1-9]\d*)$/;
4042
+ function isNumericSegment(segment) {
4043
+ return segment !== "" && !Number.isNaN(Number(segment));
4044
+ }
4045
+ function rowIndexPosition(segments) {
4046
+ if (segments[0] === "" || segments.includes("*")) return -1;
4047
+ let position = -1;
4048
+ for (let i = 0; i < segments.length; i++) {
4049
+ if (!isNumericSegment(segments[i])) continue;
4050
+ if (i === 0 || position !== -1) return -1;
4051
+ position = i;
4052
+ }
4053
+ return position;
4054
+ }
4055
+ function isPlainIndexPath(path) {
4056
+ return hasIndexSegment(path) && rowIndexPosition(path.split(".")) === -1;
4057
+ }
4058
+ function hasIndexSegment(path) {
4059
+ return path.split(".").some((segment, i) => i > 0 && isNumericSegment(segment));
4060
+ }
4061
+ function toRowPatternPath(path, pathSet) {
4062
+ const segments = path.split(".");
4063
+ const position = rowIndexPosition(segments);
4064
+ if (position === -1 || Number(segments[position]) < 0) return path;
4065
+ if (!pathSet.has(`${segments.slice(0, position).join(".")}.*`)) return path;
4066
+ segments[position] = "*";
4067
+ return segments.join(".");
4068
+ }
4069
+ function toPlainPatternPath(path, pathSet) {
4070
+ const segments = path.split(".");
4071
+ for (let i = 1; i < segments.length; i++) {
4072
+ if (ARRAY_INDEX_KEY.test(segments[i]) && pathSet.has(`${segments.slice(0, i).join(".")}.*`)) {
4073
+ segments[i] = "*";
4074
+ }
4075
+ }
4076
+ return segments.join(".");
4077
+ }
4078
+ function isPlainElementPath(path, candidates, pathSet) {
4079
+ const pattern = toPlainPatternPath(path, pathSet);
4080
+ return candidates.some((c) => c.path === pattern && (c.kind === "data" || c.kind === "list"));
4081
+ }
4082
+
4036
4083
  // src/service/bindingValidator.ts
4037
4084
  var filterMap = new Map(BUILTIN_FILTERS.map((f) => [f.name, f]));
4038
4085
  function validateBindings(html, attrName, stateTagName = "wcs-state", locale2, fileReader, applicationSchema) {
@@ -4230,14 +4277,15 @@ function validateBindings(html, attrName, stateTagName = "wcs-state", locale2, f
4230
4277
  }
4231
4278
  }
4232
4279
  }
4233
- if (/\.\d+\.|\.\d+$/.test(pathTrimmed)) {
4280
+ const isFor = propNoMod === "for";
4281
+ if (isFor ? hasIndexSegment(pathTrimmed) : isPlainIndexPath(pathTrimmed)) {
4234
4282
  const pathOffset = binding.indexOf(parsed.path);
4235
4283
  const pathStart = bindingStart + pathOffset;
4236
4284
  diagnostics.push({
4237
4285
  code: WcsDiagnosticCode.TemplateSyntax,
4238
4286
  start: pathStart,
4239
4287
  end: pathStart + pathTrimmed.length,
4240
- message: msgs.resolvedPathInUi(pathTrimmed),
4288
+ message: isFor ? msgs.indexPathInFor(pathTrimmed) : msgs.resolvedPathInUi(pathTrimmed),
4241
4289
  severity: "warning"
4242
4290
  });
4243
4291
  }
@@ -4262,7 +4310,7 @@ function validateBindings(html, attrName, stateTagName = "wcs-state", locale2, f
4262
4310
  const pathTrimmed = parsed.path.trim();
4263
4311
  if (pathTrimmed && !pathTrimmed.startsWith(".") && !isLiteral(pathTrimmed)) {
4264
4312
  const chainDiags = validateFilterChainTypes(
4265
- pathTrimmed,
4313
+ toRowPatternPath(pathTrimmed, scopedPathSet),
4266
4314
  parsed.filters,
4267
4315
  scopedPaths,
4268
4316
  bindingStart,
@@ -4278,7 +4326,8 @@ function validateBindings(html, attrName, stateTagName = "wcs-state", locale2, f
4278
4326
  if (parsed.path && scopedPaths.length > 0) {
4279
4327
  const pathTrimmed = parsed.path.trim();
4280
4328
  if (pathTrimmed && !pathTrimmed.startsWith(".") && !isLiteral(pathTrimmed)) {
4281
- const resultType = resolveResultType(pathTrimmed, parsed.filters, scopedPaths);
4329
+ const typePath = toRowPatternPath(pathTrimmed, scopedPathSet);
4330
+ const resultType = resolveResultType(typePath, parsed.filters, scopedPaths);
4282
4331
  if (resultType !== null) {
4283
4332
  const typeReq = getExpectedType(
4284
4333
  parsed.property,
@@ -4287,7 +4336,7 @@ function validateBindings(html, attrName, stateTagName = "wcs-state", locale2, f
4287
4336
  if (typeReq && resultType !== typeReq.expected) {
4288
4337
  const pathOffset = binding.indexOf(parsed.path);
4289
4338
  const pathStart = bindingStart + pathOffset;
4290
- const schemaDefinite = typeReq.expected === "array" && parsed.filters.length === 0 && applicationSchema !== void 0 && scopedPaths.some((p) => p.path === pathTrimmed && p.fromSchema === true);
4339
+ const schemaDefinite = typeReq.expected === "array" && parsed.filters.length === 0 && applicationSchema !== void 0 && scopedPaths.some((p) => p.path === typePath && p.fromSchema === true);
4291
4340
  diagnostics.push({
4292
4341
  code: schemaDefinite ? WcsDiagnosticCode.PathTypeMismatch : WcsDiagnosticCode.BindingTypeExpectation,
4293
4342
  start: pathStart,
@@ -4451,7 +4500,9 @@ function validatePathExistence(checkPath, displayPath, scopedPaths, scopedPathSe
4451
4500
  }
4452
4501
  return null;
4453
4502
  }
4454
- if (!scopedPathSet.has(checkPath) && !matchesRecursionCandidates(scopedPaths, checkPath, scopedPathSet)) {
4503
+ if (scopedPathSet.has(checkPath)) return null;
4504
+ const rowPath = toRowPatternPath(checkPath, scopedPathSet);
4505
+ if (!scopedPathSet.has(rowPath) && !matchesRecursionCandidates(scopedPaths, rowPath, scopedPathSet) && !isPlainElementPath(checkPath, scopedPaths, scopedPathSet)) {
4455
4506
  return msgs.pathMissing(displayPath);
4456
4507
  }
4457
4508
  return null;
@@ -4469,8 +4520,11 @@ function validateSchemaPathExistence(checkPath, displayPath, scopedPaths, scoped
4469
4520
  return toMissingVerdict(validatePathExistence(checkPath, displayPath, scopedPaths, scopedPathSet, commandNames, msgs));
4470
4521
  }
4471
4522
  if (scopedPathSet.has(checkPath)) return null;
4472
- if (matchesRecursionCandidates(scopedPaths, checkPath, scopedPathSet)) return null;
4473
- const resolution = resolveSchemaPath(schema, schema.$defs ?? {}, checkPath.split("."));
4523
+ const rowPath = toRowPatternPath(checkPath, scopedPathSet);
4524
+ if (scopedPathSet.has(rowPath)) return null;
4525
+ if (matchesRecursionCandidates(scopedPaths, rowPath, scopedPathSet)) return null;
4526
+ if (isPlainElementPath(checkPath, scopedPaths, scopedPathSet)) return null;
4527
+ const resolution = resolveSchemaPath(schema, schema.$defs ?? {}, toPlainPatternPath(rowPath, scopedPathSet).split("."));
4474
4528
  if (resolution.kind === "nonexistent") {
4475
4529
  return { code: WcsDiagnosticCode.PathNonexistent, message: msgs.pathNonexistent(displayPath), severity: "error" };
4476
4530
  }
@@ -4903,9 +4957,10 @@ function validateTemplateSyntax(html, stateTagName, bindAttrName = "data-wcs", l
4903
4957
  message: msgs.recursionUnsupported(path, "binding")
4904
4958
  };
4905
4959
  }
4906
- if (isValidTemplatePath(path, pathSet2, scoped)) return null;
4960
+ const rowPath = toRowPatternPath(path, pathSet2);
4961
+ if (isValidTemplatePath(path, rowPath, pathSet2, scoped)) return null;
4907
4962
  if (defaultSchema !== void 0 && !path.startsWith("$")) {
4908
- const resolution = resolveSchemaPath(defaultSchema, defaultSchema.$defs ?? {}, path.split("."));
4963
+ const resolution = resolveSchemaPath(defaultSchema, defaultSchema.$defs ?? {}, toPlainPatternPath(rowPath, pathSet2).split("."));
4909
4964
  return resolution.kind === "nonexistent" ? { code: WcsDiagnosticCode.PathNonexistent, severity: "error", message: msgs.pathNonexistent(displayPath) } : null;
4910
4965
  }
4911
4966
  return { code: WcsDiagnosticCode.BindingPathMissing, severity: "warning", message: msgs.pathMissing(displayPath) };
@@ -4975,7 +5030,7 @@ function validateTemplateSyntax(html, stateTagName, bindAttrName = "data-wcs", l
4975
5030
  }
4976
5031
  }
4977
5032
  }
4978
- if (/\.\d+\.|\.\d+$/.test(pathPart)) {
5033
+ if (isPlainIndexPath(pathPart)) {
4979
5034
  diagnostics.push({
4980
5035
  code: WcsDiagnosticCode.TemplateSyntax,
4981
5036
  start: item.exprStart,
@@ -5040,14 +5095,14 @@ function validateTemplateSyntax(html, stateTagName, bindAttrName = "data-wcs", l
5040
5095
  }
5041
5096
  return diagnostics;
5042
5097
  }
5043
- function isValidTemplatePath(path, pathSet, scopedPaths) {
5098
+ function isValidTemplatePath(path, rowPath, pathSet, scopedPaths) {
5044
5099
  if (/^\$\d+$/.test(path)) return true;
5045
5100
  if (path.startsWith("$streamStatus.") || path.startsWith("$streamError.")) {
5046
5101
  const prefix = path.startsWith("$streamStatus.") ? "$streamStatus." : "$streamError.";
5047
5102
  const hasNamespace = scopedPaths.some((p) => p.path.startsWith(prefix));
5048
5103
  return !hasNamespace || pathSet.has(path);
5049
5104
  }
5050
- return pathSet.has(path) || matchesRecursionCandidates(scopedPaths, path, pathSet);
5105
+ return pathSet.has(path) || pathSet.has(rowPath) || matchesRecursionCandidates(scopedPaths, rowPath, pathSet) || isPlainElementPath(path, scopedPaths, pathSet);
5051
5106
  }
5052
5107
 
5053
5108
  // src/core/parser/positionalParser.ts
@@ -6952,9 +7007,10 @@ function validateEntry(entry, pathSet, paths, msgs) {
6952
7007
  return invalid(msgs.watchHandlerNotFunction(key));
6953
7008
  }
6954
7009
  if (pathSet.size > 0 && !pathSet.has(key) && !matchesRecursion(collectRecursionSpecs(paths), key, (p) => pathSet.has(p))) {
7010
+ const rowKey = toRowPatternPath(key, pathSet);
6955
7011
  return {
6956
7012
  code: WcsDiagnosticCode.WatchPathMissing,
6957
- message: msgs.watchPathMissing(key),
7013
+ message: rowKey !== key && pathSet.has(rowKey) ? msgs.watchIndexKey(key) : msgs.watchPathMissing(key),
6958
7014
  severity: "warning"
6959
7015
  };
6960
7016
  }
@@ -567,7 +567,7 @@ function loadSchemaCore() {
567
567
  return cached;
568
568
  }
569
569
 
570
- var version = "3.3.0";
570
+ var version = "3.4.0";
571
571
  var pkg = {
572
572
  version: version};
573
573
 
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 = "3.3.0";
8
+ var version = "3.4.0";
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": "3.3.0",
3
+ "version": "3.4.0",
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",