atmx-cli 0.48.0 → 0.50.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.
@@ -4,91 +4,93 @@ 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
+ `/* eslint-disable @typescript-eslint/no-namespace */\n`, // ✨ FIX: Disable namespace lint error
8
9
  ];
9
- // 1. Generate flat exports (NO namespaces)
10
10
  for (const [ns, ir] of Object.entries(multiIr)) {
11
- const pascalNs = (0, utils_1.pascalCase)(ns);
12
- const enumsList = Array.isArray(ir.enums) ? ir.enums : Object.values(ir.enums || {});
13
- const modelsList = Array.isArray(ir.models) ? ir.models : Object.values(ir.models || {});
14
- enumsList.forEach((en) => sections.push(generateEnum(en, pascalNs)));
15
- modelsList.forEach((model) => sections.push(generateInterface(model, pascalNs)));
11
+ const camelNs = (0, utils_1.camelCase)(ns);
12
+ // FIX: Use proper TS namespaces
13
+ sections.push(`export namespace ${camelNs} {`);
14
+ const enumsList = Array.isArray(ir.enums)
15
+ ? ir.enums
16
+ : Object.values(ir.enums || {});
17
+ const modelsList = Array.isArray(ir.models)
18
+ ? ir.models
19
+ : Object.values(ir.models || {});
20
+ enumsList.forEach((en) => sections.push(generateEnum(en)));
21
+ modelsList.forEach((model) => sections.push(generateInterface(model, camelNs)));
22
+ sections.push(`}\n`);
16
23
  }
17
- // 2. Generate nested Mappers object
18
24
  sections.push(generateMappers(multiIr));
19
- return sections.join('\n');
25
+ return sections.join("\n");
20
26
  }
