@elaraai/tsserver-plugin-east 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.
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,69 @@
1
+ # tsserver-plugin-east
2
+
3
+ > East diagnostics as a TypeScript language service plugin
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
+ **tsserver-plugin-east** rides inside the editor's existing TypeScript language
9
+ service and surfaces [`@elaraai/east-diagnostics`](../east-diagnostics) as
10
+ native squiggles — no second type-checker, no ESLint setup required.
11
+
12
+ ## Features
13
+
14
+ - **East idiom diagnostics** - the full east-diagnostics rule set (prefer `some()`/`none`, no hand-rolled variants, redundant casts, unexecuted East expressions, prefer JSX tags, …) reported alongside TypeScript's own diagnostics.
15
+ - **Readable East type errors** - native assignability errors on East types are rewritten via east's structural type diff, so a mismatch deep inside a recursive type reads as one localized line instead of pages of restated generics.
16
+ - **In-process** - decorates the language service the editor already runs; no extra server or program build.
17
+ - **Self-contained** - a single CommonJS bundle; only `typescript` is expected from the host.
18
+
19
+ ## Usage
20
+
21
+ Add the plugin to `compilerOptions` in `tsconfig.json` and install it as a dev
22
+ dependency:
23
+
24
+ ```jsonc
25
+ {
26
+ "compilerOptions": {
27
+ "plugins": [{ "name": "@elaraai/tsserver-plugin-east" }]
28
+ }
29
+ }
30
+ ```
31
+
32
+ ```bash
33
+ npm install --save-dev @elaraai/tsserver-plugin-east
34
+ ```
35
+
36
+ In VS Code, tsconfig-configured plugins load when the **workspace TypeScript
37
+ version** is selected ("TypeScript: Select TypeScript Version…"). The
38
+ [East UI Preview extension](../east-ui/packages/east-ui-extension) contributes
39
+ the same plugin globally, which also covers VS Code's built-in TypeScript.
40
+
41
+ Projects scaffolded with `npm create @elaraai/e3` / `npm create @elaraai/east`
42
+ ship with the plugin preconfigured, alongside
43
+ [`@elaraai/eslint-plugin-east`](../eslint-plugin-east) for the same rules in CI.
44
+
45
+ ## Claude Code plugin
46
+
47
+ 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:
48
+
49
+ ```text
50
+ # Inside Claude Code
51
+ /plugin marketplace add elaraai/east-workspace
52
+ /plugin install east@elaraai
53
+ ```
54
+
55
+ ```bash
56
+ # From a terminal
57
+ claude plugin marketplace add elaraai/east-workspace
58
+ claude plugin install east@elaraai
59
+ ```
60
+
61
+ ## License
62
+
63
+ Dual-licensed:
64
+ - **Open Source**: [AGPL-3.0](LICENSE.md) - Free for open source use
65
+ - **Commercial**: Available for proprietary use - contact support@elara.ai
66
+
67
+ ---
68
+
69
+ *Developed by [Elara AI Pty Ltd](https://elaraai.com/)*
package/dist/index.cjs ADDED
@@ -0,0 +1,1192 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // ../east-diagnostics/dist/src/tsserver-plugin.js
21
+ var tsserver_plugin_exports = {};
22
+ __export(tsserver_plugin_exports, {
23
+ default: () => tsserver_plugin_default,
24
+ init: () => init
25
+ });
26
+ module.exports = __toCommonJS(tsserver_plugin_exports);
27
+ var import_node_fs = require("node:fs");
28
+ var import_node_path2 = require("node:path");
29
+
30
+ // ../east-diagnostics/dist/src/east-type.js
31
+ function isEastExprType(type) {
32
+ const seen = /* @__PURE__ */ new Set();
33
+ const stack = [type];
34
+ while (stack.length > 0) {
35
+ const current = stack.pop();
36
+ if (current === void 0 || seen.has(current))
37
+ continue;
38
+ seen.add(current);
39
+ const name = current.aliasSymbol?.name ?? current.symbol?.name;
40
+ if (name !== void 0 && (name === "Expr" || name.endsWith("Expr")))
41
+ return true;
42
+ if (current.isUnionOrIntersection())
43
+ stack.push(...current.types);
44
+ const bases = current.getBaseTypes?.();
45
+ if (bases !== void 0)
46
+ stack.push(...bases);
47
+ }
48
+ return false;
49
+ }
50
+ function isBlockBuilderType(type) {
51
+ const name = type.aliasSymbol?.name ?? type.symbol?.name;
52
+ return name === "BlockBuilder";
53
+ }
54
+
55
+ // ../east-diagnostics/dist/src/block-builder.js
56
+ function matchBlockBuilderCall(node, ctx) {
57
+ const t = ctx.ts;
58
+ if (!t.isCallExpression(node))
59
+ return void 0;
60
+ const callee = node.expression;
61
+ if (!t.isPropertyAccessExpression(callee))
62
+ return void 0;
63
+ const method = callee.name.text;
64
+ if (method !== "let" && method !== "const")
65
+ return void 0;
66
+ if (!isBlockBuilderType(ctx.checker.getTypeAtLocation(callee.expression)))
67
+ return void 0;
68
+ return { call: node, method, args: node.arguments };
69
+ }
70
+
71
+ // ../east-diagnostics/dist/src/rules/no-redundant-east-cast.js
72
+ var NAME = "no-redundant-east-cast";
73
+ var CODE = 990001;
74
+ var noRedundantEastCast = {
75
+ name: NAME,
76
+ code: CODE,
77
+ description: "Disallow a TypeScript cast on the value argument of $.let/$.const when the East type argument is present (the type argument already drives inference).",
78
+ check(node, ctx) {
79
+ const match = matchBlockBuilderCall(node, ctx);
80
+ if (match === void 0 || match.args.length < 2)
81
+ return;
82
+ const t = ctx.ts;
83
+ const value = match.args[0];
84
+ if (value === void 0)
85
+ return;
86
+ let inner;
87
+ if (t.isAsExpression(value))
88
+ inner = value.expression;
89
+ else if (t.isTypeAssertionExpression(value))
90
+ inner = value.expression;
91
+ if (inner === void 0)
92
+ return;
93
+ const sf = ctx.sourceFile;
94
+ const start = value.getStart(sf);
95
+ const length = value.getEnd() - start;
96
+ ctx.report({
97
+ ruleName: NAME,
98
+ code: CODE,
99
+ start,
100
+ length,
101
+ messageText: `Redundant cast: \`$.${match.method}\` infers the value type from the East type argument; drop the \`as \u2026\` on the value.`,
102
+ category: "warning",
103
+ fix: {
104
+ description: "Remove redundant cast",
105
+ changes: [{ start, length, newText: inner.getText(sf) }]
106
+ }
107
+ });
108
+ }
109
+ };
110
+
111
+ // ../east-diagnostics/dist/src/rules/prefer-explicit-east-type.js
112
+ var NAME2 = "prefer-explicit-east-type";
113
+ var CODE2 = 990002;
114
+ function isEmptyNewMapOrSet(node, t) {
115
+ if (!t.isNewExpression(node) || !t.isIdentifier(node.expression))
116
+ return false;
117
+ const ctor = node.expression.text;
118
+ if (ctor !== "Map" && ctor !== "Set")
119
+ return false;
120
+ return node.arguments === void 0 || node.arguments.length === 0;
121
+ }
122
+ function isRawValueLiteral(node, t) {
123
+ return t.isNumericLiteral(node) || t.isBigIntLiteral(node) || t.isStringLiteralLike(node) || node.kind === t.SyntaxKind.TrueKeyword || node.kind === t.SyntaxKind.FalseKeyword || node.kind === t.SyntaxKind.NullKeyword || t.isArrayLiteralExpression(node) || t.isObjectLiteralExpression(node) || t.isNewExpression(node);
124
+ }
125
+ var preferExplicitEastType = {
126
+ name: NAME2,
127
+ code: CODE2,
128
+ description: "Encourage passing the East type as the second argument to $.let/$.const for raw JS values whose East type is under-determined.",
129
+ check(node, ctx) {
130
+ const match = matchBlockBuilderCall(node, ctx);
131
+ if (match === void 0 || match.args.length !== 1)
132
+ return;
133
+ const t = ctx.ts;
134
+ const value = match.args[0];
135
+ if (value === void 0)
136
+ return;
137
+ const underDetermined = t.isArrayLiteralExpression(value) && value.elements.length === 0 || t.isObjectLiteralExpression(value) && value.properties.length === 0 || isEmptyNewMapOrSet(value, t);
138
+ const mode = ctx.options.preferExplicitEastType?.mode ?? "under-determined";
139
+ const flag = underDetermined || mode === "all-raw-values" && isRawValueLiteral(value, t);
140
+ if (!flag)
141
+ return;
142
+ const sf = ctx.sourceFile;
143
+ const start = value.getStart(sf);
144
+ ctx.report({
145
+ ruleName: NAME2,
146
+ code: CODE2,
147
+ start,
148
+ length: value.getEnd() - start,
149
+ messageText: `Provide the East type as the second argument to \`$.${match.method}\` \u2014 e.g. \`$.${match.method}([], ArrayType(FloatType))\` \u2014 to pin the value type; it is under-determined from the value alone.`,
150
+ category: "suggestion"
151
+ });
152
+ }
153
+ };
154
+
155
+ // ../east-diagnostics/dist/src/rules/prefer-some-none.js
156
+ var NAME3 = "prefer-some-none";
157
+ var CODE3 = 990003;
158
+ var preferSomeNone = {
159
+ name: NAME3,
160
+ code: CODE3,
161
+ description: 'Prefer some()/none over variant("some", \u2026)/variant("none", null).',
162
+ check(node, ctx) {
163
+ const t = ctx.ts;
164
+ if (!t.isCallExpression(node))
165
+ return;
166
+ const callee = node.expression;
167
+ if (!t.isIdentifier(callee) || callee.text !== "variant")
168
+ return;
169
+ const first = node.arguments[0];
170
+ if (first === void 0 || !t.isStringLiteralLike(first))
171
+ return;
172
+ const tag = first.text;
173
+ if (tag !== "some" && tag !== "none")
174
+ return;
175
+ const sf = ctx.sourceFile;
176
+ const start = node.getStart(sf);
177
+ ctx.report({
178
+ ruleName: NAME3,
179
+ code: CODE3,
180
+ start,
181
+ length: node.getEnd() - start,
182
+ messageText: tag === "some" ? 'Use `some(value)` instead of `variant("some", value)`.' : 'Use `none` instead of `variant("none", null)`.',
183
+ category: "warning"
184
+ });
185
+ }
186
+ };
187
+
188
+ // ../east-diagnostics/dist/src/rules/no-handrolled-variant.js
189
+ var NAME4 = "no-handrolled-variant";
190
+ var CODE4 = 990004;
191
+ var VARIANT_TYPE_NAMES = /* @__PURE__ */ new Set(["variant", "some", "none", "option", "VariantExpr"]);
192
+ function expectsVariant(type) {
193
+ const stack = [type];
194
+ while (stack.length > 0) {
195
+ const current = stack.pop();
196
+ if (current === void 0)
197
+ continue;
198
+ const name = current.aliasSymbol?.name ?? current.symbol?.name;
199
+ if (name !== void 0 && VARIANT_TYPE_NAMES.has(name))
200
+ return true;
201
+ if (current.isUnionOrIntersection())
202
+ stack.push(...current.types);
203
+ }
204
+ return false;
205
+ }
206
+ var noHandrolledVariant = {
207
+ name: NAME4,
208
+ code: CODE4,
209
+ description: "Disallow plain object literals where an East variant/option is expected; use variant()/some()/none.",
210
+ check(node, ctx) {
211
+ const t = ctx.ts;
212
+ if (!t.isObjectLiteralExpression(node))
213
+ return;
214
+ const contextualType = ctx.checker.getContextualType(node);
215
+ if (contextualType === void 0 || !expectsVariant(contextualType))
216
+ return;
217
+ const sf = ctx.sourceFile;
218
+ const start = node.getStart(sf);
219
+ ctx.report({
220
+ ruleName: NAME4,
221
+ code: CODE4,
222
+ start,
223
+ length: node.getEnd() - start,
224
+ messageText: 'Hand-rolled variant: build with `variant("Tag", value)`, `some(value)`, or `none` from @elaraai/east \u2014 never a plain `{ type, value }` object literal.',
225
+ category: "warning"
226
+ });
227
+ }
228
+ };
229
+
230
+ // ../east-diagnostics/dist/src/rules/no-east-namespaced-type.js
231
+ var NAME5 = "no-east-namespaced-type";
232
+ var CODE5 = 990005;
233
+ var noEastNamespacedType = {
234
+ name: NAME5,
235
+ code: CODE5,
236
+ description: "Disallow East.<X>Type member access; import the type directly from @elaraai/east.",
237
+ check(node, ctx) {
238
+ const t = ctx.ts;
239
+ if (!t.isPropertyAccessExpression(node))
240
+ return;
241
+ if (!t.isIdentifier(node.expression) || node.expression.text !== "East")
242
+ return;
243
+ const name = node.name.text;
244
+ if (!name.endsWith("Type"))
245
+ return;
246
+ const sf = ctx.sourceFile;
247
+ const start = node.getStart(sf);
248
+ ctx.report({
249
+ ruleName: NAME5,
250
+ code: CODE5,
251
+ start,
252
+ length: node.getEnd() - start,
253
+ messageText: `\`East.${name}\` is not a member of the East namespace \u2014 import \`${name}\` directly from @elaraai/east.`,
254
+ category: "warning"
255
+ });
256
+ }
257
+ };
258
+
259
+ // ../east-diagnostics/dist/src/block-scope.js
260
+ function isEastFunctionCall(node, t) {
261
+ if (!t.isCallExpression(node))
262
+ return false;
263
+ const callee = node.expression;
264
+ return t.isPropertyAccessExpression(callee) && t.isIdentifier(callee.expression) && callee.expression.text === "East" && (callee.name.text === "function" || callee.name.text === "asyncFunction");
265
+ }
266
+ function isBlockBuilderCallback(node, ctx) {
267
+ const t = ctx.ts;
268
+ if (!t.isArrowFunction(node) && !t.isFunctionExpression(node))
269
+ return false;
270
+ const first = node.parameters[0];
271
+ if (first === void 0)
272
+ return false;
273
+ return isBlockBuilderType(ctx.checker.getTypeAtLocation(first.name));
274
+ }
275
+ function enclosingBlockScope(node, ctx) {
276
+ const t = ctx.ts;
277
+ let outermost;
278
+ let current = node.parent;
279
+ while (current !== void 0) {
280
+ if (isEastFunctionCall(current, t) || isBlockBuilderCallback(current, ctx)) {
281
+ outermost = current;
282
+ }
283
+ current = current.parent;
284
+ }
285
+ return outermost;
286
+ }
287
+ function insideBlockScope(node, ctx) {
288
+ return enclosingBlockScope(node, ctx) !== void 0;
289
+ }
290
+
291
+ // ../east-diagnostics/dist/src/rules/prefer-let-const-over-east-value.js
292
+ var NAME6 = "prefer-let-const-over-east-value";
293
+ var CODE6 = 990006;
294
+ var preferLetConstOverEastValue = {
295
+ name: NAME6,
296
+ code: CODE6,
297
+ description: "Inside East.function blocks, bind with $.let/$.const (and return that) rather than East.value().",
298
+ check(node, ctx) {
299
+ const t = ctx.ts;
300
+ if (!t.isCallExpression(node))
301
+ return;
302
+ const callee = node.expression;
303
+ if (!t.isPropertyAccessExpression(callee))
304
+ return;
305
+ if (!t.isIdentifier(callee.expression) || callee.expression.text !== "East")
306
+ return;
307
+ if (callee.name.text !== "value")
308
+ return;
309
+ const parent = node.parent;
310
+ const asDeclaration = parent !== void 0 && t.isVariableDeclaration(parent) && parent.initializer === node;
311
+ const asReturn = parent !== void 0 && t.isReturnStatement(parent) && parent.expression === node;
312
+ const asCallbackBody = parent !== void 0 && t.isArrowFunction(parent) && parent.body === node && parent.parent !== void 0 && t.isCallExpression(parent.parent) && parent.parent.arguments.some((arg) => arg === parent);
313
+ if (!asDeclaration && !asReturn && !asCallbackBody)
314
+ return;
315
+ if (!insideBlockScope(node, ctx))
316
+ return;
317
+ const sf = ctx.sourceFile;
318
+ const start = node.getStart(sf);
319
+ const inner = node.arguments[0];
320
+ ctx.report({
321
+ ruleName: NAME6,
322
+ code: CODE6,
323
+ start,
324
+ length: node.getEnd() - start,
325
+ messageText: asCallbackBody ? "Don't wrap a callback's return in `East.value(...)` \u2014 the callback's expected element type already supplies the East type. Return the plain value." : asReturn ? "Don't `return East.value(...)` \u2014 it erases the East type. Bind the value with `$.let`/`$.const` (passing the East type) and return that variable." : "Inside an East block, declare with `$.const(value, Type)` / `$.let(value, Type)` instead of `East.value(...)`, which erases the East type at the call site.",
326
+ category: "suggestion",
327
+ ...asCallbackBody && inner !== void 0 ? {
328
+ fix: {
329
+ description: "Return the plain value (drop the redundant East.value wrapper)",
330
+ changes: [
331
+ { start, length: node.getEnd() - start, newText: inner.getText(sf) }
332
+ ]
333
+ }
334
+ } : {}
335
+ });
336
+ }
337
+ };
338
+
339
+ // ../east-diagnostics/dist/src/rules/no-relative-src-import.js
340
+ var NAME7 = "no-relative-src-import";
341
+ var CODE7 = 990007;
342
+ var DEEP_PACKAGE_SRC = /^@elaraai\/[^/]+\/src(\/|$)/;
343
+ var RELATIVE_SRC = /\/src(\/|$)/;
344
+ var noRelativeSrcImport = {
345
+ name: NAME7,
346
+ code: CODE7,
347
+ description: "Import East packages by published name, not via ../src or a deep /src path.",
348
+ check(node, ctx) {
349
+ const t = ctx.ts;
350
+ let specifier;
351
+ if (t.isImportDeclaration(node))
352
+ specifier = node.moduleSpecifier;
353
+ else if (t.isExportDeclaration(node))
354
+ specifier = node.moduleSpecifier;
355
+ else
356
+ return;
357
+ if (specifier === void 0 || !t.isStringLiteral(specifier))
358
+ return;
359
+ const text = specifier.text;
360
+ const relativeIntoSrc = text.startsWith(".") && RELATIVE_SRC.test(text);
361
+ const deepPackageSrc = DEEP_PACKAGE_SRC.test(text);
362
+ if (!relativeIntoSrc && !deepPackageSrc)
363
+ return;
364
+ const sf = ctx.sourceFile;
365
+ const start = specifier.getStart(sf);
366
+ ctx.report({
367
+ ruleName: NAME7,
368
+ code: CODE7,
369
+ start,
370
+ length: specifier.getEnd() - start,
371
+ messageText: "Import East packages by their published name (e.g. `@elaraai/east`), not a relative `../src/...` path or a deep `/src` import.",
372
+ category: "warning"
373
+ });
374
+ }
375
+ };
376
+
377
+ // ../east-diagnostics/dist/src/rules/no-let-const-in-expression.js
378
+ var NAME8 = "no-let-const-in-expression";
379
+ var CODE8 = 990008;
380
+ var noLetConstInExpression = {
381
+ name: NAME8,
382
+ code: CODE8,
383
+ description: "Require $.let/$.const to be bound to a const; disallow using the result inline in an expression.",
384
+ check(node, ctx) {
385
+ const match = matchBlockBuilderCall(node, ctx);
386
+ if (match === void 0)
387
+ return;
388
+ const t = ctx.ts;
389
+ const call = match.call;
390
+ let current = call;
391
+ let parent = current.parent;
392
+ while (parent !== void 0 && t.isParenthesizedExpression(parent)) {
393
+ current = parent;
394
+ parent = parent.parent;
395
+ }
396
+ if (parent === void 0)
397
+ return;
398
+ const usedInExpression = t.isPropertyAccessExpression(parent) && parent.expression === current || t.isElementAccessExpression(parent) && parent.expression === current || t.isBinaryExpression(parent) && (parent.left === current || parent.right === current) || t.isCallExpression(parent) && parent.arguments.some((arg) => arg === current);
399
+ if (!usedInExpression)
400
+ return;
401
+ const sf = ctx.sourceFile;
402
+ const start = call.getStart(sf);
403
+ ctx.report({
404
+ ruleName: NAME8,
405
+ code: CODE8,
406
+ start,
407
+ length: call.getEnd() - start,
408
+ messageText: `\`$.${match.method}\` declares a variable \u2014 bind it to a \`const\` first (\`const x = $.${match.method}(value, Type)\`), don't use the result inline (e.g. \`$.if($.let(...))\` or \`$.let(...).add(...)\`).`,
409
+ category: "warning"
410
+ });
411
+ }
412
+ };
413
+
414
+ // ../east-diagnostics/dist/src/rules/no-unexecuted-east-expression.js
415
+ var NAME9 = "no-unexecuted-east-expression";
416
+ var CODE9 = 990009;
417
+ var noUnexecutedEastExpression = {
418
+ name: NAME9,
419
+ code: CODE9,
420
+ description: "Flag a bare East expression statement that is never executed with $() or bound \u2014 it has no effect.",
421
+ check(node, ctx) {
422
+ const t = ctx.ts;
423
+ if (!t.isExpressionStatement(node))
424
+ return;
425
+ const expr = node.expression;
426
+ let root = expr;
427
+ for (; ; ) {
428
+ if (t.isCallExpression(root)) {
429
+ root = root.expression;
430
+ } else if (t.isPropertyAccessExpression(root) || t.isElementAccessExpression(root)) {
431
+ root = root.expression;
432
+ } else {
433
+ break;
434
+ }
435
+ }
436
+ if (isBlockBuilderType(ctx.checker.getTypeAtLocation(root)))
437
+ return;
438
+ if (!isEastExprType(ctx.checker.getTypeAtLocation(expr)))
439
+ return;
440
+ const sf = ctx.sourceFile;
441
+ const start = expr.getStart(sf);
442
+ ctx.report({
443
+ ruleName: NAME9,
444
+ code: CODE9,
445
+ start,
446
+ length: expr.getEnd() - start,
447
+ messageText: "This East expression is never executed or bound, so it has no effect. Wrap it in `$( \u2026 )` to run it for its effect, or bind it with `$.let` / `$.const`.",
448
+ category: "warning"
449
+ });
450
+ }
451
+ };
452
+
453
+ // ../east-diagnostics/dist/src/rules/no-reinlined-east-binding.js
454
+ var NAME10 = "no-reinlined-east-binding";
455
+ var CODE10 = 990010;
456
+ var noReinlinedEastBinding = {
457
+ name: NAME10,
458
+ code: CODE10,
459
+ 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.",
460
+ check(node, ctx) {
461
+ const t = ctx.ts;
462
+ if (!t.isVariableDeclaration(node))
463
+ return;
464
+ if (!t.isIdentifier(node.name))
465
+ return;
466
+ if (node.initializer === void 0)
467
+ return;
468
+ let init2 = node.initializer;
469
+ while (t.isParenthesizedExpression(init2))
470
+ init2 = init2.expression;
471
+ if (matchBlockBuilderCall(init2, ctx) !== void 0)
472
+ return;
473
+ if (t.isIdentifier(init2))
474
+ return;
475
+ if (!isEastExprType(ctx.checker.getTypeAtLocation(init2)))
476
+ return;
477
+ const declSymbol = ctx.checker.getSymbolAtLocation(node.name);
478
+ if (declSymbol === void 0)
479
+ return;
480
+ const name = node.name.text;
481
+ const perBody = /* @__PURE__ */ new Map();
482
+ const visit = (n) => {
483
+ if (t.isIdentifier(n) && n !== node.name && n.text === name) {
484
+ if (ctx.checker.getSymbolAtLocation(n) === declSymbol) {
485
+ const body = enclosingBlockScope(n, ctx);
486
+ if (body !== void 0)
487
+ perBody.set(body, (perBody.get(body) ?? 0) + 1);
488
+ }
489
+ }
490
+ t.forEachChild(n, visit);
491
+ };
492
+ visit(ctx.sourceFile);
493
+ let maxInBody = 0;
494
+ for (const count of perBody.values())
495
+ if (count > maxInBody)
496
+ maxInBody = count;
497
+ if (maxInBody < 2)
498
+ return;
499
+ const sf = ctx.sourceFile;
500
+ const start = node.getStart(sf);
501
+ ctx.report({
502
+ ruleName: NAME10,
503
+ code: CODE10,
504
+ start,
505
+ length: node.getEnd() - start,
506
+ 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`.",
507
+ category: "error"
508
+ });
509
+ }
510
+ };
511
+
512
+ // ../east-diagnostics/dist/src/rules/no-east-data-builder-helper.js
513
+ var NAME11 = "no-east-data-builder-helper";
514
+ var CODE11 = 990011;
515
+ var VALUE_CONSTRUCTORS = /* @__PURE__ */ new Set(["variant", "some"]);
516
+ function isEastValueConstructor(expr, t) {
517
+ if (t.isCallExpression(expr)) {
518
+ const callee = expr.expression;
519
+ if (t.isIdentifier(callee) && VALUE_CONSTRUCTORS.has(callee.text))
520
+ return true;
521
+ return t.isPropertyAccessExpression(callee) && t.isIdentifier(callee.expression) && callee.expression.text === "East" && callee.name.text === "value";
522
+ }
523
+ return t.isIdentifier(expr) && expr.text === "none";
524
+ }
525
+ function returnExpressions(fn, t) {
526
+ if (fn.body === void 0)
527
+ return [];
528
+ if (!t.isBlock(fn.body))
529
+ return [fn.body];
530
+ const out = [];
531
+ const visit = (n) => {
532
+ if (t.isFunctionDeclaration(n) || t.isFunctionExpression(n) || t.isArrowFunction(n))
533
+ return;
534
+ if (t.isReturnStatement(n) && n.expression !== void 0)
535
+ out.push(n.expression);
536
+ t.forEachChild(n, visit);
537
+ };
538
+ t.forEachChild(fn.body, visit);
539
+ return out;
540
+ }
541
+ function isBuilderFunction(fn, ctx) {
542
+ const t = ctx.ts;
543
+ const first = fn.parameters[0];
544
+ if (first !== void 0 && isBlockBuilderType(ctx.checker.getTypeAtLocation(first.name))) {
545
+ return false;
546
+ }
547
+ const returns = returnExpressions(fn, t);
548
+ return returns.length > 0 && returns.every((r) => isEastValueConstructor(r, t));
549
+ }
550
+ var noEastDataBuilderHelper = {
551
+ name: NAME11,
552
+ code: CODE11,
553
+ description: "Flag a TS helper whose only job is to return a hand-built East value (variant/some/none/East.value) \u2014 inline it or make it a real East.function.",
554
+ check(node, ctx) {
555
+ const t = ctx.ts;
556
+ let fn;
557
+ let reportNode;
558
+ if (t.isFunctionDeclaration(node) && node.body !== void 0) {
559
+ fn = node;
560
+ reportNode = node.name ?? node;
561
+ } else if (t.isVariableDeclaration(node) && node.initializer !== void 0 && (t.isArrowFunction(node.initializer) || t.isFunctionExpression(node.initializer))) {
562
+ fn = node.initializer;
563
+ reportNode = node.name;
564
+ }
565
+ if (fn === void 0 || reportNode === void 0)
566
+ return;
567
+ if (!isBuilderFunction(fn, ctx))
568
+ return;
569
+ const sf = ctx.sourceFile;
570
+ const start = reportNode.getStart(sf);
571
+ ctx.report({
572
+ ruleName: NAME11,
573
+ code: CODE11,
574
+ start,
575
+ length: reportNode.getEnd() - start,
576
+ 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.",
577
+ category: "warning"
578
+ });
579
+ }
580
+ };
581
+
582
+ // ../east-diagnostics/dist/src/rules/prefer-jsx-over-factory-call.js
583
+ var NAME12 = "prefer-jsx-over-factory-call";
584
+ var CODE12 = 990012;
585
+ var jsxElementCache = /* @__PURE__ */ new WeakMap();
586
+ function jsxElementType(ctx) {
587
+ const cached = jsxElementCache.get(ctx.sourceFile);
588
+ if (cached !== void 0)
589
+ return cached ?? void 0;
590
+ const computed = computeJsxElementType(ctx);
591
+ jsxElementCache.set(ctx.sourceFile, computed ?? null);
592
+ return computed;
593
+ }
594
+ function computeJsxElementType(ctx) {
595
+ const t = ctx.ts;
596
+ const elementOf = (ns) => {
597
+ const el = ctx.checker.getExportsOfModule(ns).find((s) => s.name === "Element");
598
+ return el ? ctx.checker.getDeclaredTypeOfSymbol(el) : void 0;
599
+ };
600
+ const resolveName = ctx.checker.resolveName;
601
+ const globalNs = resolveName?.("JSX", ctx.sourceFile, t.SymbolFlags.Namespace, false);
602
+ if (globalNs !== void 0) {
603
+ const fromGlobal = elementOf(globalNs);
604
+ if (fromGlobal !== void 0)
605
+ return fromGlobal;
606
+ }
607
+ const program = ctx.program;
608
+ if (program === void 0)
609
+ return void 0;
610
+ const options = program.getCompilerOptions();
611
+ const base = jsxImportSourceFor(options, ctx.sourceFile, t);
612
+ if (base === void 0)
613
+ return void 0;
614
+ const resolutionHost = {
615
+ fileExists: (f) => program.getSourceFile(f) !== void 0 || t.sys.fileExists(f),
616
+ readFile: (f) => program.getSourceFile(f)?.text ?? t.sys.readFile(f),
617
+ directoryExists: (d) => t.sys.directoryExists(d),
618
+ getCurrentDirectory: () => t.sys.getCurrentDirectory(),
619
+ getDirectories: (d) => t.sys.getDirectories(d)
620
+ };
621
+ if (t.sys.realpath !== void 0)
622
+ resolutionHost.realpath = (p) => t.sys.realpath(p);
623
+ const resolved = t.resolveModuleName(`${base}/jsx-runtime`, ctx.sourceFile.fileName, options, resolutionHost).resolvedModule?.resolvedFileName;
624
+ if (resolved === void 0)
625
+ return void 0;
626
+ const runtimeSf = program.getSourceFile(resolved);
627
+ const moduleSym = runtimeSf ? ctx.checker.getSymbolAtLocation(runtimeSf) : void 0;
628
+ if (moduleSym === void 0)
629
+ return void 0;
630
+ const jsxNs = ctx.checker.getExportsOfModule(moduleSym).find((s) => s.name === "JSX");
631
+ return jsxNs ? elementOf(jsxNs) : void 0;
632
+ }
633
+ function jsxImportSourceFor(options, sourceFile, t) {
634
+ const pragmas = sourceFile.pragmas;
635
+ const pragma = pragmas?.get("jsximportsource");
636
+ const fromPragma = Array.isArray(pragma) ? pragma[pragma.length - 1]?.arguments?.factory : pragma?.arguments?.factory;
637
+ if (fromPragma !== void 0)
638
+ return fromPragma;
639
+ if (options.jsx !== t.JsxEmit.ReactJSX && options.jsx !== t.JsxEmit.ReactJSXDev)
640
+ return void 0;
641
+ return options.jsxImportSource ?? "react";
642
+ }
643
+ function sameType(a, b, checker) {
644
+ const c = checker;
645
+ if (c.isTypeAssignableTo === void 0)
646
+ return a === b;
647
+ return c.isTypeAssignableTo(a, b) && c.isTypeAssignableTo(b, a);
648
+ }
649
+ var preferJsxOverFactoryCall = {
650
+ name: NAME12,
651
+ code: CODE12,
652
+ description: "In a .tsx file, prefer the <Foo> JSX tag over a factory's Foo.Root(...) when the call produces a JSX element.",
653
+ check(node, ctx) {
654
+ const t = ctx.ts;
655
+ if (ctx.sourceFile.languageVariant !== t.LanguageVariant.JSX)
656
+ return;
657
+ if (!t.isCallExpression(node))
658
+ return;
659
+ const callee = node.expression;
660
+ if (!t.isPropertyAccessExpression(callee))
661
+ return;
662
+ if (callee.name.text !== "Root")
663
+ return;
664
+ if (!t.isIdentifier(callee.expression))
665
+ return;
666
+ const factoryIdent = callee.expression;
667
+ const element = jsxElementType(ctx);
668
+ if (element === void 0)
669
+ return;
670
+ const result = ctx.checker.getTypeAtLocation(node);
671
+ if (!sameType(result, element, ctx.checker))
672
+ return;
673
+ let tagName = factoryIdent.text;
674
+ const sym = ctx.checker.getSymbolAtLocation(factoryIdent);
675
+ if (sym !== void 0 && (sym.flags & t.SymbolFlags.Alias) !== 0) {
676
+ const target = ctx.checker.getAliasedSymbol(sym);
677
+ if (target.name.length > 0)
678
+ tagName = target.name;
679
+ }
680
+ const sf = ctx.sourceFile;
681
+ const start = callee.getStart(sf);
682
+ ctx.report({
683
+ ruleName: NAME12,
684
+ code: CODE12,
685
+ start,
686
+ length: callee.getEnd() - start,
687
+ messageText: `Author this with the \`<${tagName}>\` JSX tag instead of \`${factoryIdent.text}.Root(...)\` \u2014 in a .tsx file the JSX tag is the authoring surface (the call already produces a JSX element).`,
688
+ category: "suggestion"
689
+ });
690
+ }
691
+ };
692
+
693
+ // ../east-diagnostics/dist/src/rules/index.js
694
+ var allRules = [
695
+ noRedundantEastCast,
696
+ preferExplicitEastType,
697
+ preferSomeNone,
698
+ noHandrolledVariant,
699
+ noEastNamespacedType,
700
+ preferLetConstOverEastValue,
701
+ noRelativeSrcImport,
702
+ noLetConstInExpression,
703
+ noUnexecutedEastExpression,
704
+ noReinlinedEastBinding,
705
+ noEastDataBuilderHelper,
706
+ preferJsxOverFactoryCall
707
+ ];
708
+
709
+ // ../east-diagnostics/dist/src/run.js
710
+ function runEastRules(tsModule, program, sourceFile, checker, options = {}, rules = allRules) {
711
+ const diagnostics = [];
712
+ const ctx = {
713
+ ts: tsModule,
714
+ sourceFile,
715
+ checker,
716
+ program,
717
+ options,
718
+ report: (d) => diagnostics.push(d)
719
+ };
720
+ const disabled = new Set(options.disabled ?? []);
721
+ const active = rules.filter((rule) => !disabled.has(rule.name));
722
+ const visit = (node) => {
723
+ for (const rule of active)
724
+ rule.check(node, ctx);
725
+ tsModule.forEachChild(node, visit);
726
+ };
727
+ visit(sourceFile);
728
+ return diagnostics;
729
+ }
730
+
731
+ // ../east-diagnostics/dist/src/east-module.js
732
+ var import_node_module = require("node:module");
733
+ var import_node_path = require("node:path");
734
+ var import_node_url = require("node:url");
735
+ var cache = /* @__PURE__ */ new Map();
736
+ var pendingImports = /* @__PURE__ */ new Set();
737
+ function validate(candidate) {
738
+ const m = candidate;
739
+ const mod = (typeof m?.["diffTypes"] === "function" ? m : m?.["default"]) ?? void 0;
740
+ if (mod === void 0)
741
+ return null;
742
+ const fns = ["diffTypes", "renderTypeDiff", "StructType", "VariantType", "ArrayType", "RecursiveType", "FunctionType", "TypeUnion"];
743
+ for (const f of fns)
744
+ if (typeof mod[f] !== "function")
745
+ return null;
746
+ if (mod["IntegerType"] === void 0)
747
+ return null;
748
+ return mod;
749
+ }
750
+ function getEastModule(projectDir) {
751
+ const cached = cache.get(projectDir);
752
+ if (cached !== void 0)
753
+ return cached ?? void 0;
754
+ const require_ = (0, import_node_module.createRequire)((0, import_node_path.join)(projectDir, "_.js"));
755
+ let entry;
756
+ try {
757
+ entry = require_.resolve("@elaraai/east");
758
+ } catch {
759
+ cache.set(projectDir, null);
760
+ return void 0;
761
+ }
762
+ try {
763
+ const mod = validate(require_(entry));
764
+ cache.set(projectDir, mod);
765
+ return mod ?? void 0;
766
+ } catch {
767
+ if (!pendingImports.has(projectDir)) {
768
+ pendingImports.add(projectDir);
769
+ import((0, import_node_url.pathToFileURL)(entry).href).then((m) => cache.set(projectDir, validate(m)), () => cache.set(projectDir, null));
770
+ }
771
+ return void 0;
772
+ }
773
+ }
774
+
775
+ // ../east-diagnostics/dist/src/type-reify.js
776
+ var PRIMITIVE_TAGS = /* @__PURE__ */ new Set(["Never", "Null", "Boolean", "Integer", "Float", "String", "DateTime", "Blob"]);
777
+ var MAX_DEPTH = 32;
778
+ var Bail = class extends Error {
779
+ };
780
+ function bail() {
781
+ throw new Bail();
782
+ }
783
+ function propType(ctx, type, name) {
784
+ const symbol = ctx.checker.getPropertyOfType(type, name);
785
+ if (symbol === void 0)
786
+ return void 0;
787
+ return ctx.checker.getTypeOfSymbol(symbol);
788
+ }
789
+ function literalString(type) {
790
+ return type !== void 0 && type.isStringLiteral() ? type.value : void 0;
791
+ }
792
+ function typeArguments(ctx, type) {
793
+ const { t, checker } = ctx;
794
+ if ((type.flags & t.TypeFlags.Object) === 0)
795
+ return [];
796
+ if ((type.objectFlags & t.ObjectFlags.Reference) === 0)
797
+ return [];
798
+ return checker.getTypeArguments(type);
799
+ }
800
+ function stripAbsent(ctx, type) {
801
+ const members = type.isUnion() ? type.types : [type];
802
+ return members.filter((m) => (m.flags & (ctx.t.TypeFlags.Undefined | ctx.t.TypeFlags.Void)) === 0);
803
+ }
804
+ function reifyUnion(ctx, members) {
805
+ const reified = [];
806
+ for (const m of members) {
807
+ try {
808
+ reified.push(walk(ctx, m));
809
+ } catch (e) {
810
+ if (!(e instanceof Bail))
811
+ throw e;
812
+ }
813
+ }
814
+ if (reified.length === 0)
815
+ bail();
816
+ const distinct = [...new Set(reified)];
817
+ const nonNever = distinct.filter((r) => r !== ctx.east.NeverType);
818
+ const candidates = nonNever.length > 0 ? nonNever : distinct;
819
+ if (candidates.length === 1)
820
+ return candidates[0];
821
+ try {
822
+ return candidates.reduce((a, b) => ctx.east.TypeUnion(a, b));
823
+ } catch {
824
+ bail();
825
+ }
826
+ }
827
+ function reifyEncoding(ctx, type, tag) {
828
+ const { east, checker } = ctx;
829
+ if (PRIMITIVE_TAGS.has(tag))
830
+ return east[`${tag}Type`];
831
+ switch (tag) {
832
+ case "Ref": {
833
+ const v = propType(ctx, type, "value") ?? bail();
834
+ return east.RefType(walk(ctx, v));
835
+ }
836
+ case "Array": {
837
+ const v = propType(ctx, type, "value") ?? bail();
838
+ return east.ArrayType(walk(ctx, v));
839
+ }
840
+ case "Set": {
841
+ const k = propType(ctx, type, "key") ?? bail();
842
+ return east.SetType(walk(ctx, k));
843
+ }
844
+ case "Dict": {
845
+ const k = propType(ctx, type, "key") ?? bail();
846
+ const v = propType(ctx, type, "value") ?? bail();
847
+ return east.DictType(walk(ctx, k), walk(ctx, v));
848
+ }
849
+ case "Vector": {
850
+ const e = propType(ctx, type, "element") ?? bail();
851
+ return east.VectorType(walk(ctx, e));
852
+ }
853
+ case "Matrix": {
854
+ const e = propType(ctx, type, "element") ?? bail();
855
+ return east.MatrixType(walk(ctx, e));
856
+ }
857
+ case "Struct": {
858
+ const fieldsType = propType(ctx, type, "fields") ?? bail();
859
+ const fields = {};
860
+ for (const prop of checker.getPropertiesOfType(fieldsType)) {
861
+ const name = prop.getName();
862
+ if (name.startsWith("__@"))
863
+ continue;
864
+ fields[name] = reifyUnion(ctx, stripAbsent(ctx, checker.getTypeOfSymbol(prop)));
865
+ }
866
+ return east.StructType(fields);
867
+ }
868
+ case "Variant": {
869
+ const casesType = propType(ctx, type, "cases") ?? bail();
870
+ const cases = {};
871
+ for (const prop of checker.getPropertiesOfType(casesType)) {
872
+ const name = prop.getName();
873
+ if (name.startsWith("__@"))
874
+ continue;
875
+ cases[name] = reifyUnion(ctx, stripAbsent(ctx, checker.getTypeOfSymbol(prop)));
876
+ }
877
+ return east.VariantType(cases);
878
+ }
879
+ case "Function":
880
+ case "AsyncFunction": {
881
+ const inputsType = propType(ctx, type, "inputs") ?? bail();
882
+ if (!checker.isTupleType(inputsType))
883
+ bail();
884
+ const inputs = typeArguments(ctx, inputsType).map((i) => walk(ctx, i));
885
+ const output = walk(ctx, propType(ctx, type, "output") ?? bail());
886
+ return tag === "Function" ? east.FunctionType(inputs, output) : east.AsyncFunctionType(inputs, output);
887
+ }
888
+ case "Recursive": {
889
+ const node = propType(ctx, type, "node");
890
+ if (node === void 0) {
891
+ if (ctx.selves.length === 0)
892
+ bail();
893
+ return ctx.selves[ctx.selves.length - 1];
894
+ }
895
+ return ctx.east.RecursiveType((self) => {
896
+ ctx.selves.push(self);
897
+ try {
898
+ return walk(ctx, node);
899
+ } finally {
900
+ ctx.selves.pop();
901
+ }
902
+ });
903
+ }
904
+ default:
905
+ bail();
906
+ }
907
+ }
908
+ function reifyRawValue(ctx, type) {
909
+ const { t, east, checker } = ctx;
910
+ const f = type.flags;
911
+ if (f & (t.TypeFlags.BigInt | t.TypeFlags.BigIntLiteral))
912
+ return east.IntegerType;
913
+ if (f & (t.TypeFlags.Number | t.TypeFlags.NumberLiteral))
914
+ return east.FloatType;
915
+ if (f & (t.TypeFlags.String | t.TypeFlags.StringLiteral | t.TypeFlags.TemplateLiteral))
916
+ return east.StringType;
917
+ if (f & (t.TypeFlags.Boolean | t.TypeFlags.BooleanLiteral))
918
+ return east.BooleanType;
919
+ if (f & t.TypeFlags.Null)
920
+ return east.NullType;
921
+ if ((f & t.TypeFlags.Object) === 0)
922
+ bail();
923
+ const name = type.getSymbol()?.getName();
924
+ switch (name) {
925
+ case "Date":
926
+ return east.DateTimeType;
927
+ case "Uint8Array":
928
+ return east.BlobType;
929
+ case "Float64Array":
930
+ return east.VectorType(east.FloatType);
931
+ case "BigInt64Array":
932
+ return east.VectorType(east.IntegerType);
933
+ case "Uint8ClampedArray":
934
+ return east.VectorType(east.BooleanType);
935
+ case "Array":
936
+ case "ReadonlyArray": {
937
+ const [el] = typeArguments(ctx, type);
938
+ if (el === void 0)
939
+ bail();
940
+ return east.ArrayType(reifyUnion(ctx, stripAbsent(ctx, el)));
941
+ }
942
+ case "Set":
943
+ case "ReadonlySet": {
944
+ const [el] = typeArguments(ctx, type);
945
+ if (el === void 0)
946
+ bail();
947
+ return east.SetType(reifyUnion(ctx, stripAbsent(ctx, el)));
948
+ }
949
+ case "Map":
950
+ case "ReadonlyMap": {
951
+ const [k, v] = typeArguments(ctx, type);
952
+ if (k === void 0 || v === void 0)
953
+ bail();
954
+ return east.DictType(reifyUnion(ctx, stripAbsent(ctx, k)), reifyUnion(ctx, stripAbsent(ctx, v)));
955
+ }
956
+ default:
957
+ break;
958
+ }
959
+ if (checker.isTupleType(type)) {
960
+ const elements = typeArguments(ctx, type).map((e) => reifyUnion(ctx, stripAbsent(ctx, e)));
961
+ const distinct = new Set(elements);
962
+ if (distinct.size !== 1)
963
+ bail();
964
+ return east.ArrayType(elements[0]);
965
+ }
966
+ if (type.getCallSignatures().length > 0 || type.getConstructSignatures().length > 0)
967
+ bail();
968
+ if (checker.getIndexInfosOfType(type).length > 0)
969
+ bail();
970
+ const props = checker.getPropertiesOfType(type);
971
+ if (props.length === 0)
972
+ bail();
973
+ const fields = {};
974
+ for (const prop of props) {
975
+ const propName = prop.getName();
976
+ if (propName.startsWith("__@"))
977
+ continue;
978
+ fields[propName] = reifyUnion(ctx, stripAbsent(ctx, checker.getTypeOfSymbol(prop)));
979
+ }
980
+ return east.StructType(fields);
981
+ }
982
+ function walk(ctx, type) {
983
+ if (++ctx.depth > MAX_DEPTH)
984
+ bail();
985
+ try {
986
+ if (type.isUnion())
987
+ return reifyUnion(ctx, stripAbsent(ctx, type));
988
+ const properties = type.getProperties();
989
+ const exprTypeProp = properties.find((p) => p.getName().startsWith("__@TypeSymbol"));
990
+ if (exprTypeProp !== void 0) {
991
+ ctx.sawEastShape = true;
992
+ return walk(ctx, ctx.checker.getTypeOfSymbol(exprTypeProp));
993
+ }
994
+ if (properties.some((p) => p.getName().startsWith("__@variant_symbol"))) {
995
+ const caseName = literalString(propType(ctx, type, "type")) ?? bail();
996
+ const payload = propType(ctx, type, "value") ?? bail();
997
+ ctx.sawEastShape = true;
998
+ return ctx.east.VariantType({ [caseName]: reifyUnion(ctx, stripAbsent(ctx, payload)) });
999
+ }
1000
+ if (properties.some((p) => p.getName().startsWith("__@ref_symbol"))) {
1001
+ const v = propType(ctx, type, "value") ?? bail();
1002
+ ctx.sawEastShape = true;
1003
+ return ctx.east.RefType(reifyUnion(ctx, stripAbsent(ctx, v)));
1004
+ }
1005
+ const tag = literalString(propType(ctx, type, "type"));
1006
+ if (tag !== void 0) {
1007
+ try {
1008
+ const result = reifyEncoding(ctx, type, tag);
1009
+ ctx.sawEastShape = true;
1010
+ return result;
1011
+ } catch (e) {
1012
+ if (!(e instanceof Bail))
1013
+ throw e;
1014
+ }
1015
+ }
1016
+ return reifyRawValue(ctx, type);
1017
+ } finally {
1018
+ ctx.depth--;
1019
+ }
1020
+ }
1021
+ function reifyEastType(t, checker, type, east) {
1022
+ const ctx = { t, checker, east, depth: 0, selves: [], sawEastShape: false };
1023
+ try {
1024
+ return { type: walk(ctx, type), eastShaped: ctx.sawEastShape };
1025
+ } catch (e) {
1026
+ if (e instanceof Bail)
1027
+ return void 0;
1028
+ return void 0;
1029
+ }
1030
+ }
1031
+
1032
+ // ../east-diagnostics/dist/src/type-diff-rewrite.js
1033
+ var ASSIGNABILITY_CODES = /* @__PURE__ */ new Set([
1034
+ 2322,
1035
+ // Type 'X' is not assignable to type 'Y'.
1036
+ 2345,
1037
+ // Argument of type 'X' is not assignable to parameter of type 'Y'.
1038
+ 2375,
1039
+ // Type 'X' is not assignable to type 'Y' with exactOptionalPropertyTypes.
1040
+ 2379,
1041
+ // Getter/setter assignability variant of 2322.
1042
+ 2412,
1043
+ // Property assignability with exactOptionalPropertyTypes.
1044
+ 2719,
1045
+ // Type 'X' is not assignable to type 'Y'. Two different types with this name exist.
1046
+ 2739,
1047
+ // Type 'X' is missing the following properties from type 'Y'.
1048
+ 2740,
1049
+ // Type 'X' is missing the following properties from type 'Y' (array forms).
1050
+ 2741
1051
+ // Property 'p' is missing in type 'X' but required in type 'Y'.
1052
+ ]);
1053
+ function innermostNodeAt(t, sourceFile, position) {
1054
+ function find(node) {
1055
+ if (position < node.getStart(sourceFile) || position >= node.getEnd())
1056
+ return void 0;
1057
+ return t.forEachChild(node, find) ?? node;
1058
+ }
1059
+ return find(sourceFile);
1060
+ }
1061
+ function resolveTypePair(t, checker, node) {
1062
+ let current = node;
1063
+ for (let hops = 0; current !== void 0 && hops < 6; current = current.parent, hops++) {
1064
+ if (t.isVariableDeclaration(current) && current.type !== void 0 && current.initializer !== void 0) {
1065
+ return {
1066
+ actual: checker.getTypeAtLocation(current.initializer),
1067
+ expected: checker.getTypeFromTypeNode(current.type)
1068
+ };
1069
+ }
1070
+ if (t.isPropertyAssignment(current)) {
1071
+ const expected = checker.getContextualType(current.initializer);
1072
+ if (expected !== void 0) {
1073
+ return { actual: checker.getTypeAtLocation(current.initializer), expected };
1074
+ }
1075
+ }
1076
+ if (t.isExpression(current)) {
1077
+ const expected = checker.getContextualType(current);
1078
+ if (expected !== void 0) {
1079
+ return { actual: checker.getTypeAtLocation(current), expected };
1080
+ }
1081
+ }
1082
+ }
1083
+ return void 0;
1084
+ }
1085
+ function rewriteEastAssignability(t, program, sourceFile, diagnostic, east) {
1086
+ if (diagnostic.start === void 0 || !ASSIGNABILITY_CODES.has(diagnostic.code))
1087
+ return void 0;
1088
+ const checker = program.getTypeChecker();
1089
+ const node = innermostNodeAt(t, sourceFile, diagnostic.start);
1090
+ if (node === void 0)
1091
+ return void 0;
1092
+ const pair = resolveTypePair(t, checker, node);
1093
+ if (pair === void 0)
1094
+ return void 0;
1095
+ const actual = reifyEastType(t, checker, pair.actual, east);
1096
+ const expected = reifyEastType(t, checker, pair.expected, east);
1097
+ if (actual === void 0 || expected === void 0)
1098
+ return void 0;
1099
+ if (!actual.eastShaped && !expected.eastShaped)
1100
+ return void 0;
1101
+ let rendered;
1102
+ try {
1103
+ const diffs = east.diffTypes(actual.type, expected.type);
1104
+ if (!Array.isArray(diffs) || diffs.length === 0)
1105
+ return void 0;
1106
+ rendered = east.renderTypeDiff(diffs);
1107
+ } catch {
1108
+ return void 0;
1109
+ }
1110
+ if (rendered.length === 0)
1111
+ return void 0;
1112
+ return `East type mismatch: ${rendered.split("\n").join("; ")}`;
1113
+ }
1114
+
1115
+ // ../east-diagnostics/dist/src/tsserver-plugin.js
1116
+ var CATEGORY = {
1117
+ error: "Error",
1118
+ warning: "Warning",
1119
+ suggestion: "Suggestion"
1120
+ };
1121
+ function isElaraaiPackageSrc(filePath) {
1122
+ const file = (0, import_node_path2.resolve)(filePath);
1123
+ let dir = (0, import_node_path2.dirname)(file);
1124
+ for (; ; ) {
1125
+ const candidate = (0, import_node_path2.join)(dir, "package.json");
1126
+ if ((0, import_node_fs.existsSync)(candidate)) {
1127
+ try {
1128
+ const name = JSON.parse((0, import_node_fs.readFileSync)(candidate, "utf-8")).name;
1129
+ return typeof name === "string" && name.startsWith("@elaraai/") && file.startsWith((0, import_node_path2.join)(dir, "src") + "/");
1130
+ } catch {
1131
+ return false;
1132
+ }
1133
+ }
1134
+ const parent = (0, import_node_path2.dirname)(dir);
1135
+ if (parent === dir)
1136
+ return false;
1137
+ dir = parent;
1138
+ }
1139
+ }
1140
+ function init(modules) {
1141
+ const t = modules.typescript;
1142
+ return {
1143
+ create(info) {
1144
+ const ls = info.languageService;
1145
+ const proxy = /* @__PURE__ */ Object.create(null);
1146
+ for (const key of Object.keys(ls)) {
1147
+ const member = ls[key];
1148
+ proxy[key] = (...args) => member.apply(ls, args);
1149
+ }
1150
+ proxy.getSemanticDiagnostics = (fileName) => {
1151
+ const prior = ls.getSemanticDiagnostics(fileName);
1152
+ try {
1153
+ const program = ls.getProgram();
1154
+ const sourceFile = program?.getSourceFile(fileName);
1155
+ if (program === void 0 || sourceFile === void 0)
1156
+ return prior;
1157
+ const east = getEastModule(info.project.getCurrentDirectory());
1158
+ const diagnostics = prior.map((d) => {
1159
+ if (east === void 0 || !ASSIGNABILITY_CODES.has(d.code))
1160
+ return d;
1161
+ const rewritten = rewriteEastAssignability(t, program, sourceFile, d, east);
1162
+ return rewritten === void 0 ? d : { ...d, messageText: rewritten };
1163
+ });
1164
+ if (!isElaraaiPackageSrc(fileName)) {
1165
+ const ruleDiagnostics = runEastRules(t, program, sourceFile, program.getTypeChecker(), info.config?.disabled !== void 0 ? { disabled: info.config.disabled } : {});
1166
+ for (const d of ruleDiagnostics) {
1167
+ diagnostics.push({
1168
+ file: sourceFile,
1169
+ start: d.start,
1170
+ length: d.length,
1171
+ messageText: `${d.messageText} (${d.ruleName})`,
1172
+ category: t.DiagnosticCategory[CATEGORY[d.category]],
1173
+ code: d.code,
1174
+ source: "east"
1175
+ });
1176
+ }
1177
+ }
1178
+ return diagnostics;
1179
+ } catch {
1180
+ return prior;
1181
+ }
1182
+ };
1183
+ return proxy;
1184
+ }
1185
+ };
1186
+ }
1187
+ var tsserver_plugin_default = init;
1188
+ // Annotate the CommonJS export names for ESM import in node:
1189
+ 0 && (module.exports = {
1190
+ init
1191
+ });
1192
+ module.exports = module.exports.default;
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@elaraai/tsserver-plugin-east",
3
+ "version": "1.0.4",
4
+ "license": "AGPL-3.0-or-later",
5
+ "description": "TypeScript language service plugin for East — East idiom diagnostics and localized East type-diff error messages as native editor squiggles.",
6
+ "main": "dist/index.cjs",
7
+ "files": [
8
+ "dist",
9
+ "README.md",
10
+ "LICENSE.md"
11
+ ],
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/elaraai/east-workspace.git",
15
+ "directory": "libs/tsserver-plugin-east"
16
+ },
17
+ "keywords": [
18
+ "east",
19
+ "elara",
20
+ "typescript",
21
+ "tsserver",
22
+ "language-service",
23
+ "plugin"
24
+ ],
25
+ "author": "Elara AI Pty Ltd",
26
+ "engines": {
27
+ "node": ">=22.0.0"
28
+ },
29
+ "peerDependencies": {
30
+ "typescript": ">=5.0.0"
31
+ },
32
+ "devDependencies": {
33
+ "esbuild": "^0.27.3",
34
+ "typescript": "~5.9.2",
35
+ "@elaraai/east": "1.0.4",
36
+ "@elaraai/east-diagnostics": "1.0.4"
37
+ },
38
+ "scripts": {
39
+ "build": "node scripts/build.mjs",
40
+ "test": "npm run build && node --test-reporter=spec --test 'test/**/*.test.mjs'"
41
+ }
42
+ }