@wcstack/typescript 4.0.0-rc.9 → 4.0.1

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.9";
575
+ var version = "4.0.1";
576
576
  var pkg = {
577
577
  version: version};
578
578
 
@@ -1309,6 +1309,10 @@ var SENTENCES = {
1309
1309
  121
1310
1310
  /* ForNoFilters */
1311
1311
  ]: (t) => `"${t}": "for:" takes no filters \u2014 a row is "<path>.<index>", so the rows of a filtered list would name other elements. Declare a getter that returns the filtered list and loop over it ("for: <getter>").`,
1312
+ [
1313
+ 122
1314
+ /* NamespaceNeedsName */
1315
+ ]: (t, ns) => ns === "class" ? `"${t}": "class:" needs a class name \u2014 "class.NAME:" toggles one class (a bare "class:" would set an element property named "class", which does nothing). To set the whole class attribute, bind "className:".` : `"${t}": "attr:" needs an attribute name \u2014 "attr.NAME:" sets one attribute (a bare "attr:" would set an element property named "attr", which does nothing).`,
1312
1316
  [
1313
1317
  119
1314
1318
  /* EmptySegment */
@@ -1573,6 +1577,9 @@ function parseBindTextsForElement(bindText) {
1573
1577
  bindingType: keyword === SPREAD_PROP ? "spread" : keyword
1574
1578
  };
1575
1579
  }
1580
+ if (keyword === CLASS_NAMESPACE || keyword === ATTR_NAMESPACE) {
1581
+ raise(122, [bindText2, keyword]);
1582
+ }
1576
1583
  const stateResult = parseStatePart(statePart);
1577
1584
  const propResult = parsePropPart(propPart);
1578
1585
  const [head] = propResult.propSegments;
@@ -2870,6 +2877,7 @@ function analyzeStatePaths(scriptContent) {
2870
2877
  }
2871
2878
  }
2872
2879
  collectRowShapesFromAssignments(scriptContent, paths);
2880
+ collectGetterResultShapes(topLevelProps, effectiveDescriptors, paths);
2873
2881
  return paths;
2874
2882
  }
2875
2883
  function specFromRecursionValue(prop) {
@@ -3253,6 +3261,284 @@ function pushRowFieldPaths(path, prop, paths, depth) {
3253
3261
  }
3254
3262
  }
3255
3263
  }
