@elaraai/east-claude-plugin 1.0.90 → 1.0.91

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.
@@ -766,10 +766,112 @@ var noUnexecutedEastExpression = {
766
766
  // ../east-diagnostics/dist/src/rules/no-reinlined-east-binding.js
767
767
  var NAME10 = "no-reinlined-east-binding";
768
768
  var CODE10 = 990010;
769
+ var REPEATING_METHODS = /* @__PURE__ */ new Set([
770
+ "map",
771
+ "filter",
772
+ "filterMap",
773
+ "firstMap",
774
+ "flatMap",
775
+ "flattenToArray",
776
+ "flattenToSet",
777
+ "flattenToDict",
778
+ "forEach",
779
+ "reduce",
780
+ "scan",
781
+ "mapReduce",
782
+ "groupReduce",
783
+ "groupBy",
784
+ "groupSize",
785
+ "groupSum",
786
+ "groupMean",
787
+ "groupEvery",
788
+ "groupSome",
789
+ "groupMaximum",
790
+ "groupMinimum",
791
+ "groupToArrays",
792
+ "groupToSets",
793
+ "groupToDicts",
794
+ "groupFindAll",
795
+ "groupFindFirst",
796
+ "groupFindMaximum",
797
+ "groupFindMinimum",
798
+ "toDict",
799
+ "toSet",
800
+ "toArray",
801
+ "toTree",
802
+ "sort",
803
+ "sortInPlace",
804
+ "isSorted",
805
+ "findFirst",
806
+ "findAll",
807
+ "findMaximum",
808
+ "findMinimum",
809
+ "findSortedFirst",
810
+ "findSortedLast",
811
+ "findSortedRange",
812
+ "mergeAll",
813
+ "unionInPlace",
814
+ "mapRows",
815
+ "some",
816
+ "every",
817
+ "sum",
818
+ "mean",
819
+ "maximum",
820
+ "minimum",
821
+ "getKeys",
822
+ "generate"
823
+ ]);
824
+ var LOOP_METHODS = /* @__PURE__ */ new Set(["for", "while"]);
825
+ function isFunctionLike(node, t) {
826
+ return t.isArrowFunction(node) || t.isFunctionExpression(node) || t.isFunctionDeclaration(node) || t.isMethodDeclaration(node);
827
+ }
828
+ function enclosingFunction(node, t) {
829
+ let current = node.parent;
830
+ while (current !== void 0 && !isFunctionLike(current, t))
831
+ current = current.parent;
832
+ return current;
833
+ }
834
+ function hostKind(fn, ctx) {
835
+ const t = ctx.ts;
836
+ const call = fn.parent;
837
+ if (call === void 0 || !t.isCallExpression(call) || !call.arguments.some((arg) => arg === fn))
838
+ return void 0;
839
+ const callee = call.expression;
840
+ if (isEastFunctionCall(call, t))
841
+ return "nested function";
842
+ if (!t.isPropertyAccessExpression(callee))
843
+ return void 0;
844
+ const method = callee.name.text;
845
+ const receiver = callee.expression;
846
+ if (LOOP_METHODS.has(method)) {
847
+ return isBlockBuilderType(ctx.checker.getTypeAtLocation(receiver)) ? "loop body" : void 0;
848
+ }
849
+ if (!REPEATING_METHODS.has(method))
850
+ return void 0;
851
+ if (isEastExprType(ctx.checker.getTypeAtLocation(receiver)))
852
+ return "callback";
853
+ if (t.isPropertyAccessExpression(receiver) && t.isIdentifier(receiver.expression) && receiver.expression.text === "East") {
854
+ return "callback";
855
+ }
856
+ return void 0;
857
+ }
858
+ function repeatingHostBetween(use, declFunction, ctx) {
859
+ let found;
860
+ let current = use.parent;
861
+ while (current !== void 0 && current !== declFunction) {
862
+ if (isFunctionLike(current, ctx.ts)) {
863
+ const kind = hostKind(current, ctx);
864
+ if (kind !== void 0)
865
+ found = kind;
866
+ }
867
+ current = current.parent;
868
+ }
869
+ return found;
870
+ }
769
871
  var noReinlinedEastBinding = {
770
872
  name: NAME10,
771
873
  code: CODE10,
772
- description: "An East Expr bound to a JS const/let and reused inside an East block is re-inlined per use \u2014 bind it once with $.let/$.const.",
874
+ description: "An East Expr bound to a JS const/let and read twice inside one East body, or read inside a per-element callback, a loop body or a nested East.function, is re-inlined \u2014 and re-evaluated \u2014 at each read; bind it once with $.let/$.const.",
773
875
  check(node, ctx) {
774
876
  const t = ctx.ts;
775
877
  if (!t.isVariableDeclaration(node))
@@ -791,32 +893,39 @@ var noReinlinedEastBinding = {
791
893
  if (declSymbol === void 0)
792
894
  return;
793
895
  const name = node.name.text;
794
- const perBody = /* @__PURE__ */ new Map();
896
+ const declFunction = enclosingFunction(node, t);
897
+ const perFunction = /* @__PURE__ */ new Map();
898
+ let repeating;
795
899
  const visit = (n) => {
796
- if (t.isIdentifier(n) && n !== node.name && n.text === name) {
797
- if (ctx.checker.getSymbolAtLocation(n) === declSymbol) {
798
- const body = enclosingBlockScope(n, ctx);
799
- if (body !== void 0)
800
- perBody.set(body, (perBody.get(body) ?? 0) + 1);
900
+ if (t.isIdentifier(n) && n !== node.name && n.text === name && ctx.checker.getSymbolAtLocation(n) === declSymbol) {
901
+ if (enclosingBlockScope(n, ctx) !== void 0) {
902
+ const multiplied = repeatingHostBetween(n, declFunction, ctx);
903
+ if (multiplied !== void 0) {
904
+ repeating ??= multiplied;
905
+ } else {
906
+ const bucket = enclosingFunction(n, t) ?? ctx.sourceFile;
907
+ perFunction.set(bucket, (perFunction.get(bucket) ?? 0) + 1);
908
+ }
801
909
  }
802
910
  }
803
911
  t.forEachChild(n, visit);
804
912
  };
805
913
  visit(ctx.sourceFile);
806
- let maxInBody = 0;
807
- for (const count of perBody.values())
808
- if (count > maxInBody)
809
- maxInBody = count;
810
- if (maxInBody < 2)
914
+ let worst = 0;
915
+ for (const count of perFunction.values())
916
+ if (count > worst)
917
+ worst = count;
918
+ if (worst < 2 && repeating === void 0)
811
919
  return;
812
920
  const sf = ctx.sourceFile;
813
921
  const start = node.getStart(sf);
922
+ const messageText = worst >= 2 ? `This East expression is bound to a JS \`const\`/\`let\` and read ${worst} times inside one East body, so it is re-inlined \u2014 and re-evaluated, with a fresh identity for mutable values \u2014 at each read. Bind it once with \`$.const\`/\`$.let\`.` : `This East expression is bound to a JS \`const\`/\`let\` outside a ${repeating} and read inside it, so it is re-inlined there \u2014 and re-evaluated every time the ${repeating} runs. Bind it once with \`$.const\`/\`$.let\` before the call.`;
814
923
  ctx.report({
815
924
  ruleName: NAME10,
816
925
  code: CODE10,
817
926
  start,
818
927
  length: node.getEnd() - start,
819
- messageText: "This East expression is bound to a JS `const`/`let` and used more than once inside an East block, so it is re-inlined \u2014 and re-evaluated, with a fresh identity for mutable values \u2014 at each use. Bind it once with `$.const`/`$.let`.",
928
+ messageText,
820
929
  category: "error"
821
930
  });
822
931
  }
@@ -770,10 +770,112 @@ var noUnexecutedEastExpression = {
770
770
  // ../east-diagnostics/dist/src/rules/no-reinlined-east-binding.js
771
771
  var NAME10 = "no-reinlined-east-binding";
772
772
  var CODE10 = 990010;
773
+ var REPEATING_METHODS = /* @__PURE__ */ new Set([
774
+ "map",
775
+ "filter",
776
+ "filterMap",
777
+ "firstMap",
778
+ "flatMap",
779
+ "flattenToArray",
780
+ "flattenToSet",
781
+ "flattenToDict",
782
+ "forEach",
783
+ "reduce",
784
+ "scan",
785
+ "mapReduce",
786
+ "groupReduce",
787
+ "groupBy",
788
+ "groupSize",
789
+ "groupSum",
790
+ "groupMean",
791
+ "groupEvery",
792
+ "groupSome",
793
+ "groupMaximum",
794
+ "groupMinimum",
795
+ "groupToArrays",
796
+ "groupToSets",
797
+ "groupToDicts",
798
+ "groupFindAll",
799
+ "groupFindFirst",
800
+ "groupFindMaximum",
801
+ "groupFindMinimum",
802
+ "toDict",
803
+ "toSet",
804
+ "toArray",
805
+ "toTree",
806
+ "sort",
807
+ "sortInPlace",
808
+ "isSorted",
809
+ "findFirst",
810
+ "findAll",
811
+ "findMaximum",
812
+ "findMinimum",
813
+ "findSortedFirst",
814
+ "findSortedLast",
815
+ "findSortedRange",
816
+ "mergeAll",
817
+ "unionInPlace",
818
+ "mapRows",
819
+ "some",
820
+ "every",
821
+ "sum",
822
+ "mean",
823
+ "maximum",
824
+ "minimum",
825
+ "getKeys",
826
+ "generate"
827
+ ]);
828
+ var LOOP_METHODS = /* @__PURE__ */ new Set(["for", "while"]);
829
+ function isFunctionLike(node, t) {
830
+ return t.isArrowFunction(node) || t.isFunctionExpression(node) || t.isFunctionDeclaration(node) || t.isMethodDeclaration(node);
831
+ }
832
+ function enclosingFunction(node, t) {
833
+ let current = node.parent;
834
+ while (current !== void 0 && !isFunctionLike(current, t))
835
+ current = current.parent;
836
+ return current;
837
+ }
838
+ function hostKind(fn, ctx) {
839
+ const t = ctx.ts;
840
+ const call = fn.parent;
841
+ if (call === void 0 || !t.isCallExpression(call) || !call.arguments.some((arg) => arg === fn))
842
+ return void 0;
843
+ const callee = call.expression;
844
+ if (isEastFunctionCall(call, t))
845
+ return "nested function";
846
+ if (!t.isPropertyAccessExpression(callee))
847
+ return void 0;
848
+ const method = callee.name.text;
849
+ const receiver = callee.expression;
850
+ if (LOOP_METHODS.has(method)) {
851
+ return isBlockBuilderType(ctx.checker.getTypeAtLocation(receiver)) ? "loop body" : void 0;
852
+ }
853
+ if (!REPEATING_METHODS.has(method))
854
+ return void 0;
855
+ if (isEastExprType(ctx.checker.getTypeAtLocation(receiver)))
856
+ return "callback";
857
+ if (t.isPropertyAccessExpression(receiver) && t.isIdentifier(receiver.expression) && receiver.expression.text === "East") {
858
+ return "callback";
859
+ }
860
+ return void 0;
861
+ }
862
+ function repeatingHostBetween(use, declFunction, ctx) {
863
+ let found;
864
+ let current = use.parent;
865
+ while (current !== void 0 && current !== declFunction) {
866
+ if (isFunctionLike(current, ctx.ts)) {
867
+ const kind = hostKind(current, ctx);
868
+ if (kind !== void 0)
869
+ found = kind;
870
+ }
871
+ current = current.parent;
872
+ }
873
+ return found;
874
+ }
773
875
  var noReinlinedEastBinding = {
774
876
  name: NAME10,
775
877
  code: CODE10,
776
- description: "An East Expr bound to a JS const/let and reused inside an East block is re-inlined per use \u2014 bind it once with $.let/$.const.",
878
+ description: "An East Expr bound to a JS const/let and read twice inside one East body, or read inside a per-element callback, a loop body or a nested East.function, is re-inlined \u2014 and re-evaluated \u2014 at each read; bind it once with $.let/$.const.",
777
879
  check(node, ctx) {
778
880
  const t = ctx.ts;
779
881
  if (!t.isVariableDeclaration(node))
@@ -795,32 +897,39 @@ var noReinlinedEastBinding = {
795
897
  if (declSymbol === void 0)
796
898
  return;
797
899
  const name = node.name.text;
798
- const perBody = /* @__PURE__ */ new Map();
900
+ const declFunction = enclosingFunction(node, t);
901
+ const perFunction = /* @__PURE__ */ new Map();
902
+ let repeating;
799
903
  const visit = (n) => {
800
- if (t.isIdentifier(n) && n !== node.name && n.text === name) {
801
- if (ctx.checker.getSymbolAtLocation(n) === declSymbol) {
802
- const body = enclosingBlockScope(n, ctx);
803
- if (body !== void 0)
804
- perBody.set(body, (perBody.get(body) ?? 0) + 1);
904
+ if (t.isIdentifier(n) && n !== node.name && n.text === name && ctx.checker.getSymbolAtLocation(n) === declSymbol) {
905
+ if (enclosingBlockScope(n, ctx) !== void 0) {
906
+ const multiplied = repeatingHostBetween(n, declFunction, ctx);
907
+ if (multiplied !== void 0) {
908
+ repeating ??= multiplied;
909
+ } else {
910
+ const bucket = enclosingFunction(n, t) ?? ctx.sourceFile;
911
+ perFunction.set(bucket, (perFunction.get(bucket) ?? 0) + 1);
912
+ }
805
913
  }
806
914
  }
807
915
  t.forEachChild(n, visit);
808
916
  };
809
917
  visit(ctx.sourceFile);
810
- let maxInBody = 0;
811
- for (const count of perBody.values())
812
- if (count > maxInBody)
813
- maxInBody = count;
814
- if (maxInBody < 2)
918
+ let worst = 0;
919
+ for (const count of perFunction.values())
920
+ if (count > worst)
921
+ worst = count;
922
+ if (worst < 2 && repeating === void 0)
815
923
  return;
816
924
  const sf = ctx.sourceFile;
817
925
  const start = node.getStart(sf);
926
+ const messageText = worst >= 2 ? `This East expression is bound to a JS \`const\`/\`let\` and read ${worst} times inside one East body, so it is re-inlined \u2014 and re-evaluated, with a fresh identity for mutable values \u2014 at each read. Bind it once with \`$.const\`/\`$.let\`.` : `This East expression is bound to a JS \`const\`/\`let\` outside a ${repeating} and read inside it, so it is re-inlined there \u2014 and re-evaluated every time the ${repeating} runs. Bind it once with \`$.const\`/\`$.let\` before the call.`;
818
927
  ctx.report({
819
928
  ruleName: NAME10,
820
929
  code: CODE10,
821
930
  start,
822
931
  length: node.getEnd() - start,
823
- messageText: "This East expression is bound to a JS `const`/`let` and used more than once inside an East block, so it is re-inlined \u2014 and re-evaluated, with a fresh identity for mutable values \u2014 at each use. Bind it once with `$.const`/`$.let`.",
932
+ messageText,
824
933
  category: "error"
825
934
  });
826
935
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "east",
3
- "version": "1.0.90",
3
+ "version": "1.0.91",
4
4
  "description": "East programming language ecosystem - a statically typed, expression-based language for business logic, data processing, UI definitions, and durable execution",
5
5
  "author": {
6
6
  "name": "Elara AI",