backtest-kit 2.0.4 → 2.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/index.cjs CHANGED
@@ -9918,6 +9918,83 @@ const VALID_METHOD_NAMES = [
9918
9918
  "riskRejection",
9919
9919
  "dispose",
9920
9920
  ];
9921
+ /**
9922
+ * Calculates the Levenshtein distance between two strings.
9923
+ *
9924
+ * Levenshtein distance is the minimum number of single-character edits
9925
+ * (insertions, deletions, or substitutions) required to change one string into another.
9926
+ * Used to find typos and similar method names in validation.
9927
+ *
9928
+ * @param str1 - First string to compare
9929
+ * @param str2 - Second string to compare
9930
+ * @returns Number of edits needed to transform str1 into str2
9931
+ */
9932
+ const LEVENSHTEIN_DISTANCE = (str1, str2) => {
9933
+ const len1 = str1.length;
9934
+ const len2 = str2.length;
9935
+ // Create a 2D array for dynamic programming
9936
+ const matrix = Array.from({ length: len1 + 1 }, () => Array(len2 + 1).fill(0));
9937
+ // Initialize first column and row
9938
+ for (let i = 0; i <= len1; i++) {
9939
+ matrix[i][0] = i;
9940
+ }
9941
+ for (let j = 0; j <= len2; j++) {
9942
+ matrix[0][j] = j;
9943
+ }
9944
+ // Fill the matrix
9945
+ for (let i = 1; i <= len1; i++) {
9946
+ for (let j = 1; j <= len2; j++) {
9947
+ const cost = str1[i - 1] === str2[j - 1] ? 0 : 1;
9948
+ matrix[i][j] = Math.min(matrix[i - 1][j] + 1, // deletion
9949
+ matrix[i][j - 1] + 1, // insertion
9950
+ matrix[i - 1][j - 1] + cost // substitution
9951
+ );
9952
+ }
9953
+ }
9954
+ return matrix[len1][len2];
9955
+ };
9956
+ /**
9957
+ * Finds suggestions for a method name based on similarity scoring.
9958
+ *
9959
+ * Uses Levenshtein distance and partial string matching to find similar method names.
9960
+ * Returns suggestions sorted by similarity (most similar first).
9961
+ * Used to provide helpful "Did you mean?" suggestions in validation error messages.
9962
+ *
9963
+ * @param methodName - The invalid method name to find suggestions for
9964
+ * @param validNames - List of valid method names to search through
9965
+ * @param maxDistance - Maximum Levenshtein distance to consider (default: 3)
9966
+ * @returns Array of suggested method names sorted by similarity
9967
+ */
9968
+ const FIND_SUGGESTIONS = (methodName, validNames, maxDistance = 3) => {
9969
+ const lowerMethodName = methodName.toLowerCase();
9970
+ // Calculate similarity score for each valid name
9971
+ const suggestions = validNames
9972
+ .map((validName) => {
9973
+ const lowerValidName = validName.toLowerCase();
9974
+ const distance = LEVENSHTEIN_DISTANCE(lowerMethodName, lowerValidName);
9975
+ // Check for partial matches
9976
+ const hasPartialMatch = lowerValidName.includes(lowerMethodName) ||
9977
+ lowerMethodName.includes(lowerValidName);
9978
+ return {
9979
+ name: validName,
9980
+ distance,
9981
+ hasPartialMatch,
9982
+ };
9983
+ })
9984
+ .filter((item) => item.distance <= maxDistance || item.hasPartialMatch)
9985
+ .sort((a, b) => {
9986
+ // Prioritize partial matches
9987
+ if (a.hasPartialMatch && !b.hasPartialMatch)
9988
+ return -1;
9989
+ if (!a.hasPartialMatch && b.hasPartialMatch)
9990
+ return 1;
9991
+ // Then sort by distance
9992
+ return a.distance - b.distance;
9993
+ })
9994
+ .slice(0, 3) // Limit to top 3 suggestions
9995
+ .map((item) => item.name);
9996
+ return suggestions;
9997
+ };
9921
9998
  /**
9922
9999
  * Validates that all public methods in a class-based action handler are in the allowed list.
9923
10000
  *
@@ -9944,7 +10021,18 @@ const VALIDATE_CLASS_METHODS = (actionName, handler, self) => {
9944
10021
  const descriptor = Object.getOwnPropertyDescriptor(handler.prototype, methodName);
9945
10022
  const isMethod = descriptor && typeof descriptor.value === "function";
9946
10023
  if (isMethod && !VALID_METHOD_NAMES.includes(methodName)) {
9947
- const msg = functoolsKit.str.newline(`ActionSchema ${actionName} contains invalid method "${methodName}". `, `Valid methods are: ${VALID_METHOD_NAMES.join(", ")}`, `If you want to keep this property name use one of these patterns: _${methodName} or #${methodName}`);
10024
+ const suggestions = FIND_SUGGESTIONS(methodName, VALID_METHOD_NAMES);
10025
+ const lines = [
10026
+ `ActionSchema ${actionName} contains invalid method "${methodName}". `,
10027
+ `Valid methods are: ${VALID_METHOD_NAMES.join(", ")}`,
10028
+ ];
10029
+ if (suggestions.length > 0) {
10030
+ lines.push("");
10031
+ lines.push(`Do you mean: ${suggestions.join(", ")}?`);
10032
+ lines.push("");
10033
+ }
10034
+ lines.push(`If you want to keep this property name use one of these patterns: _${methodName} or #${methodName}`);
10035
+ const msg = functoolsKit.str.newline(lines);
9948
10036
  self.loggerService.log(`actionValidationService exception thrown`, {
9949
10037
  msg,
9950
10038
  });
@@ -9973,7 +10061,18 @@ const VALIDATE_OBJECT_METHODS = (actionName, handler, self) => {
9973
10061
  }
9974
10062
  if (typeof handler[methodName] === "function" &&
9975
10063
  !VALID_METHOD_NAMES.includes(methodName)) {
9976
- const msg = functoolsKit.str.newline(`ActionSchema ${actionName} contains invalid method "${methodName}". `, `Valid methods are: ${VALID_METHOD_NAMES.join(", ")}`, `If you want to keep this property name use one of these patterns: _${methodName} or #${methodName}`);
10064
+ const suggestions = FIND_SUGGESTIONS(methodName, VALID_METHOD_NAMES);
10065
+ const lines = [
10066
+ `ActionSchema ${actionName} contains invalid method "${methodName}". `,
10067
+ `Valid methods are: ${VALID_METHOD_NAMES.join(", ")}`,
10068
+ ];
10069
+ if (suggestions.length > 0) {
10070
+ lines.push("");
10071
+ lines.push(`Do you mean: ${suggestions.join(", ")}?`);
10072
+ lines.push("");
10073
+ }
10074
+ lines.push(`If you want to keep this property name use one of these patterns: _${methodName} or #${methodName}`);
10075
+ const msg = functoolsKit.str.newline(lines);
9977
10076
  self.loggerService.log(`actionValidationService exception thrown`, {
9978
10077
  msg,
9979
10078
  });
package/build/index.mjs CHANGED
@@ -9898,6 +9898,83 @@ const VALID_METHOD_NAMES = [
9898
9898
  "riskRejection",
9899
9899
  "dispose",
9900
9900
  ];
9901
+ /**
9902
+ * Calculates the Levenshtein distance between two strings.
9903
+ *
9904
+ * Levenshtein distance is the minimum number of single-character edits
9905
+ * (insertions, deletions, or substitutions) required to change one string into another.
9906
+ * Used to find typos and similar method names in validation.
9907
+ *
9908
+ * @param str1 - First string to compare
9909
+ * @param str2 - Second string to compare
9910
+ * @returns Number of edits needed to transform str1 into str2
9911
+ */
9912
+ const LEVENSHTEIN_DISTANCE = (str1, str2) => {
9913
+ const len1 = str1.length;
9914
+ const len2 = str2.length;
9915
+ // Create a 2D array for dynamic programming
9916
+ const matrix = Array.from({ length: len1 + 1 }, () => Array(len2 + 1).fill(0));
9917
+ // Initialize first column and row
9918
+ for (let i = 0; i <= len1; i++) {
9919
+ matrix[i][0] = i;
9920
+ }
9921
+ for (let j = 0; j <= len2; j++) {
9922
+ matrix[0][j] = j;
9923
+ }
9924
+ // Fill the matrix
9925
+ for (let i = 1; i <= len1; i++) {
9926
+ for (let j = 1; j <= len2; j++) {
9927
+ const cost = str1[i - 1] === str2[j - 1] ? 0 : 1;
9928
+ matrix[i][j] = Math.min(matrix[i - 1][j] + 1, // deletion
9929
+ matrix[i][j - 1] + 1, // insertion
9930
+ matrix[i - 1][j - 1] + cost // substitution
9931
+ );
9932
+ }
9933
+ }
9934
+ return matrix[len1][len2];
9935
+ };
9936
+ /**
9937
+ * Finds suggestions for a method name based on similarity scoring.
9938
+ *
9939
+ * Uses Levenshtein distance and partial string matching to find similar method names.
9940
+ * Returns suggestions sorted by similarity (most similar first).
9941
+ * Used to provide helpful "Did you mean?" suggestions in validation error messages.
9942
+ *
9943
+ * @param methodName - The invalid method name to find suggestions for
9944
+ * @param validNames - List of valid method names to search through
9945
+ * @param maxDistance - Maximum Levenshtein distance to consider (default: 3)
9946
+ * @returns Array of suggested method names sorted by similarity
9947
+ */
9948
+ const FIND_SUGGESTIONS = (methodName, validNames, maxDistance = 3) => {
9949
+ const lowerMethodName = methodName.toLowerCase();
9950
+ // Calculate similarity score for each valid name
9951
+ const suggestions = validNames
9952
+ .map((validName) => {
9953
+ const lowerValidName = validName.toLowerCase();
9954
+ const distance = LEVENSHTEIN_DISTANCE(lowerMethodName, lowerValidName);
9955
+ // Check for partial matches
9956
+ const hasPartialMatch = lowerValidName.includes(lowerMethodName) ||
9957
+ lowerMethodName.includes(lowerValidName);
9958
+ return {
9959
+ name: validName,
9960
+ distance,
9961
+ hasPartialMatch,
9962
+ };
9963
+ })
9964
+ .filter((item) => item.distance <= maxDistance || item.hasPartialMatch)
9965
+ .sort((a, b) => {
9966
+ // Prioritize partial matches
9967
+ if (a.hasPartialMatch && !b.hasPartialMatch)
9968
+ return -1;
9969
+ if (!a.hasPartialMatch && b.hasPartialMatch)
9970
+ return 1;
9971
+ // Then sort by distance
9972
+ return a.distance - b.distance;
9973
+ })
9974
+ .slice(0, 3) // Limit to top 3 suggestions
9975
+ .map((item) => item.name);
9976
+ return suggestions;
9977
+ };
9901
9978
  /**
9902
9979
  * Validates that all public methods in a class-based action handler are in the allowed list.
9903
9980
  *
@@ -9924,7 +10001,18 @@ const VALIDATE_CLASS_METHODS = (actionName, handler, self) => {
9924
10001
  const descriptor = Object.getOwnPropertyDescriptor(handler.prototype, methodName);
9925
10002
  const isMethod = descriptor && typeof descriptor.value === "function";
9926
10003
  if (isMethod && !VALID_METHOD_NAMES.includes(methodName)) {
9927
- const msg = str.newline(`ActionSchema ${actionName} contains invalid method "${methodName}". `, `Valid methods are: ${VALID_METHOD_NAMES.join(", ")}`, `If you want to keep this property name use one of these patterns: _${methodName} or #${methodName}`);
10004
+ const suggestions = FIND_SUGGESTIONS(methodName, VALID_METHOD_NAMES);
10005
+ const lines = [
10006
+ `ActionSchema ${actionName} contains invalid method "${methodName}". `,
10007
+ `Valid methods are: ${VALID_METHOD_NAMES.join(", ")}`,
10008
+ ];
10009
+ if (suggestions.length > 0) {
10010
+ lines.push("");
10011
+ lines.push(`Do you mean: ${suggestions.join(", ")}?`);
10012
+ lines.push("");
10013
+ }
10014
+ lines.push(`If you want to keep this property name use one of these patterns: _${methodName} or #${methodName}`);
10015
+ const msg = str.newline(lines);
9928
10016
  self.loggerService.log(`actionValidationService exception thrown`, {
9929
10017
  msg,
9930
10018
  });
@@ -9953,7 +10041,18 @@ const VALIDATE_OBJECT_METHODS = (actionName, handler, self) => {
9953
10041
  }
9954
10042
  if (typeof handler[methodName] === "function" &&
9955
10043
  !VALID_METHOD_NAMES.includes(methodName)) {
9956
- const msg = str.newline(`ActionSchema ${actionName} contains invalid method "${methodName}". `, `Valid methods are: ${VALID_METHOD_NAMES.join(", ")}`, `If you want to keep this property name use one of these patterns: _${methodName} or #${methodName}`);
10044
+ const suggestions = FIND_SUGGESTIONS(methodName, VALID_METHOD_NAMES);
10045
+ const lines = [
10046
+ `ActionSchema ${actionName} contains invalid method "${methodName}". `,
10047
+ `Valid methods are: ${VALID_METHOD_NAMES.join(", ")}`,
10048
+ ];
10049
+ if (suggestions.length > 0) {
10050
+ lines.push("");
10051
+ lines.push(`Do you mean: ${suggestions.join(", ")}?`);
10052
+ lines.push("");
10053
+ }
10054
+ lines.push(`If you want to keep this property name use one of these patterns: _${methodName} or #${methodName}`);
10055
+ const msg = str.newline(lines);
9957
10056
  self.loggerService.log(`actionValidationService exception thrown`, {
9958
10057
  msg,
9959
10058
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "backtest-kit",
3
- "version": "2.0.4",
3
+ "version": "2.0.5",
4
4
  "description": "A TypeScript library for trading system backtest",
5
5
  "author": {
6
6
  "name": "Petr Tripolsky",