21
- function generateEnum(en, ns) {
22
- const name = `${ns}${(0, utils_1.pascalCase)(en.name)}`;
23
- const values = en.values.map(v => ` ${(0, utils_1.pascalCase)(v)}: "${v}"`).join(',\n');
27
+ function generateEnum(en) {
28
+ const name = (0, utils_1.pascalCase)(en.name);
29
+ const values = en.values.map((v) => ` ${(0, utils_1.pascalCase)(v)}: "${v}"`).join(",\n");
24
30
  return `
25
- export const ${name} = {
26
- ${values}
27
- } as const;
28
-
29
- export type ${name} = typeof ${name}[keyof typeof ${name}];
30
- `;
31
+ export const ${name} = {
32
+ ${values}
33
+ } as const;
34
+ export type ${name} = typeof ${name}[keyof typeof ${name}];
35
+ `;
31
36
  }
32
37
  function generateInterface(model, ns) {
33
- const name = `${ns}${(0, utils_1.pascalCase)(model.name)}`;
34
- const fields = model.fields.map(f => {
35
- const type = (0, utils_1.mapTypeToTs)(f.typeRef, ns); // ✅ pass namespace
36
- return ` ${(0, utils_1.camelCase)(f.name)}${f.isOptional ? '?' : ''}: ${type};`;
37
- }).join('\n');
38
+ const name = (0, utils_1.pascalCase)(model.name);
39
+ const fields = model.fields
40
+ .map((f) => {
41
+ const type = (0, utils_1.mapTypeToTs)(f.typeRef, ns);
42
+ return ` ${(0, utils_1.camelCase)(f.name)}${f.isOptional ? "?" : ""}: ${type};`;
43
+ })
44
+ .join("\n");
38
45
  return `
39
- export interface ${name} {
46
+ export interface ${name} {
40
47
  ${fields}
41
- }
42
- `;
48
+ }
49
+ `;
43
50
  }
44
51
  function generateMappers(multiIr) {
45
52
  const lines = [`export const Mappers: Record<string, any> = {`];
46
53
  for (const [ns, ir] of Object.entries(multiIr)) {
47
54
  const camelNs = (0, utils_1.camelCase)(ns);
48
- const pascalNs = (0, utils_1.pascalCase)(ns);
49
55
  lines.push(` ${camelNs}: {`);
50
- const modelsList = Array.isArray(ir.models) ? ir.models : Object.values(ir.models || {});
56
+ const modelsList = Array.isArray(ir.models)
57
+ ? ir.models
58
+ : Object.values(ir.models || {});
51
59
  modelsList.forEach((model) => {
52
60
  const name = (0, utils_1.pascalCase)(model.name);
53
- const fullType = `${pascalNs}${name}`; // no namespace now
61
+ const fullType = `${camelNs}.${name}`;
54
62
  lines.push(` ${name}: {\n fromJson: (json: any): ${fullType} => ({`);
55
63
  model.fields.forEach((f) => {
56
- lines.push(` ${(0, utils_1.camelCase)(f.name)}: ${generateJsonLogic(f.typeRef, `json["${f.name}"]`, f.isOptional, 'fromJson', camelNs)},`);
64
+ lines.push(` ${(0, utils_1.camelCase)(f.name)}: ${generateJsonLogic(f.typeRef, `json["${f.name}"]`, f.isOptional, "fromJson", camelNs)},`);
57
65
  });
58
66
  lines.push(` }),\n toJson: (obj: any): any => ({`);
59
67
  model.fields.forEach((f) => {
60
- lines.push(` "${f.name}": ${generateJsonLogic(f.typeRef, `obj.${(0, utils_1.camelCase)(f.name)}`, f.isOptional, 'toJson', camelNs)},`);
68
+ lines.push(` "${f.name}": ${generateJsonLogic(f.typeRef, `obj.${(0, utils_1.camelCase)(f.name)}`, f.isOptional, "toJson", camelNs)},`);
61
69
  });
62
70
  lines.push(` })\n },`);
63
71
  });
64
72
  lines.push(` },`);
65
73
  }
66
74
  lines.push(`};\n`);
67
- return lines.join('\n');
75
+ return lines.join("\n");
68
76
  }
69
77
  function generateJsonLogic(typeRef, access, isOpt, mode, ns) {
70
78
  const wrap = (logic) => isOpt ? `(${access} == null ? undefined : ${logic})` : logic;
71
79
  if (!typeRef || !typeRef.kind)
72
80
  return access;
73
- if (typeRef.kind === 'primitive') {
74
- if (typeRef.value === 'dateTime') {
75
- return mode === 'fromJson'
76
- ? wrap(`new Date(${access})`)
77
- : wrap(`${access}.toISOString()`);
78
- }
79
- if (typeRef.value === 'bytes') {
80
- return mode === 'fromJson'
81
- ? wrap(`new Uint8Array(${access})`)
82
- : wrap(`Array.from(${access})`);
83
- }
84
- return access;
85
- }
86
- if (typeRef.kind === 'named') {
81
+ if (typeRef.kind === "dateTime")
82
+ return mode === "fromJson"
83
+ ? wrap(`new Date(${access})`)
84
+ : wrap(`${access}.toISOString()`);
85
+ if (typeRef.kind === "bytes")
86
+ return mode === "fromJson"
87
+ ? wrap(`new Uint8Array(${access})`)
88
+ : wrap(`Array.from(${access})`);
89
+ if (typeRef.kind === "named") {
87
90
  const name = (0, utils_1.pascalCase)(typeRef.value);
88
91
  return wrap(`(Mappers.${ns}["${name}"] ? Mappers.${ns}["${name}"].${mode}(${access}) : ${access})`);
89
92
  }
90
- if (typeRef.kind === 'list') {
91
- return wrap(`${access}.map((e: any) => ${generateJsonLogic(typeRef.value, 'e', false, mode, ns)})`);
92
- }
93
+ if (typeRef.kind === "list")
94
+ return wrap(`${access}.map((e: any) => ${generateJsonLogic(typeRef.value, "e", false, mode, ns)})`);
93
95
  return access;
94
96
  }
@@ -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 pascalNs = (0, utils_1.pascalCase)(ns);
16
- lines.push(`export class ${pascalNs}Module {`);
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, pascalNs, isReact));
24
+ lines.push(generateEndpointMethod(ep, ns, camelNs, isReact));
23
25
  });
24
- lines.push(`}\n`);
26
+ lines.push(`};\n`);
25
27
  }
