atmx-cli 0.48.0 → 0.49.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/generators/model-generator.js +50 -49
- package/dist/generators/sdk-generator.js +23 -20
- package/dist/generators/utils.js +34 -35
- package/package.json +1 -1
- package/src/generators/model-generator.ts +112 -133
- package/src/generators/sdk-generator.ts +27 -27
- package/src/generators/utils.ts +34 -45
|
@@ -4,91 +4,92 @@ exports.generateModels = generateModels;
|
|
|
4
4
|
const utils_1 = require("./utils");
|
|
5
5
|
function generateModels(multiIr) {
|
|
6
6
|
const sections = [
|
|
7
|
-
`// GENERATED CODE – DO NOT EDIT.\n/* eslint-disable @typescript-eslint/no-explicit-any */\n
|
|
7
|
+
`// GENERATED CODE – DO NOT EDIT.\n/* eslint-disable @typescript-eslint/no-explicit-any */\n`,
|
|
8
8
|
];
|
|
9
|
-
// 1. Generate flat exports (NO namespaces)
|
|
10
9
|
for (const [ns, ir] of Object.entries(multiIr)) {
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
enumsList
|
|
15
|
-
|
|
10
|
+
const camelNs = (0, utils_1.camelCase)(ns);
|
|
11
|
+
// ✨ FIX: Use proper TS namespaces
|
|
12
|
+
sections.push(`export namespace ${camelNs} {`);
|
|
13
|
+
const enumsList = Array.isArray(ir.enums)
|
|
14
|
+
? ir.enums
|
|
15
|
+
: Object.values(ir.enums || {});
|
|
16
|
+
const modelsList = Array.isArray(ir.models)
|
|
17
|
+
? ir.models
|
|
18
|
+
: Object.values(ir.models || {});
|
|
19
|
+
enumsList.forEach((en) => sections.push(generateEnum(en)));
|
|
20
|
+
modelsList.forEach((model) => sections.push(generateInterface(model, camelNs)));
|
|
21
|
+
sections.push(`}\n`);
|
|
16
22
|
}
|
|
17
|
-
// 2. Generate nested Mappers object
|
|
18
23
|
sections.push(generateMappers(multiIr));
|
|
19
|
-
return sections.join(
|
|
24
|
+
return sections.join("\n");
|
|
20
25
|
}
|
|
21
|
-
function generateEnum(en
|
|
22
|
-
const name =
|
|
23
|
-
const values = en.values.map(v => ` ${(0, utils_1.pascalCase)(v)}: "${v}"`).join(
|
|
26
|
+
function generateEnum(en) {
|
|
27
|
+
const name = (0, utils_1.pascalCase)(en.name);
|
|
28
|
+
const values = en.values.map((v) => ` ${(0, utils_1.pascalCase)(v)}: "${v}"`).join(",\n");
|
|
24
29
|
return `
|
|
25
|
-
export const ${name} = {
|
|
26
|
-
${values}
|
|
27
|
-
} as const;
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
`;
|
|
30
|
+
export const ${name} = {
|
|
31
|
+
${values}
|
|
32
|
+
} as const;
|
|
33
|
+
export type ${name} = typeof ${name}[keyof typeof ${name}];
|
|
34
|
+
`;
|
|
31
35
|
}
|
|
32
36
|
function generateInterface(model, ns) {
|
|
33
|
-
const name =
|
|
34
|
-
const fields = model.fields
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
37
|
+
const name = (0, utils_1.pascalCase)(model.name);
|
|
38
|
+
const fields = model.fields
|
|
39
|
+
.map((f) => {
|
|
40
|
+
const type = (0, utils_1.mapTypeToTs)(f.typeRef, ns);
|
|
41
|
+
return ` ${(0, utils_1.camelCase)(f.name)}${f.isOptional ? "?" : ""}: ${type};`;
|
|
42
|
+
})
|
|
43
|
+
.join("\n");
|
|
38
44
|
return `
|
|
39
|
-
export interface ${name} {
|
|
45
|
+
export interface ${name} {
|
|
40
46
|
${fields}
|
|
41
|
-
}
|
|
42
|
-
`;
|
|
47
|
+
}
|
|
48
|
+
`;
|
|
43
49
|
}
|
|
44
50
|
function generateMappers(multiIr) {
|
|
45
51
|
const lines = [`export const Mappers: Record<string, any> = {`];
|
|
46
52
|
for (const [ns, ir] of Object.entries(multiIr)) {
|
|
47
53
|
const camelNs = (0, utils_1.camelCase)(ns);
|
|
48
|
-
const pascalNs = (0, utils_1.pascalCase)(ns);
|
|
49
54
|
lines.push(` ${camelNs}: {`);
|
|
50
|
-
const modelsList = Array.isArray(ir.models)
|
|
55
|
+
const modelsList = Array.isArray(ir.models)
|
|
56
|
+
? ir.models
|
|
57
|
+
: Object.values(ir.models || {});
|
|
51
58
|
modelsList.forEach((model) => {
|
|
52
59
|
const name = (0, utils_1.pascalCase)(model.name);
|
|
53
|
-
const fullType = `${
|
|
60
|
+
const fullType = `${camelNs}.${name}`;
|
|
54
61
|
lines.push(` ${name}: {\n fromJson: (json: any): ${fullType} => ({`);
|
|
55
62
|
model.fields.forEach((f) => {
|
|
56
|
-
lines.push(` ${(0, utils_1.camelCase)(f.name)}: ${generateJsonLogic(f.typeRef, `json["${f.name}"]`, f.isOptional,
|
|
63
|
+
lines.push(` ${(0, utils_1.camelCase)(f.name)}: ${generateJsonLogic(f.typeRef, `json["${f.name}"]`, f.isOptional, "fromJson", camelNs)},`);
|
|
57
64
|
});
|
|
58
65
|
lines.push(` }),\n toJson: (obj: any): any => ({`);
|
|
59
66
|
model.fields.forEach((f) => {
|
|
60
|
-
lines.push(` "${f.name}": ${generateJsonLogic(f.typeRef, `obj.${(0, utils_1.camelCase)(f.name)}`, f.isOptional,
|
|
67
|
+
lines.push(` "${f.name}": ${generateJsonLogic(f.typeRef, `obj.${(0, utils_1.camelCase)(f.name)}`, f.isOptional, "toJson", camelNs)},`);
|
|
61
68
|
});
|
|
62
69
|
lines.push(` })\n },`);
|
|
63
70
|
});
|
|
64
71
|
lines.push(` },`);
|
|
65
72
|
}
|
|
66
73
|
lines.push(`};\n`);
|
|
67
|
-
return lines.join(
|
|
74
|
+
return lines.join("\n");
|
|
68
75
|
}
|
|
69
76
|
function generateJsonLogic(typeRef, access, isOpt, mode, ns) {
|
|
70
77
|
const wrap = (logic) => isOpt ? `(${access} == null ? undefined : ${logic})` : logic;
|
|
71
78
|
if (!typeRef || !typeRef.kind)
|
|
72
79
|
return access;
|
|
73
|
-
if (typeRef.kind ===
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
: wrap(`Array.from(${access})`);
|
|
83
|
-
}
|
|
84
|
-
return access;
|
|
85
|
-
}
|
|
86
|
-
if (typeRef.kind === 'named') {
|
|
80
|
+
if (typeRef.kind === "dateTime")
|
|
81
|
+
return mode === "fromJson"
|
|
82
|
+
? wrap(`new Date(${access})`)
|
|
83
|
+
: wrap(`${access}.toISOString()`);
|
|
84
|
+
if (typeRef.kind === "bytes")
|
|
85
|
+
return mode === "fromJson"
|
|
86
|
+
? wrap(`new Uint8Array(${access})`)
|
|
87
|
+
: wrap(`Array.from(${access})`);
|
|
88
|
+
if (typeRef.kind === "named") {
|
|
87
89
|
const name = (0, utils_1.pascalCase)(typeRef.value);
|
|
88
90
|
return wrap(`(Mappers.${ns}["${name}"] ? Mappers.${ns}["${name}"].${mode}(${access}) : ${access})`);
|
|
89
91
|
}
|
|
90
|
-
if (typeRef.kind ===
|
|
91
|
-
return wrap(`${access}.map((e: any) => ${generateJsonLogic(typeRef.value,
|
|
92
|
-
}
|
|
92
|
+
if (typeRef.kind === "list")
|
|
93
|
+
return wrap(`${access}.map((e: any) => ${generateJsonLogic(typeRef.value, "e", false, mode, ns)})`);
|
|
93
94
|
return access;
|
|
94
95
|
}
|
|
@@ -5,6 +5,7 @@ const utils_1 = require("./utils");
|
|
|
5
5
|
function generateSdk(multiIr, isReact = false) {
|
|
6
6
|
const lines = [
|
|
7
7
|
`// GENERATED CODE – DO NOT EDIT.`,
|
|
8
|
+
`/* eslint-disable @typescript-eslint/no-explicit-any */`,
|
|
8
9
|
`import * as models from './models';\n`,
|
|
9
10
|
];
|
|
10
11
|
if (isReact) {
|
|
@@ -12,45 +13,47 @@ function generateSdk(multiIr, isReact = false) {
|
|
|
12
13
|
lines.push(`import type { AxiomQueryDef } from 'atmx-react';\n`);
|
|
13
14
|
}
|
|
14
15
|
for (const [ns, ir] of Object.entries(multiIr)) {
|
|
15
|
-
const
|
|
16
|
-
|
|
16
|
+
const camelNs = (0, utils_1.camelCase)(ns);
|
|
17
|
+
// ✨ FIX: Use an object literal instead of a Class to support React Hooks!
|
|
18
|
+
lines.push(`export const ${camelNs}Module = {`);
|
|
17
19
|
const endpointsMap = ir.endpoints || {};
|
|
18
20
|
const endpoints = Array.isArray(endpointsMap)
|
|
19
21
|
? endpointsMap
|
|
20
22
|
: Object.values(endpointsMap);
|
|
21
23
|
endpoints.forEach((ep) => {
|
|
22
|
-
lines.push(generateEndpointMethod(ep, ns,
|
|
24
|
+
lines.push(generateEndpointMethod(ep, ns, camelNs, isReact));
|
|
23
25
|
});
|
|
24
|
-
lines.push(`}
|
|
26
|
+
lines.push(`};\n`);
|
|
25
27
|
}
|
|
26
|
-
|
|
28
|
+
// Generate main SDK object
|
|
29
|
+
lines.push(`export const sdk = {`);
|
|
27
30
|
for (const ns of Object.keys(multiIr)) {
|
|
28
|
-
lines.push(`
|
|
31
|
+
lines.push(` ${(0, utils_1.camelCase)(ns)}: ${(0, utils_1.camelCase)(ns)}Module,`);
|
|
29
32
|
}
|
|
30
|
-
lines.push(`}
|
|
31
|
-
|
|
33
|
+
lines.push(`};\n`);
|
|
34
|
+
// Generate Config
|
|
35
|
+
lines.push(`export const AxiomDefaultConfig = {`);
|
|
32
36
|
lines.push(` contracts: {`);
|
|
33
37
|
for (const ns of Object.keys(multiIr)) {
|
|
34
38
|
lines.push(` "${ns}": {`);
|
|
35
39
|
lines.push(` contractUrl: "/${ns}.axiom",`);
|
|
36
|
-
lines.push(` baseUrl: "http://localhost:8000"
|
|
40
|
+
lines.push(` baseUrl: "http://localhost:8000"`);
|
|
37
41
|
lines.push(` },`);
|
|
38
42
|
}
|
|
39
43
|
lines.push(` }`);
|
|
40
44
|
lines.push(`};\n`);
|
|
41
45
|
return lines.join("\n");
|
|
42
|
-
return lines.join("\n");
|
|
43
46
|
}
|
|
44
|
-
function generateEndpointMethod(ep, ns,
|
|
47
|
+
function generateEndpointMethod(ep, ns, camelNs, isReact) {
|
|
45
48
|
const rawParams = ep.parameters || [];
|
|
46
49
|
const params = Array.isArray(rawParams)
|
|
47
50
|
? rawParams
|
|
48
51
|
: Object.values(rawParams);
|
|
49
52
|
const argType = params.length > 0
|
|
50
|
-
? `{ ${params.map((p) => `${(0, utils_1.camelCase)(p.name)}${p.isOptional ? "?" : ""}: ${prefixModels((0, utils_1.mapTypeToTs)(p.typeRef,
|
|
53
|
+
? `{ ${params.map((p) => `${(0, utils_1.camelCase)(p.name)}${p.isOptional ? "?" : ""}: ${prefixModels((0, utils_1.mapTypeToTs)(p.typeRef, camelNs))}`).join(", ")} }`
|
|
51
54
|
: "void";
|
|
52
55
|
const isQuery = ep.method ? ep.method.toUpperCase() === "GET" : true;
|
|
53
|
-
const rawReturnType = (0, utils_1.mapTypeToTs)(ep.returnType,
|
|
56
|
+
const rawReturnType = (0, utils_1.mapTypeToTs)(ep.returnType, camelNs);
|
|
54
57
|
const returnType = rawReturnType === "void" || rawReturnType === "any"
|
|
55
58
|
? rawReturnType
|
|
56
59
|
: prefixModels(rawReturnType);
|
|
@@ -59,9 +62,9 @@ function generateEndpointMethod(ep, ns, pascalNs, isReact) {
|
|
|
59
62
|
const payloadLogic = bodyParam
|
|
60
63
|
? `const payload = (args as any)?.${(0, utils_1.camelCase)(bodyParam.name)};`
|
|
61
64
|
: `const payload = undefined;`;
|
|
62
|
-
const decLogic = generateLambda(ep.returnType, "fromJson",
|
|
65
|
+
const decLogic = generateLambda(ep.returnType, "fromJson", camelNs);
|
|
63
66
|
const serLogic = bodyParam
|
|
64
|
-
? generateLambda(bodyParam.typeRef, "toJson",
|
|
67
|
+
? generateLambda(bodyParam.typeRef, "toJson", camelNs)
|
|
65
68
|
: `(p: any) => p`;
|
|
66
69
|
return `
|
|
67
70
|
get${(0, utils_1.pascalCase)(ep.name)}Def(args${params.length > 0 ? "?" : ""}: ${argType === "void" ? "any" : argType}): AxiomQueryDef<${returnType}> {
|
|
@@ -78,29 +81,29 @@ function generateEndpointMethod(ep, ns, pascalNs, isReact) {
|
|
|
78
81
|
serializer: ${serLogic},
|
|
79
82
|
isStream: ${ep.isStream === true}
|
|
80
83
|
};
|
|
81
|
-
}
|
|
84
|
+
},
|
|
82
85
|
|
|
83
86
|
use${(0, utils_1.pascalCase)(ep.name)}${!isQuery ? "Mutation" : ""}(${isQuery ? `args${params.length > 0 ? "?" : ""}: ${argType === "void" ? "any" : argType}, options?: { enabled?: boolean }` : ""}) {
|
|
84
87
|
${isQuery
|
|
85
88
|
? `return useAxiomQuery<${returnType}>(this.get${(0, utils_1.pascalCase)(ep.name)}Def(args), options);`
|
|
86
89
|
: `return useAxiomMutation<${returnType}, ${argType === "void" ? "void | Record<string,any>" : argType}>((args) => this.get${(0, utils_1.pascalCase)(ep.name)}Def(args));`}
|
|
87
|
-
}
|
|
90
|
+
},`;
|
|
88
91
|
}
|
|
89
92
|
else {
|
|
90
93
|
return `
|
|
91
94
|
${(0, utils_1.camelCase)(ep.name)}(args${params.length > 0 ? "?" : ""}: ${argType === "void" ? "any" : argType}): string {
|
|
92
95
|
const argsStr = args && Object.keys(args).length > 0 ? JSON.stringify(args) : '';
|
|
93
96
|
return \`${ns}.${ep.name}(\${argsStr})\`;
|
|
94
|
-
}
|
|
97
|
+
},`;
|
|
95
98
|
}
|
|
96
99
|
}
|
|
97
100
|
function generateLambda(typeRef, mode, ns) {
|
|
98
101
|
if (!typeRef || !typeRef.kind || typeRef.kind === "void")
|
|
99
102
|
return mode === "fromJson" ? `() => undefined` : `(p: any) => p`;
|
|
100
103
|
if (typeRef.kind === "list" && typeRef.value?.kind === "named")
|
|
101
|
-
return `(data: any[]) => data.map(models.Mappers.${
|
|
104
|
+
return `(data: any[]) => data.map(models.Mappers.${ns}.${(0, utils_1.pascalCase)(typeRef.value.value)}.${mode})`;
|
|
102
105
|
if (typeRef.kind === "named")
|
|
103
|
-
return `models.Mappers.${
|
|
106
|
+
return `models.Mappers.${ns}.${(0, utils_1.pascalCase)(typeRef.value)}.${mode}`;
|
|
104
107
|
return `(data: any) => data`;
|
|
105
108
|
}
|
|
106
109
|
function prefixModels(type) {
|
package/dist/generators/utils.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// atmx-cli/src/generators/utils.ts
|
|
3
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
3
|
exports.pascalCase = pascalCase;
|
|
5
4
|
exports.camelCase = camelCase;
|
|
6
5
|
exports.normalizeIr = normalizeIr;
|
|
7
6
|
exports.mapTypeToTs = mapTypeToTs;
|
|
7
|
+
// FILE: atmx-cli/src/generators/utils.ts
|
|
8
8
|
function pascalCase(str) {
|
|
9
9
|
if (!str)
|
|
10
10
|
return "";
|
|
@@ -18,17 +18,14 @@ function camelCase(str) {
|
|
|
18
18
|
return pascal.charAt(0).toLowerCase() + pascal.slice(1);
|
|
19
19
|
}
|
|
20
20
|
function normalizeIr(obj) {
|
|
21
|
-
|
|
22
|
-
if (Array.isArray(obj)) {
|
|
21
|
+
if (Array.isArray(obj))
|
|
23
22
|
return obj.map(normalizeIr);
|
|
24
|
-
}
|
|
25
23
|
if (obj !== null && typeof obj === "object") {
|
|
26
24
|
const newObj = {};
|
|
27
25
|
for (const key of Object.keys(obj)) {
|
|
28
26
|
const camelKey = key.replace(/_([a-z])/g, (g) => g[1].toUpperCase());
|
|
29
27
|
newObj[camelKey] = normalizeIr(obj[key]);
|
|
30
28
|
}
|
|
31
|
-
// Convert common Maps to Arrays if they exist and are not already arrays
|
|
32
29
|
if (newObj.endpoints &&
|
|
33
30
|
typeof newObj.endpoints === "object" &&
|
|
34
31
|
!Array.isArray(newObj.endpoints)) {
|
|
@@ -44,7 +41,6 @@ function normalizeIr(obj) {
|
|
|
44
41
|
!Array.isArray(newObj.enums)) {
|
|
45
42
|
newObj.enums = Object.values(newObj.enums);
|
|
46
43
|
}
|
|
47
|
-
// Traverse down into models to normalize fields
|
|
48
44
|
if (Array.isArray(newObj.models)) {
|
|
49
45
|
newObj.models = newObj.models.map((model) => {
|
|
50
46
|
if (model.fields &&
|
|
@@ -59,36 +55,39 @@ function normalizeIr(obj) {
|
|
|
59
55
|
}
|
|
60
56
|
return obj;
|
|
61
57
|
}
|
|
62
|
-
|
|
63
|
-
* @param scopedNamespace If provided (e.g. 'Auth'), prefixes named types with 'models.Auth.'
|
|
64
|
-
*/
|
|
58
|
+
// ✨ FIX: Properly maps all Axiom primitives to TypeScript
|
|
65
59
|
function mapTypeToTs(typeRef, ns) {
|
|
66
|
-
if (!typeRef)
|
|
60
|
+
if (!typeRef || !typeRef.kind)
|
|
67
61
|
return "any";
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
62
|
+
switch (typeRef.kind) {
|
|
63
|
+
case "string":
|
|
64
|
+
return "string";
|
|
65
|
+
case "int32":
|
|
66
|
+
case "int64":
|
|
67
|
+
case "float32":
|
|
68
|
+
case "float64":
|
|
69
|
+
return "number";
|
|
70
|
+
case "bool":
|
|
71
|
+
return "boolean";
|
|
72
|
+
case "dateTime":
|
|
73
|
+
return "Date";
|
|
74
|
+
case "bytes":
|
|
75
|
+
return "Uint8Array";
|
|
76
|
+
case "json":
|
|
77
|
+
return "any";
|
|
78
|
+
case "void":
|
|
79
|
+
return "void";
|
|
80
|
+
case "named":
|
|
81
|
+
const name = pascalCase(typeRef.value);
|
|
82
|
+
return ns ? `${ns}.${name}` : name;
|
|
83
|
+
case "list":
|
|
84
|
+
return `${mapTypeToTs(typeRef.value, ns)}[]`;
|
|
85
|
+
case "map":
|
|
86
|
+
const valType = typeRef.value?.[1]
|
|
87
|
+
? mapTypeToTs(typeRef.value[1], ns)
|
|
88
|
+
: "any";
|
|
89
|
+
return `Record<string, ${valType}>`;
|
|
90
|
+
default:
|
|
91
|
+
return "any";
|
|
92
92
|
}
|
|
93
|
-
return "any";
|
|
94
93
|
}
|
package/package.json
CHANGED
|
@@ -1,148 +1,127 @@
|
|
|
1
|
-
// atmx-cli/src/generators/model-generator.ts
|
|
2
|
-
import { AxiomEnum, AxiomModel, MultiIR } from
|
|
3
|
-
import { pascalCase, camelCase, mapTypeToTs } from
|
|
1
|
+
// FILE: atmx-cli/src/generators/model-generator.ts
|
|
2
|
+
import { AxiomEnum, AxiomModel, MultiIR } from "../types";
|
|
3
|
+
import { pascalCase, camelCase, mapTypeToTs } from "./utils";
|
|
4
4
|
|
|
5
5
|
export function generateModels(multiIr: MultiIR): string {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
sections.push(
|
|
23
|
-
|
|
24
|
-
|
|
6
|
+
const sections: string[] = [
|
|
7
|
+
`// GENERATED CODE – DO NOT EDIT.\n/* eslint-disable @typescript-eslint/no-explicit-any */\n`,
|
|
8
|
+
];
|
|
9
|
+
|
|
10
|
+
for (const [ns, ir] of Object.entries(multiIr)) {
|
|
11
|
+
const camelNs = camelCase(ns);
|
|
12
|
+
// ✨ FIX: Use proper TS namespaces
|
|
13
|
+
sections.push(`export namespace ${camelNs} {`);
|
|
14
|
+
|
|
15
|
+
const enumsList = Array.isArray(ir.enums)
|
|
16
|
+
? ir.enums
|
|
17
|
+
: Object.values(ir.enums || {});
|
|
18
|
+
const modelsList = Array.isArray(ir.models)
|
|
19
|
+
? ir.models
|
|
20
|
+
: Object.values(ir.models || {});
|
|
21
|
+
|
|
22
|
+
enumsList.forEach((en: any) => sections.push(generateEnum(en)));
|
|
23
|
+
modelsList.forEach((model: any) =>
|
|
24
|
+
sections.push(generateInterface(model, camelNs)),
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
sections.push(`}\n`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
sections.push(generateMappers(multiIr));
|
|
31
|
+
return sections.join("\n");
|
|
25
32
|
}
|
|
26
33
|
|
|
27
|
-
function generateEnum(en: AxiomEnum
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export type ${name} = typeof ${name}[keyof typeof ${name}];
|
|
37
|
-
`;
|
|
34
|
+
function generateEnum(en: AxiomEnum): string {
|
|
35
|
+
const name = pascalCase(en.name);
|
|
36
|
+
const values = en.values.map((v) => ` ${pascalCase(v)}: "${v}"`).join(",\n");
|
|
37
|
+
return `
|
|
38
|
+
export const ${name} = {
|
|
39
|
+
${values}
|
|
40
|
+
} as const;
|
|
41
|
+
export type ${name} = typeof ${name}[keyof typeof ${name}];
|
|
42
|
+
`;
|
|
38
43
|
}
|
|
39
44
|
|
|
40
45
|
function generateInterface(model: AxiomModel, ns: string): string {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
})
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
46
|
+
const name = pascalCase(model.name);
|
|
47
|
+
const fields = model.fields
|
|
48
|
+
.map((f) => {
|
|
49
|
+
const type = mapTypeToTs(f.typeRef, ns);
|
|
50
|
+
return ` ${camelCase(f.name)}${f.isOptional ? "?" : ""}: ${type};`;
|
|
51
|
+
})
|
|
52
|
+
.join("\n");
|
|
53
|
+
|
|
54
|
+
return `
|
|
55
|
+
export interface ${name} {
|
|
50
56
|
${fields}
|
|
51
|
-
}
|
|
52
|
-
`;
|
|
57
|
+
}
|
|
58
|
+
`;
|
|
53
59
|
}
|
|
54
60
|
|
|
55
61
|
function generateMappers(multiIr: MultiIR): string {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
`obj.${camelCase(f.name)}`,
|
|
91
|
-
f.isOptional,
|
|
92
|
-
'toJson',
|
|
93
|
-
camelNs
|
|
94
|
-
)},`
|
|
95
|
-
);
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
lines.push(` })\n },`);
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
lines.push(` },`);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
lines.push(`};\n`);
|
|
105
|
-
return lines.join('\n');
|
|
62
|
+
const lines: string[] = [`export const Mappers: Record<string, any> = {`];
|
|
63
|
+
|
|
64
|
+
for (const [ns, ir] of Object.entries(multiIr)) {
|
|
65
|
+
const camelNs = camelCase(ns);
|
|
66
|
+
lines.push(` ${camelNs}: {`);
|
|
67
|
+
|
|
68
|
+
const modelsList = Array.isArray(ir.models)
|
|
69
|
+
? ir.models
|
|
70
|
+
: Object.values(ir.models || {});
|
|
71
|
+
|
|
72
|
+
modelsList.forEach((model: any) => {
|
|
73
|
+
const name = pascalCase(model.name);
|
|
74
|
+
const fullType = `${camelNs}.${name}`;
|
|
75
|
+
|
|
76
|
+
lines.push(
|
|
77
|
+
` ${name}: {\n fromJson: (json: any): ${fullType} => ({`,
|
|
78
|
+
);
|
|
79
|
+
model.fields.forEach((f: any) => {
|
|
80
|
+
lines.push(
|
|
81
|
+
` ${camelCase(f.name)}: ${generateJsonLogic(f.typeRef, `json["${f.name}"]`, f.isOptional, "fromJson", camelNs)},`,
|
|
82
|
+
);
|
|
83
|
+
});
|
|
84
|
+
lines.push(` }),\n toJson: (obj: any): any => ({`);
|
|
85
|
+
model.fields.forEach((f: any) => {
|
|
86
|
+
lines.push(
|
|
87
|
+
` "${f.name}": ${generateJsonLogic(f.typeRef, `obj.${camelCase(f.name)}`, f.isOptional, "toJson", camelNs)},`,
|
|
88
|
+
);
|
|
89
|
+
});
|
|
90
|
+
lines.push(` })\n },`);
|
|
91
|
+
});
|
|
92
|
+
lines.push(` },`);
|
|
93
|
+
}
|
|
94
|
+
lines.push(`};\n`);
|
|
95
|
+
return lines.join("\n");
|
|
106
96
|
}
|
|
107
97
|
|
|
108
98
|
function generateJsonLogic(
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
99
|
+
typeRef: any,
|
|
100
|
+
access: string,
|
|
101
|
+
isOpt: boolean,
|
|
102
|
+
mode: "fromJson" | "toJson",
|
|
103
|
+
ns: string,
|
|
114
104
|
): string {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
if (typeRef.kind === 'list') {
|
|
142
|
-
return wrap(
|
|
143
|
-
`${access}.map((e: any) => ${generateJsonLogic(typeRef.value, 'e', false, mode, ns)})`
|
|
144
|
-
);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
return access;
|
|
148
|
-
}
|
|
105
|
+
const wrap = (logic: string) =>
|
|
106
|
+
isOpt ? `(${access} == null ? undefined : ${logic})` : logic;
|
|
107
|
+
if (!typeRef || !typeRef.kind) return access;
|
|
108
|
+
if (typeRef.kind === "dateTime")
|
|
109
|
+
return mode === "fromJson"
|
|
110
|
+
? wrap(`new Date(${access})`)
|
|
111
|
+
: wrap(`${access}.toISOString()`);
|
|
112
|
+
if (typeRef.kind === "bytes")
|
|
113
|
+
return mode === "fromJson"
|
|
114
|
+
? wrap(`new Uint8Array(${access})`)
|
|
115
|
+
: wrap(`Array.from(${access})`);
|
|
116
|
+
if (typeRef.kind === "named") {
|
|
117
|
+
const name = pascalCase(typeRef.value);
|
|
118
|
+
return wrap(
|
|
119
|
+
`(Mappers.${ns}["${name}"] ? Mappers.${ns}["${name}"].${mode}(${access}) : ${access})`,
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
if (typeRef.kind === "list")
|
|
123
|
+
return wrap(
|
|
124
|
+
`${access}.map((e: any) => ${generateJsonLogic(typeRef.value, "e", false, mode, ns)})`,
|
|
125
|
+
);
|
|
126
|
+
return access;
|
|
127
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// FILE: atmx-cli/src/generators/sdk-generator.ts
|
|
2
|
-
import { AxiomEndpoint, MultiIR } from "../types";
|
|
2
|
+
import { AxiomEndpoint, AxiomParameter, MultiIR } from "../types";
|
|
3
3
|
import { pascalCase, camelCase, mapTypeToTs } from "./utils";
|
|
4
4
|
|
|
5
5
|
export function generateSdk(
|
|
@@ -8,6 +8,7 @@ export function generateSdk(
|
|
|
8
8
|
): string {
|
|
9
9
|
const lines: string[] = [
|
|
10
10
|
`// GENERATED CODE – DO NOT EDIT.`,
|
|
11
|
+
`/* eslint-disable @typescript-eslint/no-explicit-any */`,
|
|
11
12
|
`import * as models from './models';\n`,
|
|
12
13
|
];
|
|
13
14
|
|
|
@@ -17,8 +18,9 @@ export function generateSdk(
|
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
for (const [ns, ir] of Object.entries(multiIr)) {
|
|
20
|
-
const
|
|
21
|
-
|
|
21
|
+
const camelNs = camelCase(ns);
|
|
22
|
+
// ✨ FIX: Use an object literal instead of a Class to support React Hooks!
|
|
23
|
+
lines.push(`export const ${camelNs}Module = {`);
|
|
22
24
|
|
|
23
25
|
const endpointsMap = ir.endpoints || {};
|
|
24
26
|
const endpoints = Array.isArray(endpointsMap)
|
|
@@ -26,41 +28,37 @@ export function generateSdk(
|
|
|
26
28
|
: Object.values(endpointsMap);
|
|
27
29
|
|
|
28
30
|
endpoints.forEach((ep: any) => {
|
|
29
|
-
lines.push(generateEndpointMethod(ep, ns,
|
|
31
|
+
lines.push(generateEndpointMethod(ep, ns, camelNs, isReact));
|
|
30
32
|
});
|
|
31
|
-
lines.push(`}
|
|
33
|
+
lines.push(`};\n`);
|
|
32
34
|
}
|
|
33
35
|
|
|
34
|
-
|
|
36
|
+
// Generate main SDK object
|
|
37
|
+
lines.push(`export const sdk = {`);
|
|
35
38
|
for (const ns of Object.keys(multiIr)) {
|
|
36
|
-
lines.push(
|
|
37
|
-
` public readonly ${camelCase(ns)} = new ${pascalCase(ns)}Module();`,
|
|
38
|
-
);
|
|
39
|
+
lines.push(` ${camelCase(ns)}: ${camelCase(ns)}Module,`);
|
|
39
40
|
}
|
|
40
|
-
lines.push(`}
|
|
41
|
+
lines.push(`};\n`);
|
|
41
42
|
|
|
42
|
-
|
|
43
|
+
// Generate Config
|
|
44
|
+
lines.push(`export const AxiomDefaultConfig = {`);
|
|
43
45
|
lines.push(` contracts: {`);
|
|
44
46
|
for (const ns of Object.keys(multiIr)) {
|
|
45
47
|
lines.push(` "${ns}": {`);
|
|
46
48
|
lines.push(` contractUrl: "/${ns}.axiom",`);
|
|
47
|
-
lines.push(
|
|
48
|
-
` baseUrl: "http://localhost:8000" // Override this in production!`,
|
|
49
|
-
);
|
|
49
|
+
lines.push(` baseUrl: "http://localhost:8000"`);
|
|
50
50
|
lines.push(` },`);
|
|
51
51
|
}
|
|
52
52
|
lines.push(` }`);
|
|
53
53
|
lines.push(`};\n`);
|
|
54
54
|
|
|
55
55
|
return lines.join("\n");
|
|
56
|
-
|
|
57
|
-
return lines.join("\n");
|
|
58
56
|
}
|
|
59
57
|
|
|
60
58
|
function generateEndpointMethod(
|
|
61
59
|
ep: AxiomEndpoint,
|
|
62
60
|
ns: string,
|
|
63
|
-
|
|
61
|
+
camelNs: string,
|
|
64
62
|
isReact: boolean,
|
|
65
63
|
): string {
|
|
66
64
|
const rawParams = ep.parameters || [];
|
|
@@ -70,24 +68,26 @@ function generateEndpointMethod(
|
|
|
70
68
|
|
|
71
69
|
const argType =
|
|
72
70
|
params.length > 0
|
|
73
|
-
? `{ ${params.map((p: any) => `${camelCase(p.name)}${p.isOptional ? "?" : ""}: ${prefixModels(mapTypeToTs(p.typeRef,
|
|
71
|
+
? `{ ${params.map((p: any) => `${camelCase(p.name)}${p.isOptional ? "?" : ""}: ${prefixModels(mapTypeToTs(p.typeRef, camelNs))}`).join(", ")} }`
|
|
74
72
|
: "void";
|
|
75
73
|
|
|
76
74
|
const isQuery = ep.method ? ep.method.toUpperCase() === "GET" : true;
|
|
77
|
-
const rawReturnType = mapTypeToTs(ep.returnType,
|
|
75
|
+
const rawReturnType = mapTypeToTs(ep.returnType, camelNs);
|
|
78
76
|
const returnType =
|
|
79
77
|
rawReturnType === "void" || rawReturnType === "any"
|
|
80
78
|
? rawReturnType
|
|
81
79
|
: prefixModels(rawReturnType);
|
|
82
80
|
|
|
83
81
|
if (isReact) {
|
|
84
|
-
const bodyParam = params.find(
|
|
82
|
+
const bodyParam = params.find(
|
|
83
|
+
(p: any) => p.source === "body",
|
|
84
|
+
) as AxiomParameter;
|
|
85
85
|
const payloadLogic = bodyParam
|
|
86
86
|
? `const payload = (args as any)?.${camelCase(bodyParam.name)};`
|
|
87
87
|
: `const payload = undefined;`;
|
|
88
|
-
const decLogic = generateLambda(ep.returnType, "fromJson",
|
|
88
|
+
const decLogic = generateLambda(ep.returnType, "fromJson", camelNs);
|
|
89
89
|
const serLogic = bodyParam
|
|
90
|
-
? generateLambda(bodyParam.typeRef, "toJson",
|
|
90
|
+
? generateLambda(bodyParam.typeRef, "toJson", camelNs)
|
|
91
91
|
: `(p: any) => p`;
|
|
92
92
|
|
|
93
93
|
return `
|
|
@@ -105,7 +105,7 @@ function generateEndpointMethod(
|
|
|
105
105
|
serializer: ${serLogic},
|
|
106
106
|
isStream: ${ep.isStream === true}
|
|
107
107
|
};
|
|
108
|
-
}
|
|
108
|
+
},
|
|
109
109
|
|
|
110
110
|
use${pascalCase(ep.name)}${!isQuery ? "Mutation" : ""}(${isQuery ? `args${params.length > 0 ? "?" : ""}: ${argType === "void" ? "any" : argType}, options?: { enabled?: boolean }` : ""}) {
|
|
111
111
|
${
|
|
@@ -113,13 +113,13 @@ function generateEndpointMethod(
|
|
|
113
113
|
? `return useAxiomQuery<${returnType}>(this.get${pascalCase(ep.name)}Def(args), options);`
|
|
114
114
|
: `return useAxiomMutation<${returnType}, ${argType === "void" ? "void | Record<string,any>" : argType}>((args) => this.get${pascalCase(ep.name)}Def(args));`
|
|
115
115
|
}
|
|
116
|
-
}
|
|
116
|
+
},`;
|
|
117
117
|
} else {
|
|
118
118
|
return `
|
|
119
119
|
${camelCase(ep.name)}(args${params.length > 0 ? "?" : ""}: ${argType === "void" ? "any" : argType}): string {
|
|
120
120
|
const argsStr = args && Object.keys(args).length > 0 ? JSON.stringify(args) : '';
|
|
121
121
|
return \`${ns}.${ep.name}(\${argsStr})\`;
|
|
122
|
-
}
|
|
122
|
+
},`;
|
|
123
123
|
}
|
|
124
124
|
}
|
|
125
125
|
|
|
@@ -131,9 +131,9 @@ function generateLambda(
|
|
|
131
131
|
if (!typeRef || !typeRef.kind || typeRef.kind === "void")
|
|
132
132
|
return mode === "fromJson" ? `() => undefined` : `(p: any) => p`;
|
|
133
133
|
if (typeRef.kind === "list" && typeRef.value?.kind === "named")
|
|
134
|
-
return `(data: any[]) => data.map(models.Mappers.${
|
|
134
|
+
return `(data: any[]) => data.map(models.Mappers.${ns}.${pascalCase(typeRef.value.value)}.${mode})`;
|
|
135
135
|
if (typeRef.kind === "named")
|
|
136
|
-
return `models.Mappers.${
|
|
136
|
+
return `models.Mappers.${ns}.${pascalCase(typeRef.value)}.${mode}`;
|
|
137
137
|
return `(data: any) => data`;
|
|
138
138
|
}
|
|
139
139
|
|
package/src/generators/utils.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
// atmx-cli/src/generators/utils.ts
|
|
2
|
-
|
|
1
|
+
// FILE: atmx-cli/src/generators/utils.ts
|
|
3
2
|
export function pascalCase(str: string): string {
|
|
4
3
|
if (!str) return "";
|
|
5
4
|
return str
|
|
@@ -14,19 +13,13 @@ export function camelCase(str: string): string {
|
|
|
14
13
|
}
|
|
15
14
|
|
|
16
15
|
export function normalizeIr(obj: any): any {
|
|
17
|
-
|
|
18
|
-
if (Array.isArray(obj)) {
|
|
19
|
-
return obj.map(normalizeIr);
|
|
20
|
-
}
|
|
21
|
-
|
|
16
|
+
if (Array.isArray(obj)) return obj.map(normalizeIr);
|
|
22
17
|
if (obj !== null && typeof obj === "object") {
|
|
23
18
|
const newObj: any = {};
|
|
24
19
|
for (const key of Object.keys(obj)) {
|
|
25
20
|
const camelKey = key.replace(/_([a-z])/g, (g) => g[1].toUpperCase());
|
|
26
21
|
newObj[camelKey] = normalizeIr(obj[key]);
|
|
27
22
|
}
|
|
28
|
-
|
|
29
|
-
// Convert common Maps to Arrays if they exist and are not already arrays
|
|
30
23
|
if (
|
|
31
24
|
newObj.endpoints &&
|
|
32
25
|
typeof newObj.endpoints === "object" &&
|
|
@@ -48,8 +41,6 @@ export function normalizeIr(obj: any): any {
|
|
|
48
41
|
) {
|
|
49
42
|
newObj.enums = Object.values(newObj.enums);
|
|
50
43
|
}
|
|
51
|
-
|
|
52
|
-
// Traverse down into models to normalize fields
|
|
53
44
|
if (Array.isArray(newObj.models)) {
|
|
54
45
|
newObj.models = newObj.models.map((model: any) => {
|
|
55
46
|
if (
|
|
@@ -62,46 +53,44 @@ export function normalizeIr(obj: any): any {
|
|
|
62
53
|
return model;
|
|
63
54
|
});
|
|
64
55
|
}
|
|
65
|
-
|
|
66
56
|
return newObj;
|
|
67
57
|
}
|
|
68
|
-
|
|
69
58
|
return obj;
|
|
70
59
|
}
|
|
71
60
|
|
|
72
|
-
|
|
73
|
-
* @param scopedNamespace If provided (e.g. 'Auth'), prefixes named types with 'models.Auth.'
|
|
74
|
-
*/
|
|
61
|
+
// ✨ FIX: Properly maps all Axiom primitives to TypeScript
|
|
75
62
|
export function mapTypeToTs(typeRef: any, ns?: string): string {
|
|
76
|
-
if (!typeRef) return "any";
|
|
63
|
+
if (!typeRef || !typeRef.kind) return "any";
|
|
77
64
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
65
|
+
switch (typeRef.kind) {
|
|
66
|
+
case "string":
|
|
67
|
+
return "string";
|
|
68
|
+
case "int32":
|
|
69
|
+
case "int64":
|
|
70
|
+
case "float32":
|
|
71
|
+
case "float64":
|
|
72
|
+
return "number";
|
|
73
|
+
case "bool":
|
|
74
|
+
return "boolean";
|
|
75
|
+
case "dateTime":
|
|
76
|
+
return "Date";
|
|
77
|
+
case "bytes":
|
|
78
|
+
return "Uint8Array";
|
|
79
|
+
case "json":
|
|
80
|
+
return "any";
|
|
81
|
+
case "void":
|
|
82
|
+
return "void";
|
|
83
|
+
case "named":
|
|
84
|
+
const name = pascalCase(typeRef.value);
|
|
85
|
+
return ns ? `${ns}.${name}` : name;
|
|
86
|
+
case "list":
|
|
87
|
+
return `${mapTypeToTs(typeRef.value, ns)}[]`;
|
|
88
|
+
case "map":
|
|
89
|
+
const valType = typeRef.value?.[1]
|
|
90
|
+
? mapTypeToTs(typeRef.value[1], ns)
|
|
91
|
+
: "any";
|
|
92
|
+
return `Record<string, ${valType}>`;
|
|
93
|
+
default:
|
|
94
|
+
return "any";
|
|
100
95
|
}
|
|
101
|
-
|
|
102
|
-
if (typeRef.kind === "list") {
|
|
103
|
-
return `${mapTypeToTs(typeRef.value, ns)}[]`;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
return "any";
|
|
107
96
|
}
|