@auto-engineer/narrative 0.16.0 → 0.17.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.
Files changed (87) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +15 -0
  3. package/dist/src/getNarratives.js +1 -1
  4. package/dist/src/getNarratives.js.map +1 -1
  5. package/dist/src/id/addAutoIds.d.ts.map +1 -1
  6. package/dist/src/id/addAutoIds.js +15 -0
  7. package/dist/src/id/addAutoIds.js.map +1 -1
  8. package/dist/src/id/hasAllIds.d.ts.map +1 -1
  9. package/dist/src/id/hasAllIds.js +6 -1
  10. package/dist/src/id/hasAllIds.js.map +1 -1
  11. package/dist/src/index.d.ts +5 -2
  12. package/dist/src/index.d.ts.map +1 -1
  13. package/dist/src/index.js +1 -1
  14. package/dist/src/index.js.map +1 -1
  15. package/dist/src/loader/runtime-cjs.d.ts.map +1 -1
  16. package/dist/src/loader/runtime-cjs.js +3 -2
  17. package/dist/src/loader/runtime-cjs.js.map +1 -1
  18. package/dist/src/schema.d.ts +301 -143
  19. package/dist/src/schema.d.ts.map +1 -1
  20. package/dist/src/schema.js +26 -0
  21. package/dist/src/schema.js.map +1 -1
  22. package/dist/src/transformers/model-to-narrative/cross-module-imports.d.ts +6 -0
  23. package/dist/src/transformers/model-to-narrative/cross-module-imports.d.ts.map +1 -0
  24. package/dist/src/transformers/model-to-narrative/cross-module-imports.js +63 -0
  25. package/dist/src/transformers/model-to-narrative/cross-module-imports.js.map +1 -0
  26. package/dist/src/transformers/model-to-narrative/generators/flow.d.ts +1 -4
  27. package/dist/src/transformers/model-to-narrative/generators/flow.d.ts.map +1 -1
  28. package/dist/src/transformers/model-to-narrative/generators/flow.js.map +1 -1
  29. package/dist/src/transformers/model-to-narrative/generators/module-code.d.ts +9 -0
  30. package/dist/src/transformers/model-to-narrative/generators/module-code.d.ts.map +1 -0
  31. package/dist/src/transformers/model-to-narrative/generators/module-code.js +102 -0
  32. package/dist/src/transformers/model-to-narrative/generators/module-code.js.map +1 -0
  33. package/dist/src/transformers/model-to-narrative/index.d.ts +6 -4
  34. package/dist/src/transformers/model-to-narrative/index.d.ts.map +1 -1
  35. package/dist/src/transformers/model-to-narrative/index.js +10 -6
  36. package/dist/src/transformers/model-to-narrative/index.js.map +1 -1
  37. package/dist/src/transformers/model-to-narrative/ordering.d.ts +10 -0
  38. package/dist/src/transformers/model-to-narrative/ordering.d.ts.map +1 -0
  39. package/dist/src/transformers/model-to-narrative/ordering.js +37 -0
  40. package/dist/src/transformers/model-to-narrative/ordering.js.map +1 -0
  41. package/dist/src/transformers/model-to-narrative/spec-traversal.d.ts +3 -0
  42. package/dist/src/transformers/model-to-narrative/spec-traversal.d.ts.map +1 -0
  43. package/dist/src/transformers/model-to-narrative/spec-traversal.js +54 -0
  44. package/dist/src/transformers/model-to-narrative/spec-traversal.js.map +1 -0
  45. package/dist/src/transformers/model-to-narrative/types.d.ts +12 -0
  46. package/dist/src/transformers/model-to-narrative/types.d.ts.map +1 -0
  47. package/dist/src/transformers/model-to-narrative/types.js +2 -0
  48. package/dist/src/transformers/model-to-narrative/types.js.map +1 -0
  49. package/dist/src/transformers/model-to-narrative/validate-modules.d.ts +8 -0
  50. package/dist/src/transformers/model-to-narrative/validate-modules.d.ts.map +1 -0
  51. package/dist/src/transformers/model-to-narrative/validate-modules.js +121 -0
  52. package/dist/src/transformers/model-to-narrative/validate-modules.js.map +1 -0
  53. package/dist/src/transformers/narrative-to-model/assemble.d.ts.map +1 -1
  54. package/dist/src/transformers/narrative-to-model/assemble.js +5 -1
  55. package/dist/src/transformers/narrative-to-model/assemble.js.map +1 -1
  56. package/dist/src/transformers/narrative-to-model/derive-modules.d.ts +3 -0
  57. package/dist/src/transformers/narrative-to-model/derive-modules.d.ts.map +1 -0
  58. package/dist/src/transformers/narrative-to-model/derive-modules.js +29 -0
  59. package/dist/src/transformers/narrative-to-model/derive-modules.js.map +1 -0
  60. package/dist/tsconfig.tsbuildinfo +1 -1
  61. package/package.json +4 -4
  62. package/src/getNarratives.specs.ts +214 -1
  63. package/src/getNarratives.ts +1 -1
  64. package/src/id/addAutoIds.specs.ts +180 -0
  65. package/src/id/addAutoIds.ts +16 -1
  66. package/src/id/hasAllIds.specs.ts +87 -0
  67. package/src/id/hasAllIds.ts +10 -2
  68. package/src/index.ts +7 -0
  69. package/src/loader/runtime-cjs.ts +3 -2
  70. package/src/model-to-narrative.specs.ts +467 -17
  71. package/src/schema.ts +28 -0
  72. package/src/transformers/model-to-narrative/cross-module-imports.specs.ts +450 -0
  73. package/src/transformers/model-to-narrative/cross-module-imports.ts +83 -0
  74. package/src/transformers/model-to-narrative/generators/flow.ts +11 -10
  75. package/src/transformers/model-to-narrative/generators/module-code.ts +186 -0
  76. package/src/transformers/model-to-narrative/index.ts +19 -7
  77. package/src/transformers/model-to-narrative/modules.specs.ts +625 -0
  78. package/src/transformers/model-to-narrative/ordering.specs.ts +104 -0
  79. package/src/transformers/model-to-narrative/ordering.ts +46 -0
  80. package/src/transformers/model-to-narrative/spec-traversal.specs.ts +418 -0
  81. package/src/transformers/model-to-narrative/spec-traversal.ts +63 -0
  82. package/src/transformers/model-to-narrative/types.ts +13 -0
  83. package/src/transformers/model-to-narrative/validate-modules.ts +159 -0
  84. package/src/transformers/narrative-to-model/assemble.ts +7 -2
  85. package/src/transformers/narrative-to-model/derive-modules.specs.ts +121 -0
  86. package/src/transformers/narrative-to-model/derive-modules.ts +36 -0
  87. package/tsconfig.test.json +2 -1
