atmx-cli 0.32.0 → 0.34.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.
@@ -5,16 +5,18 @@ const utils_1 = require("./utils");
5
5
  function generateSdk(multiIr) {
6
6
  const lines = [
7
7
  `// GENERATED CODE – DO NOT EDIT.`,
8
- `import { useAxiomQuery, useAxiomMutation } from 'atmx-react';`,
9
- `import type { AxiomQueryDef } from 'atmx-react';`,
10
8
  `import * as models from './models';\n`,
11
9
  ];
12
10
  for (const [ns, ir] of Object.entries(multiIr)) {
13
11
  const pascalNs = (0, utils_1.pascalCase)(ns);
14
12
  lines.push(`export class ${pascalNs}Module {`);
15
- const endpoints = ir.endpoints || [];
13
+ // Support both old array format and new object map format
14
+ const endpointsMap = ir.endpoints || {};
15
+ const endpoints = Array.isArray(endpointsMap)
16
+ ? endpointsMap
17
+ : Object.values(endpointsMap);
16
18
  endpoints.forEach((ep) => {
17
- lines.push(generateEndpointHook(ep, ns, pascalNs));
19
+ lines.push(generateEndpointMethod(ep, ns, pascalNs));
18
20
  });
19
21
  lines.push(`}\n`);
20
22
  }
@@ -25,68 +27,30 @@ function generateSdk(multiIr) {
25
27
  lines.push(`}\nexport const sdk = new AxiomSdk();`);
26
28
  return lines.join("\n");
27
29
  }
28
- function generateEndpointHook(ep, ns, pascalNs) {
29
- const isQuery = ep.method ? ep.method.toUpperCase() === "GET" : true;
30
- const rawReturnType = (0, utils_1.mapTypeToTs)(ep.returnType, pascalNs);
31
- const returnType = rawReturnType === "void" || rawReturnType === "any"
32
- ? rawReturnType
33
- : prefixModels(rawReturnType);
30
+ function generateEndpointMethod(ep, ns, pascalNs) {
34
31
  const params = ep.parameters || [];
35
32
  const argType = params.length > 0
36
33
  ? `{ ${params.map((p) => `${(0, utils_1.camelCase)(p.name)}${p.isOptional ? "?" : ""}: ${prefixModels((0, utils_1.mapTypeToTs)(p.typeRef, pascalNs))}`).join(", ")} }`
37
34
  : "void";
38
- const factoryLogic = generateQueryDefFactory(ep, returnType, ns);
39
- // NEW: Every endpoint now generates BOTH a raw definition getter and a hook!
35
+ // We generate a method that takes the typed arguments and returns the exact string ATMX expects
40
36
  return `
41
- /** Raw definition for <AxQuery> or <AxMutate> */
42
- get${(0, utils_1.pascalCase)(ep.name)}Def(args: ${argType}): AxiomQueryDef<${returnType}> {
43
- return (${factoryLogic})(args);
44
- }
45
-
46
- /** Reactive hook for functional components */
47
- use${(0, utils_1.pascalCase)(ep.name)}${!isQuery ? "Mutation" : ""}(${isQuery ? `args: ${argType}, options?: { enabled?: boolean }` : ""}) {
48
- const factory = ${factoryLogic};
49
- return ${isQuery ? `useAxiomQuery<${returnType}>(factory(args), options)` : `useAxiomMutation<${returnType}, ${argType}>(factory)`};
37
+ /** RPC String Generator for <AxQuery> or <AxMutate> */
38
+ ${(0, utils_1.camelCase)(ep.name)}(args${params.length > 0 ? "" : "?"}: ${argType}): string {
39
+ const argsStr = args && Object.keys(args).length > 0 ? JSON.stringify(args) : '';
40
+ return \`${ns}.${ep.name}(\${argsStr})\`;
50
41
  }\n`;
51
42
  }
52
- function generateQueryDefFactory(ep, returnType, ns) {
53
- const params = ep.parameters || [];
54
- const bodyParam = params.find((p) => p.source === "body");
55
- const payloadLogic = bodyParam
56
- ? `const payload = (args as any).${(0, utils_1.camelCase)(bodyParam.name)};`
57
- : `const payload = undefined;`;
58
- const decLogic = generateLambda(ep.returnType, "fromJson", ns);
59
- const serLogic = bodyParam
60
- ? generateLambda(bodyParam.typeRef, "toJson", ns)
61
- : `(p: any) => p`;
62
- // THIN DEFINITION: Leaves URL logic up to ATMX Core Router
63
- return `(args: any): AxiomQueryDef<${returnType}> => {
64
- ${payloadLogic}
65
- return {
66
- namespace: "${ns}",
67
- name: "${ep.name}",
68
- endpointId: ${ep.id},
69
- method: "${ep.method ? ep.method.toUpperCase() : "GET"}",
70
- path: "${ep.path}",
71
- payload: payload,
72
- args: args as any,
73
- decoder: ${decLogic},
74
- serializer: ${serLogic},
75
- isStream: ${ep.isStream === true}
76
- };
77
- }`;
78
- }
79
- function generateLambda(typeRef, mode, ns) {
80
- if (!typeRef || !typeRef.kind || typeRef.kind === "void")
81
- return mode === "fromJson" ? `() => undefined` : `(p: any) => p`;
82
- if (typeRef.kind === "list" && typeRef.value?.kind === "named")
83
- return `(data: any[]) => data.map(models.Mappers.${(0, utils_1.camelCase)(ns)}.${(0, utils_1.pascalCase)(typeRef.value.value)}.${mode})`;
84
- if (typeRef.kind === "named")
85
- return `models.Mappers.${(0, utils_1.camelCase)(ns)}.${(0, utils_1.pascalCase)(typeRef.value)}.${mode}`;
86
- return `(data: any) => data`;
87
- }
88
43
  function prefixModels(type) {
89
- if (!type || type === "void" || type === "any")
44
+ const primitives = [
45
+ "string",
46
+ "number",
47
+ "boolean",
48
+ "Date",
49
+ "Uint8Array",
50
+ "void",
51
+ "any",
52
+ ];
53
+ if (!type || primitives.includes(type))
90
54
  return type;
91
55
  if (type.endsWith("[]"))
92
56
  return `${prefixModels(type.slice(0, -2))}[]`;
@@ -7,27 +7,55 @@ exports.normalizeIr = normalizeIr;
7
7
  exports.mapTypeToTs = mapTypeToTs;
8
8
  function pascalCase(str) {
9
9
  if (!str)
10
- return '';
10
+ return "";
11
11
  return str
12
12
  .split(/[_\-\s]+/)
13
- .map(part => part.charAt(0).toUpperCase() + part.slice(1))
14
- .join('');
13
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
14
+ .join("");
15
15
  }
16
16
  function camelCase(str) {
17
17
  const pascal = pascalCase(str);
18
18
  return pascal.charAt(0).toLowerCase() + pascal.slice(1);
19
19
  }
20
20
  function normalizeIr(obj) {
21
- if (Array.isArray(obj))
22
- return obj.map(normalizeIr);
23
- if (obj !== null && typeof obj === 'object') {
21
+ // ✨ FIX: Recursively convert Axiom Core HashMaps back into Arrays for the generator
22
+ if (obj !== null && typeof obj === "object") {
24
23
  const newObj = {};
25
24
  for (const key of Object.keys(obj)) {
26
25
  const camelKey = key.replace(/_([a-z])/g, (g) => g[1].toUpperCase());
27
26
  newObj[camelKey] = normalizeIr(obj[key]);
28
27
  }
28
+ // Convert common Maps to Arrays if they exist
29
+ if (newObj.endpoints &&
30
+ typeof newObj.endpoints === "object" &&
31
+ !Array.isArray(newObj.endpoints)) {
32
+ newObj.endpoints = Object.values(newObj.endpoints);
33
+ }
34
+ if (newObj.models &&
35
+ typeof newObj.models === "object" &&
36
+ !Array.isArray(newObj.models)) {
37
+ newObj.models = Object.values(newObj.models);
38
+ }
39
+ if (newObj.enums &&
40
+ typeof newObj.enums === "object" &&
41
+ !Array.isArray(newObj.enums)) {
42
+ newObj.enums = Object.values(newObj.enums);
43
+ }
44
+ // Traverse down into models to normalize fields
45
+ if (Array.isArray(newObj.models)) {
46
+ newObj.models = newObj.models.map((model) => {
47
+ if (model.fields &&
48
+ typeof model.fields === "object" &&
49
+ !Array.isArray(model.fields)) {
50
+ model.fields = Object.values(model.fields);
51
+ }
52
+ return model;
53
+ });
54
+ }
29
55
  return newObj;
30
56
  }
57
+ if (Array.isArray(obj))
58
+ return obj.map(normalizeIr);
31
59
  return obj;
32
60
  }
33
61
  /**
@@ -35,25 +63,31 @@ function normalizeIr(obj) {
35
63
  */
36
64
  function mapTypeToTs(typeRef, ns) {
37
65
  if (!typeRef)
38
- return 'any';
39
- if (typeRef.kind === 'primitive') {
66
+ return "any";
67
+ if (typeRef.kind === "primitive") {
40
68
  switch (typeRef.value) {
41
- case 'string': return 'string';
42
- case 'int':
43
- case 'float':
44
- case 'double': return 'number';
45
- case 'boolean': return 'boolean';
46
- case 'dateTime': return 'Date';
47
- case 'bytes': return 'Uint8Array';
48
- default: return 'any';
69
+ case "string":
70
+ return "string";
71
+ case "int":
72
+ case "float":
73
+ case "double":
74
+ return "number";
75
+ case "boolean":
76
+ return "boolean";
77
+ case "dateTime":
78
+ return "Date";
79
+ case "bytes":
80
+ return "Uint8Array";
81
+ default:
82
+ return "any";
49
83
  }
50
84
  }
51
- if (typeRef.kind === 'named') {
85
+ if (typeRef.kind === "named") {
52
86
  const name = pascalCase(typeRef.value);
53
- return ns ? `${ns}${name}` : name; // ✅ FIX HERE
87
+ return ns ? `${ns}${name}` : name;
54
88
  }
55
- if (typeRef.kind === 'list') {
89
+ if (typeRef.kind === "list") {
56
90
  return `${mapTypeToTs(typeRef.value, ns)}[]`;
57
91
  }
58
- return 'any';
92
+ return "any";
59
93
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "atmx-cli",
3
- "version": "0.32.0",
3
+ "version": "0.34.0",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
@@ -5,17 +5,21 @@ import { pascalCase, camelCase, mapTypeToTs } from "./utils";
5
5
  export function generateSdk(multiIr: MultiIR): string {
6
6
  const lines: string[] = [
7
7
  `// GENERATED CODE – DO NOT EDIT.`,
8
- `import { useAxiomQuery, useAxiomMutation } from 'atmx-react';`,
9
- `import type { AxiomQueryDef } from 'atmx-react';`,
10
8
  `import * as models from './models';\n`,
11
9
  ];
12
10
 
13
11
  for (const [ns, ir] of Object.entries(multiIr)) {
14
12
  const pascalNs = pascalCase(ns);
15
13
  lines.push(`export class ${pascalNs}Module {`);
16
- const endpoints = ir.endpoints || [];
14
+
15
+ // Support both old array format and new object map format
16
+ const endpointsMap = ir.endpoints || {};
17
+ const endpoints = Array.isArray(endpointsMap)
18
+ ? endpointsMap
19
+ : Object.values(endpointsMap);
20
+
17
21
  endpoints.forEach((ep: any) => {
18
- lines.push(generateEndpointHook(ep, ns, pascalNs));
22
+ lines.push(generateEndpointMethod(ep, ns, pascalNs));
19
23
  });
20
24
  lines.push(`}\n`);
21
25
  }
@@ -31,89 +35,38 @@ export function generateSdk(multiIr: MultiIR): string {
31
35
  return lines.join("\n");
32
36
  }
33
37
 
34
- function generateEndpointHook(
38
+ function generateEndpointMethod(
35
39
  ep: AxiomEndpoint,
36
40
  ns: string,
37
41
  pascalNs: string,
38
42
  ): string {
39
- const isQuery = ep.method ? ep.method.toUpperCase() === "GET" : true;
40
- const rawReturnType = mapTypeToTs(ep.returnType, pascalNs);
41
- const returnType =
42
- rawReturnType === "void" || rawReturnType === "any"
43
- ? rawReturnType
44
- : prefixModels(rawReturnType);
45
-
46
43
  const params = ep.parameters || [];
44
+
47
45
  const argType =
48
46
  params.length > 0
49
47
  ? `{ ${params.map((p) => `${camelCase(p.name)}${p.isOptional ? "?" : ""}: ${prefixModels(mapTypeToTs(p.typeRef, pascalNs))}`).join(", ")} }`
50
48
  : "void";
51
49
 
52
- const factoryLogic = generateQueryDefFactory(ep, returnType, ns);
53
-
54
- // NEW: Every endpoint now generates BOTH a raw definition getter and a hook!
50
+ // We generate a method that takes the typed arguments and returns the exact string ATMX expects
55
51
  return `
56
- /** Raw definition for <AxQuery> or <AxMutate> */
57
- get${pascalCase(ep.name)}Def(args: ${argType}): AxiomQueryDef<${returnType}> {
58
- return (${factoryLogic})(args);
59
- }
60
-
61
- /** Reactive hook for functional components */
62
- use${pascalCase(ep.name)}${!isQuery ? "Mutation" : ""}(${isQuery ? `args: ${argType}, options?: { enabled?: boolean }` : ""}) {
63
- const factory = ${factoryLogic};
64
- return ${isQuery ? `useAxiomQuery<${returnType}>(factory(args), options)` : `useAxiomMutation<${returnType}, ${argType}>(factory)`};
52
+ /** RPC String Generator for <AxQuery> or <AxMutate> */
53
+ ${camelCase(ep.name)}(args${params.length > 0 ? "" : "?"}: ${argType}): string {
54
+ const argsStr = args && Object.keys(args).length > 0 ? JSON.stringify(args) : '';
55
+ return \`${ns}.${ep.name}(\${argsStr})\`;
65
56
  }\n`;
66
57
  }
67
58
 
68
- function generateQueryDefFactory(
69
- ep: AxiomEndpoint,
70
- returnType: string,
71
- ns: string,
72
- ): string {
73
- const params = ep.parameters || [];
74
- const bodyParam = params.find((p) => p.source === "body");
75
- const payloadLogic = bodyParam
76
- ? `const payload = (args as any).${camelCase(bodyParam.name)};`
77
- : `const payload = undefined;`;
78
- const decLogic = generateLambda(ep.returnType, "fromJson", ns);
79
- const serLogic = bodyParam
80
- ? generateLambda(bodyParam.typeRef, "toJson", ns)
81
- : `(p: any) => p`;
82
-
83
- // THIN DEFINITION: Leaves URL logic up to ATMX Core Router
84
- return `(args: any): AxiomQueryDef<${returnType}> => {
85
- ${payloadLogic}
86
- return {
87
- namespace: "${ns}",
88
- name: "${ep.name}",
89
- endpointId: ${ep.id},
90
- method: "${ep.method ? ep.method.toUpperCase() : "GET"}",
91
- path: "${ep.path}",
92
- payload: payload,
93
- args: args as any,
94
- decoder: ${decLogic},
95
- serializer: ${serLogic},
96
- isStream: ${ep.isStream === true}
97
- };
98
- }`;
99
- }
100
-
101
- function generateLambda(
102
- typeRef: any,
103
- mode: "fromJson" | "toJson",
104
- ns: string,
105
- ): string {
106
- if (!typeRef || !typeRef.kind || typeRef.kind === "void")
107
- return mode === "fromJson" ? `() => undefined` : `(p: any) => p`;
108
- if (typeRef.kind === "list" && typeRef.value?.kind === "named")
109
- return `(data: any[]) => data.map(models.Mappers.${camelCase(ns)}.${pascalCase(typeRef.value.value)}.${mode})`;
110
- if (typeRef.kind === "named")
111
- return `models.Mappers.${camelCase(ns)}.${pascalCase(typeRef.value)}.${mode}`;
112
- return `(data: any) => data`;
113
- }
114
-
115
59
  function prefixModels(type: string): string {
116
- if (!type || type === "void" || type === "any") return type;
60
+ const primitives = [
61
+ "string",
62
+ "number",
63
+ "boolean",
64
+ "Date",
65
+ "Uint8Array",
66
+ "void",
67
+ "any",
68
+ ];
69
+ if (!type || primitives.includes(type)) return type;
117
70
  if (type.endsWith("[]")) return `${prefixModels(type.slice(0, -2))}[]`;
118
71
  if (type.startsWith("models.")) return type;
119
72
  return `models.${type}`;
@@ -1,58 +1,104 @@
1
1
  // atmx-cli/src/generators/utils.ts
2
2
 
3
3
  export function pascalCase(str: string): string {
4
- if (!str) return '';
5
- return str
6
- .split(/[_\-\s]+/)
7
- .map(part => part.charAt(0).toUpperCase() + part.slice(1))
8
- .join('');
4
+ if (!str) return "";
5
+ return str
6
+ .split(/[_\-\s]+/)
7
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
8
+ .join("");
9
9
  }
10
10
 
11
11
  export function camelCase(str: string): string {
12
- const pascal = pascalCase(str);
13
- return pascal.charAt(0).toLowerCase() + pascal.slice(1);
12
+ const pascal = pascalCase(str);
13
+ return pascal.charAt(0).toLowerCase() + pascal.slice(1);
14
14
  }
15
15
 
16
16
  export function normalizeIr(obj: any): any {
17
- if (Array.isArray(obj)) return obj.map(normalizeIr);
18
- if (obj !== null && typeof obj === 'object') {
19
- const newObj: any = {};
20
- for (const key of Object.keys(obj)) {
21
- const camelKey = key.replace(/_([a-z])/g, (g) => g[1].toUpperCase());
22
- newObj[camelKey] = normalizeIr(obj[key]);
17
+ // FIX: Recursively convert Axiom Core HashMaps back into Arrays for the generator
18
+ if (obj !== null && typeof obj === "object") {
19
+ const newObj: any = {};
20
+ for (const key of Object.keys(obj)) {
21
+ const camelKey = key.replace(/_([a-z])/g, (g) => g[1].toUpperCase());
22
+ newObj[camelKey] = normalizeIr(obj[key]);
23
+ }
24
+
25
+ // Convert common Maps to Arrays if they exist
26
+ if (
27
+ newObj.endpoints &&
28
+ typeof newObj.endpoints === "object" &&
29
+ !Array.isArray(newObj.endpoints)
30
+ ) {
31
+ newObj.endpoints = Object.values(newObj.endpoints);
32
+ }
33
+ if (
34
+ newObj.models &&
35
+ typeof newObj.models === "object" &&
36
+ !Array.isArray(newObj.models)
37
+ ) {
38
+ newObj.models = Object.values(newObj.models);
39
+ }
40
+ if (
41
+ newObj.enums &&
42
+ typeof newObj.enums === "object" &&
43
+ !Array.isArray(newObj.enums)
44
+ ) {
45
+ newObj.enums = Object.values(newObj.enums);
46
+ }
47
+
48
+ // Traverse down into models to normalize fields
49
+ if (Array.isArray(newObj.models)) {
50
+ newObj.models = newObj.models.map((model: any) => {
51
+ if (
52
+ model.fields &&
53
+ typeof model.fields === "object" &&
54
+ !Array.isArray(model.fields)
55
+ ) {
56
+ model.fields = Object.values(model.fields);
23
57
  }
24
- return newObj;
58
+ return model;
59
+ });
25
60
  }
26
- return obj;
61
+
62
+ return newObj;
63
+ }
64
+
65
+ if (Array.isArray(obj)) return obj.map(normalizeIr);
66
+ return obj;
27
67
  }
28
68
 
29
69
  /**
30
70
  * @param scopedNamespace If provided (e.g. 'Auth'), prefixes named types with 'models.Auth.'
31
71
  */
32
72
  export function mapTypeToTs(typeRef: any, ns?: string): string {
33
- if (!typeRef) return 'any';
34
-
35
- if (typeRef.kind === 'primitive') {
36
- switch (typeRef.value) {
37
- case 'string': return 'string';
38
- case 'int':
39
- case 'float':
40
- case 'double': return 'number';
41
- case 'boolean': return 'boolean';
42
- case 'dateTime': return 'Date';
43
- case 'bytes': return 'Uint8Array';
44
- default: return 'any';
45
- }
46
- }
73
+ if (!typeRef) return "any";
47
74
 
48
- if (typeRef.kind === 'named') {
49
- const name = pascalCase(typeRef.value);
50
- return ns ? `${ns}${name}` : name; // ✅ FIX HERE
75
+ if (typeRef.kind === "primitive") {
76
+ switch (typeRef.value) {
77
+ case "string":
78
+ return "string";
79
+ case "int":
80
+ case "float":
81
+ case "double":
82
+ return "number";
83
+ case "boolean":
84
+ return "boolean";
85
+ case "dateTime":
86
+ return "Date";
87
+ case "bytes":
88
+ return "Uint8Array";
89
+ default:
90
+ return "any";
51
91
  }
92
+ }
52
93
 
53
- if (typeRef.kind === 'list') {
54
- return `${mapTypeToTs(typeRef.value, ns)}[]`;
55
- }
94
+ if (typeRef.kind === "named") {
95
+ const name = pascalCase(typeRef.value);
96
+ return ns ? `${ns}${name}` : name;
97
+ }
98
+
99
+ if (typeRef.kind === "list") {
100
+ return `${mapTypeToTs(typeRef.value, ns)}[]`;
101
+ }
56
102
 
57
- return 'any';
58
- }
103
+ return "any";
104
+ }