@violetflux/eslint-plugin-kerros 0.3.0 → 0.3.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/index.cjs CHANGED
@@ -472,32 +472,51 @@ const setMutationMethods = /* @__PURE__ */ new Set([
472
472
  "clear",
473
473
  "delete"
474
474
  ]);
475
- /** Build separate dynamic call-site contexts for one local function. */
476
- function getFunctionCallSiteContexts(target, edges) {
475
+ /** Build a reusable index that streams dynamic call-site contexts without retaining every path. */
476
+ function createFunctionCallSiteContexts(edges) {
477
477
  const incoming = /* @__PURE__ */ new Map();
478
478
  for (const edge of edges) {
479
479
  const existing = incoming.get(edge.callee) ?? [];
480
480
  existing.push(edge);
481
481
  incoming.set(edge.callee, existing);
482
482
  }
483
- const contexts = [];
484
- /** Trace callers independently so states from different invocation paths never merge globally. */
485
- const trace = (fn, calls, stack) => {
486
- const edgesForFunction = incoming.get(fn) ?? [];
487
- let advanced = false;
488
- for (const edge of edgesForFunction) {
489
- if (stack.has(edge.caller)) continue;
490
- advanced = true;
491
- const nextCalls = new Map(calls);
492
- nextCalls.set(fn, edge.site);
493
- const nextStack = new Set(stack);
494
- nextStack.add(edge.caller);
495
- trace(edge.caller, nextCalls, nextStack);
483
+ /** Visit paths independently while retaining only the current DFS path. */
484
+ const some = (target, predicate) => {
485
+ const calls = /* @__PURE__ */ new Map();
486
+ const active = /* @__PURE__ */ new Set([target]);
487
+ const frames = [{
488
+ advanced: false,
489
+ edges: incoming.get(target) ?? [],
490
+ fn: target,
491
+ index: 0
492
+ }];
493
+ while (frames.length > 0) {
494
+ const frame = frames.at(-1);
495
+ if (!frame) break;
496
+ const edge = frame.edges[frame.index];
497
+ if (edge) {
498
+ frame.index += 1;
499
+ if (active.has(edge.caller)) continue;
500
+ frame.advanced = true;
501
+ calls.set(frame.fn, edge.site);
502
+ active.add(edge.caller);
503
+ frames.push({
504
+ advanced: false,
505
+ edges: incoming.get(edge.caller) ?? [],
506
+ fn: edge.caller,
507
+ index: 0,
508
+ parent: frame.fn
509
+ });
510
+ continue;
511
+ }
512
+ if (!frame.advanced && predicate(calls)) return true;
513
+ frames.pop();
514
+ active.delete(frame.fn);
515
+ if (frame.parent) calls.delete(frame.parent);
496
516
  }
497
- if (!advanced) contexts.push(calls);
517
+ return false;
498
518
  };
499
- trace(target, /* @__PURE__ */ new Map(), /* @__PURE__ */ new Set([target]));
500
- return contexts;
519
+ return { some };
501
520
  }
502
521
  /** Track assignment sources and resolve definitions that reach a concrete reference point. */
503
522
  function createReferenceOriginTracker(program) {
@@ -699,7 +718,7 @@ const objectEnumerationMethods = /* @__PURE__ */ new Set([
699
718
  "keys",
700
719
  "values"
701
720
  ]);
702
- /** Read the snapshot argument from a complete enumeration or serialization call. */
721
+ /** Read the tracked value argument from a broad enumeration or serialization call. */
703
722
  function getBroadArgument(node) {
704
723
  const [argument] = node.arguments;
705
724
  if (!argument || argument.type === "SpreadElement") return false;
@@ -714,48 +733,72 @@ const noBroadStoreAccess = createRule({
714
733
  name: "no-broad-store-access",
715
734
  meta: {
716
735
  type: "problem",
717
- docs: { description: "Prevent complete Store enumeration and serialization." },
718
- schema: [],
719
- messages: { broadAccess: "Do not enumerate or spread the complete Store snapshot." }
736
+ docs: { description: "Prevent broad enumeration and serialization of complete Store snapshots." },
737
+ schema: [{
738
+ type: "object",
739
+ properties: { includeObjectFields: { type: "boolean" } },
740
+ additionalProperties: false
741
+ }],
742
+ messages: {
743
+ broadAccess: "Do not enumerate, serialize, or spread a complete selector-free Store snapshot.",
744
+ broadObjectField: "Do not enumerate, serialize, or spread an object field from a selector-free Store snapshot."
745
+ }
720
746
  },
721
- defaultOptions: [],
722
- create(context) {
747
+ defaultOptions: [{ includeObjectFields: false }],
748
+ create(context, [options]) {
723
749
  const { getIdentifierSymbol, getType, isStoreHookCall } = createKerrosTypeTools(context);
724
750
  const origins = createReferenceOriginTracker(context.sourceCode.ast);
725
- /** Test whether an expression originates from a selector-free Store snapshot. */
726
- const isSnapshotDerived = (input, seen = /* @__PURE__ */ new Set()) => {
751
+ /** Classify whether an expression is a complete Store snapshot or one object field from it. */
752
+ const readStoreOrigin = (input, seen = /* @__PURE__ */ new Set()) => {
727
753
  const node = unwrapExpression(input);
728
754
  if (node.type === "CallExpression") {
729
755
  const selector = node.arguments[0];
730
- return (node.arguments.length === 0 || node.arguments.length === 1 && selector?.type !== "SpreadElement" && (getType(selector).flags & typescript.default.TypeFlags.Undefined) !== 0) && isStoreHookCall(node);
756
+ return (node.arguments.length === 0 || node.arguments.length === 1 && selector?.type !== "SpreadElement" && (getType(selector).flags & typescript.default.TypeFlags.Undefined) !== 0) && isStoreHookCall(node) ? "snapshot" : void 0;
731
757
  }
732
- if (node.type === "AssignmentExpression") return isSnapshotDerived(node.right, seen);
733
- if (node.type === "MemberExpression") return isSnapshotDerived(node.object, seen);
734
- if (node.type !== "Identifier") return false;
758
+ if (node.type === "AssignmentExpression") return readStoreOrigin(node.right, seen);
759
+ if (node.type === "MemberExpression") return readStoreOrigin(node.object, seen) ? "objectField" : void 0;
760
+ if (node.type !== "Identifier") return;
735
761
  const symbol = getIdentifierSymbol(node);
736
- if (!symbol || seen.has(symbol)) return false;
762
+ if (!symbol || seen.has(symbol)) return;
737
763
  seen.add(symbol);
738
- const derived = origins.resolve(symbol, node).some((source) => isSnapshotDerived(source, seen));
764
+ let result;
765
+ for (const source of origins.resolve(symbol, node)) {
766
+ const origin = readStoreOrigin(source.expression, seen);
767
+ if (!origin) continue;
768
+ if (!source.objectField && origin === "snapshot") {
769
+ result = "snapshot";
770
+ break;
771
+ }
772
+ result = "objectField";
773
+ }
739
774
  seen.delete(symbol);
740
- return derived;
775
+ return result;
741
776
  };
742
777
  /** Report one operation that subscribes to every enumerable field. */
743
778
  const reportBroadAccess = (expression) => {
744
- if (isSnapshotDerived(expression)) context.report({
779
+ const origin = readStoreOrigin(expression);
780
+ if (origin === "snapshot") context.report({
745
781
  node: expression,
746
782
  messageId: "broadAccess"
747
783
  });
784
+ else if (origin === "objectField" && options.includeObjectFields) context.report({
785
+ node: expression,
786
+ messageId: "broadObjectField"
787
+ });
748
788
  };
749
789
  /** Track object-valued bindings destructured from a snapshot. */
750
- const recordObjectBindings = (pattern, source, write) => {
790
+ const recordObjectBindings = (pattern, source, write, objectField = false) => {
751
791
  if (pattern.type === "Identifier") {
752
792
  if ((getType(pattern).flags & typescript.default.TypeFlags.Object) === 0) return;
753
793
  const symbol = getIdentifierSymbol(pattern);
754
- if (symbol) origins.record(symbol, source, write);
794
+ if (symbol) origins.record(symbol, {
795
+ expression: source,
796
+ objectField
797
+ }, write);
755
798
  return;
756
799
  }
757
800
  if (pattern.type === "AssignmentPattern") {
758
- recordObjectBindings(pattern.left, source, write);
801
+ recordObjectBindings(pattern.left, source, write, objectField);
759
802
  return;
760
803
  }
761
804
  if (pattern.type === "RestElement") return;
@@ -763,8 +806,8 @@ const noBroadStoreAccess = createRule({
763
806
  const entries = pattern.type === "ObjectPattern" ? pattern.properties : pattern.elements;
764
807
  for (const entry of entries) {
765
808
  if (!entry) continue;
766
- if (entry.type === "Property") recordObjectBindings(entry.value, source, write);
767
- else recordObjectBindings(entry, source, write);
809
+ if (entry.type === "Property") recordObjectBindings(entry.value, source, write, true);
810
+ else recordObjectBindings(entry, source, write, true);
768
811
  }
769
812
  };
770
813
  return {
@@ -779,7 +822,10 @@ const noBroadStoreAccess = createRule({
779
822
  if (!node.init) return;
780
823
  if (node.id.type === "Identifier") {
781
824
  const symbol = getIdentifierSymbol(node.id);
782
- if (symbol) origins.record(symbol, node.init, node);
825
+ if (symbol) origins.record(symbol, {
826
+ expression: node.init,
827
+ objectField: false
828
+ }, node);
783
829
  return;
784
830
  }
785
831
  if (node.id.type === "ObjectPattern" && node.id.properties.some((property) => property.type === "RestElement")) reportBroadAccess(node.init);
@@ -788,7 +834,10 @@ const noBroadStoreAccess = createRule({
788
834
  AssignmentExpression(node) {
789
835
  if (node.left.type !== "Identifier") return;
790
836
  const symbol = getIdentifierSymbol(node.left);
791
- if (symbol) origins.record(symbol, node.right, node);
837
+ if (symbol) origins.record(symbol, {
838
+ expression: node.right,
839
+ objectField: false
840
+ }, node);
792
841
  }
793
842
  };
794
843
  }
@@ -1481,7 +1530,7 @@ const noRenderInstanceSnapshot = createRule({
1481
1530
  changed = true;
1482
1531
  }
1483
1532
  }
1484
- const renderedEdges = localEdges.filter((edge) => rendered.has(edge.caller));
1533
+ const callContexts = createFunctionCallSiteContexts(localEdges.filter((edge) => rendered.has(edge.caller)));
1485
1534
  /** Test whether an expression is a local alias of a Store instance Hook result. */
1486
1535
  const isInstanceDerived = (input, calls, seen = /* @__PURE__ */ new Set()) => {
1487
1536
  const node = unwrapExpression(input);
@@ -1514,7 +1563,7 @@ const noRenderInstanceSnapshot = createRule({
1514
1563
  };
1515
1564
  for (const snapshot of snapshots) {
1516
1565
  if (!snapshot.owner || !rendered.has(snapshot.owner)) continue;
1517
- if (getFunctionCallSiteContexts(snapshot.owner, renderedEdges).some((calls) => {
1566
+ if (callContexts.some(snapshot.owner, (calls) => {
1518
1567
  return snapshot.kind === "instance" ? isInstanceDerived(snapshot.source, calls) : isSnapshotReaderDerived(snapshot.source, calls);
1519
1568
  })) context.report({
1520
1569
  node: snapshot.node,
@@ -1682,9 +1731,10 @@ const noStoreMutation = createRule({
1682
1731
  site: node
1683
1732
  });
1684
1733
  }
1685
- for (const candidate of candidates) if ((candidate.owner ? getFunctionCallSiteContexts(candidate.owner, localEdges) : [/* @__PURE__ */ new Map()]).some((calls) => {
1734
+ const callContexts = createFunctionCallSiteContexts(localEdges);
1735
+ for (const candidate of candidates) if (candidate.owner ? callContexts.some(candidate.owner, (calls) => {
1686
1736
  return isSnapshotDerived(candidate.expression, maxAliasDepth, /* @__PURE__ */ new Set(), calls);
1687
- })) context.report({
1737
+ }) : isSnapshotDerived(candidate.expression, maxAliasDepth, /* @__PURE__ */ new Set(), /* @__PURE__ */ new Map())) context.report({
1688
1738
  node: candidate.node,
1689
1739
  messageId: "mutation"
1690
1740
  });
@@ -2451,7 +2501,7 @@ const rules = {
2451
2501
  const plugin = {
2452
2502
  meta: {
2453
2503
  name: "@violetflux/eslint-plugin-kerros",
2454
- version: "0.3.0"
2504
+ version: "0.3.2"
2455
2505
  },
2456
2506
  rules
2457
2507
  };
package/dist/index.mjs CHANGED
@@ -444,32 +444,51 @@ const setMutationMethods = /* @__PURE__ */ new Set([
444
444
  "clear",
445
445
  "delete"
446
446
  ]);
447
- /** Build separate dynamic call-site contexts for one local function. */
448
- function getFunctionCallSiteContexts(target, edges) {
447
+ /** Build a reusable index that streams dynamic call-site contexts without retaining every path. */
448
+ function createFunctionCallSiteContexts(edges) {
449
449
  const incoming = /* @__PURE__ */ new Map();
450
450
  for (const edge of edges) {
451
451
  const existing = incoming.get(edge.callee) ?? [];
452
452
  existing.push(edge);
453
453
  incoming.set(edge.callee, existing);
454
454
  }
455
- const contexts = [];
456
- /** Trace callers independently so states from different invocation paths never merge globally. */
457
- const trace = (fn, calls, stack) => {
458
- const edgesForFunction = incoming.get(fn) ?? [];
459
- let advanced = false;
460
- for (const edge of edgesForFunction) {
461
- if (stack.has(edge.caller)) continue;
462
- advanced = true;
463
- const nextCalls = new Map(calls);
464
- nextCalls.set(fn, edge.site);
465
- const nextStack = new Set(stack);
466
- nextStack.add(edge.caller);
467
- trace(edge.caller, nextCalls, nextStack);
455
+ /** Visit paths independently while retaining only the current DFS path. */
456
+ const some = (target, predicate) => {
457
+ const calls = /* @__PURE__ */ new Map();
458
+ const active = /* @__PURE__ */ new Set([target]);
459
+ const frames = [{
460
+ advanced: false,
461
+ edges: incoming.get(target) ?? [],
462
+ fn: target,
463
+ index: 0
464
+ }];
465
+ while (frames.length > 0) {
466
+ const frame = frames.at(-1);
467
+ if (!frame) break;
468
+ const edge = frame.edges[frame.index];
469
+ if (edge) {
470
+ frame.index += 1;
471
+ if (active.has(edge.caller)) continue;
472
+ frame.advanced = true;
473
+ calls.set(frame.fn, edge.site);
474
+ active.add(edge.caller);
475
+ frames.push({
476
+ advanced: false,
477
+ edges: incoming.get(edge.caller) ?? [],
478
+ fn: edge.caller,
479
+ index: 0,
480
+ parent: frame.fn
481
+ });
482
+ continue;
483
+ }
484
+ if (!frame.advanced && predicate(calls)) return true;
485
+ frames.pop();
486
+ active.delete(frame.fn);
487
+ if (frame.parent) calls.delete(frame.parent);
468
488
  }
469
- if (!advanced) contexts.push(calls);
489
+ return false;
470
490
  };
471
- trace(target, /* @__PURE__ */ new Map(), /* @__PURE__ */ new Set([target]));
472
- return contexts;
491
+ return { some };
473
492
  }
474
493
  /** Track assignment sources and resolve definitions that reach a concrete reference point. */
475
494
  function createReferenceOriginTracker(program) {
@@ -671,7 +690,7 @@ const objectEnumerationMethods = /* @__PURE__ */ new Set([
671
690
  "keys",
672
691
  "values"
673
692
  ]);
674
- /** Read the snapshot argument from a complete enumeration or serialization call. */
693
+ /** Read the tracked value argument from a broad enumeration or serialization call. */
675
694
  function getBroadArgument(node) {
676
695
  const [argument] = node.arguments;
677
696
  if (!argument || argument.type === "SpreadElement") return false;
@@ -686,48 +705,72 @@ const noBroadStoreAccess = createRule({
686
705
  name: "no-broad-store-access",
687
706
  meta: {
688
707
  type: "problem",
689
- docs: { description: "Prevent complete Store enumeration and serialization." },
690
- schema: [],
691
- messages: { broadAccess: "Do not enumerate or spread the complete Store snapshot." }
708
+ docs: { description: "Prevent broad enumeration and serialization of complete Store snapshots." },
709
+ schema: [{
710
+ type: "object",
711
+ properties: { includeObjectFields: { type: "boolean" } },
712
+ additionalProperties: false
713
+ }],
714
+ messages: {
715
+ broadAccess: "Do not enumerate, serialize, or spread a complete selector-free Store snapshot.",
716
+ broadObjectField: "Do not enumerate, serialize, or spread an object field from a selector-free Store snapshot."
717
+ }
692
718
  },
693
- defaultOptions: [],
694
- create(context) {
719
+ defaultOptions: [{ includeObjectFields: false }],
720
+ create(context, [options]) {
695
721
  const { getIdentifierSymbol, getType, isStoreHookCall } = createKerrosTypeTools(context);
696
722
  const origins = createReferenceOriginTracker(context.sourceCode.ast);
697
- /** Test whether an expression originates from a selector-free Store snapshot. */
698
- const isSnapshotDerived = (input, seen = /* @__PURE__ */ new Set()) => {
723
+ /** Classify whether an expression is a complete Store snapshot or one object field from it. */
724
+ const readStoreOrigin = (input, seen = /* @__PURE__ */ new Set()) => {
699
725
  const node = unwrapExpression(input);
700
726
  if (node.type === "CallExpression") {
701
727
  const selector = node.arguments[0];
702
- return (node.arguments.length === 0 || node.arguments.length === 1 && selector?.type !== "SpreadElement" && (getType(selector).flags & ts.TypeFlags.Undefined) !== 0) && isStoreHookCall(node);
728
+ return (node.arguments.length === 0 || node.arguments.length === 1 && selector?.type !== "SpreadElement" && (getType(selector).flags & ts.TypeFlags.Undefined) !== 0) && isStoreHookCall(node) ? "snapshot" : void 0;
703
729
  }
704
- if (node.type === "AssignmentExpression") return isSnapshotDerived(node.right, seen);
705
- if (node.type === "MemberExpression") return isSnapshotDerived(node.object, seen);
706
- if (node.type !== "Identifier") return false;
730
+ if (node.type === "AssignmentExpression") return readStoreOrigin(node.right, seen);
731
+ if (node.type === "MemberExpression") return readStoreOrigin(node.object, seen) ? "objectField" : void 0;
732
+ if (node.type !== "Identifier") return;
707
733
  const symbol = getIdentifierSymbol(node);
708
- if (!symbol || seen.has(symbol)) return false;
734
+ if (!symbol || seen.has(symbol)) return;
709
735
  seen.add(symbol);
710
- const derived = origins.resolve(symbol, node).some((source) => isSnapshotDerived(source, seen));
736
+ let result;
737
+ for (const source of origins.resolve(symbol, node)) {
738
+ const origin = readStoreOrigin(source.expression, seen);
739
+ if (!origin) continue;
740
+ if (!source.objectField && origin === "snapshot") {
741
+ result = "snapshot";
742
+ break;
743
+ }
744
+ result = "objectField";
745
+ }
711
746
  seen.delete(symbol);
712
- return derived;
747
+ return result;
713
748
  };
714
749
  /** Report one operation that subscribes to every enumerable field. */
715
750
  const reportBroadAccess = (expression) => {
716
- if (isSnapshotDerived(expression)) context.report({
751
+ const origin = readStoreOrigin(expression);
752
+ if (origin === "snapshot") context.report({
717
753
  node: expression,
718
754
  messageId: "broadAccess"
719
755
  });
756
+ else if (origin === "objectField" && options.includeObjectFields) context.report({
757
+ node: expression,
758
+ messageId: "broadObjectField"
759
+ });
720
760
  };
721
761
  /** Track object-valued bindings destructured from a snapshot. */
722
- const recordObjectBindings = (pattern, source, write) => {
762
+ const recordObjectBindings = (pattern, source, write, objectField = false) => {
723
763
  if (pattern.type === "Identifier") {
724
764
  if ((getType(pattern).flags & ts.TypeFlags.Object) === 0) return;
725
765
  const symbol = getIdentifierSymbol(pattern);
726
- if (symbol) origins.record(symbol, source, write);
766
+ if (symbol) origins.record(symbol, {
767
+ expression: source,
768
+ objectField
769
+ }, write);
727
770
  return;
728
771
  }
729
772
  if (pattern.type === "AssignmentPattern") {
730
- recordObjectBindings(pattern.left, source, write);
773
+ recordObjectBindings(pattern.left, source, write, objectField);
731
774
  return;
732
775
  }
733
776
  if (pattern.type === "RestElement") return;
@@ -735,8 +778,8 @@ const noBroadStoreAccess = createRule({
735
778
  const entries = pattern.type === "ObjectPattern" ? pattern.properties : pattern.elements;
736
779
  for (const entry of entries) {
737
780
  if (!entry) continue;
738
- if (entry.type === "Property") recordObjectBindings(entry.value, source, write);
739
- else recordObjectBindings(entry, source, write);
781
+ if (entry.type === "Property") recordObjectBindings(entry.value, source, write, true);
782
+ else recordObjectBindings(entry, source, write, true);
740
783
  }
741
784
  };
742
785
  return {
@@ -751,7 +794,10 @@ const noBroadStoreAccess = createRule({
751
794
  if (!node.init) return;
752
795
  if (node.id.type === "Identifier") {
753
796
  const symbol = getIdentifierSymbol(node.id);
754
- if (symbol) origins.record(symbol, node.init, node);
797
+ if (symbol) origins.record(symbol, {
798
+ expression: node.init,
799
+ objectField: false
800
+ }, node);
755
801
  return;
756
802
  }
757
803
  if (node.id.type === "ObjectPattern" && node.id.properties.some((property) => property.type === "RestElement")) reportBroadAccess(node.init);
@@ -760,7 +806,10 @@ const noBroadStoreAccess = createRule({
760
806
  AssignmentExpression(node) {
761
807
  if (node.left.type !== "Identifier") return;
762
808
  const symbol = getIdentifierSymbol(node.left);
763
- if (symbol) origins.record(symbol, node.right, node);
809
+ if (symbol) origins.record(symbol, {
810
+ expression: node.right,
811
+ objectField: false
812
+ }, node);
764
813
  }
765
814
  };
766
815
  }
@@ -1453,7 +1502,7 @@ const noRenderInstanceSnapshot = createRule({
1453
1502
  changed = true;
1454
1503
  }
1455
1504
  }
1456
- const renderedEdges = localEdges.filter((edge) => rendered.has(edge.caller));
1505
+ const callContexts = createFunctionCallSiteContexts(localEdges.filter((edge) => rendered.has(edge.caller)));
1457
1506
  /** Test whether an expression is a local alias of a Store instance Hook result. */
1458
1507
  const isInstanceDerived = (input, calls, seen = /* @__PURE__ */ new Set()) => {
1459
1508
  const node = unwrapExpression(input);
@@ -1486,7 +1535,7 @@ const noRenderInstanceSnapshot = createRule({
1486
1535
  };
1487
1536
  for (const snapshot of snapshots) {
1488
1537
  if (!snapshot.owner || !rendered.has(snapshot.owner)) continue;
1489
- if (getFunctionCallSiteContexts(snapshot.owner, renderedEdges).some((calls) => {
1538
+ if (callContexts.some(snapshot.owner, (calls) => {
1490
1539
  return snapshot.kind === "instance" ? isInstanceDerived(snapshot.source, calls) : isSnapshotReaderDerived(snapshot.source, calls);
1491
1540
  })) context.report({
1492
1541
  node: snapshot.node,
@@ -1654,9 +1703,10 @@ const noStoreMutation = createRule({
1654
1703
  site: node
1655
1704
  });
1656
1705
  }
1657
- for (const candidate of candidates) if ((candidate.owner ? getFunctionCallSiteContexts(candidate.owner, localEdges) : [/* @__PURE__ */ new Map()]).some((calls) => {
1706
+ const callContexts = createFunctionCallSiteContexts(localEdges);
1707
+ for (const candidate of candidates) if (candidate.owner ? callContexts.some(candidate.owner, (calls) => {
1658
1708
  return isSnapshotDerived(candidate.expression, maxAliasDepth, /* @__PURE__ */ new Set(), calls);
1659
- })) context.report({
1709
+ }) : isSnapshotDerived(candidate.expression, maxAliasDepth, /* @__PURE__ */ new Set(), /* @__PURE__ */ new Map())) context.report({
1660
1710
  node: candidate.node,
1661
1711
  messageId: "mutation"
1662
1712
  });
@@ -2423,7 +2473,7 @@ const rules = {
2423
2473
  const plugin = {
2424
2474
  meta: {
2425
2475
  name: "@violetflux/eslint-plugin-kerros",
2426
- version: "0.3.0"
2476
+ version: "0.3.2"
2427
2477
  },
2428
2478
  rules
2429
2479
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@violetflux/eslint-plugin-kerros",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "Type-aware ESLint rules for Kerros stores.",
5
5
  "repository": {
6
6
  "type": "git",