ajsc 1.0.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.
- package/dist/JSONSchemaConverter.js +370 -0
- package/dist/JSONSchemaConverter.js.map +1 -0
- package/dist/JSONSchemaConverter.test.js +302 -0
- package/dist/JSONSchemaConverter.test.js.map +1 -0
- package/dist/TypescriptBaseConverter.js +131 -0
- package/dist/TypescriptBaseConverter.js.map +1 -0
- package/dist/TypescriptConverter.js +107 -0
- package/dist/TypescriptConverter.js.map +1 -0
- package/dist/TypescriptConverter.test.js +199 -0
- package/dist/TypescriptConverter.test.js.map +1 -0
- package/dist/TypescriptProcedureConverter.js +118 -0
- package/dist/TypescriptProcedureConverter.js.map +1 -0
- package/dist/TypescriptProceduresConverter.test.js +948 -0
- package/dist/TypescriptProceduresConverter.test.js.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/path-utils.js +78 -0
- package/dist/utils/path-utils.js.map +1 -0
- package/dist/utils/path-utils.test.js +92 -0
- package/dist/utils/path-utils.test.js.map +1 -0
- package/dist/utils/to-pascal-case.js +11 -0
- package/dist/utils/to-pascal-case.js.map +1 -0
- package/package.json +56 -0
- package/src/JSONSchemaConverter.test.ts +342 -0
- package/src/JSONSchemaConverter.ts +459 -0
- package/src/TypescriptBaseConverter.ts +161 -0
- package/src/TypescriptConverter.test.ts +264 -0
- package/src/TypescriptConverter.ts +161 -0
- package/src/TypescriptProcedureConverter.ts +160 -0
- package/src/TypescriptProceduresConverter.test.ts +952 -0
- package/src/types.ts +101 -0
- package/src/utils/path-utils.test.ts +102 -0
- package/src/utils/path-utils.ts +89 -0
- package/src/utils/to-pascal-case.ts +10 -0
package/src/types.ts
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
// A simple example of an intermediate representation for types
|
|
2
|
+
export type IRType =
|
|
3
|
+
| "string"
|
|
4
|
+
| "number"
|
|
5
|
+
| "integer"
|
|
6
|
+
| "boolean"
|
|
7
|
+
| "object"
|
|
8
|
+
| "array"
|
|
9
|
+
| "null"
|
|
10
|
+
| "enum"
|
|
11
|
+
| "union"
|
|
12
|
+
| "intersection"
|
|
13
|
+
| "literal";
|
|
14
|
+
|
|
15
|
+
export interface IRNode {
|
|
16
|
+
signature?: string;
|
|
17
|
+
/**
|
|
18
|
+
* The base type of the IR node.
|
|
19
|
+
*/
|
|
20
|
+
type: IRType;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Optional name for the node (useful for objects, enums, etc.).
|
|
24
|
+
*/
|
|
25
|
+
name?: string;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Dot notation path to the node in a runtime object.
|
|
29
|
+
* For example, "user.profile.name".
|
|
30
|
+
*/
|
|
31
|
+
path: string;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Indicates whether this property/node is required.
|
|
35
|
+
* For object properties, this can be derived from the JSON Schema 'required' array.
|
|
36
|
+
*/
|
|
37
|
+
required?: boolean;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* For object types: a mapping of property names to their IR node definitions.
|
|
41
|
+
*/
|
|
42
|
+
properties?: { [key: string]: IRNode };
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* For array types: the IR node representing the type of items in the array.
|
|
46
|
+
*/
|
|
47
|
+
items?: IRNode;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* For enum types: a list of allowed literal values.
|
|
51
|
+
*/
|
|
52
|
+
values?: any[];
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* For union or intersection types: an array of candidate IR nodes.
|
|
56
|
+
* Note: This is used regardless of whether the union originated from a 'oneOf' or an 'anyOf'.
|
|
57
|
+
*/
|
|
58
|
+
options?: IRNode[];
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Additional constraints such as minimum, maximum, pattern, etc.
|
|
62
|
+
*/
|
|
63
|
+
constraints?: {
|
|
64
|
+
[key: string]: any;
|
|
65
|
+
combinator?: "oneOf" | "anyOf";
|
|
66
|
+
// Value represents a enum or literal value
|
|
67
|
+
maxLength?: number;
|
|
68
|
+
minLength?: number;
|
|
69
|
+
pattern?: string;
|
|
70
|
+
maximum?: number;
|
|
71
|
+
minimum?: number;
|
|
72
|
+
value?: any;
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export type SignatureOccurrenceValue = {
|
|
77
|
+
occurrences: { node: IRNode; nodePath: string; count: number }[];
|
|
78
|
+
signature: string;
|
|
79
|
+
total: number;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export type SignatureOccurrences = Map<string, SignatureOccurrenceValue>;
|
|
83
|
+
|
|
84
|
+
export interface Context {
|
|
85
|
+
signatureOccurrences: SignatureOccurrences;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface LanguagePlugin {
|
|
89
|
+
/**
|
|
90
|
+
* The name of the language this plugin converts to (e.g., "typescript", "java", "python").
|
|
91
|
+
*/
|
|
92
|
+
language: string;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// allow configuration options that affect the conversion process
|
|
96
|
+
export interface ConverterOptions {
|
|
97
|
+
// For example, whether to perform strict schema validation
|
|
98
|
+
validateSchema?: boolean;
|
|
99
|
+
// Any custom transformations on the IR
|
|
100
|
+
transform?: (ir: IRNode) => IRNode;
|
|
101
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { PathUtils } from "./path-utils.js";
|
|
3
|
+
import { SignatureOccurrenceValue } from "../types.js";
|
|
4
|
+
|
|
5
|
+
describe("SignatureOccurrences", () => {
|
|
6
|
+
describe("parsePaths", () => {
|
|
7
|
+
it('should parse dot-separated paths and remove "0" segments', () => {
|
|
8
|
+
const input = ["a.b.c", "x.0.y.z", "1.2.0.3"];
|
|
9
|
+
const expected = [
|
|
10
|
+
["a", "b", "c"],
|
|
11
|
+
["x", "y", "z"],
|
|
12
|
+
["1", "2", "3"],
|
|
13
|
+
];
|
|
14
|
+
expect(PathUtils.parsePaths(input)).toEqual(expected);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it("should handle empty input", () => {
|
|
18
|
+
expect(PathUtils.parsePaths([])).toEqual([]);
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
describe("commonSequence", () => {
|
|
23
|
+
it("should find the longest common prefix among arrays", () => {
|
|
24
|
+
const input = [
|
|
25
|
+
["organizations", "migration", "step", "create_regions"],
|
|
26
|
+
["organizations", "migration", "step", "create_location_groups"],
|
|
27
|
+
["organizations", "migration", "step", "create_locations"],
|
|
28
|
+
["organizations", "migration", "step", "create_locations_bolos"],
|
|
29
|
+
];
|
|
30
|
+
const expected = ["organizations", "migration", "step"];
|
|
31
|
+
expect(PathUtils.commonSequence(input)).toEqual(expected);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("should find the longest common prefix among arrays", () => {
|
|
35
|
+
const input = [
|
|
36
|
+
["organizations", "migration", "step", "create_regions"],
|
|
37
|
+
["org", "migration", "step"],
|
|
38
|
+
["organizations", "migration", "step", "create_locations"],
|
|
39
|
+
["organizations", "migration", "step", "create_locationsam_bolos"],
|
|
40
|
+
];
|
|
41
|
+
const expected = ["migration", "step"];
|
|
42
|
+
expect(PathUtils.commonSequence(input)).toEqual(expected);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("should return an empty array for empty input", () => {
|
|
46
|
+
expect(PathUtils.commonSequence([])).toEqual([]);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("should handle arrays with no common prefix", () => {
|
|
50
|
+
const input = [
|
|
51
|
+
["a", "b", "c"],
|
|
52
|
+
["x", "y", "z"],
|
|
53
|
+
["1", "2", "3"],
|
|
54
|
+
];
|
|
55
|
+
expect(PathUtils.commonSequence(input)).toEqual([]);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
describe("commonSequenceReversed", () => {
|
|
60
|
+
it("should return the specified number of shared path segments", () => {
|
|
61
|
+
const input = [
|
|
62
|
+
["organizations", "migration", "step", "create_regions"],
|
|
63
|
+
["organizations", "migration", "step", "create_user"],
|
|
64
|
+
];
|
|
65
|
+
const expected = ["step"];
|
|
66
|
+
expect(PathUtils.commonSequenceReversed(input)).toEqual(expected);
|
|
67
|
+
});
|
|
68
|
+
it("should return the specified number of deeply shared path segments", () => {
|
|
69
|
+
const input = [
|
|
70
|
+
["organizations", "migration", "step", "create_regions"],
|
|
71
|
+
["organizations", "migration", "step"],
|
|
72
|
+
];
|
|
73
|
+
const expected = ["step"];
|
|
74
|
+
expect(PathUtils.commonSequenceReversed(input)).toEqual(expected);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("should return the specified number of shared path segments", () => {
|
|
78
|
+
const input = [
|
|
79
|
+
["organizations", "migration", "step", "create_regions"],
|
|
80
|
+
["organizations", "migration", "step", "create_location_groups"],
|
|
81
|
+
["organizations", "migration", "step", "create_locations"],
|
|
82
|
+
["organizations", "migration", "step", "create_locations_bolos"],
|
|
83
|
+
];
|
|
84
|
+
const expected = ["migration", "step"];
|
|
85
|
+
expect(PathUtils.commonSequenceReversed(input, 2)).toEqual(expected);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("should return the specified number of deeply shared path segments", () => {
|
|
89
|
+
const input = [
|
|
90
|
+
["organizations", "migration", "step", "create_regions"],
|
|
91
|
+
["organizations", "migration", "step"],
|
|
92
|
+
["organizations", "migration", "step", "create_user"],
|
|
93
|
+
];
|
|
94
|
+
const expected = ["migration", "step"];
|
|
95
|
+
expect(PathUtils.commonSequenceReversed(input, 2)).toEqual(expected);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it("should handle empty input", () => {
|
|
99
|
+
expect(PathUtils.commonSequenceReversed([])).toEqual([]);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
});
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
export const PathUtils = {
|
|
2
|
+
/**
|
|
3
|
+
* Parses a list of dot-separated paths into an array of string arrays.
|
|
4
|
+
* Removes any "0" character segments.
|
|
5
|
+
*
|
|
6
|
+
* @param list
|
|
7
|
+
* @protected
|
|
8
|
+
*/
|
|
9
|
+
parsePaths: (list: string[]): string[][] => {
|
|
10
|
+
return list.map((item) =>
|
|
11
|
+
item.split(".").filter((segment) => segment !== "0"),
|
|
12
|
+
);
|
|
13
|
+
},
|
|
14
|
+
|
|
15
|
+
parsePath: (path: string): string[] => {
|
|
16
|
+
return path.split(".").filter((segment) => segment !== "0");
|
|
17
|
+
},
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Returns the last `count` common elements (shared path) among all given path arrays,
|
|
21
|
+
* comparing from the end (i.e. common suffix).
|
|
22
|
+
*
|
|
23
|
+
* @param paths - An array of string arrays (paths) to compare.
|
|
24
|
+
* @param count - The number of common elements (starting from the end) to return.
|
|
25
|
+
* @returns An array containing the shared path elements in original order.
|
|
26
|
+
*/
|
|
27
|
+
commonSequenceReversed: (paths: string[][], count: number = 1): string[] => {
|
|
28
|
+
if (paths.length === 0) return [];
|
|
29
|
+
|
|
30
|
+
// Find the shortest path to use as the candidate guide.
|
|
31
|
+
const minPath = paths.reduce(
|
|
32
|
+
(acc, cur) => (cur.length < acc.length ? cur : acc),
|
|
33
|
+
paths[0],
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
// Initialize pointers for each path. They represent the index limit (exclusive) to search backwards.
|
|
37
|
+
const pointers = paths.map((path) => path.length);
|
|
38
|
+
const commonSegmentsReversed: string[] = [];
|
|
39
|
+
|
|
40
|
+
// Iterate backwards over the shortest path.
|
|
41
|
+
for (let i = minPath.length - 1; i >= 0; i--) {
|
|
42
|
+
const candidate = minPath[i];
|
|
43
|
+
let foundInAll = true;
|
|
44
|
+
// Temporarily hold the found indices for this candidate in each path.
|
|
45
|
+
const foundIndices: number[] = [];
|
|
46
|
+
|
|
47
|
+
// For each path, search for the candidate before the current pointer.
|
|
48
|
+
for (let j = 0; j < paths.length; j++) {
|
|
49
|
+
// Search up to (pointer[j] - 1) so we always look to the left of the previous found element.
|
|
50
|
+
const idx = paths[j].lastIndexOf(candidate, pointers[j] - 1);
|
|
51
|
+
if (idx === -1) {
|
|
52
|
+
foundInAll = false;
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
foundIndices.push(idx);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// If the candidate exists in all arrays, update each pointer and record the candidate.
|
|
59
|
+
if (foundInAll) {
|
|
60
|
+
for (let j = 0; j < pointers.length; j++) {
|
|
61
|
+
pointers[j] = foundIndices[j];
|
|
62
|
+
}
|
|
63
|
+
commonSegmentsReversed.push(candidate);
|
|
64
|
+
if (commonSegmentsReversed.length === count) break;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Reverse to restore the original order (from left-to-right).
|
|
69
|
+
return commonSegmentsReversed.reverse();
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Returns the longest common prefix (sequence) among an array of string arrays.
|
|
74
|
+
* @param paths - An array of string arrays.
|
|
75
|
+
* @returns An array representing the common sequence.
|
|
76
|
+
*/
|
|
77
|
+
commonSequence: (paths: string[][]): string[] => {
|
|
78
|
+
if (paths.length === 0) return [];
|
|
79
|
+
|
|
80
|
+
const common: string[] = [];
|
|
81
|
+
for (let i = 0; i < paths[0].length; i++) {
|
|
82
|
+
const segment = paths[0][i];
|
|
83
|
+
if (paths.every((path) => path[i] === segment)) {
|
|
84
|
+
common.push(segment);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return common;
|
|
88
|
+
},
|
|
89
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Converts a string to PascalCase.
|
|
3
|
+
* Splits on whitespace, hyphens, or underscores.
|
|
4
|
+
*/
|
|
5
|
+
export function toPascalCase(str: string): string {
|
|
6
|
+
return str
|
|
7
|
+
.split(/[\s-_]+/)
|
|
8
|
+
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
|
9
|
+
.join('');
|
|
10
|
+
}
|