@elaraai/east-diagnostics 1.0.4

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.
Files changed (87) hide show
  1. package/LICENSE.md +31 -0
  2. package/README.md +74 -0
  3. package/dist/src/block-builder.d.ts +16 -0
  4. package/dist/src/block-builder.d.ts.map +1 -0
  5. package/dist/src/block-builder.js +19 -0
  6. package/dist/src/block-builder.js.map +1 -0
  7. package/dist/src/block-scope.d.ts +27 -0
  8. package/dist/src/block-scope.d.ts.map +1 -0
  9. package/dist/src/block-scope.js +53 -0
  10. package/dist/src/block-scope.js.map +1 -0
  11. package/dist/src/east-type.d.ts +8 -0
  12. package/dist/src/east-type.d.ts.map +1 -0
  13. package/dist/src/east-type.js +28 -0
  14. package/dist/src/east-type.js.map +1 -0
  15. package/dist/src/index.d.ts +10 -0
  16. package/dist/src/index.d.ts.map +1 -0
  17. package/dist/src/index.js +4 -0
  18. package/dist/src/index.js.map +1 -0
  19. package/dist/src/rules/index.d.ts +19 -0
  20. package/dist/src/rules/index.d.ts.map +1 -0
  21. package/dist/src/rules/index.js +39 -0
  22. package/dist/src/rules/index.js.map +1 -0
  23. package/dist/src/rules/no-east-data-builder-helper.d.ts +3 -0
  24. package/dist/src/rules/no-east-data-builder-helper.d.ts.map +1 -0
  25. package/dist/src/rules/no-east-data-builder-helper.js +90 -0
  26. package/dist/src/rules/no-east-data-builder-helper.js.map +1 -0
  27. package/dist/src/rules/no-east-namespaced-type.d.ts +7 -0
  28. package/dist/src/rules/no-east-namespaced-type.d.ts.map +1 -0
  29. package/dist/src/rules/no-east-namespaced-type.js +32 -0
  30. package/dist/src/rules/no-east-namespaced-type.js.map +1 -0
  31. package/dist/src/rules/no-handrolled-variant.d.ts +3 -0
  32. package/dist/src/rules/no-handrolled-variant.d.ts.map +1 -0
  33. package/dist/src/rules/no-handrolled-variant.js +45 -0
  34. package/dist/src/rules/no-handrolled-variant.js.map +1 -0
  35. package/dist/src/rules/no-let-const-in-expression.d.ts +3 -0
  36. package/dist/src/rules/no-let-const-in-expression.d.ts.map +1 -0
  37. package/dist/src/rules/no-let-const-in-expression.js +51 -0
  38. package/dist/src/rules/no-let-const-in-expression.js.map +1 -0
  39. package/dist/src/rules/no-redundant-east-cast.d.ts +3 -0
  40. package/dist/src/rules/no-redundant-east-cast.d.ts.map +1 -0
  41. package/dist/src/rules/no-redundant-east-cast.js +44 -0
  42. package/dist/src/rules/no-redundant-east-cast.js.map +1 -0
  43. package/dist/src/rules/no-reinlined-east-binding.d.ts +3 -0
  44. package/dist/src/rules/no-reinlined-east-binding.d.ts.map +1 -0
  45. package/dist/src/rules/no-reinlined-east-binding.js +79 -0
  46. package/dist/src/rules/no-reinlined-east-binding.js.map +1 -0
  47. package/dist/src/rules/no-relative-src-import.d.ts +3 -0
  48. package/dist/src/rules/no-relative-src-import.d.ts.map +1 -0
  49. package/dist/src/rules/no-relative-src-import.js +40 -0
  50. package/dist/src/rules/no-relative-src-import.js.map +1 -0
  51. package/dist/src/rules/no-ts-helpers-in-east.d.ts +3 -0
  52. package/dist/src/rules/no-ts-helpers-in-east.d.ts.map +1 -0
  53. package/dist/src/rules/no-ts-helpers-in-east.js +50 -0
  54. package/dist/src/rules/no-ts-helpers-in-east.js.map +1 -0
  55. package/dist/src/rules/no-unexecuted-east-expression.d.ts +3 -0
  56. package/dist/src/rules/no-unexecuted-east-expression.d.ts.map +1 -0
  57. package/dist/src/rules/no-unexecuted-east-expression.js +50 -0
  58. package/dist/src/rules/no-unexecuted-east-expression.js.map +1 -0
  59. package/dist/src/rules/prefer-explicit-east-type.d.ts +3 -0
  60. package/dist/src/rules/prefer-explicit-east-type.d.ts.map +1 -0
  61. package/dist/src/rules/prefer-explicit-east-type.js +60 -0
  62. package/dist/src/rules/prefer-explicit-east-type.js.map +1 -0
  63. package/dist/src/rules/prefer-jsx-over-factory-call.d.ts +3 -0
  64. package/dist/src/rules/prefer-jsx-over-factory-call.d.ts.map +1 -0
  65. package/dist/src/rules/prefer-jsx-over-factory-call.js +141 -0
  66. package/dist/src/rules/prefer-jsx-over-factory-call.js.map +1 -0
  67. package/dist/src/rules/prefer-let-const-over-east-value.d.ts +7 -0
  68. package/dist/src/rules/prefer-let-const-over-east-value.d.ts.map +1 -0
  69. package/dist/src/rules/prefer-let-const-over-east-value.js +71 -0
  70. package/dist/src/rules/prefer-let-const-over-east-value.js.map +1 -0
  71. package/dist/src/rules/prefer-some-none.d.ts +7 -0
  72. package/dist/src/rules/prefer-some-none.d.ts.map +1 -0
  73. package/dist/src/rules/prefer-some-none.js +36 -0
  74. package/dist/src/rules/prefer-some-none.js.map +1 -0
  75. package/dist/src/run.d.ts +11 -0
  76. package/dist/src/run.d.ts.map +1 -0
  77. package/dist/src/run.js +25 -0
  78. package/dist/src/run.js.map +1 -0
  79. package/dist/src/service.d.ts +19 -0
  80. package/dist/src/service.d.ts.map +1 -0
  81. package/dist/src/service.js +149 -0
  82. package/dist/src/service.js.map +1 -0
  83. package/dist/src/types.d.ts +56 -0
  84. package/dist/src/types.d.ts.map +1 -0
  85. package/dist/src/types.js +2 -0
  86. package/dist/src/types.js.map +1 -0
  87. package/package.json +49 -0
