ondc-code-generator 0.4.6 → 0.5.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 (30) hide show
  1. package/dist/constants/syntax.d.ts +1 -1
  2. package/dist/constants/syntax.js +98 -2
  3. package/dist/generator/config-compiler.js +7 -3
  4. package/dist/generator/generators/python/py-ast.d.ts +1 -0
  5. package/dist/generator/generators/python/py-ast.js +72 -0
  6. package/dist/generator/generators/python/py-generator.d.ts +19 -0
  7. package/dist/generator/generators/python/py-generator.js +247 -1
  8. package/dist/generator/generators/python/templates/api-test.mustache +29 -0
  9. package/dist/generator/generators/python/templates/api-tests-init.mustache +12 -0
  10. package/dist/generator/generators/python/templates/json-normalizer.mustache +86 -0
  11. package/dist/generator/generators/python/templates/json-path-utils.mustache +33 -0
  12. package/dist/generator/generators/python/templates/master-doc.mustache +40 -0
  13. package/dist/generator/generators/python/templates/requirements.mustache +2 -0
  14. package/dist/generator/generators/python/templates/test-config.mustache +62 -0
  15. package/dist/generator/generators/python/templates/test-object.mustache +29 -0
  16. package/dist/generator/generators/python/templates/validation-code.mustache +35 -0
  17. package/dist/generator/generators/python/templates/validation-utils.mustache +93 -0
  18. package/dist/generator/generators/typescript/templates/api-test.mustache +29 -1
  19. package/dist/generator/generators/typescript/templates/index.mustache +33 -0
  20. package/dist/generator/generators/typescript/templates/test-config.mustache +45 -5
  21. package/dist/generator/generators/typescript/templates/test-object.mustache +11 -1
  22. package/dist/generator/generators/typescript/templates/validation-code.mustache +12 -12
  23. package/dist/generator/generators/typescript/ts-generator.js +25 -13
  24. package/dist/generator/validators/tests-config/sub-validations.js +3 -3
  25. package/dist/index.js +15 -2
  26. package/dist/types/compiler-types.d.ts +2 -1
  27. package/dist/types/compiler-types.js +1 -0
  28. package/dist/utils/fs-utils.js +43 -0
  29. package/dist/utils/general-utils/string-utils.js +2 -2
  30. package/package.json +1 -1