26
- lines.push(`export class AxiomSdk {`);
28
+ // Generate main SDK object
29
+ lines.push(`export const sdk = {`);
27
30
  for (const ns of Object.keys(multiIr)) {
28
- lines.push(` public readonly ${(0, utils_1.camelCase)(ns)} = new ${(0, utils_1.pascalCase)(ns)}Module();`);
31
+ lines.push(` ${(0, utils_1.camelCase)(ns)}: ${(0, utils_1.camelCase)(ns)}Module,`);
29
32
  }
30
- lines.push(`}\nexport const sdk = new AxiomSdk();`);
31
- lines.push(`\nexport const AxiomDefaultConfig = {`);
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" // Override this in production!`);
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, pascalNs, isReact) {
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, pascalNs))}`).join(", ")} }`
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, pascalNs);
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", pascalNs);
65
+ const decLogic = generateLambda(ep.returnType, "fromJson", camelNs);
63
66
  const serLogic = bodyParam
64
- ? generateLambda(bodyParam.typeRef, "toJson", pascalNs)
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
- }\n`;
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
- }\n`;
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.${(0, utils_1.camelCase)(ns)}.${(0, utils_1.pascalCase)(typeRef.value.value)}.${mode})`;
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.${(0, utils_1.camelCase)(ns)}.${(0, utils_1.pascalCase)(typeRef.value)}.${mode}`;
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) {
@@ -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
- // ✨ FIX: Arrays in JavaScript are Objects! We must check Array.isArray FIRST.
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,38 @@ 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
- */
65
58
  function mapTypeToTs(typeRef, ns) {
66
- if (!typeRef)
59
+ if (!typeRef || !typeRef.kind)
67
60
  return "any";
68
- if (typeRef.kind === "primitive") {
69
- switch (typeRef.value) {
70
- case "string":
71
- return "string";
72
- case "int":
73
- case "float":
74
- case "double":
75
- return "number";
76
- case "boolean":
77
- return "boolean";
78
- case "dateTime":
79
- return "Date";
80
- case "bytes":
81
- return "Uint8Array";
82
- default:
83
- return "any";
84
- }
85
- }
86
- if (typeRef.kind === "named") {
87
- const name = pascalCase(typeRef.value);
88
- return ns ? `${ns}${name}` : name;
89
- }
90
- if (typeRef.kind === "list") {
91
- return `${mapTypeToTs(typeRef.value, ns)}[]`;
61
+ switch (typeRef.kind) {
62
+ case "string":
63
+ return "string";
64
+ case "int32":
65
+ case "int64":
66
+ case "float32":
67
+ case "float64":
68
+ return "number";
69
+ case "bool":
70
+ return "boolean";
71
+ case "dateTime":
72
+ return "Date";
73
+ case "bytes":
74
+ return "Uint8Array";
75
+ case "void":
76
+ return "void";
77
+ case "json":
78
+ return "any";
79
+ case "named":
80
+ const name = pascalCase(typeRef.value);
81
+ return ns ? `${ns}.${name}` : name;
82
+ case "list":
83
+ return `${mapTypeToTs(typeRef.value, ns)}[]`;
84
+ case "map":
85
+ const valType = typeRef.value?.[1]
86
+ ? mapTypeToTs(typeRef.value[1], ns)
87
+ : "any";
88
+ return `Record<string, ${valType}>`;
89
+ default:
90
+ return "any";
92
91
  }
93
- return "any";
94
92
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "atmx-cli",
3
- "version": "0.48.0",
3
+ "version": "0.50.0",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
@@ -1,148 +1,128 @@
1
- // atmx-cli/src/generators/model-generator.ts
2
- import { AxiomEnum, AxiomModel, MultiIR } from '../types';
3
- import { pascalCase, camelCase, mapTypeToTs } from './utils';
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
- const sections: string[] = [
7
- `// GENERATED CODE – DO NOT EDIT.\n/* eslint-disable @typescript-eslint/no-explicit-any */\n`
8
- ];
9
-
10
- // 1. Generate flat exports (NO namespaces)
11
- for (const [ns, ir] of Object.entries(multiIr)) {
12
- const pascalNs = pascalCase(ns);
13
-
14
- const enumsList = Array.isArray(ir.enums) ? ir.enums : Object.values(ir.enums || {});
15
- const modelsList = Array.isArray(ir.models) ? ir.models : Object.values(ir.models || {});
16
-
17
- enumsList.forEach((en: any) => sections.push(generateEnum(en, pascalNs)));
18
- modelsList.forEach((model: any) => sections.push(generateInterface(model, pascalNs)));
19
- }
20
-
21
- // 2. Generate nested Mappers object
22
- sections.push(generateMappers(multiIr));
23
-
24
- return sections.join('\n');
6
+ const sections: string[] = [
7
+ `// GENERATED CODE – DO NOT EDIT.\n/* eslint-disable @typescript-eslint/no-explicit-any */\n`,
8
+ `/* eslint-disable @typescript-eslint/no-namespace */\n`, // ✨ FIX: Disable namespace lint error
9
+ ];
10
+
11
+ for (const [ns, ir] of Object.entries(multiIr)) {
12
+ const camelNs = camelCase(ns);
13
+ // ✨ FIX: Use proper TS namespaces
14
+ sections.push(`export namespace ${camelNs} {`);
15
+
16
+ const enumsList = Array.isArray(ir.enums)
17
+ ? ir.enums
18
+ : Object.values(ir.enums || {});
19
+ const modelsList = Array.isArray(ir.models)
20
+ ? ir.models
21
+ : Object.values(ir.models || {});
22
+
23
+ enumsList.forEach((en: any) => sections.push(generateEnum(en)));
24
+ modelsList.forEach((model: any) =>
25
+ sections.push(generateInterface(model, camelNs)),
26
+ );
27
+
28
+ sections.push(`}\n`);
29
+ }
30
+
31
+ sections.push(generateMappers(multiIr));
32
+ return sections.join("\n");
25
33
  }
26
34
 
27
- function generateEnum(en: AxiomEnum, ns: string): string {
28
- const name = `${ns}${pascalCase(en.name)}`;
29
- const values = en.values.map(v => ` ${pascalCase(v)}: "${v}"`).join(',\n');
30
-
31
- return `
32
- export const ${name} = {
33
- ${values}
34
- } as const;
35
-
36
- export type ${name} = typeof ${name}[keyof typeof ${name}];
37
- `;
35
+ function generateEnum(en: AxiomEnum): string {
36
+ const name = pascalCase(en.name);
37
+ const values = en.values.map((v) => ` ${pascalCase(v)}: "${v}"`).join(",\n");
38
+ return `
39
+ export const ${name} = {
40
+ ${values}
41
+ } as const;
42
+ export type ${name} = typeof ${name}[keyof typeof ${name}];
43
+ `;
38
44
  }
39
45
 
40
46
  function generateInterface(model: AxiomModel, ns: string): string {
41
- const name = `${ns}${pascalCase(model.name)}`;
42
-
43
- const fields = model.fields.map(f => {
44
- const type = mapTypeToTs(f.typeRef, ns); // ✅ pass namespace
45
- return ` ${camelCase(f.name)}${f.isOptional ? '?' : ''}: ${type};`;
46
- }).join('\n');
47
-
48
- return `
49
- export interface ${name} {
47
+ const name = pascalCase(model.name);
48
+ const fields = model.fields
49
+ .map((f) => {
50
+ const type = mapTypeToTs(f.typeRef, ns);
51
+ return ` ${camelCase(f.name)}${f.isOptional ? "?" : ""}: ${type};`;
52
+ })
53
+ .join("\n");
54
+
55
+ return `
56
+ export interface ${name} {
50
57
  ${fields}
51
- }
52
- `;
58
+ }
59
+ `;
53
60
  }
54
61
 
55
62
  function generateMappers(multiIr: MultiIR): string {
56
- const lines: string[] = [`export const Mappers: Record<string, any> = {`];
57
-
58
- for (const [ns, ir] of Object.entries(multiIr)) {
59
- const camelNs = camelCase(ns);
60
- const pascalNs = pascalCase(ns);
61
-
62
- lines.push(` ${camelNs}: {`);
63
-
64
- const modelsList = Array.isArray(ir.models) ? ir.models : Object.values(ir.models || {});
65
-
66
- modelsList.forEach((model: any) => {
67
- const name = pascalCase(model.name);
68
- const fullType = `${pascalNs}${name}`; // no namespace now
69
-
70
- lines.push(` ${name}: {\n fromJson: (json: any): ${fullType} => ({`);
71
-
72
- model.fields.forEach((f: any) => {
73
- lines.push(
74
- ` ${camelCase(f.name)}: ${generateJsonLogic(
75
- f.typeRef,
76
- `json["${f.name}"]`,
77
- f.isOptional,
78
- 'fromJson',
79
- camelNs
80
- )},`
81
- );
82
- });
83
-
84
- lines.push(` }),\n toJson: (obj: any): any => ({`);
85
-
86
- model.fields.forEach((f: any) => {
87
- lines.push(
88
- ` "${f.name}": ${generateJsonLogic(
89
- f.typeRef,
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');
63
+ const lines: string[] = [`export const Mappers: Record<string, any> = {`];
64
+
65
+ for (const [ns, ir] of Object.entries(multiIr)) {
66
+ const camelNs = camelCase(ns);
67
+ lines.push(` ${camelNs}: {`);
68
+
69
+ const modelsList = Array.isArray(ir.models)
70
+ ? ir.models
71
+ : Object.values(ir.models || {});
72
+
73
+ modelsList.forEach((model: any) => {
74
+ const name = pascalCase(model.name);
75
+ const fullType = `${camelNs}.${name}`;
76
+
77
+ lines.push(
78
+ ` ${name}: {\n fromJson: (json: any): ${fullType} => ({`,
79
+ );
80
+ model.fields.forEach((f: any) => {
81
+ lines.push(
82
+ ` ${camelCase(f.name)}: ${generateJsonLogic(f.typeRef, `json["${f.name}"]`, f.isOptional, "fromJson", camelNs)},`,
83
+ );
84
+ });
85
+ lines.push(` }),\n toJson: (obj: any): any => ({`);
86
+ model.fields.forEach((f: any) => {
87
+ lines.push(
88
+ ` "${f.name}": ${generateJsonLogic(f.typeRef, `obj.${camelCase(f.name)}`, f.isOptional, "toJson", camelNs)},`,
89
+ );
90
+ });
91
+ lines.push(` })\n },`);
92
+ });
93
+ lines.push(` },`);
94
+ }
95
+ lines.push(`};\n`);
96
+ return lines.join("\n");
106
97
  }
107
98
 
108
99
  function generateJsonLogic(
109
- typeRef: any,
110
- access: string,
111
- isOpt: boolean,
112
- mode: 'fromJson' | 'toJson',
113
- ns: string
100
+ typeRef: any,
101
+ access: string,
102
+ isOpt: boolean,
103
+ mode: "fromJson" | "toJson",
104
+ ns: string,
114
105
  ): string {
115
- const wrap = (logic: string) =>
116
- isOpt ? `(${access} == null ? undefined : ${logic})` : logic;
117
-
118
- if (!typeRef || !typeRef.kind) return access;
119
-
120
- if (typeRef.kind === 'primitive') {
121
- if (typeRef.value === 'dateTime') {
122
- return mode === 'fromJson'
123
- ? wrap(`new Date(${access})`)
124
- : wrap(`${access}.toISOString()`);
125
- }
126
- if (typeRef.value === 'bytes') {
127
- return mode === 'fromJson'
128
- ? wrap(`new Uint8Array(${access})`)
129
- : wrap(`Array.from(${access})`);
130
- }
131
- return access;
132
- }
133
-
134
- if (typeRef.kind === 'named') {
135
- const name = pascalCase(typeRef.value);
136
- return wrap(
137
- `(Mappers.${ns}["${name}"] ? Mappers.${ns}["${name}"].${mode}(${access}) : ${access})`
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
- }
106
+ const wrap = (logic: string) =>
107
+ isOpt ? `(${access} == null ? undefined : ${logic})` : logic;
108
+ if (!typeRef || !typeRef.kind) return access;
109
+ if (typeRef.kind === "dateTime")
110
+ return mode === "fromJson"
111
+ ? wrap(`new Date(${access})`)
112
+ : wrap(`${access}.toISOString()`);
113
+ if (typeRef.kind === "bytes")
114
+ return mode === "fromJson"
115
+ ? wrap(`new Uint8Array(${access})`)
116
+ : wrap(`Array.from(${access})`);
117
+ if (typeRef.kind === "named") {
118
+ const name = pascalCase(typeRef.value);
119
+ return wrap(
120
+ `(Mappers.${ns}["${name}"] ? Mappers.${ns}["${name}"].${mode}(${access}) : ${access})`,
121
+ );
122
+ }
123
+ if (typeRef.kind === "list")
124
+ return wrap(
125
+ `${access}.map((e: any) => ${generateJsonLogic(typeRef.value, "e", false, mode, ns)})`,
126
+ );
127
+ return access;
128
+ }
@@ -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 pascalNs = pascalCase(ns);
21
- lines.push(`export class ${pascalNs}Module {`);
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, pascalNs, isReact));
31
+ lines.push(generateEndpointMethod(ep, ns, camelNs, isReact));
30
32
  });
31
- lines.push(`}\n`);
33
+ lines.push(`};\n`);
32
34
  }
33
35
 
34
- lines.push(`export class AxiomSdk {`);
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(`}\nexport const sdk = new AxiomSdk();`);
41
+ lines.push(`};\n`);
41
42
 
42
- lines.push(`\nexport const AxiomDefaultConfig = {`);
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
- pascalNs: string,
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, pascalNs))}`).join(", ")} }`
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, pascalNs);
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((p: any) => p.source === "body") as any;
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", pascalNs);
88
+ const decLogic = generateLambda(ep.returnType, "fromJson", camelNs);
89
89
  const serLogic = bodyParam
90
- ? generateLambda(bodyParam.typeRef, "toJson", pascalNs)
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
- }\n`;
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
- }\n`;
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.${camelCase(ns)}.${pascalCase(typeRef.value.value)}.${mode})`;
134
+ return `(data: any[]) => data.map(models.Mappers.${ns}.${pascalCase(typeRef.value.value)}.${mode})`;
135
135
  if (typeRef.kind === "named")
136
- return `models.Mappers.${camelCase(ns)}.${pascalCase(typeRef.value)}.${mode}`;
136
+ return `models.Mappers.${ns}.${pascalCase(typeRef.value)}.${mode}`;
137
137
  return `(data: any) => data`;
138
138
  }
139
139
 
@@ -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
- // ✨ FIX: Arrays in JavaScript are Objects! We must check Array.isArray FIRST.
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,43 @@ 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
- */
75
61
  export function mapTypeToTs(typeRef: any, ns?: string): string {
76
- if (!typeRef) return "any";
62
+ if (!typeRef || !typeRef.kind) return "any";
77
63
 
78
- if (typeRef.kind === "primitive") {
79
- switch (typeRef.value) {
80
- case "string":
81
- return "string";
82
- case "int":
83
- case "float":
84
- case "double":
85
- return "number";
86
- case "boolean":
87
- return "boolean";
88
- case "dateTime":
89
- return "Date";
90
- case "bytes":
91
- return "Uint8Array";
92
- default:
93
- return "any";
94
- }
95
- }
96
-
97
- if (typeRef.kind === "named") {
98
- const name = pascalCase(typeRef.value);
99
- return ns ? `${ns}${name}` : name;
64
+ switch (typeRef.kind) {
65
+ case "string":
66
+ return "string";
67
+ case "int32":
68
+ case "int64":
69
+ case "float32":
70
+ case "float64":
71
+ return "number";
72
+ case "bool":
73
+ return "boolean";
74
+ case "dateTime":
75
+ return "Date";
76
+ case "bytes":
77
+ return "Uint8Array";
78
+ case "void":
79
+ return "void";
80
+ case "json":
81
+ return "any";
82
+ case "named":
83
+ const name = pascalCase(typeRef.value);
84
+ return ns ? `${ns}.${name}` : name;
85
+ case "list":
86
+ return `${mapTypeToTs(typeRef.value, ns)}[]`;
87
+ case "map":
88
+ const valType = typeRef.value?.[1]
89
+ ? mapTypeToTs(typeRef.value[1], ns)
90
+ : "any";
91
+ return `Record<string, ${valType}>`;
92
+ default:
93
+ return "any";
100
94
  }
101
-
102
- if (typeRef.kind === "list") {
103
- return `${mapTypeToTs(typeRef.value, ns)}[]`;
104
- }
105
-
106
- return "any";
107
95
  }