@@ -0,0 +1,186 @@
1
+ import ts from 'typescript';
2
+ import type { Model, Module } from '../../../index';
3
+ import { deriveModules } from '../../narrative-to-model/derive-modules';
4
+ import { analyzeCodeUsage } from '../analysis/usage';
5
+ import { computeCrossModuleImports } from '../cross-module-imports';
6
+ import { sortFilesByPath } from '../ordering';
7
+ import type { CrossModuleImport, GeneratedFile } from '../types';
8
+ import { extractTypeIntegrationNames } from '../utils/integration-extractor';
9
+ import { integrationNameToPascalCase } from '../utils/strings';
10
+ import { buildFlowStatements } from './flow';
11
+ import { ALL_FLOW_FUNCTION_NAMES, buildImports } from './imports';
12
+ import { buildTypeAliases } from './types';
13
+
14
+ interface GenerateModuleCodeOptions {
15
+ flowImport: string;
16
+ integrationImport: string;
17
+ }
18
+
19
+ export type { GeneratedFile };
20
+
21
+ export function generateAllModulesCode(model: Model, opts: GenerateModuleCodeOptions): GeneratedFile[] {
22
+ const modules =
23
+ model.modules && model.modules.length > 0 ? model.modules : deriveModules(model.narratives, model.messages);
24
+
25
+ const files: GeneratedFile[] = [];
26
+
27
+ for (const module of modules) {
28
+ const crossModuleImports = computeCrossModuleImports(module, modules, model);
29
+ const code = generateModuleCode(module, model, opts, crossModuleImports);
30
+ files.push({
31
+ path: module.sourceFile,
32
+ code,
33
+ });
34
+ }
35
+
36
+ return sortFilesByPath(files);
37
+ }
38
+
39
+ function getNarrativesForModule(module: Module, model: Model): Model['narratives'] {
40
+ if (module.isDerived) {
41
+ return model.narratives.filter((n) => {
42
+ const narrativeSourceFile = n.sourceFile ?? 'generated.narrative.ts';
43
+ return narrativeSourceFile === module.sourceFile;
44
+ });
45
+ }
46
+
47
+ const narrativeIds = new Set(module.contains.narrativeIds);
48
+ return model.narratives.filter((n) => n.id && narrativeIds.has(n.id));
49
+ }
50
+
51
+ function generateModuleCode(
52
+ module: Module,
53
+ model: Model,
54
+ opts: GenerateModuleCodeOptions,
55
+ crossModuleImports: CrossModuleImport[],
56
+ ): string {
57
+ const f = ts.factory;
58
+
59
+ const narratives = getNarrativesForModule(module, model);
60
+ const declaredMessageKeys = new Set(module.declares.messages.map((m) => `${m.kind}:${m.name}`));
61
+ const messages = model.messages.filter((m) => declaredMessageKeys.has(`${m.type}:${m.name}`));
62
+ const integrations = model.integrations ?? [];
63
+
64
+ const allTypeIntegrationNames = extractTypeIntegrationNames(narratives);
65
+ const allValueIntegrationNames = integrations.map((integration) => integrationNameToPascalCase(integration.name));
66
+
67
+ const allFlowFunctionNames = [...ALL_FLOW_FUNCTION_NAMES];
68
+ const preliminaryStatements = buildStatements(
69
+ ts,
70
+ opts,
71
+ messages,
72
+ allTypeIntegrationNames,
73
+ allValueIntegrationNames,
74
+ allFlowFunctionNames,
75
+ narratives,
76
+ [],
77
+ );
78
+ const preliminaryFile = f.createSourceFile(
79
+ preliminaryStatements,
80
+ f.createToken(ts.SyntaxKind.EndOfFileToken),
81
+ ts.NodeFlags.None,
82
+ );
83
+ const printer = ts.createPrinter({
84
+ newLine: ts.NewLineKind.LineFeed,
85
+ removeComments: false,
86
+ omitTrailingSemicolon: false,
87
+ });
88
+ const preliminaryCode = printer.printFile(preliminaryFile);
89
+
90
+ const allTypeNames = messages.map((msg) => msg.name);
91
+ const usageAnalysis = analyzeCodeUsage(
92
+ preliminaryCode,
93
+ allTypeNames,
94
+ allTypeIntegrationNames,
95
+ allValueIntegrationNames,
96
+ allFlowFunctionNames,
97
+ );
98
+
99
+ const usedTypeIntegrationNames = allTypeIntegrationNames.filter((name) => usageAnalysis.usedTypes.has(name));
100
+ const usedValueIntegrationNames = allValueIntegrationNames.filter((name) => usageAnalysis.usedIntegrations.has(name));
101
+ const usedFlowFunctionNames = Array.from(usageAnalysis.usedFlowFunctions);
102
+
103
+ const usedMessages = messages.filter((msg) => {
104
+ const isImportedFromIntegration = usedTypeIntegrationNames.includes(msg.name);
105
+ const isUsedInFlow = usageAnalysis.usedTypes.has(msg.name);
106
+ const hasEmptyFlowSlices = narratives.length === 0 || narratives.every((flow) => flow.slices.length === 0);
107
+
108
+ if (isImportedFromIntegration) {
109
+ return false;
110
+ }
111
+
112
+ return isUsedInFlow || hasEmptyFlowSlices;
113
+ });
114
+
115
+ const statements = buildStatements(
116
+ ts,
117
+ opts,
118
+ usedMessages,
119
+ usedTypeIntegrationNames,
120
+ usedValueIntegrationNames,
121
+ usedFlowFunctionNames,
122
+ narratives,
123
+ crossModuleImports,
124
+ );
125
+
126
+ const file = f.createSourceFile(statements, f.createToken(ts.SyntaxKind.EndOfFileToken), ts.NodeFlags.None);
127
+
128
+ return printer.printFile(file);
129
+ }
130
+
131
+ function buildStatements(
132
+ tsModule: typeof ts,
133
+ opts: GenerateModuleCodeOptions,
134
+ messages: Model['messages'],
135
+ typeIntegrationNames: string[],
136
+ valueIntegrationNames: string[],
137
+ usedFlowFunctionNames: string[],
138
+ flows: Model['narratives'],
139
+ crossModuleImports: CrossModuleImport[],
140
+ ): ts.Statement[] {
141
+ const f = tsModule.factory;
142
+ const statements: ts.Statement[] = [];
143
+
144
+ const imports = buildImports(
145
+ tsModule,
146
+ opts,
147
+ messages,
148
+ typeIntegrationNames,
149
+ valueIntegrationNames,
150
+ usedFlowFunctionNames,
151
+ );
152
+ if (imports.importFlowValues !== null) statements.push(imports.importFlowValues);
153
+ if (imports.importFlowTypes !== null) statements.push(imports.importFlowTypes);
154
+ if (imports.importIntegrationValues !== null) statements.push(imports.importIntegrationValues);
155
+ if (imports.importIntegrationTypes !== null) statements.push(imports.importIntegrationTypes);
156
+
157
+ for (const imp of crossModuleImports) {
158
+ const isTypeOnly = true;
159
+ const importClause = f.createImportClause(
160
+ isTypeOnly,
161
+ undefined,
162
+ f.createNamedImports(
163
+ imp.typeNames.map((name) => f.createImportSpecifier(false, undefined, f.createIdentifier(name))),
164
+ ),
165
+ );
166
+ const importDecl = f.createImportDeclaration(undefined, importClause, f.createStringLiteral(imp.fromPath));
167
+ statements.push(importDecl);
168
+ }
169
+
170
+ const adaptedMessages = messages.map((msg) => ({
171
+ type: msg.type,
172
+ name: msg.name,
173
+ fields: msg.fields.map((field) => ({
174
+ name: field.name,
175
+ type: field.type,
176
+ required: field.required,
177
+ })),
178
+ }));
179
+ statements.push(...buildTypeAliases(tsModule, adaptedMessages));
180
+
181
+ for (const flow of flows) {
182
+ statements.push(...buildFlowStatements(tsModule, flow, messages));
183
+ }
184
+
185
+ return statements;
186
+ }
@@ -1,22 +1,32 @@
1
1
  import type { Model } from '../../index';
