boperators 0.1.0

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 (51) hide show
  1. package/README.md +202 -0
  2. package/dist/cjs/core/BopConfig.js +73 -0
  3. package/dist/cjs/core/ErrorManager.js +126 -0
  4. package/dist/cjs/core/OverloadInjector.js +136 -0
  5. package/dist/cjs/core/OverloadStore.js +622 -0
  6. package/dist/cjs/core/SourceMap.js +168 -0
  7. package/dist/cjs/core/helpers/ensureImportedName.js +50 -0
  8. package/dist/cjs/core/helpers/getImportedNameForSymbol.js +64 -0
  9. package/dist/cjs/core/helpers/getModuleSpecifier.js +61 -0
  10. package/dist/cjs/core/helpers/getOperatorStringFromProperty.js +33 -0
  11. package/dist/cjs/core/helpers/resolveExpressionType.js +30 -0
  12. package/dist/cjs/core/helpers/unwrapInitializer.js +18 -0
  13. package/dist/cjs/core/operatorMap.js +95 -0
  14. package/dist/cjs/core/validateExports.js +87 -0
  15. package/dist/cjs/index.js +36 -0
  16. package/dist/cjs/lib/index.js +17 -0
  17. package/dist/cjs/lib/operatorSymbols.js +37 -0
  18. package/dist/esm/core/BopConfig.d.ts +34 -0
  19. package/dist/esm/core/BopConfig.js +65 -0
  20. package/dist/esm/core/ErrorManager.d.ts +90 -0
  21. package/dist/esm/core/ErrorManager.js +118 -0
  22. package/dist/esm/core/OverloadInjector.d.ts +33 -0
  23. package/dist/esm/core/OverloadInjector.js +129 -0
  24. package/dist/esm/core/OverloadStore.d.ts +114 -0
  25. package/dist/esm/core/OverloadStore.js +618 -0
  26. package/dist/esm/core/SourceMap.d.ts +41 -0
  27. package/dist/esm/core/SourceMap.js +164 -0
  28. package/dist/esm/core/helpers/ensureImportedName.d.ts +11 -0
  29. package/dist/esm/core/helpers/ensureImportedName.js +46 -0
  30. package/dist/esm/core/helpers/getImportedNameForSymbol.d.ts +8 -0
  31. package/dist/esm/core/helpers/getImportedNameForSymbol.js +60 -0
  32. package/dist/esm/core/helpers/getModuleSpecifier.d.ts +2 -0
  33. package/dist/esm/core/helpers/getModuleSpecifier.js +57 -0
  34. package/dist/esm/core/helpers/getOperatorStringFromProperty.d.ts +13 -0
  35. package/dist/esm/core/helpers/getOperatorStringFromProperty.js +30 -0
  36. package/dist/esm/core/helpers/resolveExpressionType.d.ts +11 -0
  37. package/dist/esm/core/helpers/resolveExpressionType.js +27 -0
  38. package/dist/esm/core/helpers/unwrapInitializer.d.ts +9 -0
  39. package/dist/esm/core/helpers/unwrapInitializer.js +15 -0
  40. package/dist/esm/core/operatorMap.d.ts +77 -0
  41. package/dist/esm/core/operatorMap.js +89 -0
  42. package/dist/esm/core/validateExports.d.ts +30 -0
  43. package/dist/esm/core/validateExports.js +84 -0
  44. package/dist/esm/index.d.ts +19 -0
  45. package/dist/esm/index.js +14 -0
  46. package/dist/esm/lib/index.d.ts +1 -0
  47. package/dist/esm/lib/index.js +1 -0
  48. package/dist/esm/lib/operatorSymbols.d.ts +33 -0
  49. package/dist/esm/lib/operatorSymbols.js +34 -0
  50. package/license.txt +8 -0
  51. package/package.json +54 -0
