@elaraai/east-claude-plugin 1.0.90 → 1.0.92
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/.build/daemon/lsp.js +122 -13
- package/.build/daemon/server.js +122 -13
- package/.claude-plugin/plugin.json +1 -1
- package/index.json +844 -799
- package/package.json +1 -1
- package/skills/east-py/SKILL.md +58 -53
package/.build/daemon/lsp.js
CHANGED
|
@@ -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
|
|
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
|
|
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 (
|
|
798
|
-
const
|
|
799
|
-
if (
|
|
800
|
-
|
|
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
|
|
807
|
-
for (const count of
|
|
808
|
-
if (count >
|
|
809
|
-
|
|
810
|
-
if (
|
|
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
|
|
928
|
+
messageText,
|
|
820
929
|
category: "error"
|
|
821
930
|
});
|
|
822
931
|
}
|
package/.build/daemon/server.js
CHANGED
|
@@ -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
|
|
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
|
|
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 (
|
|
802
|
-
const
|
|
803
|
-
if (
|
|
804
|
-
|
|
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
|
|
811
|
-
for (const count of
|
|
812
|
-
if (count >
|
|
813
|
-
|
|
814
|
-
if (
|
|
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
|
|
932
|
+
messageText,
|
|
824
933
|
category: "error"
|
|
825
934
|
});
|
|
826
935
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "east",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.92",
|
|
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",
|