atmx-cli 0.137.0 → 0.139.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.
@@ -19,20 +19,44 @@ function generateModels(multiIr) {
19
19
  : Object.values(ir.models || {});
20
20
  enumsList.forEach((en) => sections.push(generateEnum(en)));
21
21
  modelsList.forEach((model) => sections.push(generateInterface(model, camelNs)));
22
+ sections.push(generateDomainProjectionTypes(ir.domain, modelsList));
22
23
  sections.push(`}\n`);
23
24
  }
24
25
  sections.push(generateMappers(multiIr));
25
26
  return sections.join("\n");
26
27
  }
28
+ function generateDomainProjectionTypes(domain, models) {
29
+ if (!domain?.projections || !domain?.entities)
30
+ return "";
31
+ const entities = domain.entities || {};
32
+ const projections = domain.projections || {};
33
+ const availableModels = new Set(models.map((model) => String(model?.name || "")));
34
+ const lines = [" export namespace Domain {"];
35
+ let count = 0;
36
+ for (const [name, projection] of Object.entries(projections)) {
37
+ const entity = entities[projection.entity];
38
+ const model = entity?.model;
39
+ const fields = Array.isArray(projection.fields) ? projection.fields : [];
40
+ if (!model || !availableModels.has(model) || fields.length === 0)
41
+ continue;
42
+ const fieldUnion = fields.map((field) => JSON.stringify((0, utils_1.camelCase)(field))).join(" | ");
43
+ lines.push(` export type ${(0, utils_1.pascalCase)(name)} = Pick<${(0, utils_1.pascalCase)(model)}, ${fieldUnion}>;`);
44
+ count += 1;
45
+ }
46
+ lines.push(" }");
47
+ return count ? lines.join("\n") : "";
48
+ }
27
49
  function generateEnum(en) {
28
50
  const name = (0, utils_1.pascalCase)(en.name);
29
51
  const values = en.values.map((v) => ` ${(0, utils_1.pascalCase)(v)}: "${v}"`).join(",\n");
30
- return `
31
- export const ${name} = {
32
- ${values}
33
- } as const;
34
- export type ${name} = typeof ${name}[keyof typeof ${name}];
35
- `;
52
+ return [
53
+ "",
54
+ ` export const ${name} = {`,
55
+ values,
56
+ " } as const;",
57
+ ` export type ${name} = typeof ${name}[keyof typeof ${name}];`,
58
+ "",
59
+ ].join("\n");
36
60
  }
37
61
  function generateInterface(model, ns) {
38
62
  const name = (0, utils_1.pascalCase)(model.name);
@@ -42,11 +66,7 @@ function generateInterface(model, ns) {
42
66
  return ` ${(0, utils_1.camelCase)(f.name)}${f.isOptional ? "?" : ""}: ${type};`;
43
67
  })
44
68
  .join("\n");
45
- return `
46
- export interface ${name} {
47
- ${fields}
48
- }
49
- `;
69
+ return ["", ` export interface ${name} {`, fields, " }", ""].join("\n");
50
70
  }