3264
+ var ROW_PRESERVING_METHODS = /* @__PURE__ */ new Set([
3265
+ "filter",
3266
+ "toSorted",
3267
+ "toReversed",
3268
+ "slice",
3269
+ "toSpliced",
3270
+ "concat",
3271
+ "with",
3272
+ "sort",
3273
+ "reverse"
3274
+ ]);
3275
+ var ROW_PICKING_METHODS = /* @__PURE__ */ new Set(["find", "findLast", "at"]);
3276
+ var MAX_GETTER_SHAPE_PASSES = 4;
3277
+ function collectGetterResultShapes(props, effective, paths) {
3278
+ const getters = /* @__PURE__ */ new Map();
3279
+ for (const prop of props) {
3280
+ if (prop.kind !== "getter" || prop.accessor !== "get" || prop.value === void 0) continue;
3281
+ if (prop.name.startsWith("$") || hasRecursionWildcard(prop.name) || effective.get(prop.name) === "data") continue;
3282
+ getters.set(prop.name, prop);
3283
+ }
3284
+ for (let pass = 0; pass < MAX_GETTER_SHAPE_PASSES; pass++) {
3285
+ const before = paths.length;
3286
+ for (const [name, prop] of getters) {
3287
+ const shape = getterResultShape(prop.value, paths);
3288
+ if (shape !== null) applyResultShape(name, shape, paths);
3289
+ }
3290
+ if (paths.length === before) break;
3291
+ }
3292
+ }
3293
+ function getterResultShape(body, paths) {
3294
+ const scan = maskCommentsAndStrings(body);
3295
+ const aliases = /* @__PURE__ */ new Map();
3296
+ for (const m of scan.matchAll(/\b(?:const|let|var)\s+([$\w]+)\s*=(?![=>])/g)) {
3297
+ const name = m[1];
3298
+ if (aliases.has(name)) continue;
3299
+ const start = m.index + m[0].length;
3300
+ const shape = expressionShape(readExpression(body, scan, start), paths, aliases);
3301
+ if (shape !== null) aliases.set(name, shape);
3302
+ }
3303
+ const shapes = [];
3304
+ for (const m of scan.matchAll(/\breturn\b/g)) {
3305
+ const start = m.index + m[0].length;
3306
+ const shape = expressionShape(readExpression(body, scan, start), paths, aliases);
3307
+ if (shape !== null) shapes.push(shape);
3308
+ }
3309
+ return shapes.length === 0 ? null : mergeShapes(shapes);
3310
+ }
3311
+ function readExpression(text3, scan, start) {
3312
+ let depth = 0;
3313
+ let i = start;
3314
+ for (; i < scan.length; i++) {
3315
+ const ch = scan[i];
3316
+ if (ch === "(" || ch === "[" || ch === "{") depth++;
3317
+ else if (ch === ")" || ch === "]" || ch === "}") {
3318
+ if (depth === 0) break;
3319
+ depth--;
3320
+ } else if (depth === 0 && (ch === ";" || ch === ",")) break;
3321
+ else if (depth === 0 && ch === "\n") {
3322
+ const before = scan.slice(start, i).trimEnd();
3323
+ const after = scan.slice(i + 1).trimStart();
3324
+ if (before === "") continue;
3325
+ if (!/[.?:|&+\-*/=(,]$/.test(before) && !/^[.?:|&]/.test(after)) break;
3326
+ }
3327
+ }
3328
+ return text3.slice(start, i).trim();
3329
+ }
3330
+ function expressionShape(text3, paths, aliases) {
3331
+ const expr = stripWrappingParens(text3.trim());
3332
+ if (expr === "") return null;
3333
+ const scan = maskCommentsAndStrings(expr);
3334
+ const ternary = splitTernary(scan);
3335
+ if (ternary !== null) {
3336
+ const branches = [expr.slice(ternary[0] + 1, ternary[1]), expr.slice(ternary[1] + 1)].map((part) => expressionShape(part, paths, aliases)).filter((s) => s !== null);
3337
+ return branches.length === 0 ? null : mergeShapes(branches);
3338
+ }
3339
+ const operands = splitTopLevel(scan, /\|\||\?\?/g);
3340
+ if (operands.length > 1) {
3341
+ const sides = operands.map(([s, e]) => expressionShape(expr.slice(s, e), paths, aliases)).filter((s) => s !== null);
3342
+ return sides.length === 0 ? null : mergeShapes(sides);
3343
+ }
3344
+ if (scan.startsWith("{")) {
3345
+ if (extractDelimitedContent(expr, scan, 0, "{", "}").length !== expr.length - 2) return null;
3346
+ return { isList: false, mirrors: [], fields: extractRowLiteralFields(expr).map((field) => ({ to: "", field })) };
3347
+ }
3348
+ let shape;
3349
+ let rest;
3350
+ if (scan.startsWith("[")) {
3351
+ const inner = extractDelimitedContent(expr, scan, 0, "[", "]");
3352
+ rest = inner.length + 2;
3353
+ const spread = /^\s*\.\.\.([\s\S]+)$/.exec(inner);
3354
+ if (spread === null || splitTopLevelElements(inner).length !== 1) return null;
3355
+ const source = expressionShape(spread[1], paths, aliases);
3356
+ if (source === null || !source.isList) return null;
3357
+ shape = source;
3358
+ } else {
3359
+ const base = readBase(expr, scan, aliases, paths);
3360
+ if (base === null) return null;
3361
+ ({ shape, rest } = base);
3362
+ }
3363
+ return applyChain(expr, scan, rest, shape);
3364
+ }
3365
+ function readBase(expr, scan, aliases, paths) {
3366
+ const alias = /^([$\w]+)/.exec(scan);
3367
+ if (alias === null) return null;
3368
+ if (alias[1] !== "this") {
3369
+ const shape = aliases.get(alias[1]);
3370
+ return shape === void 0 ? null : { shape, rest: alias[1].length };
3371
+ }
3372
+ const segments = [];
3373
+ let i = 4;
3374
+ for (; ; ) {
3375
+ const dot = /^\s*\??\.\s*([$\w]+)/.exec(scan.slice(i));
3376
+ if (dot !== null) {
3377
+ if (/^\s*\(/.test(scan.slice(i + dot[0].length))) break;
3378
+ segments.push(dot[1]);
3379
+ i += dot[0].length;
3380
+ continue;
3381
+ }
3382
+ const bracket = /^\s*\[\s*(["'])/.exec(scan.slice(i));
3383
+ if (bracket !== null) {
3384
+ const open = i + bracket[0].length - 1;
3385
+ const close = scan.indexOf("]", open);
3386
+ const literal2 = /^(["'])([^"']*)\1\s*$/.exec(expr.slice(open, close));
3387
+ if (close === -1 || literal2 === null) return null;
3388
+ segments.push(literal2[2]);
3389
+ i = close + 1;
3390
+ continue;
3391
+ }
3392
+ break;
3393
+ }
3394
+ const path = segments.join(".");
3395
+ if (path === "" || path.startsWith("$")) return null;
3396
+ return {
3397
+ shape: { isList: hasPath(paths, `${path}.*`), mirrors: [{ from: path, to: "" }], fields: [] },
3398
+ rest: i
3399
+ };
3400
+ }
3401
+ function applyChain(expr, scan, start, initial) {
3402
+ let shape = initial;
3403
+ let i = start;
3404
+ while (scan.slice(i).trim() !== "") {
3405
+ const call = /^\s*\??\.\s*([$\w]+)\s*\(/.exec(scan.slice(i));
3406
+ if (call === null) return null;
3407
+ const open = i + call[0].length - 1;
3408
+ const args = extractDelimitedContent(expr, scan, open, "(", ")");
3409
+ i = open + args.length + 2;
3410
+ const method = call[1];
3411
+ if (ROW_PRESERVING_METHODS.has(method)) {
3412
+ if (!shape.isList) return null;
3413
+ } else if (ROW_PICKING_METHODS.has(method)) {
3414
+ if (!shape.isList) return null;
3415
+ shape = pickRow(shape);
3416
+ } else if (method === "map") {
3417
+ if (!shape.isList) return null;
3418
+ const mapped = mapRows(args, shape);
3419
+ if (mapped === null) return null;
3420
+ shape = mapped;
3421
+ } else {
3422
+ return null;
3423
+ }
3424
+ }
3425
+ return shape;
3426
+ }
3427
+ function pickRow(shape) {
3428
+ return {
3429
+ isList: false,
3430
+ mirrors: shape.mirrors.map((m) => m.to === "" ? { from: `${m.from}.*`, to: "" } : { from: m.from, to: "" }),
3431
+ fields: shape.fields.filter((f) => f.to === ".*").map((f) => ({ to: "", field: f.field }))
3432
+ };
3433
+ }
3434
+ function mapRows(args, shape) {
3435
+ const [callback] = splitTopLevelElements(args);
3436
+ if (callback === void 0) return null;
3437
+ const head = /^(?:async\s+)?(?:function\b[^(]*\(\s*([$\w]+)[^)]*\)|\(\s*([$\w]+)[^)]*\)|([$\w]+))\s*(=>)?\s*/.exec(callback);
3438
+ if (head === null) return null;
3439
+ const param = head[1] ?? head[2] ?? head[3];
3440
+ const body = callback.slice(head[0].length);
3441
+ let literal2 = null;
3442
+ const bodyScan = maskCommentsAndStrings(body);
3443
+ if (bodyScan.startsWith("(")) {
3444
+ const inner = stripWrappingParens(body).trim();
3445
+ if (inner.startsWith("{")) literal2 = inner;
3446
+ } else if (bodyScan.startsWith("{")) {
3447
+ const block = extractDelimitedContent(body, bodyScan, 0, "{", "}");
3448
+ const blockScan = maskCommentsAndStrings(block);
3449
+ const ret = /\breturn\s*\{/.exec(blockScan);
3450
+ if (ret !== null) {
3451
+ const open = ret.index + ret[0].length - 1;
3452
+ literal2 = `{${extractDelimitedContent(block, blockScan, open, "{", "}")}}`;
3453
+ }
3454
+ }
3455
+ if (literal2 === null) return null;
3456
+ const content = extractObjectContent(literal2);
3457
+ const spreadsRow = splitTopLevelElements(content).some((e) => e === `...${param}`);
3458
+ return {
3459
+ isList: true,
3460
+ mirrors: spreadsRow ? shape.mirrors.map((m) => m.to === "" ? { from: `${m.from}.*`, to: ".*" } : m) : [],
3461
+ fields: [
3462
+ ...spreadsRow ? shape.fields.filter((f) => f.to === ".*") : [],
3463
+ ...extractRowLiteralFields(literal2).map((field) => ({ to: ".*", field }))
3464
+ ]
3465
+ };
3466
+ }
3467
+ function applyResultShape(getter, shape, paths) {
3468
+ if (shape.isList) {
3469
+ if (!hasPath(paths, `${getter}.*`)) paths.push({ path: `${getter}.*`, kind: "list" });
3470
+ if (!hasPath(paths, `${getter}.length`)) paths.push({ path: `${getter}.length`, kind: "data", typeHint: "number" });
3471
+ }
3472
+ for (const { from, to } of shape.mirrors) {
3473
+ if (from === getter || from.startsWith(`${getter}.`)) continue;
3474
+ const sources = paths.filter((p) => p.path.startsWith(`${from}.`) && (p.kind === "data" || p.kind === "list"));
3475
+ for (const source of sources) {
3476
+ const target = `${getter}${to}${source.path.slice(from.length)}`;
3477
+ if (!hasPath(paths, target)) paths.push(withHint({ path: target, kind: source.kind }, source.typeHint));
3478
+ }
3479
+ }
3480
+ for (const { to, field } of shape.fields) {
3481
+ pushRowFieldPaths(`${getter}${to}.${field.name}`, field, paths, 1);
3482
+ }
3483
+ }
3484
+ function mergeShapes(shapes) {
3485
+ return {
3486
+ isList: shapes.some((s) => s.isList),
3487
+ mirrors: shapes.flatMap((s) => s.mirrors),
3488
+ fields: shapes.flatMap((s) => s.fields)
3489
+ };
3490
+ }
3491
+ function stripWrappingParens(text3) {
3492
+ let expr = text3.trim();
3493
+ for (; ; ) {
3494
+ if (!expr.startsWith("(")) return expr;
3495
+ const scan = maskCommentsAndStrings(expr);
3496
+ const inner = extractDelimitedContent(expr, scan, 0, "(", ")");
3497
+ if (inner.length !== expr.length - 2) return expr;
3498
+ expr = inner.trim();
3499
+ }
3500
+ }
3501
+ function splitTernary(scan) {
3502
+ let depth = 0;
3503
+ let question = -1;
3504
+ let nested = 0;
3505
+ for (let i = 0; i < scan.length; i++) {
3506
+ const ch = scan[i];
3507
+ if (ch === "(" || ch === "[" || ch === "{") depth++;
3508
+ else if (ch === ")" || ch === "]" || ch === "}") depth--;
3509
+ else if (depth !== 0) continue;
3510
+ else if (ch === "?") {
3511
+ if (scan[i + 1] === "." || scan[i + 1] === "?" || scan[i - 1] === "?") continue;
3512
+ if (question === -1) question = i;
3513
+ else nested++;
3514
+ } else if (ch === ":" && question !== -1) {
3515
+ if (nested > 0) nested--;
3516
+ else return [question, i];
3517
+ }
3518
+ }
3519
+ return null;
3520
+ }
3521
+ function splitTopLevel(scan, separator) {
3522
+ const out = [];
3523
+ let depth = 0;
3524
+ let start = 0;
3525
+ for (let i = 0; i < scan.length; i++) {
3526
+ const ch = scan[i];
3527
+ if (ch === "(" || ch === "[" || ch === "{") depth++;
3528
+ else if (ch === ")" || ch === "]" || ch === "}") depth--;
3529
+ else if (depth === 0) {
3530
+ separator.lastIndex = i;
3531
+ const m = separator.exec(scan);
3532
+ if (m !== null && m.index === i) {
3533
+ out.push([start, i]);
3534
+ start = i + m[0].length;
3535
+ i = start - 1;
3536
+ }
3537
+ }
3538
+ }
3539
+ out.push([start, scan.length]);
3540
+ return out;
3541
+ }
3256
3542
  function hasPath(paths, path) {
3257
3543
  return paths.some((p) => p.path === path);
3258
3544
  }
@@ -11290,7 +11576,7 @@ function validateFilterUsage(filter, bindingStart, msgs) {
11290
11576
  start: bindingStart + filter.offset,
11291
11577
  end: bindingStart + filter.offset + filter.name.length,
11292
11578
  message: removedFilterMessage(filter.name, filter.args, msgs) ?? msgs.filterUnknown(filter.name),
11293
- severity: "warning"
11579
+ severity: "error"
11294
11580
  });
11295
11581
  return diagnostics;
11296
11582
  }
@@ -11978,7 +12264,7 @@ function validateTemplateSyntax(html, stateTagName, bindAttrName = "data-wcs", l
11978
12264
  start: item.exprStart + filterOffset,
11979
12265
  end: item.exprStart + filterOffset + filterName.length,
11980
12266
  message: removedFilterMessage(filterName, filterArgsOf(segment), msgs) ?? msgs.filterUnknown(filterName),
11981
- severity: "warning"
12267
+ severity: "error"
11982
12268
  });
11983
12269
  }
11984
12270
  }
@@ -12595,7 +12881,11 @@ var BUILTIN_TAGS = {
12595
12881
  "wcs-defined": {
12596
12882
  "package": "defined",
12597
12883
  "hasWcBindable": true,
12598
- "observedAttributes": [],
12884
+ "observedAttributes": [
12885
+ "tags",
12886
+ "mode",
12887
+ "timeout"
12888
+ ],
12599
12889
  "inputs": {
12600
12890
  "tags": "tags",
12601
12891
  "mode": "mode",
@@ -573,7 +573,7 @@ function loadSchemaCore() {
573
573
  return cached;
574
574
  }
575
575
 
576
- var version = "4.0.0-rc.9";
576
+ var version = "4.0.1";
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.9";
8
+ var version = "4.0.1";
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.9",
3
+ "version": "4.0.1",
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",