@@ -0,0 +1,40 @@
1
+ """
2
+ Perform Level-1 validations for a given `action`.
3
+
4
+ Args:
5
+ action: string identifier of the action to validate against.
6
+ payload: Any JSON-like structure to validate (dict/list/nested).
7
+ config: Partial configuration. Can be:
8
+ - a plain dict (snake_case or camelCase keys),
9
+ - a dataclass/pydantic/object with fields,
10
+ - or a `ValidationConfig` TypedDict.
11
+ Missing fields are filled with defaults.
12
+ external_data: Optional dict of extra data available to rules.
13
+ `_SELF` is automatically set to the normalized payload.
14
+
15
+ Config fields (merged with defaults):
16
+ - only_invalid (bool, default: True)
17
+ - hide_parent_errors (bool, default: True)
18
+ - _debug (bool, default: False)
19
+
20
+ Returns:
21
+ ValidationOutput: a list of validation results, each shaped like:
22
+ {
23
+ "test_name": str,
24
+ "valid": bool,
25
+ "code": int,
26
+ "description"?: str,
27
+ "_debug_info"?: {
28
+ "fed_config"?: str,
29
+ "output_code"?: Any
30
+ }
31
+ }
32
+
33
+ Raises:
34
+ ValueError: if the action is unknown.
35
+
36
+ Example:
37
+ >>> out = perform_l1_validations("search", payload, {"only_invalid": False})
38
+ >>> out[0]["test_name"]
39
+ 'REGEX_CONTEXT_LOCATION_CITY_CODE'
40
+ """
@@ -0,0 +1,2 @@
1
+ # requirements.txt
2
+ jsonpath-ng>=1.6.0
@@ -0,0 +1,62 @@
1
+ from typing import Dict, Any, List
2
+ from typing import TypedDict, Optional
3
+
4
+
5
+ # External data type definitions
6
+ ExternalData = Dict[str, Any]
7
+
8
+
9
+ class ValidationConfig(TypedDict, total=False):
10
+ """Config options for validations.
11
+ Attributes:
12
+ only_invalid (bool): If True, only invalid results will be returned.
13
+ hide_parent_errors (Optional[bool]): If True, parent errors will be hidden.
14
+ _debug (Optional[bool]): If True, debug mode will be enabled.
15
+ """
16
+ only_invalid: Optional[bool]
17
+ hide_parent_errors: Optional[bool]
18
+ _debug: Optional[bool]
19
+
20
+ # Input structure for validation functions
21
+ ValidationInput = Dict[str, Any]
22
+
23
+ class DebugInfo(TypedDict, total=False):
24
+ """
25
+ Diagnostic information useful for debugging.
26
+
27
+ Attributes:
28
+ fed_config: The configuration used to generate the validation.
29
+ output_code: The identifier/code of the validation rule that was executed.
30
+ """
31
+ fed_config: Any
32
+ output_code: Any
33
+
34
+
35
+ class ValidationResult(TypedDict, total=False):
36
+ """
37
+ Represents the output of a single validation test.
38
+
39
+ Attributes:
40
+ test_name: The name of the validation test.
41
+ valid: Whether the test passed (True) or failed (False).
42
+ code: Numeric code representing the result or error type.
43
+ description: Optional. Additional details about the test result.
44
+ _debug_info: Optional. Diagnostic information useful for debugging.
45
+ """
46
+ test_name: str
47
+ valid: bool
48
+ code: int
49
+ description: Optional[str]
50
+ _debug_info: Optional[DebugInfo]
51
+
52
+
53
+ # Represents the output of a validation run:
54
+ # a list of individual validation results.
55
+ ValidationOutput = List[ValidationResult]
56
+
57
+ # Test function array type
58
+ TestFunctionArray = List[callable]
59
+
60
+ {{#externalData}}
61
+ # {{name}}: Any
62
+ {{/externalData}}
@@ -0,0 +1,29 @@
1
+ def {{name}}(input_data):
2
+ scope = payload_utils["get_json_path"](input_data["payload"], "{{{scopePath}}}")
3
+ sub_results = []
4
+ valid = True
5
+
6
+ for {{testName}}_obj in scope:
7
+ {{testName}}_obj["_EXTERNAL"] = input_data["external_data"]
8
+ {{#variables}}
9
+ {{name}} = {{{value}}}
10
+ {{/variables}}
11
+
12
+ {{#hasContinue}}
13
+ skip_check = {{{skipCheckStatement}}}
14
+ if skip_check:
15
+ continue
16
+ {{/hasContinue}}
17
+
18
+ {{{validationCode}}}
19
+ # del {{testName}}_obj["_EXTERNAL"]
20
+
21
+ return [{
22
+ "test_name": "{{testName}}",
23
+ "valid": valid,
24
+ "code": {{successCode}} if valid else {{errorCode}},
25
+ "_debug_info": {
26
+ "fed_config": r"""
27
+ {{{TEST_OBJECT}}}
28
+ """
29
+ }}] + sub_results
@@ -0,0 +1,35 @@
1
+ {{#isNested}}
2
+ {{{nestedFunctions}}}
3
+
4
+ test_functions = [
5
+ {{#names}}
6
+ {{name}},
7
+ {{/names}}
8
+ ]
9
+
10
+ all_results = []
11
+ for fn in test_functions:
12
+ sub_result = fn(input_data)
13
+ all_results.extend(sub_result)
14
+
15
+
16
+ sub_results = all_results
17
+ valid = all(r["valid"] for r in sub_results)
18
+ {{/isNested}}
19
+ {{^isNested}}
20
+ validate = {{{returnStatement}}}
21
+
22
+ if not validate:
23
+ del {{testName}}_obj["_EXTERNAL"]
24
+ return [{
25
+ "test_name": "{{testName}}",
26
+ "valid": False,
27
+ "code": {{errorCode}},
28
+ "description": r"""{{{errorDescription}}}""",
29
+ "_debug_info": {
30
+ "fed_config": r"""
31
+ {{{TEST_OBJECT}}}
32
+ """
33
+ }
34
+ }]
35
+ {{/isNested}}
@@ -0,0 +1,93 @@
1
+ import re
2
+ from datetime import datetime
3
+ from typing import List
4
+
5
+ def are_unique(operand: List[str]) -> bool:
6
+ return len(set(operand)) == len(operand)
7
+
8
+ def are_present(operand: List[str]) -> bool:
9
+ return none_in(operand, ["null", "undefined", None]) and len(operand) > 0
10
+
11
+ def all_in(left: List[str], right: List[str]) -> bool:
12
+ if len(left) == 0 and len(right) != 0:
13
+ return False
14
+ return all(v in right for v in left)
15
+
16
+ def any_in(left: List[str], right: List[str]) -> bool:
17
+ if len(left) == 0 and len(right) != 0:
18
+ return False
19
+ return any(v in right for v in left)
20
+
21
+ def none_in(left: List[str], right: List[str]) -> bool:
22
+ return all(v not in right for v in left)
23
+
24
+ def equal_to(left: List[str], right: List[str]) -> bool:
25
+ if len(left) != len(right):
26
+ return False
27
+ return all(v == right[i] for i, v in enumerate(left))
28
+
29
+ def is_iso8601(s: str) -> bool:
30
+ iso8601_regex = r'^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$'
31
+ if not re.match(iso8601_regex, s):
32
+ return False
33
+ try:
34
+ datetime.fromisoformat(s.replace('Z', '+00:00'))
35
+ return True
36
+ except ValueError:
37
+ return False
38
+
39
+ def greater_than(left: List[str], right: List[str]) -> bool:
40
+ def are_all_iso(arr): return all(is_iso8601(v) for v in arr)
41
+ def are_all_numbers(arr): return all(is_number(v) for v in arr)
42
+
43
+ if are_all_iso(left) and are_all_iso(right):
44
+ left_dates = [datetime.fromisoformat(d.replace('Z', '+00:00')).timestamp() for d in left]
45
+ right_dates = [datetime.fromisoformat(d.replace('Z', '+00:00')).timestamp() for d in right]
46
+ return all(ld > right_dates[i] if i < len(right_dates) else True for i, ld in enumerate(left_dates))
47
+ elif are_all_numbers(left) and are_all_numbers(right):
48
+ left_numbers = [float(n) for n in left]
49
+ right_numbers = [float(n) for n in right]
50
+ return all(ln > right_numbers[i] if i < len(right_numbers) else True for i, ln in enumerate(left_numbers))
51
+ return False
52
+
53
+ def less_than(left: List[str], right: List[str]) -> bool:
54
+ def are_all_iso(arr): return all(is_iso8601(v) for v in arr)
55
+ def are_all_numbers(arr): return all(is_number(v) for v in arr)
56
+
57
+ if are_all_iso(left) and are_all_iso(right):
58
+ left_dates = [datetime.fromisoformat(d.replace('Z', '+00:00')).timestamp() for d in left]
59
+ right_dates = [datetime.fromisoformat(d.replace('Z', '+00:00')).timestamp() for d in right]
60
+ return all(ld < right_dates[i] if i < len(right_dates) else True for i, ld in enumerate(left_dates))
61
+ elif are_all_numbers(left) and are_all_numbers(right):
62
+ left_numbers = [float(n) for n in left]
63
+ right_numbers = [float(n) for n in right]
64
+ return all(ln < right_numbers[i] if i < len(right_numbers) else True for i, ln in enumerate(left_numbers))
65
+ return False
66
+
67
+ def follow_regex(left: List[str], regex_array: List[str]) -> bool:
68
+ if len(left) == 0 and len(regex_array) != 0:
69
+ return False
70
+ for regex in regex_array:
71
+ re_obj = re.compile(regex)
72
+ if any(not re_obj.match(v) for v in left):
73
+ return False
74
+ return True
75
+
76
+ def is_number(s: str) -> bool:
77
+ try:
78
+ float(s)
79
+ return True
80
+ except ValueError:
81
+ return False
82
+
83
+ validation_utils = {
84
+ "are_unique": are_unique,
85
+ "are_present": are_present,
86
+ "all_in": all_in,
87
+ "any_in": any_in,
88
+ "none_in": none_in,
89
+ "equal_to": equal_to,
90
+ "follow_regex": follow_regex,
91
+ "greater_than": greater_than,
92
+ "less_than": less_than,
93
+ }
@@ -4,4 +4,32 @@ import { testFunctionArray, validationInput, validationOutput } from "../types/t
4
4
 
5
5
 
6
6
 
7
- export default {{{functionCode}}}
7
+ export default function {{apiName}}(input: validationInput): validationOutput{
8
+
9
+ let totalResults = {{apiName}}Validations(input);
10
+
11
+ if (input.config._debug === false) {
12
+ totalResults.forEach((r) => {
13
+ delete r._debugInfo;
14
+ });
15
+ }
16
+ if(input.config.hideParentErrors === true) {
17
+ // delete results with valid false and no description
18
+ totalResults = totalResults.filter((r) => !(r.valid === false && !r.description));
19
+ }
20
+ if (input.config.onlyInvalid === true) {
21
+ const res = totalResults.filter((r) => r.valid === false);
22
+ if(res.length === 0) {
23
+ const targetSuccess = totalResults.find((r) => r.testName === "{{apiName}}_validations");
24
+ if(!targetSuccess) {
25
+ throw new Error("Critical: Overall test result not found");
26
+ }
27
+ return [targetSuccess];
28
+ }
29
+ return res;
30
+ }
31
+
32
+ return totalResults;
33
+ }
34
+
35
+ {{{functionCode}}}
@@ -0,0 +1,33 @@
1
+ {{{importsCode}}}
2
+
3
+ /**
4
+ * Perform Level-1 validations against a payload for a given action.
5
+ *
6
+ * @remarks
7
+ * **Output shape** — {@link validationOutput} is an array of:
8
+ * - `testName: string`
9
+ * - `valid: boolean`
10
+ * - `code: number`
11
+ * - `description?: string`
12
+ * - `_debugInfo?: { fedConfig?: string; outputCode?: unknown }`
13
+ *
14
+ * **Config** — {@link ValidationConfig} (partial accepted here):
15
+ * - `onlyInvalid` (default `true`)
16
+ * - `hideParentErrors` (default `true`)
17
+ * - `_debug` (default `false`)
18
+ *
19
+ * @param action - One of {@link Action}.
20
+ * @param payload - The JSON payload to validate.
21
+ * @param config - Partial {@link ValidationConfig}. Merged with defaults.
22
+ * @param externalData - Extra data accessible to rules (we set `_SELF` to the normalized payload).
23
+ * @returns {@link validationOutput}
24
+ *
25
+ * @example
26
+ * ```ts
27
+ * import { performL1_validations } from "@your/pkg";
28
+ *
29
+ * const out = performL1_validations("search", payload, { onlyInvalid: false });
30
+ * // e.g. out[0] => { testName, valid, code, description?, _debugInfo? }
31
+ * ```
32
+ */
33
+ {{{masterFunction}}}
@@ -1,14 +1,54 @@
1
-
1
+ /**
2
+ * Configuration options for running validation routines.
3
+ *
4
+ * @property onlyInvalid - If true, only invalid results will be returned.
5
+ * @property hideParentErrors - Optional. Hides nested error details if set to true.
6
+ * @property _debug - Optional. Enables debug mode for additional diagnostic information.
7
+ */
2
8
  export interface ValidationConfig {
3
- runAllValidations: boolean;
9
+ onlyInvalid: boolean;
10
+ hideParentErrors: boolean;
11
+ _debug: boolean;
4
12
  }
5
13
 
14
+
15
+ /**
16
+ * Represents the output of a validation run.
17
+ *
18
+ * Each element in the array corresponds to a single validation test result.
19
+ *
20
+ * Object shape:
21
+ * ```ts
22
+ * {
23
+ * testName: string;
24
+ * valid: boolean;
25
+ * code: number;
26
+ * description?: string;
27
+ * _debugInfo?: {
28
+ * fedConfig: any;
29
+ * };
30
+ * }
31
+ * ```
32
+ *
33
+ * ### Properties
34
+ * - **testName** — The name of the validation test.
35
+ * - **valid** — Whether the test passed (`true`) or failed (`false`).
36
+ * - **code** — Numeric code representing the result or error type.
37
+ * - **description** — *(Optional)* Additional details about the test result.
38
+ * - **_debugInfo** — *(Optional)* Diagnostic information useful for debugging.
39
+ * - **fedConfig** — The configuration used to generate the validation.
40
+ */
6
41
  export type validationOutput = {
7
- valid: boolean;
8
- code: number;
9
- description?: string;
42
+ testName: string;
43
+ valid: boolean;
44
+ code: number;
45
+ description?: string;
46
+ _debugInfo?: {
47
+ fedConfig: any;
48
+ };
10
49
  }[];
11
50
 
51
+
12
52
  /*
13
53
  {% comment %} export type ExternalData = {
14
54
  {{#externalData}}
@@ -16,5 +16,15 @@ function {{name}}(input: validationInput): validationOutput {
16
16
  {{{validationCode}}}
17
17
  delete testObj._EXTERNAL;
18
18
  }
19
- return [{valid: valid,code: {{successCode}} },...subResults];
19
+ return [{
20
+ testName: "{{testName}}",
21
+ valid: valid,
22
+ code: valid ? {{successCode}} : {{errorCode}},
23
+ _debugInfo: {
24
+ fedConfig:
25
+ `
26
+ {{{TEST_OBJECT}}}
27
+ `,
28
+ }
29
+ },...subResults];
20
30
  }
@@ -9,20 +9,13 @@ const testFunctions: testFunctionArray = [
9
9
  {{/names}}
10
10
  ];
11
11
 
12
- let invalidResults: validationOutput = [];
12
+ let allResults : validationOutput = [];
13
13
  for (const fn of testFunctions) {
14
14
  const subResult = fn(input);
15
- // .filter(r => !r.valid);
16
- invalidResults = [...invalidResults, ...subResult];
17
- if(!input.config.runAllValidations && invalidResults.length > 0) {
18
- return invalidResults;
19
- }
20
- }
21
- if(invalidResults.length > 0) {
22
- // return invalidResults;
23
- subResults = invalidResults;
24
- valid = subResults.every(r => r.valid);
15
+ allResults = [...allResults , ...subResult];
25
16
  }
17
+ subResults = allResults ;
18
+ valid = subResults.every((r) => r.valid);
26
19
 
27
20
  {{/isNested}}
28
21
 
@@ -32,9 +25,16 @@ const validate = {{{returnStatement}}}
32
25
  if(!validate){
33
26
  delete testObj._EXTERNAL;
34
27
  return [{
28
+ testName: "{{testName}}",
35
29
  valid: false,
36
30
  code: {{errorCode}},
37
- description: `{{{errorDescription}}}`
31
+ description: `{{{errorDescription}}}`,
32
+ _debugInfo: {
33
+ fedConfig:
34
+ `
35
+ {{{TEST_OBJECT}}}
36
+ `,
37
+ }
38
38
  }]
39
39
  }
40
40
  {{/isNested}}
@@ -23,13 +23,14 @@ export class TypescriptGenerator extends CodeGenerator {
23
23
  for (const key in testConfig) {
24
24
  const testObjects = testConfig[key];
25
25
  const betaConfig = {
26
- [TestObjectSyntax.Name]: key,
26
+ [TestObjectSyntax.Name]: key + "Validations",
27
27
  [TestObjectSyntax.Return]: testObjects,
28
28
  };
29
29
  const testFunction = await this.generateTestFunction(betaConfig);
30
30
  const apiTestTemplate = readFileSync(path.resolve(__dirname, "./templates/api-test.mustache"), "utf-8");
31
31
  const finalCode = Mustache.render(apiTestTemplate, {
32
32
  functionCode: testFunction.code,
33
+ apiName: key,
33
34
  });
34
35
  await writeAndFormatCode(this.rootPath, `./api-tests/${key}.ts`, finalCode, "typescript");
35
36
  }
@@ -63,6 +64,9 @@ export class TypescriptGenerator extends CodeGenerator {
63
64
  : undefined,
64
65
  validationCode: await this.createValidationLogicCode(testObject),
65
66
  successCode: testObject[TestObjectSyntax.SuccessCode] ?? 200,
67
+ errorCode: testObject[TestObjectSyntax.ErrorCode] ?? 30000,
68
+ testName: testObject[TestObjectSyntax.Name],
69
+ TEST_OBJECT: `${JSON.stringify(testObject)}`,
66
70
  };
67
71
  return {
68
72
  funcName: testObject[TestObjectSyntax.Name],
@@ -80,6 +84,8 @@ export class TypescriptGenerator extends CodeGenerator {
80
84
  returnStatement: returnStatement,
81
85
  errorCode: testObject[TestObjectSyntax.ErrorCode] ?? 30000,
82
86
  errorDescription: this.CreateErrorMarkdown(testObject, skipList),
87
+ testName: testObject[TestObjectSyntax.Name],
88
+ TEST_OBJECT: `${JSON.stringify(testObject)}`,
83
89
  });
84
90
  }
85
91
  else {
@@ -96,6 +102,8 @@ export class TypescriptGenerator extends CodeGenerator {
96
102
  isNested: true,
97
103
  nestedFunctions: functionCodes.map((f) => f.code).join("\n"),
98
104
  names: names,
105
+ testName: testObject[TestObjectSyntax.Name],
106
+ TEST_OBJECT: `${JSON.stringify(testObject)}`,
99
107
  });
100
108
  }
101
109
  };
@@ -158,31 +166,35 @@ export class TypescriptGenerator extends CodeGenerator {
158
166
  }
159
167
  generateIndexFile(apis, functionName = "L1Validations") {
160
168
  functionName = functionName.replace(/[^a-zA-Z0-9_]/g, "");
161
- const importsCode = apis
169
+ let importsCode = apis
162
170
  .map((api) => `import ${api} from "./api-tests/${api}";`)
163
171
  .join("\n");
172
+ importsCode += `\nimport { ValidationConfig,validationOutput } from "./types/test-config";`;
173
+ importsCode += `\nimport normalizeKeys from "./utils/json-normalizer";`;
174
+ const masterTemplate = readFileSync(path.resolve(__dirname, "./templates/index.mustache"), "utf-8");
164
175
  const masterFunction = `
165
- export function perform${functionName}(action: string, payload: any,allErrors = false, externalData : any = {}) {
166
- const normalizedPayload = normalizeKeys(JSON.parse(JSON.stringify(payload)));
167
- externalData._SELF = normalizedPayload;
176
+ export function perform${functionName}(action: string, payload: any, config?: Partial<ValidationConfig>, externalData: any = {}) {
177
+ const completeConfig: ValidationConfig = {
178
+ ...{ onlyInvalid: true, standardLogs: false, hideParentErrors: true, _debug: false },
179
+ ...config,
180
+ };
181
+ const normalizedPayload = normalizeKeys(JSON.parse(JSON.stringify(payload)));
182
+ externalData._SELF = normalizedPayload;
168
183
  switch (action) {
169
184
  ${apis
170
185
  .map((api) => `case "${api}": return ${api}({
171
186
  payload: normalizedPayload,
172
187
  externalData: externalData,
173
- config: {
174
- runAllValidations: allErrors,
175
- },
188
+ config: completeConfig,
176
189
  });`)
177
190
  .join("\n")}
178
191
  default:
179
192
  throw new Error("Action not found");
180
193
  }
181
194
  }`;
182
- return `
183
- import normalizeKeys from "./utils/json-normalizer";
184
- ${importsCode}
185
- ${masterFunction}
186
- `;
195
+ return Mustache.render(masterTemplate, {
196
+ importsCode: importsCode,
197
+ masterFunction: masterFunction,
198
+ });
187
199
  }
188
200
  }
@@ -1,4 +1,4 @@
1
- import { TestObjectSyntax, nodeReservedKeywords, ExternalDataSyntax, ConfigSyntax, } from "../../../constants/syntax.js";
1
+ import { TestObjectSyntax, ReservedKeywords, ExternalDataSyntax, ConfigSyntax, } from "../../../constants/syntax.js";
2
2
  import { buildAst } from "../../../services/return-complier/ast.js";
3
3
  import { checkValidVariables } from "../../../services/return-complier/ast-functions/semantic-validations.js";
4
4
  import { parseReturnInput } from "../../../services/return-complier/parser.js";
@@ -31,7 +31,7 @@ export class NameValidator extends TestObjectValidator {
31
31
  if (name.length < 1) {
32
32
  throw new Error(`${TestObjectSyntax.Name} can't be a non-empty string at path ${this.validationPath}`);
33
33
  }
34
- if (nodeReservedKeywords.has(name)) {
34
+ if (ReservedKeywords.has(name)) {
35
35
  throw new Error(`${TestObjectSyntax.Name} can't be a reserved keyword at path ${this.validationPath}`);
36
36
  }
37
37
  if (!isSnakeCase(name)) {
@@ -141,7 +141,7 @@ export class VariableValidator extends TestObjectValidator {
141
141
  this.minimal = minimal;
142
142
  }
143
143
  validateKey(key) {
144
- if (nodeReservedKeywords.has(key)) {
144
+ if (ReservedKeywords.has(key)) {
145
145
  throw new Error(`${key} can't be a reserved keyword at path ${this.validationPath}`);
146
146
  }
147
147
  if (key.includes(" ")) {
package/dist/index.js CHANGED
@@ -7,7 +7,7 @@ export { ConfigCompiler };
7
7
  // const __dirname = path.dirname(__filename);
8
8
  // import { SupportedLanguages } from "./types/compiler-types.js";
9
9
  // const main = async () => {
10
- // const compiler = new ConfigCompiler(SupportedLanguages.Typescript);
10
+ // const compiler = new ConfigCompiler(SupportedLanguages.Python);
11
11
  // const buildPath = path.resolve(__dirname, "../samples/build.yaml");
12
12
  // const valConfigPath = path.resolve(
13
13
  // __dirname,
@@ -16,7 +16,20 @@ export { ConfigCompiler };
16
16
  // const buildYaml = readFileSync(buildPath, "utf-8");
17
17
  // const valConfig = JSON.parse(readFileSync(valConfigPath, "utf-8"));
18
18
  // await compiler.initialize(buildYaml);
19
- // await compiler.generateCode(valConfig, "L1-validations", false, "./alpha/");
19
+ // await compiler.generateCode(
20
+ // valConfig,
21
+ // "L1_validations",
22
+ // false,
23
+ // "./alpha/python/"
24
+ // );
25
+ // const compilerTy = new ConfigCompiler(SupportedLanguages.Typescript);
26
+ // await compilerTy.initialize(buildYaml);
27
+ // await compilerTy.generateCode(
28
+ // valConfig,
29
+ // "L1_validations",
30
+ // false,
31
+ // "./alpha/typescript/"
32
+ // );
20
33
  // };
21
34
  // (async () => {
22
35
  // await main();
@@ -1,3 +1,4 @@
1
1
  export declare enum SupportedLanguages {
2
- Typescript = "typescript"
2
+ Typescript = "typescript",
3
+ Python = "python"
3
4
  }
@@ -1,4 +1,5 @@
1
1
  export var SupportedLanguages;
2
2
  (function (SupportedLanguages) {
3
3
  SupportedLanguages["Typescript"] = "typescript";
4
+ SupportedLanguages["Python"] = "python";
4
5
  })(SupportedLanguages || (SupportedLanguages = {}));
@@ -9,11 +9,54 @@ export function writeFileWithFsExtra(rootPath, relativeFilePath, content) {
9
9
  fs.outputFileSync(filePath, content);
10
10
  }
11
11
  export async function formatCode(code, lang) {
12
+ if (lang === "text") {
13
+ // No formatting for plain text files
14
+ return code;
15
+ }
16
+ if (lang == "python") {
17
+ // Basic Python formatting - clean up extra whitespace and blank lines
18
+ return formatPythonCode(code);
19
+ }
12
20
  return await prettier.format(code, {
13
21
  parser: lang,
14
22
  tabWidth: 4,
15
23
  });
16
24
  }
25
+ function formatPythonCode(code) {
26
+ const lines = code.split("\n");
27
+ const formattedLines = [];
28
+ for (let i = 0; i < lines.length; i++) {
29
+ const line = lines[i];
30
+ // Skip lines that are only whitespace
31
+ if (line.trim() === "") {
32
+ // Only add empty line if the previous line wasn't empty
33
+ if (formattedLines.length > 0 &&
34
+ formattedLines[formattedLines.length - 1].trim() !== "") {
35
+ formattedLines.push("");
36
+ }
37
+ continue;
38
+ }
39
+ // Add the line as-is (preserve existing indentation)
40
+ formattedLines.push(line);
41
+ }
42
+ // Remove multiple consecutive empty lines
43
+ const cleanedLines = [];
44
+ let lastWasEmpty = false;
45
+ for (const line of formattedLines) {
46
+ const isEmpty = line.trim() === "";
47
+ if (isEmpty && lastWasEmpty) {
48
+ continue; // Skip consecutive empty lines
49
+ }
50
+ cleanedLines.push(line);
51
+ lastWasEmpty = isEmpty;
52
+ }
53
+ // Remove trailing empty lines
54
+ while (cleanedLines.length > 0 &&
55
+ cleanedLines[cleanedLines.length - 1].trim() === "") {
56
+ cleanedLines.pop();
57
+ }
58
+ return cleanedLines.join("\n") + "\n";
59
+ }
17
60
  export async function writeAndFormatCode(rootPath, relativeFilePath, content, lang) {
18
61
  const formattedCode = await formatCode(content, lang);
19
62
  writeFileWithFsExtra(rootPath, relativeFilePath, formattedCode);