2
2
  import { formatWithPrettier } from './formatting/prettier';
3
- import { generateTypeScriptCode } from './generators/compose';
3
+ import { generateAllModulesCode } from './generators/module-code';
4
+ import type { GeneratedNarratives } from './types';
4
5
 
5
6
  /**
6
- * Converts a schema specification to TypeScript flow DSL code.
7
+ * Converts a schema specification to TypeScript flow DSL code files.
7
8
  *
8
9
  * This function takes a complete schema specification and generates the corresponding
9
10
  * TypeScript code using the flow DSL, including imports, type definitions, builders,
10
- * and flow specifications.
11
+ * and flow specifications. Each module in the model produces one output file.
11
12
  *
12
13
  * @param model The complete schema specification conforming to SpecsSchema
13
- * @returns Promise resolving to formatted TypeScript code string
14
+ * @returns Promise resolving to GeneratedNarratives with files array
14
15
  */
15
- export async function modelToNarrative(model: Model): Promise<string> {
16
+ export async function modelToNarrative(model: Model): Promise<GeneratedNarratives> {
16
17
  const flowImport = '@auto-engineer/narrative';
17
18
  const integrationImport = extractIntegrationImportFromModel(model);
18
- const rawCode = generateTypeScriptCode(model, { flowImport, integrationImport });
19
- return await formatWithPrettier(rawCode);
19
+
20
+ const rawFiles = generateAllModulesCode(model, { flowImport, integrationImport });
21
+
22
+ const formattedFiles = await Promise.all(
23
+ rawFiles.map(async (file) => ({
24
+ path: file.path,
25
+ code: await formatWithPrettier(file.code),
26
+ })),
27
+ );
28
+
29
+ return { files: formattedFiles };
20
30
  }
21
31
 
22
32
  function extractIntegrationImportFromModel(model: Model): string {
@@ -25,3 +35,5 @@ function extractIntegrationImportFromModel(model: Model): string {
25
35
  }
26
36
  return '';
27
37
  }
38
+
39
+ export type { GeneratedNarratives } from './types';