@@ -0,0 +1,622 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OverloadStore = void 0;
4
+ const ts_morph_1 = require("ts-morph");
5
+ const operatorSymbols_1 = require("../lib/operatorSymbols");
6
+ const ErrorManager_1 = require("./ErrorManager");
7
+ const getOperatorStringFromProperty_1 = require("./helpers/getOperatorStringFromProperty");
8
+ const unwrapInitializer_1 = require("./helpers/unwrapInitializer");
9
+ const operatorMap_1 = require("./operatorMap");
10
+ class OverloadStore extends Map {
11
+ constructor(project, errorManager, logger) {
12
+ super();
13
+ this._parsedFiles = new Set();
14
+ /** Tracks which map entries came from which file, for targeted invalidation. */
15
+ this._fileEntries = new Map();
16
+ /** Cache for type hierarchy chains: type name → [self, parent, grandparent, ...] */
17
+ this._typeChainCache = new Map();
18
+ /** Storage for prefix unary overloads: operator → operandType → description */
19
+ this._prefixUnaryOverloads = new Map();
20
+ /** Storage for postfix unary overloads: operator → operandType → description */
21
+ this._postfixUnaryOverloads = new Map();
22
+ /** Tracks which prefix unary entries came from which file, for targeted invalidation. */
23
+ this._prefixUnaryFileEntries = new Map();
24
+ /** Tracks which postfix unary entries came from which file, for targeted invalidation. */
25
+ this._postfixUnaryFileEntries = new Map();
26
+ this._project = project;
27
+ this._errorManager = errorManager;
28
+ this._logger = logger;
29
+ }
30
+ /**
31
+ * Looks up an overload description for the given operator kind and
32
+ * LHS/RHS type pair. Walks up the class hierarchy for both sides
33
+ * when an exact match isn't found.
34
+ */
35
+ findOverload(operatorKind, lhsType, rhsType) {
36
+ const operatorOverloads = this.get(operatorKind);
37
+ if (!operatorOverloads)
38
+ return undefined;
39
+ const lhsChain = this._getTypeChain(lhsType);
40
+ const rhsChain = this._getTypeChain(rhsType);
41
+ // Try each combination, most specific types first
42
+ for (const lhs of lhsChain) {
43
+ const lhsMap = operatorOverloads.get(lhs);
44
+ if (!lhsMap)
45
+ continue;
46
+ for (const rhs of rhsChain) {
47
+ const match = lhsMap.get(rhs);
48
+ if (match)
49
+ return match;
50
+ }
51
+ }
52
+ return undefined;
53
+ }
54
+ /**
55
+ * Looks up a prefix unary overload description for the given operator kind
56
+ * and operand type. Walks up the class hierarchy when an exact match isn't found.
57
+ */
58
+ findPrefixUnaryOverload(operatorKind, operandType) {
59
+ const operatorOverloads = this._prefixUnaryOverloads.get(operatorKind);
60
+ if (!operatorOverloads)
61
+ return undefined;
62
+ const typeChain = this._getTypeChain(operandType);
63
+ for (const t of typeChain) {
64
+ const match = operatorOverloads.get(t);
65
+ if (match)
66
+ return match;
67
+ }
68
+ return undefined;
69
+ }
70
+ /**
71
+ * Looks up a postfix unary overload description for the given operator kind
72
+ * and operand type. Walks up the class hierarchy when an exact match isn't found.
73
+ */
74
+ findPostfixUnaryOverload(operatorKind, operandType) {
75
+ const operatorOverloads = this._postfixUnaryOverloads.get(operatorKind);
76
+ if (!operatorOverloads)
77
+ return undefined;
78
+ const typeChain = this._getTypeChain(operandType);
79
+ for (const t of typeChain) {
80
+ const match = operatorOverloads.get(t);
81
+ if (match)
82
+ return match;
83
+ }
84
+ return undefined;
85
+ }
86
+ /**
87
+ * Returns a flat array of all registered overloads (binary, prefix unary,
88
+ * and postfix unary) with clean type names.
89
+ */
90
+ getAllOverloads() {
91
+ const results = [];
92
+ const sl = this._shortTypeName.bind(this);
93
+ for (const [_syntaxKind, lhsMap] of this) {
94
+ for (const [lhsType, rhsMap] of lhsMap) {
95
+ for (const [rhsType, desc] of rhsMap) {
96
+ results.push(Object.assign(Object.assign({ kind: "binary" }, desc), { lhsType: sl(lhsType), rhsType: sl(rhsType) }));
97
+ }
98
+ }
99
+ }
100
+ for (const [_syntaxKind, operandMap] of this._prefixUnaryOverloads) {
101
+ for (const [operandType, desc] of operandMap) {
102
+ results.push(Object.assign(Object.assign({ kind: "prefixUnary" }, desc), { operandType: sl(operandType) }));
103
+ }
104
+ }
105
+ for (const [_syntaxKind, operandMap] of this._postfixUnaryOverloads) {
106
+ for (const [operandType, desc] of operandMap) {
107
+ results.push(Object.assign(Object.assign({ kind: "postfixUnary" }, desc), { operandType: sl(operandType) }));
108
+ }
109
+ }
110
+ return results;
111
+ }
112
+ /**
113
+ * Removes all overload entries that originated from the given file
114
+ * and marks it as unparsed so it will be re-scanned on the next call
115
+ * to `addOverloadsFromFile`.
116
+ *
117
+ * @returns `true` if the file had any overload entries (meaning files
118
+ * that were transformed using those overloads may now be stale).
119
+ */
120
+ invalidateFile(filePath) {
121
+ var _a, _b, _c, _d;
122
+ this._parsedFiles.delete(filePath);
123
+ this._typeChainCache.clear();
124
+ let hadEntries = false;
125
+ const entries = this._fileEntries.get(filePath);
126
+ if (entries) {
127
+ for (const { syntaxKind, lhsType, rhsType } of entries) {
128
+ (_b = (_a = this.get(syntaxKind)) === null || _a === void 0 ? void 0 : _a.get(lhsType)) === null || _b === void 0 ? void 0 : _b.delete(rhsType);
129
+ }
130
+ this._fileEntries.delete(filePath);
131
+ hadEntries = true;
132
+ }
133
+ const prefixEntries = this._prefixUnaryFileEntries.get(filePath);
134
+ if (prefixEntries) {
135
+ for (const { syntaxKind, operandType } of prefixEntries) {
136
+ (_c = this._prefixUnaryOverloads.get(syntaxKind)) === null || _c === void 0 ? void 0 : _c.delete(operandType);
137
+ }
138
+ this._prefixUnaryFileEntries.delete(filePath);
139
+ hadEntries = true;
140
+ }
141
+ const postfixEntries = this._postfixUnaryFileEntries.get(filePath);
142
+ if (postfixEntries) {
143
+ for (const { syntaxKind, operandType } of postfixEntries) {
144
+ (_d = this._postfixUnaryOverloads.get(syntaxKind)) === null || _d === void 0 ? void 0 : _d.delete(operandType);
145
+ }
146
+ this._postfixUnaryFileEntries.delete(filePath);
147
+ hadEntries = true;
148
+ }
149
+ return hadEntries;
150
+ }
151
+ addOverloadsFromFile(file) {
152
+ const sourceFile = file instanceof ts_morph_1.SourceFile
153
+ ? file
154
+ : this._project.getSourceFileOrThrow(file);
155
+ const filePath = sourceFile.getFilePath();
156
+ if (this._parsedFiles.has(filePath))
157
+ return;
158
+ this._parsedFiles.add(filePath);
159
+ const classes = sourceFile.getClasses();
160
+ classes.forEach((classDecl) => {
161
+ const classType = classDecl.getType().getText();
162
+ classDecl.getProperties().forEach((property) => {
163
+ var _a, _b;
164
+ if (!ts_morph_1.Node.isPropertyDeclaration(property))
165
+ return;
166
+ const isStatic = property.isStatic();
167
+ const operatorString = (0, getOperatorStringFromProperty_1.getOperatorStringFromProperty)(property);
168
+ if (!operatorString || !operatorSymbols_1.operatorSymbols.includes(operatorString))
169
+ return;
170
+ // Look up the operator in all three maps
171
+ const binarySyntaxKind = operatorMap_1.operatorMap[operatorString];
172
+ const prefixUnarySyntaxKind = operatorMap_1.prefixUnaryOperatorMap[operatorString];
173
+ const postfixUnarySyntaxKind = operatorMap_1.postfixUnaryOperatorMap[operatorString];
174
+ if (!binarySyntaxKind &&
175
+ !prefixUnarySyntaxKind &&
176
+ !postfixUnarySyntaxKind)
177
+ return;
178
+ // Property-level static/instance validation.
179
+ // Determine what this property should be based on the operator kinds it supports.
180
+ const shouldBeStatic = (binarySyntaxKind != null &&
181
+ !operatorMap_1.instanceOperators.has(binarySyntaxKind)) ||
182
+ prefixUnarySyntaxKind != null;
183
+ const shouldBeInstance = (binarySyntaxKind != null &&
184
+ operatorMap_1.instanceOperators.has(binarySyntaxKind)) ||
185
+ postfixUnarySyntaxKind != null;
186
+ if ((isStatic && !shouldBeStatic) || (!isStatic && !shouldBeInstance)) {
187
+ this._errorManager.addWarning(new ErrorManager_1.ErrorDescription(`Expected overload for operator ${operatorString} ` +
188
+ `to be ${isStatic ? "a static" : "an instance"} field.`, property.getSourceFile().getFilePath(), property.getStartLineNumber(), property.getText().split("\n")[0]));
189
+ return;
190
+ }
191
+ const rawInitializer = property.getInitializer();
192
+ // No initializer — try type-annotation-based extraction (.d.ts files)
193
+ if (!rawInitializer) {
194
+ this._addOverloadsFromTypeAnnotation(property, classDecl, classType, filePath, isStatic, operatorString, binarySyntaxKind, prefixUnarySyntaxKind, postfixUnarySyntaxKind);
195
+ return;
196
+ }
197
+ const hasAsConst = ts_morph_1.Node.isAsExpression(rawInitializer) &&
198
+ ((_a = rawInitializer.getTypeNode()) === null || _a === void 0 ? void 0 : _a.getText()) === "const";
199
+ const initializer = (0, unwrapInitializer_1.unwrapInitializer)(rawInitializer);
200
+ if (!initializer || !ts_morph_1.Node.isArrayLiteralExpression(initializer)) {
201
+ this._errorManager.addWarning(new ErrorManager_1.ErrorDescription(`Overload field for operator ${operatorString} ` +
202
+ "must be an array of overload functions.", property.getSourceFile().getFilePath(), property.getStartLineNumber(), this._minifyString(property.getName())));
203
+ return;
204
+ }
205
+ if (!hasAsConst) {
206
+ this._errorManager.addError(new ErrorManager_1.ErrorDescription(`Overload array for operator ${operatorString} must use "as const". ` +
207
+ "Without it, TypeScript widens the array type and loses individual " +
208
+ "function signatures, causing type errors in generated code.", property.getSourceFile().getFilePath(), property.getStartLineNumber(), this._minifyString((_b = property.getText().split("\n")[0]) !== null && _b !== void 0 ? _b : "")));
209
+ return;
210
+ }
211
+ initializer.getElements().forEach((element, index) => {
212
+ if (element.isKind(ts_morph_1.SyntaxKind.ArrowFunction) && !isStatic) {
213
+ this._errorManager.addError(new ErrorManager_1.ErrorDescription(`Overload ${index} for operator ${operatorString} must not be an arrow function. ` +
214
+ "Use a function expression instead, as arrow functions cannot bind `this` correctly for instance operators.", element.getSourceFile().getFilePath(), element.getStartLineNumber(), this._minifyString(element.getText())));
215
+ return;
216
+ }
217
+ if (!element.isKind(ts_morph_1.SyntaxKind.FunctionExpression) &&
218
+ !element.isKind(ts_morph_1.SyntaxKind.FunctionDeclaration) &&
219
+ !element.isKind(ts_morph_1.SyntaxKind.ArrowFunction)) {
220
+ this._errorManager.addWarning(new ErrorManager_1.ErrorDescription(`Expected overload ${index} for operator ${operatorString} to be a function.`, element.getSourceFile().getFilePath(), element.getStartLineNumber(), this._minifyString(element.getText())));
221
+ return;
222
+ }
223
+ // At this point element is guaranteed to be a function-like node
224
+ const funcElement = element;
225
+ // Exclude `this` pseudo-parameter from count
226
+ const parameters = funcElement
227
+ .getParameters()
228
+ .filter((p) => p.getName() !== "this");
229
+ const paramCount = parameters.length;
230
+ // Dispatch by parameter count to determine overload kind
231
+ if (paramCount === 2 &&
232
+ isStatic &&
233
+ binarySyntaxKind &&
234
+ !operatorMap_1.instanceOperators.has(binarySyntaxKind)) {
235
+ this._addBinaryOverload(binarySyntaxKind, classDecl, classType, filePath, property, funcElement, parameters, operatorString, index, true);
236
+ }
237
+ else if (paramCount === 1 &&
238
+ !isStatic &&
239
+ binarySyntaxKind &&
240
+ operatorMap_1.instanceOperators.has(binarySyntaxKind)) {
241
+ this._addBinaryOverload(binarySyntaxKind, classDecl, classType, filePath, property, funcElement, parameters, operatorString, index, false);
242
+ }
243
+ else if (paramCount === 1 && isStatic && prefixUnarySyntaxKind) {
244
+ this._addPrefixUnaryOverload(prefixUnarySyntaxKind, classDecl, classType, filePath, property, funcElement, parameters, operatorString, index);
245
+ }
246
+ else if (paramCount === 0 && !isStatic && postfixUnarySyntaxKind) {
247
+ this._addPostfixUnaryOverload(postfixUnarySyntaxKind, classDecl, classType, filePath, property, funcElement, operatorString, index);
248
+ }
249
+ else {
250
+ this._errorManager.addWarning(new ErrorManager_1.ErrorDescription(`Overload function ${index} for operator ${operatorString} ` +
251
+ `has invalid parameter count (${paramCount}) for this operator context.`, property.getSourceFile().getFilePath(), property.getStartLineNumber(), this._minifyString(funcElement.getText())));
252
+ }
253
+ });
254
+ });
255
+ });
256
+ }
257
+ _addBinaryOverload(syntaxKind, classDecl, classType, filePath, property, element, parameters, operatorString, index, isStatic) {
258
+ var _a, _b, _c, _d, _e;
259
+ let hasWarning = false;
260
+ const lhsType = isStatic ? (_a = parameters[0]) === null || _a === void 0 ? void 0 : _a.getType().getText() : classType;
261
+ const rhsType = isStatic
262
+ ? (_b = parameters[1]) === null || _b === void 0 ? void 0 : _b.getType().getText()
263
+ : (_c = parameters[0]) === null || _c === void 0 ? void 0 : _c.getType().getText();
264
+ if (isStatic && lhsType !== classType && rhsType !== classType) {
265
+ this._errorManager.addWarning(new ErrorManager_1.ErrorDescription(`Overload for operator ${operatorString} ` +
266
+ "must have either LHS or RHS parameter matching its class type.", property.getSourceFile().getFilePath(), property.getStartLineNumber(), this._minifyString(element.getText())));
267
+ hasWarning = true;
268
+ }
269
+ const returnType = element.getReturnType().getText();
270
+ if (operatorMap_1.comparisonOperators.has(syntaxKind) && returnType !== "boolean") {
271
+ this._errorManager.addWarning(new ErrorManager_1.ErrorDescription(`Overload function ${index} for comparison operator ${operatorString} ` +
272
+ `must have a return type of 'boolean', got '${returnType}'.`, property.getSourceFile().getFilePath(), property.getStartLineNumber(), this._minifyString(element.getText())));
273
+ hasWarning = true;
274
+ }
275
+ if (!isStatic && returnType !== "void") {
276
+ this._errorManager.addWarning(new ErrorManager_1.ErrorDescription(`Overload function ${index} for instance operator ${operatorString} ` +
277
+ `must have a return type of 'void', got '${returnType}'.`, property.getSourceFile().getFilePath(), property.getStartLineNumber(), this._minifyString(element.getText())));
278
+ hasWarning = true;
279
+ }
280
+ const operatorOverloads = (_d = this.get(syntaxKind)) !== null && _d !== void 0 ? _d : new Map();
281
+ const lhsMap = (_e = operatorOverloads.get(lhsType)) !== null && _e !== void 0 ? _e : new Map();
282
+ if (lhsMap.has(rhsType)) {
283
+ this._errorManager.addWarning(new ErrorManager_1.ErrorDescription(`Duplicate overload for operator ${operatorString} with LHS type ${lhsType} and RHS type ${rhsType}`, property.getSourceFile().getFilePath(), property.getStartLineNumber(), this._minifyString(element.getText())));
284
+ hasWarning = true;
285
+ }
286
+ if (hasWarning)
287
+ return;
288
+ lhsMap.set(rhsType, {
289
+ isStatic,
290
+ className: classDecl.getName(),
291
+ classFilePath: filePath,
292
+ operatorString,
293
+ index,
294
+ returnType,
295
+ });
296
+ operatorOverloads.set(lhsType, lhsMap);
297
+ this.set(syntaxKind, operatorOverloads);
298
+ const funcName = ts_morph_1.Node.isFunctionExpression(element)
299
+ ? element.getName()
300
+ : undefined;
301
+ const sl = this._shortTypeName.bind(this);
302
+ const label = funcName
303
+ ? `${funcName}(${sl(lhsType)}, ${sl(rhsType)})`
304
+ : `(${sl(lhsType)}, ${sl(rhsType)})`;
305
+ this._logger.debug(`Loaded ${classDecl.getName()}["${operatorString}"][${index}]: ${label} => ${sl(element.getReturnType().getText())}${isStatic ? " (static)" : " (instance)"}`);
306
+ let fileEntries = this._fileEntries.get(filePath);
307
+ if (!fileEntries) {
308
+ fileEntries = [];
309
+ this._fileEntries.set(filePath, fileEntries);
310
+ }
311
+ fileEntries.push({ syntaxKind, lhsType, rhsType });
312
+ }
313
+ _addPrefixUnaryOverload(syntaxKind, classDecl, classType, filePath, property, element, parameters, operatorString, index) {
314
+ var _a, _b;
315
+ let hasWarning = false;
316
+ const operandType = (_a = parameters[0]) === null || _a === void 0 ? void 0 : _a.getType().getText();
317
+ if (operandType !== classType) {
318
+ this._errorManager.addWarning(new ErrorManager_1.ErrorDescription(`Prefix unary overload for operator ${operatorString} ` +
319
+ "must have its parameter matching its class type.", property.getSourceFile().getFilePath(), property.getStartLineNumber(), this._minifyString(element.getText())));
320
+ hasWarning = true;
321
+ }
322
+ const operatorOverloads = (_b = this._prefixUnaryOverloads.get(syntaxKind)) !== null && _b !== void 0 ? _b : new Map();
323
+ if (operatorOverloads.has(operandType)) {
324
+ this._errorManager.addWarning(new ErrorManager_1.ErrorDescription(`Duplicate prefix unary overload for operator ${operatorString} with operand type ${operandType}`, property.getSourceFile().getFilePath(), property.getStartLineNumber(), this._minifyString(element.getText())));
325
+ hasWarning = true;
326
+ }
327
+ if (hasWarning)
328
+ return;
329
+ const returnType = element.getReturnType().getText();
330
+ operatorOverloads.set(operandType, {
331
+ isStatic: true,
332
+ className: classDecl.getName(),
333
+ classFilePath: filePath,
334
+ operatorString,
335
+ index,
336
+ returnType,
337
+ });
338
+ this._prefixUnaryOverloads.set(syntaxKind, operatorOverloads);
339
+ const funcName = ts_morph_1.Node.isFunctionExpression(element)
340
+ ? element.getName()
341
+ : undefined;
342
+ const sl = this._shortTypeName.bind(this);
343
+ const label = funcName
344
+ ? `${funcName}(${sl(operandType)})`
345
+ : `(${sl(operandType)})`;
346
+ this._logger.debug(`Loaded ${classDecl.getName()}["${operatorString}"][${index}]: ${operatorString}${label} => ${sl(returnType)} (prefix unary)`);
347
+ let fileEntries = this._prefixUnaryFileEntries.get(filePath);
348
+ if (!fileEntries) {
349
+ fileEntries = [];
350
+ this._prefixUnaryFileEntries.set(filePath, fileEntries);
351
+ }
352
+ fileEntries.push({ syntaxKind, operandType });
353
+ }
354
+ _addPostfixUnaryOverload(syntaxKind, classDecl, classType, filePath, property, element, operatorString, index) {
355
+ var _a;
356
+ let hasWarning = false;
357
+ const returnType = element.getReturnType().getText();
358
+ if (returnType !== "void") {
359
+ this._errorManager.addWarning(new ErrorManager_1.ErrorDescription(`Overload function ${index} for postfix operator ${operatorString} ` +
360
+ `must have a return type of 'void', got '${returnType}'.`, property.getSourceFile().getFilePath(), property.getStartLineNumber(), this._minifyString(element.getText())));
361
+ hasWarning = true;
362
+ }
363
+ const operandType = classType;
364
+ const operatorOverloads = (_a = this._postfixUnaryOverloads.get(syntaxKind)) !== null && _a !== void 0 ? _a : new Map();
365
+ if (operatorOverloads.has(operandType)) {
366
+ this._errorManager.addWarning(new ErrorManager_1.ErrorDescription(`Duplicate postfix unary overload for operator ${operatorString} with operand type ${operandType}`, property.getSourceFile().getFilePath(), property.getStartLineNumber(), this._minifyString(element.getText())));
367
+ hasWarning = true;
368
+ }
369
+ if (hasWarning)
370
+ return;
371
+ operatorOverloads.set(operandType, {
372
+ isStatic: false,
373
+ className: classDecl.getName(),
374
+ classFilePath: filePath,
375
+ operatorString,
376
+ index,
377
+ returnType,
378
+ });
379
+ this._postfixUnaryOverloads.set(syntaxKind, operatorOverloads);
380
+ const funcName = ts_morph_1.Node.isFunctionExpression(element)
381
+ ? element.getName()
382
+ : undefined;
383
+ const label = funcName ? `${funcName}()` : "()";
384
+ this._logger.debug(`Loaded ${classDecl.getName()}["${operatorString}"][${index}]: ${this._shortTypeName(operandType)}${operatorString} ${label} (postfix unary)`);
385
+ let fileEntries = this._postfixUnaryFileEntries.get(filePath);
386
+ if (!fileEntries) {
387
+ fileEntries = [];
388
+ this._postfixUnaryFileEntries.set(filePath, fileEntries);
389
+ }
390
+ fileEntries.push({ syntaxKind, operandType });
391
+ }
392
+ /**
393
+ * Extracts overload info from a property's type annotation instead of its
394
+ * initializer. This handles `.d.ts` declaration files where the array
395
+ * literal (`= [...] as const`) has been replaced by a readonly tuple type.
396
+ */
397
+ _addOverloadsFromTypeAnnotation(property, classDecl, classType, filePath, isStatic, operatorString, binarySyntaxKind, prefixUnarySyntaxKind, postfixUnarySyntaxKind) {
398
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
399
+ const propertyType = property.getType();
400
+ if (!propertyType.isTuple())
401
+ return;
402
+ const tupleElements = propertyType.getTupleElements();
403
+ const className = classDecl.getName();
404
+ if (!className)
405
+ return;
406
+ const sl = this._shortTypeName.bind(this);
407
+ for (let index = 0; index < tupleElements.length; index++) {
408
+ const elementType = tupleElements[index];
409
+ const callSigs = elementType.getCallSignatures();
410
+ if (callSigs.length === 0)
411
+ continue;
412
+ const sig = callSigs[0];
413
+ // Extract parameter types, filtering out the `this` pseudo-parameter
414
+ const params = [];
415
+ for (const sym of sig.getParameters()) {
416
+ const name = sym.getName();
417
+ if (name === "this")
418
+ continue;
419
+ const decl = sym.getValueDeclaration();
420
+ if (!decl)
421
+ continue;
422
+ params.push({ name, type: decl.getType().getText() });
423
+ }
424
+ const paramCount = params.length;
425
+ const returnType = sig.getReturnType().getText();
426
+ // --- Binary static ---
427
+ if (paramCount === 2 &&
428
+ isStatic &&
429
+ binarySyntaxKind &&
430
+ !operatorMap_1.instanceOperators.has(binarySyntaxKind)) {
431
+ const lhsType = params[0].type;
432
+ const rhsType = params[1].type;
433
+ if (lhsType !== classType && rhsType !== classType) {
434
+ this._errorManager.addWarning(new ErrorManager_1.ErrorDescription(`Overload for operator ${operatorString} ` +
435
+ "must have either LHS or RHS parameter matching its class type.", filePath, property.getStartLineNumber(), this._minifyString((_a = property.getText().split("\n")[0]) !== null && _a !== void 0 ? _a : "")));
436
+ continue;
437
+ }
438
+ if (operatorMap_1.comparisonOperators.has(binarySyntaxKind) &&
439
+ returnType !== "boolean") {
440
+ this._errorManager.addWarning(new ErrorManager_1.ErrorDescription(`Overload function ${index} for comparison operator ${operatorString} ` +
441
+ `must have a return type of 'boolean', got '${returnType}'.`, filePath, property.getStartLineNumber(), this._minifyString((_b = property.getText().split("\n")[0]) !== null && _b !== void 0 ? _b : "")));
442
+ continue;
443
+ }
444
+ const operatorOverloads = (_c = this.get(binarySyntaxKind)) !== null && _c !== void 0 ? _c : new Map();
445
+ const lhsMap = (_d = operatorOverloads.get(lhsType)) !== null && _d !== void 0 ? _d : new Map();
446
+ if (lhsMap.has(rhsType))
447
+ continue; // duplicate — skip silently for .d.ts
448
+ lhsMap.set(rhsType, {
449
+ isStatic: true,
450
+ className,
451
+ classFilePath: filePath,
452
+ operatorString,
453
+ index,
454
+ returnType,
455
+ });
456
+ operatorOverloads.set(lhsType, lhsMap);
457
+ this.set(binarySyntaxKind, operatorOverloads);
458
+ this._logger.debug(`Loaded ${className}["${operatorString}"][${index}]: (${sl(lhsType)}, ${sl(rhsType)}) => ${sl(returnType)} (static, from .d.ts)`);
459
+ let fileEntries = this._fileEntries.get(filePath);
460
+ if (!fileEntries) {
461
+ fileEntries = [];
462
+ this._fileEntries.set(filePath, fileEntries);
463
+ }
464
+ fileEntries.push({ syntaxKind: binarySyntaxKind, lhsType, rhsType });
465
+ }
466
+ // --- Binary instance (compound assignment) ---
467
+ else if (paramCount === 1 &&
468
+ !isStatic &&
469
+ binarySyntaxKind &&
470
+ operatorMap_1.instanceOperators.has(binarySyntaxKind)) {
471
+ const lhsType = classType;
472
+ const rhsType = params[0].type;
473
+ if (returnType !== "void") {
474
+ this._errorManager.addWarning(new ErrorManager_1.ErrorDescription(`Overload function ${index} for instance operator ${operatorString} ` +
475
+ `must have a return type of 'void', got '${returnType}'.`, filePath, property.getStartLineNumber(), this._minifyString((_e = property.getText().split("\n")[0]) !== null && _e !== void 0 ? _e : "")));
476
+ continue;
477
+ }
478
+ const operatorOverloads = (_f = this.get(binarySyntaxKind)) !== null && _f !== void 0 ? _f : new Map();
479
+ const lhsMap = (_g = operatorOverloads.get(lhsType)) !== null && _g !== void 0 ? _g : new Map();
480
+ if (lhsMap.has(rhsType))
481
+ continue;
482
+ lhsMap.set(rhsType, {
483
+ isStatic: false,
484
+ className,
485
+ classFilePath: filePath,
486
+ operatorString,
487
+ index,
488
+ returnType,
489
+ });
490
+ operatorOverloads.set(lhsType, lhsMap);
491
+ this.set(binarySyntaxKind, operatorOverloads);
492
+ this._logger.debug(`Loaded ${className}["${operatorString}"][${index}]: (${sl(lhsType)}, ${sl(rhsType)}) => void (instance, from .d.ts)`);
493
+ let fileEntries = this._fileEntries.get(filePath);
494
+ if (!fileEntries) {
495
+ fileEntries = [];
496
+ this._fileEntries.set(filePath, fileEntries);
497
+ }
498
+ fileEntries.push({ syntaxKind: binarySyntaxKind, lhsType, rhsType });
499
+ }
500
+ // --- Prefix unary ---
501
+ else if (paramCount === 1 && isStatic && prefixUnarySyntaxKind) {
502
+ const operandType = params[0].type;
503
+ if (operandType !== classType) {
504
+ this._errorManager.addWarning(new ErrorManager_1.ErrorDescription(`Prefix unary overload for operator ${operatorString} ` +
505
+ "must have its parameter matching its class type.", filePath, property.getStartLineNumber(), this._minifyString((_h = property.getText().split("\n")[0]) !== null && _h !== void 0 ? _h : "")));
506
+ continue;
507
+ }
508
+ const operatorOverloads = (_j = this._prefixUnaryOverloads.get(prefixUnarySyntaxKind)) !== null && _j !== void 0 ? _j : new Map();
509
+ if (operatorOverloads.has(operandType))
510
+ continue;
511
+ operatorOverloads.set(operandType, {
512
+ isStatic: true,
513
+ className,
514
+ classFilePath: filePath,
515
+ operatorString,
516
+ index,
517
+ returnType,
518
+ });
519
+ this._prefixUnaryOverloads.set(prefixUnarySyntaxKind, operatorOverloads);
520
+ this._logger.debug(`Loaded ${className}["${operatorString}"][${index}]: ${operatorString}(${sl(operandType)}) => ${sl(returnType)} (prefix unary, from .d.ts)`);
521
+ let fileEntries = this._prefixUnaryFileEntries.get(filePath);
522
+ if (!fileEntries) {
523
+ fileEntries = [];
524
+ this._prefixUnaryFileEntries.set(filePath, fileEntries);
525
+ }
526
+ fileEntries.push({ syntaxKind: prefixUnarySyntaxKind, operandType });
527
+ }
528
+ // --- Postfix unary ---
529
+ else if (paramCount === 0 && !isStatic && postfixUnarySyntaxKind) {
530
+ if (returnType !== "void") {
531
+ this._errorManager.addWarning(new ErrorManager_1.ErrorDescription(`Overload function ${index} for postfix operator ${operatorString} ` +
532
+ `must have a return type of 'void', got '${returnType}'.`, filePath, property.getStartLineNumber(), this._minifyString((_k = property.getText().split("\n")[0]) !== null && _k !== void 0 ? _k : "")));
533
+ continue;
534
+ }
535
+ const operandType = classType;
536
+ const operatorOverloads = (_l = this._postfixUnaryOverloads.get(postfixUnarySyntaxKind)) !== null && _l !== void 0 ? _l : new Map();
537
+ if (operatorOverloads.has(operandType))
538
+ continue;
539
+ operatorOverloads.set(operandType, {
540
+ isStatic: false,
541
+ className,
542
+ classFilePath: filePath,
543
+ operatorString,
544
+ index,
545
+ returnType,
546
+ });
547
+ this._postfixUnaryOverloads.set(postfixUnarySyntaxKind, operatorOverloads);
548
+ this._logger.debug(`Loaded ${className}["${operatorString}"][${index}]: ${sl(operandType)}${operatorString} () (postfix unary, from .d.ts)`);
549
+ let fileEntries = this._postfixUnaryFileEntries.get(filePath);
550
+ if (!fileEntries) {
551
+ fileEntries = [];
552
+ this._postfixUnaryFileEntries.set(filePath, fileEntries);
553
+ }
554
+ fileEntries.push({ syntaxKind: postfixUnarySyntaxKind, operandType });
555
+ }
556
+ }
557
+ }
558
+ /**
559
+ * Returns the type hierarchy chain for a given type name:
560
+ * [self, parent, grandparent, ...]. Primitives like "number"
561
+ * return a single-element array.
562
+ */
563
+ _getTypeChain(typeName) {
564
+ var _a, _b;
565
+ const cached = this._typeChainCache.get(typeName);
566
+ if (cached)
567
+ return cached;
568
+ const chain = [typeName];
569
+ // Extract simple class name from fully-qualified type strings
570
+ // e.g. 'import("C:/path/to/Foo").Foo' → 'Foo'
571
+ const simpleName = (_b = (_a = typeName.match(/\.(\w+)$/)) === null || _a === void 0 ? void 0 : _a[1]) !== null && _b !== void 0 ? _b : typeName;
572
+ for (const sourceFile of this._project.getSourceFiles()) {
573
+ const classDecl = sourceFile.getClass(simpleName);
574
+ if (classDecl) {
575
+ let current = classDecl.getBaseClass();
576
+ while (current) {
577
+ chain.push(current.getType().getText());
578
+ current = current.getBaseClass();
579
+ }
580
+ break;
581
+ }
582
+ }
583
+ this._typeChainCache.set(typeName, chain);
584
+ return chain;
585
+ }
586
+ _minifyString(str) {
587
+ return str.replace(/\s+/g, " ").replace("\n", "").trim();
588
+ }
589
+ /** Strips `import("...").` prefixes from fully-qualified type names for readable logs. */
590
+ _shortTypeName(typeName) {
591
+ return typeName.replace(/import\("[^"]*"\)\./g, "");
592
+ }
593
+ toString() {
594
+ let str = "";
595
+ for (const [operatorSyntaxKind, lhsMap] of this) {
596
+ str += `\n\nBinary Operator: ${ts_morph_1.SyntaxKind[operatorSyntaxKind]}`;
597
+ for (const [lhsType, rhsMap] of lhsMap) {
598
+ str += `\n - LHS Type: ${lhsType}`;
599
+ for (const [rhsType, overload] of rhsMap) {
600
+ str += `\n - RHS Type: ${rhsType}`;
601
+ str += `\n Overload: ${JSON.stringify(overload)}`;
602
+ }
603
+ }
604
+ }
605
+ for (const [syntaxKind, operandMap] of this._prefixUnaryOverloads) {
606
+ str += `\n\nPrefix Unary Operator: ${ts_morph_1.SyntaxKind[syntaxKind]}`;
607
+ for (const [operandType, overload] of operandMap) {
608
+ str += `\n - Operand Type: ${operandType}`;
609
+ str += `\n Overload: ${JSON.stringify(overload)}`;
610
+ }
611
+ }
612
+ for (const [syntaxKind, operandMap] of this._postfixUnaryOverloads) {
613
+ str += `\n\nPostfix Unary Operator: ${ts_morph_1.SyntaxKind[syntaxKind]}`;
614
+ for (const [operandType, overload] of operandMap) {
615
+ str += `\n - Operand Type: ${operandType}`;
616
+ str += `\n Overload: ${JSON.stringify(overload)}`;
617
+ }
618
+ }
619
+ return str;
620
+ }
621
+ }
622
+ exports.OverloadStore = OverloadStore;