package/LICENSE.md ADDED
@@ -0,0 +1,31 @@
1
+ Copyright (c) 2025 Elara AI Pty Ltd
2
+
3
+ # Dual License — AGPL-3.0 / Commercial
4
+
5
+ This software is available under two licenses. You may choose which license applies to your use:
6
+
7
+ ## Option 1: AGPL-3.0 (Open Source)
8
+
9
+ You may use, modify, and distribute this software under the terms of the GNU Affero General Public License v3.0.
10
+
11
+ This requires that if you use this software in a network service, you must make your complete source code available under AGPL-3.0.
12
+
13
+ Full text: https://www.gnu.org/licenses/agpl-3.0.html
14
+
15
+ ## Option 2: Commercial License
16
+
17
+ If you wish to use this software without the source code disclosure requirements of AGPL-3.0, you must obtain a commercial license from Elara AI Pty Ltd.
18
+
19
+ Contact: support@elara.ai
20
+
21
+ ## Contributions
22
+
23
+ Contributions are welcome. By submitting a pull request, you agree to license your contribution under both AGPL-3.0 and our commercial license terms.
24
+
25
+ ## Governing Law
26
+
27
+ This license is governed by the laws of New South Wales, Australia.
28
+
29
+ ---
30
+
31
+ *Elara AI Pty Ltd*
package/README.md ADDED
@@ -0,0 +1,74 @@
1
+ # East Diagnostics
2
+
3
+ > East-aware diagnostic rules over the TypeScript checker
4
+
5
+ [![License](https://img.shields.io/badge/license-AGPL--3.0-blue.svg)](LICENSE.md)
6
+ [![Node Version](https://img.shields.io/badge/node-%3E%3D22.0.0-brightgreen.svg)](https://nodejs.org)
7
+
8
+ **East Diagnostics** is the shared rule engine that catches East-specific
9
+ mistakes which plain TypeScript can't — the same checks surfaced to the agent at
10
+ write-time (the Claude plugin daemon) and to developers in the editor and CI
11
+ ([`@elaraai/eslint-plugin-east`](../eslint-plugin-east)). The rules run against a
12
+ real `ts.Program`, so they are type-aware, not regex heuristics.
13
+
14
+ ## Features
15
+
16
+ - **Shared rule set** - One engine, `runEastRules(ts, program, sourceFile, checker)`, reused across every surface.
17
+ - **Type-aware** - Rules consult the TypeScript checker (e.g. resolving `BlockBuilder`, variant contextual types).
18
+ - **No bundled compiler** - `typescript` is a peer dependency; the host's version is injected.
19
+ - **Diagnostics service** - `createDiagnosticsService()` resolves the nearest tsconfig, holds a warm `LanguageService`, and merges native type errors with the East rules for a file.
20
+
21
+ ## Rules
22
+
23
+ - **`no-redundant-east-cast`** - A TypeScript cast on the value of `$.let`/`$.const` when the East type argument is present.
24
+ - **`prefer-explicit-east-type`** - One-arg `$.let`/`$.const` on an under-determined value (`[]`, `{}`, `new Map()`).
25
+ - **`prefer-some-none`** - `variant("some"/"none", …)` instead of `some()` / `none`.
26
+ - **`no-handrolled-variant`** - A plain object literal where an East variant/option is expected.
27
+ - **`no-east-namespaced-type`** - `East.IntegerType` etc. instead of a bare import.
28
+ - **`prefer-let-const-over-east-value`** - `East.value(…)` declared or returned inside an `East.function` block.
29
+ - **`no-relative-src-import`** - Importing `../src/…` instead of the published package name.
30
+ - **`no-let-const-in-expression`** - Using `$.let`/`$.const` inline in an expression instead of binding to a `const`.
31
+ - **`no-unexecuted-east-expression`** - A bare East expression statement that is never executed with `$( … )` or bound.
32
+ - **`no-reinlined-east-binding`** - An East `Expr` bound to a JS `const`/`let` and reused inside a block is re-inlined per use — bind it once with `$.let`/`$.const`.
33
+ - **`no-east-data-builder-helper`** - A TS helper whose only job is to return a hand-built East value — inline it or make it a real `East.function`.
34
+ - **`prefer-jsx-over-factory-call`** - In a `.tsx` file, a factory's `Foo.Root(...)` whose result is a JSX element — author it with the `<Foo>` tag instead.
35
+
36
+ ## Usage
37
+
38
+ ```typescript
39
+ import * as ts from "typescript";
40
+ import { runEastRules, createDiagnosticsService } from "@elaraai/east-diagnostics";
41
+
42
+ // Pure: run the rules over one source file you already have a program + checker for.
43
+ const diagnostics = runEastRules(ts, program, sourceFile, checker, { disabled: ["prefer-some-none"] });
44
+
45
+ // Or let the service resolve the project and merge native + rule diagnostics.
46
+ const service = createDiagnosticsService();
47
+ const text = service.diagnoseText("/path/to/file.ts"); // "" when clean
48
+ ```
49
+
50
+ ## Claude Code plugin
51
+
52
+ The East ecosystem also ships a [Claude Code](https://claude.com/claude-code) plugin — East language skills, example search, and preemptive diagnostics for East code — installed separately from the `elaraai` marketplace:
53
+
54
+ ```text
55
+ # Inside Claude Code
56
+ /plugin marketplace add elaraai/east-workspace
57
+ /plugin install east@elaraai
58
+ ```
59
+
60
+ ```bash
61
+ # From a terminal
62
+ claude plugin marketplace add elaraai/east-workspace
63
+ claude plugin install east@elaraai
64
+ ```
65
+
66
+ ## License
67
+
68
+ Dual-licensed:
69
+ - **Open Source**: [AGPL-3.0](LICENSE.md) - Free for open source use
70
+ - **Commercial**: Available for proprietary use - contact support@elara.ai
71
+
72
+ ---
73
+
74
+ *Developed by [Elara AI Pty Ltd](https://elaraai.com/)*
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Copyright (c) 2025 Elara AI Pty Ltd
3
+ * Dual-licensed under AGPL-3.0 and commercial license. See LICENSE for details.
4
+ */
5
+ import type * as ts from "typescript";
6
+ import type { RuleContext } from "./types.js";
7
+ export interface BlockBuilderCall {
8
+ call: ts.CallExpression;
9
+ method: "let" | "const";
10
+ args: ts.NodeArray<ts.Expression>;
11
+ }
12
+ /** Match `<expr>.let(...)` / `<expr>.const(...)` where `<expr>` is an East
13
+ * `BlockBuilder`. Confirmed through the checker, so an unrelated `.let` /
14
+ * `.const` (lodash, a Map wrapper, …) never matches. */
15
+ export declare function matchBlockBuilderCall(node: ts.Node, ctx: RuleContext): BlockBuilderCall | undefined;
16
+ //# sourceMappingURL=block-builder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"block-builder.d.ts","sourceRoot":"","sources":["../../src/block-builder.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,KAAK,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAG9C,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,EAAE,CAAC,cAAc,CAAC;IACxB,MAAM,EAAE,KAAK,GAAG,OAAO,CAAC;IACxB,IAAI,EAAE,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC;CACnC;AAED;;wDAEwD;AACxD,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,EAAE,CAAC,IAAI,EACb,GAAG,EAAE,WAAW,GACf,gBAAgB,GAAG,SAAS,CAW9B"}
@@ -0,0 +1,19 @@
1
+ import { isBlockBuilderType } from "./east-type.js";
2
+ /** Match `<expr>.let(...)` / `<expr>.const(...)` where `<expr>` is an East
3
+ * `BlockBuilder`. Confirmed through the checker, so an unrelated `.let` /
4
+ * `.const` (lodash, a Map wrapper, …) never matches. */
5
+ export function matchBlockBuilderCall(node, ctx) {
6
+ const t = ctx.ts;
7
+ if (!t.isCallExpression(node))
8
+ return undefined;
9
+ const callee = node.expression;
10
+ if (!t.isPropertyAccessExpression(callee))
11
+ return undefined;
12
+ const method = callee.name.text;
13
+ if (method !== "let" && method !== "const")
14
+ return undefined;
15
+ if (!isBlockBuilderType(ctx.checker.getTypeAtLocation(callee.expression)))
16
+ return undefined;
17
+ return { call: node, method, args: node.arguments };
18
+ }
19
+ //# sourceMappingURL=block-builder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"block-builder.js","sourceRoot":"","sources":["../../src/block-builder.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAQpD;;wDAEwD;AACxD,MAAM,UAAU,qBAAqB,CACnC,IAAa,EACb,GAAgB;IAEhB,MAAM,CAAC,GAAG,GAAG,CAAC,EAAE,CAAC;IACjB,IAAI,CAAC,CAAC,CAAC,gBAAgB,CAAC,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC;IAChD,MAAM,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC;IAC/B,IAAI,CAAC,CAAC,CAAC,0BAA0B,CAAC,MAAM,CAAC;QAAE,OAAO,SAAS,CAAC;IAC5D,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;IAChC,IAAI,MAAM,KAAK,KAAK,IAAI,MAAM,KAAK,OAAO;QAAE,OAAO,SAAS,CAAC;IAE7D,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,OAAO,CAAC,iBAAiB,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QAAE,OAAO,SAAS,CAAC;IAE5F,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC;AACtD,CAAC"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Copyright (c) 2025 Elara AI Pty Ltd
3
+ * Dual-licensed under AGPL-3.0 and commercial license. See LICENSE for details.
4
+ */
5
+ import type * as ts from "typescript";
6
+ import type { RuleContext } from "./types.js";
7
+ /** True for an arrow/function whose first parameter is an East `BlockBuilder` —
8
+ * the shape of every East block callback: `East.function`'s `$ => …`, the JSX
9
+ * `<Reactive>{$ => …}}`, `ui(…, $ => …)`, a `.map(($, x) => …)` body. Keying on
10
+ * the parameter type (not the wrapping function's name) makes the check work for
11
+ * JSX authoring forms that have no `East.function` wrapper at all. */
12
+ export declare function isBlockBuilderCallback(node: ts.Node, ctx: RuleContext): boolean;
13
+ /**
14
+ * The outermost enclosing East block scope of `node` — an `East.function` call
15
+ * or a `BlockBuilder`-parameter callback — or `undefined` if there is none.
16
+ *
17
+ * Block-scoped rules used to look only for an enclosing `East.function`; East UI
18
+ * authored as `<Reactive>{$ => …}}` or `ui(…)` has a `BlockBuilder` `$` with no
19
+ * such wrapper, so those rules went silent inside it. Rooting on the BlockBuilder
20
+ * callback restores coverage while still bucketing `East.function`-wrapped code
21
+ * at the `East.function` call (it is the outermost scope, so the bucket key is
22
+ * unchanged for existing code).
23
+ */
24
+ export declare function enclosingBlockScope(node: ts.Node, ctx: RuleContext): ts.Node | undefined;
25
+ /** Is `node` inside an East block scope (`East.function` body or BlockBuilder callback)? */
26
+ export declare function insideBlockScope(node: ts.Node, ctx: RuleContext): boolean;
27
+ //# sourceMappingURL=block-scope.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"block-scope.d.ts","sourceRoot":"","sources":["../../src/block-scope.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,KAAK,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,KAAK,EAAE,WAAW,EAAY,MAAM,YAAY,CAAC;AAexD;;;;sEAIsE;AACtE,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,WAAW,GAAG,OAAO,CAM/E;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,WAAW,GAAG,EAAE,CAAC,IAAI,GAAG,SAAS,CAWxF;AAED,4FAA4F;AAC5F,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,WAAW,GAAG,OAAO,CAEzE"}
@@ -0,0 +1,53 @@
1
+ import { isBlockBuilderType } from "./east-type.js";
2
+ /** `East.function(...)` / `East.asyncFunction(...)` — the classic East block root. */
3
+ function isEastFunctionCall(node, t) {
4
+ if (!t.isCallExpression(node))
5
+ return false;
6
+ const callee = node.expression;
7
+ return (t.isPropertyAccessExpression(callee) &&
8
+ t.isIdentifier(callee.expression) &&
9
+ callee.expression.text === "East" &&
10
+ (callee.name.text === "function" || callee.name.text === "asyncFunction"));
11
+ }
12
+ /** True for an arrow/function whose first parameter is an East `BlockBuilder` —
13
+ * the shape of every East block callback: `East.function`'s `$ => …`, the JSX
14
+ * `<Reactive>{$ => …}}`, `ui(…, $ => …)`, a `.map(($, x) => …)` body. Keying on
15
+ * the parameter type (not the wrapping function's name) makes the check work for
16
+ * JSX authoring forms that have no `East.function` wrapper at all. */
17
+ export function isBlockBuilderCallback(node, ctx) {
18
+ const t = ctx.ts;
19
+ if (!t.isArrowFunction(node) && !t.isFunctionExpression(node))
20
+ return false;
21
+ const first = node.parameters[0];
22
+ if (first === undefined)
23
+ return false;
24
+ return isBlockBuilderType(ctx.checker.getTypeAtLocation(first.name));
25
+ }
26
+ /**
27
+ * The outermost enclosing East block scope of `node` — an `East.function` call
28
+ * or a `BlockBuilder`-parameter callback — or `undefined` if there is none.
29
+ *
30
+ * Block-scoped rules used to look only for an enclosing `East.function`; East UI
31
+ * authored as `<Reactive>{$ => …}}` or `ui(…)` has a `BlockBuilder` `$` with no
32
+ * such wrapper, so those rules went silent inside it. Rooting on the BlockBuilder
33
+ * callback restores coverage while still bucketing `East.function`-wrapped code
34
+ * at the `East.function` call (it is the outermost scope, so the bucket key is
35
+ * unchanged for existing code).
36
+ */
37
+ export function enclosingBlockScope(node, ctx) {
38
+ const t = ctx.ts;
39
+ let outermost;
40
+ let current = node.parent;
41
+ while (current !== undefined) {
42
+ if (isEastFunctionCall(current, t) || isBlockBuilderCallback(current, ctx)) {
43
+ outermost = current;
44
+ }
45
+ current = current.parent;
46
+ }
47
+ return outermost;
48
+ }
49
+ /** Is `node` inside an East block scope (`East.function` body or BlockBuilder callback)? */
50
+ export function insideBlockScope(node, ctx) {
51
+ return enclosingBlockScope(node, ctx) !== undefined;
52
+ }
53
+ //# sourceMappingURL=block-scope.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"block-scope.js","sourceRoot":"","sources":["../../src/block-scope.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAEpD,sFAAsF;AACtF,SAAS,kBAAkB,CAAC,IAAa,EAAE,CAAW;IACpD,IAAI,CAAC,CAAC,CAAC,gBAAgB,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IAC5C,MAAM,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC;IAC/B,OAAO,CACL,CAAC,CAAC,0BAA0B,CAAC,MAAM,CAAC;QACpC,CAAC,CAAC,YAAY,CAAC,MAAM,CAAC,UAAU,CAAC;QACjC,MAAM,CAAC,UAAU,CAAC,IAAI,KAAK,MAAM;QACjC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,KAAK,UAAU,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,KAAK,eAAe,CAAC,CAC1E,CAAC;AACJ,CAAC;AAED;;;;sEAIsE;AACtE,MAAM,UAAU,sBAAsB,CAAC,IAAa,EAAE,GAAgB;IACpE,MAAM,CAAC,GAAG,GAAG,CAAC,EAAE,CAAC;IACjB,IAAI,CAAC,CAAC,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,oBAAoB,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IAC5E,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IACjC,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IACtC,OAAO,kBAAkB,CAAC,GAAG,CAAC,OAAO,CAAC,iBAAiB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;AACvE,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAa,EAAE,GAAgB;IACjE,MAAM,CAAC,GAAG,GAAG,CAAC,EAAE,CAAC;IACjB,IAAI,SAA8B,CAAC;IACnC,IAAI,OAAO,GAAwB,IAAI,CAAC,MAAM,CAAC;IAC/C,OAAO,OAAO,KAAK,SAAS,EAAE,CAAC;QAC7B,IAAI,kBAAkB,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,sBAAsB,CAAC,OAAO,EAAE,GAAG,CAAC,EAAE,CAAC;YAC3E,SAAS,GAAG,OAAO,CAAC;QACtB,CAAC;QACD,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC;IAC3B,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,4FAA4F;AAC5F,MAAM,UAAU,gBAAgB,CAAC,IAAa,EAAE,GAAgB;IAC9D,OAAO,mBAAmB,CAAC,IAAI,EAAE,GAAG,CAAC,KAAK,SAAS,CAAC;AACtD,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Copyright (c) 2025 Elara AI Pty Ltd
3
+ * Dual-licensed under AGPL-3.0 and commercial license. See LICENSE for details.
4
+ */
5
+ import type * as ts from "typescript";
6
+ export declare function isEastExprType(type: ts.Type): boolean;
7
+ export declare function isBlockBuilderType(type: ts.Type): boolean;
8
+ //# sourceMappingURL=east-type.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"east-type.d.ts","sourceRoot":"","sources":["../../src/east-type.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,KAAK,EAAE,MAAM,YAAY,CAAC;AAMtC,wBAAgB,cAAc,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,GAAG,OAAO,CAgBrD;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,GAAG,OAAO,CAGzD"}
@@ -0,0 +1,28 @@
1
+ // East expression classes all extend `Expr` and are named `<Kind>Expr`
2
+ // (IntegerExpr, VariantExpr, NullExpr, …); `ExprType<T>` resolves to one of
3
+ // them. Walk union/intersection members and base types so subclasses and
4
+ // aliases are caught.
5
+ export function isEastExprType(type) {
6
+ const seen = new Set();
7
+ const stack = [type];
8
+ while (stack.length > 0) {
9
+ const current = stack.pop();
10
+ if (current === undefined || seen.has(current))
11
+ continue;
12
+ seen.add(current);
13
+ const name = current.aliasSymbol?.name ?? current.symbol?.name;
14
+ if (name !== undefined && (name === "Expr" || name.endsWith("Expr")))
15
+ return true;
16
+ if (current.isUnionOrIntersection())
17
+ stack.push(...current.types);
18
+ const bases = current.getBaseTypes?.();
19
+ if (bases !== undefined)
20
+ stack.push(...bases);
21
+ }
22
+ return false;
23
+ }
24
+ export function isBlockBuilderType(type) {
25
+ const name = type.aliasSymbol?.name ?? type.symbol?.name;
26
+ return name === "BlockBuilder";
27
+ }
28
+ //# sourceMappingURL=east-type.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"east-type.js","sourceRoot":"","sources":["../../src/east-type.ts"],"names":[],"mappings":"AAMA,uEAAuE;AACvE,4EAA4E;AAC5E,yEAAyE;AACzE,sBAAsB;AACtB,MAAM,UAAU,cAAc,CAAC,IAAa;IAC1C,MAAM,IAAI,GAAG,IAAI,GAAG,EAAW,CAAC;IAChC,MAAM,KAAK,GAAc,CAAC,IAAI,CAAC,CAAC;IAChC,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,MAAM,OAAO,GAAG,KAAK,CAAC,GAAG,EAAE,CAAC;QAC5B,IAAI,OAAO,KAAK,SAAS,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC;YAAE,SAAS;QACzD,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAElB,MAAM,IAAI,GAAG,OAAO,CAAC,WAAW,EAAE,IAAI,IAAI,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC;QAC/D,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,IAAI,KAAK,MAAM,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YAAE,OAAO,IAAI,CAAC;QAElF,IAAI,OAAO,CAAC,qBAAqB,EAAE;YAAE,KAAK,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;QAClE,MAAM,KAAK,GAAG,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC;QACvC,IAAI,KAAK,KAAK,SAAS;YAAE,KAAK,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,CAAC;IAChD,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,IAAa;IAC9C,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,EAAE,IAAI,IAAI,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC;IACzD,OAAO,IAAI,KAAK,cAAc,CAAC;AACjC,CAAC"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Copyright (c) 2025 Elara AI Pty Ltd
3
+ * Dual-licensed under AGPL-3.0 and commercial license. See LICENSE for details.
4
+ */
5
+ export type * from "./types.js";
6
+ export * from "./rules/index.js";
7
+ export { runEastRules } from "./run.js";
8
+ export { createDiagnosticsService } from "./service.js";
9
+ export type { DiagnosticsService, DiagnosticsServiceOptions } from "./service.js";
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,mBAAmB,YAAY,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC;AACxD,YAAY,EAAE,kBAAkB,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,4 @@
1
+ export * from "./rules/index.js";
2
+ export { runEastRules } from "./run.js";
3
+ export { createDiagnosticsService } from "./service.js";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAKA,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Copyright (c) 2025 Elara AI Pty Ltd
3
+ * Dual-licensed under AGPL-3.0 and commercial license. See LICENSE for details.
4
+ */
5
+ import type { EastRule } from "../types.js";
6
+ export { noRedundantEastCast } from "./no-redundant-east-cast.js";
7
+ export { preferExplicitEastType } from "./prefer-explicit-east-type.js";
8
+ export { preferSomeNone } from "./prefer-some-none.js";
9
+ export { noHandrolledVariant } from "./no-handrolled-variant.js";
10
+ export { noEastNamespacedType } from "./no-east-namespaced-type.js";
11
+ export { preferLetConstOverEastValue } from "./prefer-let-const-over-east-value.js";
12
+ export { noRelativeSrcImport } from "./no-relative-src-import.js";
13
+ export { noLetConstInExpression } from "./no-let-const-in-expression.js";
14
+ export { noUnexecutedEastExpression } from "./no-unexecuted-east-expression.js";
15
+ export { noReinlinedEastBinding } from "./no-reinlined-east-binding.js";
16
+ export { noEastDataBuilderHelper } from "./no-east-data-builder-helper.js";
17
+ export { preferJsxOverFactoryCall } from "./prefer-jsx-over-factory-call.js";
18
+ export declare const allRules: readonly EastRule[];
19
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/rules/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAc5C,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AACxE,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AACpE,OAAO,EAAE,2BAA2B,EAAE,MAAM,uCAAuC,CAAC;AACpF,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AACzE,OAAO,EAAE,0BAA0B,EAAE,MAAM,oCAAoC,CAAC;AAChF,OAAO,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AACxE,OAAO,EAAE,uBAAuB,EAAE,MAAM,kCAAkC,CAAC;AAC3E,OAAO,EAAE,wBAAwB,EAAE,MAAM,mCAAmC,CAAC;AAE7E,eAAO,MAAM,QAAQ,EAAE,SAAS,QAAQ,EAavC,CAAC"}
@@ -0,0 +1,39 @@
1
+ import { noRedundantEastCast } from "./no-redundant-east-cast.js";
2
+ import { preferExplicitEastType } from "./prefer-explicit-east-type.js";
3
+ import { preferSomeNone } from "./prefer-some-none.js";
4
+ import { noHandrolledVariant } from "./no-handrolled-variant.js";
5
+ import { noEastNamespacedType } from "./no-east-namespaced-type.js";
6
+ import { preferLetConstOverEastValue } from "./prefer-let-const-over-east-value.js";
7
+ import { noRelativeSrcImport } from "./no-relative-src-import.js";
8
+ import { noLetConstInExpression } from "./no-let-const-in-expression.js";
9
+ import { noUnexecutedEastExpression } from "./no-unexecuted-east-expression.js";
10
+ import { noReinlinedEastBinding } from "./no-reinlined-east-binding.js";
11
+ import { noEastDataBuilderHelper } from "./no-east-data-builder-helper.js";
12
+ import { preferJsxOverFactoryCall } from "./prefer-jsx-over-factory-call.js";
13
+ export { noRedundantEastCast } from "./no-redundant-east-cast.js";
14
+ export { preferExplicitEastType } from "./prefer-explicit-east-type.js";
15
+ export { preferSomeNone } from "./prefer-some-none.js";
16
+ export { noHandrolledVariant } from "./no-handrolled-variant.js";
17
+ export { noEastNamespacedType } from "./no-east-namespaced-type.js";
18
+ export { preferLetConstOverEastValue } from "./prefer-let-const-over-east-value.js";
19
+ export { noRelativeSrcImport } from "./no-relative-src-import.js";
20
+ export { noLetConstInExpression } from "./no-let-const-in-expression.js";
21
+ export { noUnexecutedEastExpression } from "./no-unexecuted-east-expression.js";
22
+ export { noReinlinedEastBinding } from "./no-reinlined-east-binding.js";
23
+ export { noEastDataBuilderHelper } from "./no-east-data-builder-helper.js";
24
+ export { preferJsxOverFactoryCall } from "./prefer-jsx-over-factory-call.js";
25
+ export const allRules = [
26
+ noRedundantEastCast,
27
+ preferExplicitEastType,
28
+ preferSomeNone,
29
+ noHandrolledVariant,
30
+ noEastNamespacedType,
31
+ preferLetConstOverEastValue,
32
+ noRelativeSrcImport,
33
+ noLetConstInExpression,
34
+ noUnexecutedEastExpression,
35
+ noReinlinedEastBinding,
36
+ noEastDataBuilderHelper,
37
+ preferJsxOverFactoryCall,
38
+ ];
39
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/rules/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AACxE,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AACpE,OAAO,EAAE,2BAA2B,EAAE,MAAM,uCAAuC,CAAC;AACpF,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AACzE,OAAO,EAAE,0BAA0B,EAAE,MAAM,oCAAoC,CAAC;AAChF,OAAO,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AACxE,OAAO,EAAE,uBAAuB,EAAE,MAAM,kCAAkC,CAAC;AAC3E,OAAO,EAAE,wBAAwB,EAAE,MAAM,mCAAmC,CAAC;AAE7E,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AACxE,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AACpE,OAAO,EAAE,2BAA2B,EAAE,MAAM,uCAAuC,CAAC;AACpF,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AACzE,OAAO,EAAE,0BAA0B,EAAE,MAAM,oCAAoC,CAAC;AAChF,OAAO,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AACxE,OAAO,EAAE,uBAAuB,EAAE,MAAM,kCAAkC,CAAC;AAC3E,OAAO,EAAE,wBAAwB,EAAE,MAAM,mCAAmC,CAAC;AAE7E,MAAM,CAAC,MAAM,QAAQ,GAAwB;IAC3C,mBAAmB;IACnB,sBAAsB;IACtB,cAAc;IACd,mBAAmB;IACnB,oBAAoB;IACpB,2BAA2B;IAC3B,mBAAmB;IACnB,sBAAsB;IACtB,0BAA0B;IAC1B,sBAAsB;IACtB,uBAAuB;IACvB,wBAAwB;CACzB,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { EastRule } from "../types.js";
2
+ export declare const noEastDataBuilderHelper: EastRule;
3
+ //# sourceMappingURL=no-east-data-builder-helper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-east-data-builder-helper.d.ts","sourceRoot":"","sources":["../../../src/rules/no-east-data-builder-helper.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,QAAQ,EAAyB,MAAM,aAAa,CAAC;AAiEnE,eAAO,MAAM,uBAAuB,EAAE,QAqCrC,CAAC"}
@@ -0,0 +1,90 @@
1
+ import { isBlockBuilderType } from "../east-type.js";
2
+ const NAME = "no-east-data-builder-helper";
3
+ const CODE = 990011;
4
+ const VALUE_CONSTRUCTORS = new Set(["variant", "some"]);
5
+ // Is `expr` a hand-built East value constructor — `variant(...)`, `some(...)`,
6
+ // `none`, or `East.value(...)`? These produce East data at TS evaluation time.
7
+ function isEastValueConstructor(expr, t) {
8
+ if (t.isCallExpression(expr)) {
9
+ const callee = expr.expression;
10
+ if (t.isIdentifier(callee) && VALUE_CONSTRUCTORS.has(callee.text))
11
+ return true;
12
+ return (t.isPropertyAccessExpression(callee) &&
13
+ t.isIdentifier(callee.expression) &&
14
+ callee.expression.text === "East" &&
15
+ callee.name.text === "value");
16
+ }
17
+ return t.isIdentifier(expr) && expr.text === "none";
18
+ }
19
+ // The expressions a function/arrow returns, not descending into nested functions
20
+ // (a concise arrow body counts as its single return).
21
+ function returnExpressions(fn, t) {
22
+ if (fn.body === undefined)
23
+ return [];
24
+ if (!t.isBlock(fn.body))
25
+ return [fn.body];
26
+ const out = [];
27
+ const visit = (n) => {
28
+ if (t.isFunctionDeclaration(n) || t.isFunctionExpression(n) || t.isArrowFunction(n))
29
+ return;
30
+ if (t.isReturnStatement(n) && n.expression !== undefined)
31
+ out.push(n.expression);
32
+ t.forEachChild(n, visit);
33
+ };
34
+ t.forEachChild(fn.body, visit);
35
+ return out;
36
+ }
37
+ function isBuilderFunction(fn, ctx) {
38
+ const t = ctx.ts;
39
+ // East block callbacks (`($, x) => …`) take a BlockBuilder first; they are East
40
+ // callbacks, not TS-level data helpers — exclude them.
41
+ const first = fn.parameters[0];
42
+ if (first !== undefined && isBlockBuilderType(ctx.checker.getTypeAtLocation(first.name))) {
43
+ return false;
44
+ }
45
+ const returns = returnExpressions(fn, t);
46
+ return returns.length > 0 && returns.every((r) => isEastValueConstructor(r, t));
47
+ }
48
+ // A TS function/arrow whose whole job is to return a hand-built East value
49
+ // (`variant`/`some`/`none`/`East.value`) is an authoring-time macro: it expands
50
+ // inline at each call, it is not a real `East.function` (it cannot be serialized,
51
+ // recursed, or sent to the engine), and it hides the East value's shape behind a
52
+ // JS call. Inline the constructor at each call site (repetition is welcome) or,
53
+ // when reuse is genuine, make it a real `East.function`. UI-composition helpers
54
+ // returning JSX (`(l) => <Badge>{l}</Badge>`) and `East.function(…)` bindings are
55
+ // structurally distinct (their bodies are not value constructors) and never match.
56
+ export const noEastDataBuilderHelper = {
57
+ name: NAME,
58
+ code: CODE,
59
+ description: "Flag a TS helper whose only job is to return a hand-built East value (variant/some/none/East.value) — inline it or make it a real East.function.",
60
+ check(node, ctx) {
61
+ const t = ctx.ts;
62
+ let fn;
63
+ let reportNode;
64
+ if (t.isFunctionDeclaration(node) && node.body !== undefined) {
65
+ fn = node;
66
+ reportNode = node.name ?? node;
67
+ }
68
+ else if (t.isVariableDeclaration(node) &&
69
+ node.initializer !== undefined &&
70
+ (t.isArrowFunction(node.initializer) || t.isFunctionExpression(node.initializer))) {
71
+ fn = node.initializer;
72
+ reportNode = node.name;
73
+ }
74
+ if (fn === undefined || reportNode === undefined)
75
+ return;
76
+ if (!isBuilderFunction(fn, ctx))
77
+ return;
78
+ const sf = ctx.sourceFile;
79
+ const start = reportNode.getStart(sf);
80
+ ctx.report({
81
+ ruleName: NAME,
82
+ code: CODE,
83
+ start,
84
+ length: reportNode.getEnd() - start,
85
+ messageText: "This helper just returns a hand-built East value (`variant`/`some`/`none`/`East.value`), so it is an authoring-time macro, not a real East function. Inline the constructor at each call site (repetition is welcome), or make it a real `East.function` if you need a reusable East computation.",
86
+ category: "warning",
87
+ });
88
+ },
89
+ };
90
+ //# sourceMappingURL=no-east-data-builder-helper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-east-data-builder-helper.js","sourceRoot":"","sources":["../../../src/rules/no-east-data-builder-helper.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Copyright (c) 2025 Elara AI Pty Ltd
3
+ * Dual-licensed under AGPL-3.0 and commercial license. See LICENSE for details.
4
+ */
5
+ import type { EastRule } from "../types.js";
6
+ export declare const noEastNamespacedType: EastRule;
7
+ //# sourceMappingURL=no-east-namespaced-type.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-east-namespaced-type.d.ts","sourceRoot":"","sources":["../../../src/rules/no-east-namespaced-type.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAS5C,eAAO,MAAM,oBAAoB,EAAE,QAsBlC,CAAC"}
@@ -0,0 +1,32 @@
1
+ const NAME = "no-east-namespaced-type";
2
+ const CODE = 990005;
3
+ // East type constructors (IntegerType, ArrayType, …) are top-level exports, not
4
+ // members of the `East` namespace object. `East.IntegerType` is already a type
5
+ // error, but the native message dumps the entire `East` type; this gives a
6
+ // clear, actionable one.
7
+ export const noEastNamespacedType = {
8
+ name: NAME,
9
+ code: CODE,
10
+ description: "Disallow East.<X>Type member access; import the type directly from @elaraai/east.",
11
+ check(node, ctx) {
12
+ const t = ctx.ts;
13
+ if (!t.isPropertyAccessExpression(node))
14
+ return;
15
+ if (!t.isIdentifier(node.expression) || node.expression.text !== "East")
16
+ return;
17
+ const name = node.name.text;
18
+ if (!name.endsWith("Type"))
19
+ return;
20
+ const sf = ctx.sourceFile;
21
+ const start = node.getStart(sf);
22
+ ctx.report({
23
+ ruleName: NAME,
24
+ code: CODE,
25
+ start,
26
+ length: node.getEnd() - start,
27
+ messageText: `\`East.${name}\` is not a member of the East namespace — import \`${name}\` directly from @elaraai/east.`,
28
+ category: "warning",
29
+ });
30
+ },
31
+ };
32
+ //# sourceMappingURL=no-east-namespaced-type.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-east-namespaced-type.js","sourceRoot":"","sources":["../../../src/rules/no-east-namespaced-type.ts"],"names":[],"mappings":"AAMA,MAAM,IAAI,GAAG,yBAAyB,CAAC;AACvC,MAAM,IAAI,GAAG,MAAM,CAAC;AAEpB,gFAAgF;AAChF,+EAA+E;AAC/E,2EAA2E;AAC3E,yBAAyB;AACzB,MAAM,CAAC,MAAM,oBAAoB,GAAa;IAC5C,IAAI,EAAE,IAAI;IACV,IAAI,EAAE,IAAI;IACV,WAAW,EAAE,mFAAmF;IAChG,KAAK,CAAC,IAAI,EAAE,GAAG;QACb,MAAM,CAAC,GAAG,GAAG,CAAC,EAAE,CAAC;QACjB,IAAI,CAAC,CAAC,CAAC,0BAA0B,CAAC,IAAI,CAAC;YAAE,OAAO;QAChD,IAAI,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,KAAK,MAAM;YAAE,OAAO;QAChF,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;QAC5B,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;YAAE,OAAO;QAEnC,MAAM,EAAE,GAAG,GAAG,CAAC,UAAU,CAAC;QAC1B,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QAChC,GAAG,CAAC,MAAM,CAAC;YACT,QAAQ,EAAE,IAAI;YACd,IAAI,EAAE,IAAI;YACV,KAAK;YACL,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,GAAG,KAAK;YAC7B,WAAW,EAAE,UAAU,IAAI,uDAAuD,IAAI,iCAAiC;YACvH,QAAQ,EAAE,SAAS;SACpB,CAAC,CAAC;IACL,CAAC;CACF,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { EastRule } from "../types.js";
2
+ export declare const noHandrolledVariant: EastRule;
3
+ //# sourceMappingURL=no-handrolled-variant.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-handrolled-variant.d.ts","sourceRoot":"","sources":["../../../src/rules/no-handrolled-variant.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,QAAQ,EAAe,MAAM,aAAa,CAAC;AAuBzD,eAAO,MAAM,mBAAmB,EAAE,QAwBjC,CAAC"}
@@ -0,0 +1,45 @@
1
+ const NAME = "no-handrolled-variant";
2
+ const CODE = 990004;
3
+ const VARIANT_TYPE_NAMES = new Set(["variant", "some", "none", "option", "VariantExpr"]);
4
+ // Does `type` (or, for unions/intersections, any constituent) name an East
5
+ // variant / option type? A hand-rolled `{ type, value }` lacks the brand symbol
6
+ // East variants carry, so it drifts silently — flag it wherever a variant is
7
+ // contextually expected.
8
+ function expectsVariant(type) {
9
+ const stack = [type];
10
+ while (stack.length > 0) {
11
+ const current = stack.pop();
12
+ if (current === undefined)
13
+ continue;
14
+ const name = current.aliasSymbol?.name ?? current.symbol?.name;
15
+ if (name !== undefined && VARIANT_TYPE_NAMES.has(name))
16
+ return true;
17
+ if (current.isUnionOrIntersection())
18
+ stack.push(...current.types);
19
+ }
20
+ return false;
21
+ }
22
+ export const noHandrolledVariant = {
23
+ name: NAME,
24
+ code: CODE,
25
+ description: "Disallow plain object literals where an East variant/option is expected; use variant()/some()/none.",
26
+ check(node, ctx) {
27
+ const t = ctx.ts;
28
+ if (!t.isObjectLiteralExpression(node))
29
+ return;
30
+ const contextualType = ctx.checker.getContextualType(node);
31
+ if (contextualType === undefined || !expectsVariant(contextualType))
32
+ return;
33
+ const sf = ctx.sourceFile;
34
+ const start = node.getStart(sf);
35
+ ctx.report({
36
+ ruleName: NAME,
37
+ code: CODE,
38
+ start,
39
+ length: node.getEnd() - start,
40
+ messageText: 'Hand-rolled variant: build with `variant("Tag", value)`, `some(value)`, or `none` from @elaraai/east — never a plain `{ type, value }` object literal.',
41
+ category: "warning",
42
+ });
43
+ },
44
+ };
45
+ //# sourceMappingURL=no-handrolled-variant.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-handrolled-variant.js","sourceRoot":"","sources":["../../../src/rules/no-handrolled-variant.ts"],"names":[],"mappings":"AAOA,MAAM,IAAI,GAAG,uBAAuB,CAAC;AACrC,MAAM,IAAI,GAAG,MAAM,CAAC;AAEpB,MAAM,kBAAkB,GAAG,IAAI,GAAG,CAAC,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC;AAEzF,2EAA2E;AAC3E,gFAAgF;AAChF,6EAA6E;AAC7E,yBAAyB;AACzB,SAAS,cAAc,CAAC,IAAa;IACnC,MAAM,KAAK,GAAc,CAAC,IAAI,CAAC,CAAC;IAChC,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,MAAM,OAAO,GAAG,KAAK,CAAC,GAAG,EAAE,CAAC;QAC5B,IAAI,OAAO,KAAK,SAAS;YAAE,SAAS;QACpC,MAAM,IAAI,GAAG,OAAO,CAAC,WAAW,EAAE,IAAI,IAAI,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC;QAC/D,IAAI,IAAI,KAAK,SAAS,IAAI,kBAAkB,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,CAAC;QACpE,IAAI,OAAO,CAAC,qBAAqB,EAAE;YAAE,KAAK,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;IACpE,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,CAAC,MAAM,mBAAmB,GAAa;IAC3C,IAAI,EAAE,IAAI;IACV,IAAI,EAAE,IAAI;IACV,WAAW,EACT,qGAAqG;IACvG,KAAK,CAAC,IAAa,EAAE,GAAgB;QACnC,MAAM,CAAC,GAAG,GAAG,CAAC,EAAE,CAAC;QACjB,IAAI,CAAC,CAAC,CAAC,yBAAyB,CAAC,IAAI,CAAC;YAAE,OAAO;QAE/C,MAAM,cAAc,GAAG,GAAG,CAAC,OAAO,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;QAC3D,IAAI,cAAc,KAAK,SAAS,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC;YAAE,OAAO;QAE5E,MAAM,EAAE,GAAG,GAAG,CAAC,UAAU,CAAC;QAC1B,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QAChC,GAAG,CAAC,MAAM,CAAC;YACT,QAAQ,EAAE,IAAI;YACd,IAAI,EAAE,IAAI;YACV,KAAK;YACL,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,GAAG,KAAK;YAC7B,WAAW,EACT,wJAAwJ;YAC1J,QAAQ,EAAE,SAAS;SACpB,CAAC,CAAC;IACL,CAAC;CACF,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { EastRule } from "../types.js";
2
+ export declare const noLetConstInExpression: EastRule;
3
+ //# sourceMappingURL=no-let-const-in-expression.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-let-const-in-expression.d.ts","sourceRoot":"","sources":["../../../src/rules/no-let-const-in-expression.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAW5C,eAAO,MAAM,sBAAsB,EAAE,QA4CpC,CAAC"}