51
71
  function generateMappers(multiIr) {
52
72
  const lines = [`export const Mappers: Record<string, any> = {`];
@@ -16,7 +16,9 @@ function moduleIdentifier(namespace) {
16
16
  return `${namespaceIdentifier(namespace)}Module`;
17
17
  }
18
18
  // Helper to convert Axiom TypeRef to TypeScript Types
19
- function getTsType(namespace, typeRef) {
19
+ function getTsType(namespace, typeRef, projection) {
20
+ if (projection)
21
+ return `models.${namespaceIdentifier(namespace)}.Domain.${(0, utils_js_1.pascalCase)(projection)}`;
20
22
  if (!typeRef)
21
23
  return "any";
22
24
  if (typeRef.kind === "named") {
@@ -128,8 +130,10 @@ function generateSDKContent(contracts, isReact) {
128
130
  const fnName = endpoint.name.replace(/_([a-z])/g, (g) => g[1].toUpperCase());
129
131
  const capFnName = fnName.charAt(0).toUpperCase() + fnName.slice(1);
130
132
  if (isReact) {
131
- const tsType = getTsType(namespace, endpoint.returnType);
132
- const decoder = getDecoder(namespace, endpoint.returnType);
133
+ const tsType = getTsType(namespace, endpoint.returnType, endpoint.responseProjection);
134
+ const decoder = endpoint.responseProjection
135
+ ? `(json: any) => json as ${tsType}`
136
+ : getDecoder(namespace, endpoint.returnType);
133
137
  content += ` get${capFnName}Def(\n`;
134
138
  content += ` args?: Record<string, any>,\n`;
135
139
  content += ` ): AxiomQueryDef<${tsType}> {\n`;
@@ -26,22 +26,22 @@ function normalizeIr(obj) {
26
26
  const camelKey = key.replace(/_([a-z])/g, (g) => g[1].toUpperCase());
27
27
  newObj[camelKey] = normalizeIr(obj[key]);
28
28
  }
29
- if (newObj.endpoints &&
30
- typeof newObj.endpoints === "object" &&
31
- !Array.isArray(newObj.endpoints)) {
29
+ // Only the IR root owns endpoint/model/enum maps. A previous generic
30
+ // conversion treated *any* field named `endpoints` as an IR map, which
31
+ // corrupted valid model fields such as AgentSemanticView.endpoints into an
32
+ // array of field-property values. Keep the structural normalization tied
33
+ // to the actual IR shape.
34
+ const isIrRoot = typeof newObj.serviceName === "string";
35
+ if (isIrRoot && newObj.endpoints && typeof newObj.endpoints === "object" && !Array.isArray(newObj.endpoints)) {
32
36
  newObj.endpoints = Object.values(newObj.endpoints);
33
37
  }
34
- if (newObj.models &&
35
- typeof newObj.models === "object" &&
36
- !Array.isArray(newObj.models)) {
38
+ if (isIrRoot && newObj.models && typeof newObj.models === "object" && !Array.isArray(newObj.models)) {
37
39
  newObj.models = Object.values(newObj.models);
38
40
  }
39
- if (newObj.enums &&
40
- typeof newObj.enums === "object" &&
41
- !Array.isArray(newObj.enums)) {
41
+ if (isIrRoot && newObj.enums && typeof newObj.enums === "object" && !Array.isArray(newObj.enums)) {
42
42
  newObj.enums = Object.values(newObj.enums);
43
43
  }
44
- if (Array.isArray(newObj.models)) {
44
+ if (isIrRoot && Array.isArray(newObj.models)) {
45
45
  newObj.models = newObj.models.map((model) => {
46
46
  if (model.fields &&
47
47
  typeof model.fields === "object" &&
package/dist/index.js CHANGED
@@ -81,6 +81,9 @@ program
81
81
  if (!rawFile.ir)
82
82
  continue;
83
83
  multiIr[namespace] = (0, utils_1.normalizeIr)(rawFile.ir);
84
+ // Domain Model v1 is carried inside `ir` and drives generated projection
85
+ // types. The immutable artifact remains the source of truth; the TOML
86
+ // file continues to contain only dependency and verification metadata.
84
87
  // ✨ NEW: Combine IR with TOML config for the SDK generator
85
88
  generatorPayload[namespace] = {
86
89
  ir: multiIr[namespace],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "atmx-cli",
3
- "version": "0.137.0",
3
+ "version": "0.139.0",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
@@ -1,5 +1,5 @@
1
1
  // FILE: atmx-cli/src/generators/model-generator.ts
2
- import { AxiomEnum, AxiomModel, MultiIR } from "../types";
2
+ import { AxiomDomainModel, AxiomEnum, AxiomModel, MultiIR } from "../types";
3
3
  import { pascalCase, camelCase, mapTypeToTs } from "./utils";
4
4
 
5
5
  export function generateModels(multiIr: MultiIR): string {
@@ -24,6 +24,7 @@ export function generateModels(multiIr: MultiIR): string {
24
24
  modelsList.forEach((model: any) =>
25
25
  sections.push(generateInterface(model, camelNs)),
26
26
  );
27
+ sections.push(generateDomainProjectionTypes(ir.domain, modelsList));
27
28
 
28
29
  sections.push(`}\n`);
29
30
  }
@@ -32,15 +33,37 @@ export function generateModels(multiIr: MultiIR): string {
32
33
  return sections.join("\n");
33
34
  }
34
35
 
36
+ function generateDomainProjectionTypes(domain: AxiomDomainModel | undefined, models: any[]): string {
37
+ if (!domain?.projections || !domain?.entities) return "";
38
+ const entities = domain.entities || {};
39
+ const projections = domain.projections || {};
40
+ const availableModels = new Set(models.map((model) => String(model?.name || "")));
41
+ const lines = [" export namespace Domain {"];
42
+ let count = 0;
43
+ for (const [name, projection] of Object.entries(projections)) {
44
+ const entity = entities[(projection as any).entity];
45
+ const model = entity?.model;
46
+ const fields = Array.isArray((projection as any).fields) ? (projection as any).fields : [];
47
+ if (!model || !availableModels.has(model) || fields.length === 0) continue;
48
+ const fieldUnion = fields.map((field: string) => JSON.stringify(camelCase(field))).join(" | ");
49
+ lines.push(` export type ${pascalCase(name)} = Pick<${pascalCase(model)}, ${fieldUnion}>;`);
50
+ count += 1;
51
+ }
52
+ lines.push(" }");
53
+ return count ? lines.join("\n") : "";
54
+ }
55
+
35
56
  function generateEnum(en: AxiomEnum): string {
36
57
  const name = pascalCase(en.name);
37
58
  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
- `;
59
+ return [
60
+ "",
61
+ ` export const ${name} = {`,
62
+ values,
63
+ " } as const;",
64
+ ` export type ${name} = typeof ${name}[keyof typeof ${name}];`,
65
+ "",
66
+ ].join("\n");
44
67
  }
45
68
 
46
69
  function generateInterface(model: AxiomModel, ns: string): string {
@@ -52,11 +75,7 @@ function generateInterface(model: AxiomModel, ns: string): string {
52
75
  })
53
76
  .join("\n");
54
77
 
55
- return `
56
- export interface ${name} {
57
- ${fields}
58
- }
59
- `;
78
+ return ["", ` export interface ${name} {`, fields, " }", ""].join("\n");
60
79
  }
61
80
 
62
81
  function generateMappers(multiIr: MultiIR): string {
@@ -1,5 +1,5 @@
1
1
  import { AxiomIR, AxiomEndpoint, AxiomTypeRef } from "../types.js";
2
- import { camelCase } from "./utils.js";
2
+ import { camelCase, pascalCase } from "./utils.js";
3
3
 
4
4
  export interface ContractPayload {
5
5
  ir: AxiomIR;
@@ -24,7 +24,8 @@ function moduleIdentifier(namespace: string): string {
24
24
  }
25
25
 
26
26
  // Helper to convert Axiom TypeRef to TypeScript Types
27
- function getTsType(namespace: string, typeRef?: AxiomTypeRef): string {
27
+ function getTsType(namespace: string, typeRef?: AxiomTypeRef, projection?: string): string {
28
+ if (projection) return `models.${namespaceIdentifier(namespace)}.Domain.${pascalCase(projection)}`;
28
29
  if (!typeRef) return "any";
29
30
  if (typeRef.kind === "named") {
30
31
  return `models.${namespaceIdentifier(namespace)}.${typeRef.value}`;
@@ -139,8 +140,10 @@ export function generateSDKContent(
139
140
  const capFnName = fnName.charAt(0).toUpperCase() + fnName.slice(1);
140
141
 
141
142
  if (isReact) {
142
- const tsType = getTsType(namespace, endpoint.returnType);
143
- const decoder = getDecoder(namespace, endpoint.returnType);
143
+ const tsType = getTsType(namespace, endpoint.returnType, endpoint.responseProjection);
144
+ const decoder = endpoint.responseProjection
145
+ ? `(json: any) => json as ${tsType}`
146
+ : getDecoder(namespace, endpoint.returnType);
144
147
 
145
148
  content += ` get${capFnName}Def(\n`;
146
149
  content += ` args?: Record<string, any>,\n`;
@@ -20,28 +20,22 @@ export function normalizeIr(obj: any): any {
20
20
  const camelKey = key.replace(/_([a-z])/g, (g) => g[1].toUpperCase());
21
21
  newObj[camelKey] = normalizeIr(obj[key]);
22
22
  }
23
- if (
24
- newObj.endpoints &&
25
- typeof newObj.endpoints === "object" &&
26
- !Array.isArray(newObj.endpoints)
27
- ) {
23
+ // Only the IR root owns endpoint/model/enum maps. A previous generic
24
+ // conversion treated *any* field named `endpoints` as an IR map, which
25
+ // corrupted valid model fields such as AgentSemanticView.endpoints into an
26
+ // array of field-property values. Keep the structural normalization tied
27
+ // to the actual IR shape.
28
+ const isIrRoot = typeof newObj.serviceName === "string";
29
+ if (isIrRoot && newObj.endpoints && typeof newObj.endpoints === "object" && !Array.isArray(newObj.endpoints)) {
28
30
  newObj.endpoints = Object.values(newObj.endpoints);
29
31
  }
30
- if (
31
- newObj.models &&
32
- typeof newObj.models === "object" &&
33
- !Array.isArray(newObj.models)
34
- ) {
32
+ if (isIrRoot && newObj.models && typeof newObj.models === "object" && !Array.isArray(newObj.models)) {
35
33
  newObj.models = Object.values(newObj.models);
36
34
  }
37
- if (
38
- newObj.enums &&
39
- typeof newObj.enums === "object" &&
40
- !Array.isArray(newObj.enums)
41
- ) {
35
+ if (isIrRoot && newObj.enums && typeof newObj.enums === "object" && !Array.isArray(newObj.enums)) {
42
36
  newObj.enums = Object.values(newObj.enums);
43
37
  }
44
- if (Array.isArray(newObj.models)) {
38
+ if (isIrRoot && Array.isArray(newObj.models)) {
45
39
  newObj.models = newObj.models.map((model: any) => {
46
40
  if (
47
41
  model.fields &&
package/src/index.ts CHANGED
@@ -66,6 +66,10 @@ program
66
66
 
67
67
  multiIr[namespace] = normalizeIr(rawFile.ir);
68
68
 
69
+ // Domain Model v1 is carried inside `ir` and drives generated projection
70
+ // types. The immutable artifact remains the source of truth; the TOML
71
+ // file continues to contain only dependency and verification metadata.
72
+
69
73
  // ✨ NEW: Combine IR with TOML config for the SDK generator
70
74
  generatorPayload[namespace] = {
71
75
  ir: multiIr[namespace],
package/src/types.ts CHANGED
@@ -3,6 +3,7 @@ export interface AxiomIR {
3
3
  endpoints: AxiomEndpoint[];
4
4
  models: Record<string, AxiomModel>;
5
5
  enums: Record<string, AxiomEnum>;
6
+ domain?: AxiomDomainModel;
6
7
  }
7
8
 
8
9
  export interface AxiomEndpoint {
@@ -14,6 +15,8 @@ export interface AxiomEndpoint {
14
15
  returnType: AxiomTypeRef;
15
16
  returnIsOptional: boolean;
16
17
  isStream: boolean;
18
+ requestProjection?: string;
19
+ responseProjection?: string;
17
20
  }
18
21
 
19
22
  export interface AxiomParameter {
@@ -45,6 +48,24 @@ export interface AxiomEnum {
45
48
  values: string[];
46
49
  }
47
50
 
51
+ export interface AxiomDomainModel {
52
+ entities: Record<string, AxiomDomainEntity>;
53
+ projections: Record<string, AxiomDomainProjection>;
54
+ }
55
+
56
+ export interface AxiomDomainEntity {
57
+ id?: string;
58
+ model: string;
59
+ key: string[];
60
+ }
61
+
62
+ export interface AxiomDomainProjection {
63
+ id?: string;
64
+ entity: string;
65
+ fields: string[];
66
+ audience: string;
67
+ }
68
+
48
69
  export interface AtmxContractConfig {
49
70
  file: string; // Path relative to the config file (e.g., "./auth.axiom")
50
71
  baseUrl: string; // The URL for runtime (not used during code generation, but part of schema)