@auto-engineer/server-generator-apollo-emmett 1.138.0 → 1.140.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 (170) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/.turbo/turbo-test.log +4 -4
  3. package/.turbo/turbo-type-check.log +1 -1
  4. package/CHANGELOG.md +39 -0
  5. package/DEBUG.md +4 -4
  6. package/dist/src/codegen/extract/data-sink.d.ts +2 -2
  7. package/dist/src/codegen/extract/data-sink.d.ts.map +1 -1
  8. package/dist/src/codegen/extract/data-sink.js +2 -2
  9. package/dist/src/codegen/extract/data-sink.js.map +1 -1
  10. package/dist/src/codegen/extract/events.d.ts +3 -3
  11. package/dist/src/codegen/extract/events.d.ts.map +1 -1
  12. package/dist/src/codegen/extract/events.js +11 -11
  13. package/dist/src/codegen/extract/events.js.map +1 -1
  14. package/dist/src/codegen/extract/gwt.d.ts +2 -2
  15. package/dist/src/codegen/extract/gwt.d.ts.map +1 -1
  16. package/dist/src/codegen/extract/gwt.js +2 -2
  17. package/dist/src/codegen/extract/gwt.js.map +1 -1
  18. package/dist/src/codegen/extract/imports.d.ts +4 -4
  19. package/dist/src/codegen/extract/imports.d.ts.map +1 -1
  20. package/dist/src/codegen/extract/imports.js +8 -8
  21. package/dist/src/codegen/extract/imports.js.map +1 -1
  22. package/dist/src/codegen/extract/messages.d.ts +2 -2
  23. package/dist/src/codegen/extract/messages.d.ts.map +1 -1
  24. package/dist/src/codegen/extract/messages.js +9 -9
  25. package/dist/src/codegen/extract/messages.js.map +1 -1
  26. package/dist/src/codegen/extract/projection.d.ts +7 -7
  27. package/dist/src/codegen/extract/projection.d.ts.map +1 -1
  28. package/dist/src/codegen/extract/projection.js +3 -3
  29. package/dist/src/codegen/extract/projection.js.map +1 -1
  30. package/dist/src/codegen/extract/query.d.ts +2 -2
  31. package/dist/src/codegen/extract/query.d.ts.map +1 -1
  32. package/dist/src/codegen/extract/query.js.map +1 -1
  33. package/dist/src/codegen/extract/slice-normalizer.d.ts +4 -4
  34. package/dist/src/codegen/extract/slice-normalizer.d.ts.map +1 -1
  35. package/dist/src/codegen/extract/slice-normalizer.js +7 -7
  36. package/dist/src/codegen/extract/slice-normalizer.js.map +1 -1
  37. package/dist/src/codegen/extract/states.d.ts +3 -3
  38. package/dist/src/codegen/extract/states.d.ts.map +1 -1
  39. package/dist/src/codegen/extract/states.js.map +1 -1
  40. package/dist/src/codegen/extract/step-converter.d.ts +7 -7
  41. package/dist/src/codegen/extract/step-converter.d.ts.map +1 -1
  42. package/dist/src/codegen/extract/step-converter.js +12 -12
  43. package/dist/src/codegen/extract/step-converter.js.map +1 -1
  44. package/dist/src/codegen/extract/step-types.d.ts +4 -4
  45. package/dist/src/codegen/extract/step-types.d.ts.map +1 -1
  46. package/dist/src/codegen/extract/step-types.js +1 -1
  47. package/dist/src/codegen/extract/step-types.js.map +1 -1
  48. package/dist/src/codegen/scaffoldFromSchema.d.ts +12 -12
  49. package/dist/src/codegen/scaffoldFromSchema.d.ts.map +1 -1
  50. package/dist/src/codegen/scaffoldFromSchema.js +113 -101
  51. package/dist/src/codegen/scaffoldFromSchema.js.map +1 -1
  52. package/dist/src/codegen/templates/command/commands.specs.ts +3 -3
  53. package/dist/src/codegen/templates/command/decide.specs.specs.ts +52 -52
  54. package/dist/src/codegen/templates/command/decide.specs.ts +12 -12
  55. package/dist/src/codegen/templates/command/decide.specs.ts.ejs +1 -1
  56. package/dist/src/codegen/templates/command/events.specs.ts +3 -3
  57. package/dist/src/codegen/templates/command/evolve.specs.ts +3 -3
  58. package/dist/src/codegen/templates/command/handle.specs.ts +13 -13
  59. package/dist/src/codegen/templates/command/mutation.resolver.specs.ts +19 -19
  60. package/dist/src/codegen/templates/command/register.specs.ts +3 -3
  61. package/dist/src/codegen/templates/command/state.specs.ts +3 -3
  62. package/dist/src/codegen/templates/query/events.specs.ts +4 -4
  63. package/dist/src/codegen/templates/query/projection.specs.specs.ts +60 -60
  64. package/dist/src/codegen/templates/query/projection.specs.ts +54 -29
  65. package/dist/src/codegen/templates/query/projection.specs.ts.ejs +2 -2
  66. package/dist/src/codegen/templates/query/query.resolver.specs.ts +63 -63
  67. package/dist/src/codegen/templates/query/query.resolver.ts.ejs +1 -1
  68. package/dist/src/codegen/templates/query/state.specs.ts +9 -9
  69. package/dist/src/codegen/templates/react/react.specs.specs.ts +15 -15
  70. package/dist/src/codegen/templates/react/react.specs.ts +16 -16
  71. package/dist/src/codegen/templates/react/react.specs.ts.ejs +9 -9
  72. package/dist/src/codegen/templates/react/react.ts.ejs +5 -5
  73. package/dist/src/codegen/templates/react/react.ts.specs.ts +33 -33
  74. package/dist/src/codegen/templates/react/register.specs.ts +7 -7
  75. package/dist/src/codegen/templates/react/register.ts.ejs +4 -4
  76. package/dist/src/codegen/test-data/specVariant1.d.ts.map +1 -1
  77. package/dist/src/codegen/test-data/specVariant1.js +3 -2
  78. package/dist/src/codegen/test-data/specVariant1.js.map +1 -1
  79. package/dist/src/codegen/types.d.ts +2 -2
  80. package/dist/src/codegen/types.d.ts.map +1 -1
  81. package/dist/src/commands/generate-server.d.ts +21 -21
  82. package/dist/src/commands/generate-server.d.ts.map +1 -1
  83. package/dist/src/commands/generate-server.js +81 -63
  84. package/dist/src/commands/generate-server.js.map +1 -1
  85. package/dist/src/commands/initialize-server.d.ts.map +1 -1
  86. package/dist/src/commands/initialize-server.js +2 -2
  87. package/dist/src/commands/initialize-server.js.map +1 -1
  88. package/dist/src/domain/flows/shared/types.d.ts +14 -0
  89. package/dist/src/domain/flows/shared/types.d.ts.map +1 -0
  90. package/dist/src/domain/flows/shared/types.js +2 -0
  91. package/dist/src/domain/flows/shared/types.js.map +1 -0
  92. package/dist/src/domain/flows/shared/types.ts +15 -0
  93. package/dist/src/domain/narratives/shared/types.d.ts +14 -0
  94. package/dist/src/domain/narratives/shared/types.d.ts.map +1 -0
  95. package/dist/src/domain/narratives/shared/types.js +2 -0
  96. package/dist/src/domain/narratives/shared/types.js.map +1 -0
  97. package/dist/src/domain/narratives/shared/types.ts +15 -0
  98. package/dist/src/index.d.ts +1 -1
  99. package/dist/src/index.d.ts.map +1 -1
  100. package/dist/src/server.js +3 -3
  101. package/dist/src/server.js.map +1 -1
  102. package/dist/src/server.ts +3 -3
  103. package/dist/src/utils/loadRegisterFiles.d.ts +2 -2
  104. package/dist/src/utils/loadRegisterFiles.d.ts.map +1 -1
  105. package/dist/src/utils/loadRegisterFiles.js.map +1 -1
  106. package/dist/src/utils/loadRegisterFiles.ts +5 -5
  107. package/dist/src/utils/loadResolvers.js +1 -1
  108. package/dist/src/utils/loadResolvers.js.map +1 -1
  109. package/dist/src/utils/loadResolvers.ts +1 -1
  110. package/dist/tsconfig.tsbuildinfo +1 -1
  111. package/package.json +4 -4
  112. package/src/codegen/extract/data-sink.ts +5 -5
  113. package/src/codegen/extract/events.ts +15 -15
  114. package/src/codegen/extract/gwt.ts +4 -4
  115. package/src/codegen/extract/imports.specs.ts +19 -19
  116. package/src/codegen/extract/imports.ts +13 -13
  117. package/src/codegen/extract/messages.specs.ts +30 -30
  118. package/src/codegen/extract/messages.ts +16 -16
  119. package/src/codegen/extract/projection.specs.ts +22 -22
  120. package/src/codegen/extract/projection.ts +9 -9
  121. package/src/codegen/extract/query.ts +2 -2
  122. package/src/codegen/extract/slice-normalizer.specs.ts +11 -11
  123. package/src/codegen/extract/slice-normalizer.ts +14 -14
  124. package/src/codegen/extract/states.ts +4 -4
  125. package/src/codegen/extract/step-converter.specs.ts +9 -9
  126. package/src/codegen/extract/step-converter.ts +15 -15
  127. package/src/codegen/extract/step-types.specs.ts +12 -12
  128. package/src/codegen/extract/step-types.ts +4 -4
  129. package/src/codegen/findEventSource.specs.ts +23 -23
  130. package/src/codegen/scaffoldErrors.specs.ts +4 -4
  131. package/src/codegen/scaffoldFromSchema.filter.specs.ts +32 -32
  132. package/src/codegen/scaffoldFromSchema.ts +146 -132
  133. package/src/codegen/templates/command/commands.specs.ts +3 -3
  134. package/src/codegen/templates/command/decide.specs.specs.ts +52 -52
  135. package/src/codegen/templates/command/decide.specs.ts +12 -12
  136. package/src/codegen/templates/command/decide.specs.ts.ejs +1 -1
  137. package/src/codegen/templates/command/events.specs.ts +3 -3
  138. package/src/codegen/templates/command/evolve.specs.ts +3 -3
  139. package/src/codegen/templates/command/handle.specs.ts +13 -13
  140. package/src/codegen/templates/command/mutation.resolver.specs.ts +19 -19
  141. package/src/codegen/templates/command/register.specs.ts +3 -3
  142. package/src/codegen/templates/command/state.specs.ts +3 -3
  143. package/src/codegen/templates/query/events.specs.ts +4 -4
  144. package/src/codegen/templates/query/projection.specs.specs.ts +60 -60
  145. package/src/codegen/templates/query/projection.specs.ts +54 -29
  146. package/src/codegen/templates/query/projection.specs.ts.ejs +2 -2
  147. package/src/codegen/templates/query/query.resolver.specs.ts +63 -63
  148. package/src/codegen/templates/query/query.resolver.ts.ejs +1 -1
  149. package/src/codegen/templates/query/state.specs.ts +9 -9
  150. package/src/codegen/templates/react/react.specs.specs.ts +15 -15
  151. package/src/codegen/templates/react/react.specs.ts +16 -16
  152. package/src/codegen/templates/react/react.specs.ts.ejs +9 -9
  153. package/src/codegen/templates/react/react.ts.ejs +5 -5
  154. package/src/codegen/templates/react/react.ts.specs.ts +33 -33
  155. package/src/codegen/templates/react/register.specs.ts +7 -7
  156. package/src/codegen/templates/react/register.ts.ejs +4 -4
  157. package/src/codegen/test-data/specVariant1.json +1 -1
  158. package/src/codegen/test-data/specVariant1.ts +3 -2
  159. package/src/codegen/test-data/specVariant2.json +1 -1
  160. package/src/codegen/types.ts +2 -2
  161. package/src/commands/generate-server.specs.ts +81 -79
  162. package/src/commands/generate-server.ts +110 -88
  163. package/src/commands/initialize-server.specs.ts +4 -4
  164. package/src/commands/initialize-server.ts +5 -2
  165. package/src/domain/flows/shared/types.ts +15 -0
  166. package/src/domain/narratives/shared/types.ts +15 -0
  167. package/src/index.ts +1 -1
  168. package/src/server.ts +3 -3
  169. package/src/utils/loadRegisterFiles.ts +5 -5
  170. package/src/utils/loadResolvers.ts +1 -1
@@ -1,7 +1,7 @@
1
1
  import fs from 'node:fs/promises';
2
2
  import path, { dirname } from 'node:path';
3
3
  import { fileURLToPath } from 'node:url';
4
- import type { Model, Narrative, Slice } from '@auto-engineer/narrative';
4
+ import type { Model, Moment, Scene } from '@auto-engineer/narrative';
5
5
  import { camelCase, pascalCase } from 'change-case';
6
6
  import createDebug from 'debug';
7
7
  import ejs from 'ejs';
@@ -11,8 +11,8 @@ import { ensureDirExists, ensureDirPath, toKebabCase } from './utils/path';
11
11
  const debug = createDebug('auto:server-generator-apollo-emmett:scaffold');
12
12
  const debugTemplate = createDebug('auto:server-generator-apollo-emmett:scaffold:template');
13
13
  const debugFiles = createDebug('auto:server-generator-apollo-emmett:scaffold:files');
14
- const debugFlow = createDebug('auto:server-generator-apollo-emmett:scaffold:flow');
15
- const debugSlice = createDebug('auto:server-generator-apollo-emmett:scaffold:slice');
14
+ const debugScene = createDebug('auto:server-generator-apollo-emmett:scaffold:flow');
15
+ const debugMoment = createDebug('auto:server-generator-apollo-emmett:scaffold:moment');
16
16
  const debugPlan = createDebug('auto:server-generator-apollo-emmett:scaffold:plan');
17
17
  const debugFormat = createDebug('auto:server-generator-apollo-emmett:scaffold:format');
18
18
 
@@ -20,7 +20,7 @@ import {
20
20
  isInlineObject,
21
21
  isInlineObjectArray,
22
22
  parseInlineObjectFields,
23
- parseSliceRequest,
23
+ parseMomentRequest,
24
24
  } from '@auto-engineer/narrative';
25
25
  import {
26
26
  baseTs,
@@ -45,8 +45,8 @@ import {
45
45
  import { getStreamFromSink } from './extract/data-sink';
46
46
  import { buildEventIdFieldMap } from './extract/projection';
47
47
  import { buildArgToStateFieldMap } from './extract/query';
48
- import { normalizeSliceForTemplate } from './extract/slice-normalizer';
49
- import { extractGwtSpecsFromSlice, type GwtResult } from './extract/step-converter';
48
+ import { normalizeMomentForTemplate } from './extract/slice-normalizer';
49
+ import { extractGwtSpecsFromMoment, type GwtResult } from './extract/step-converter';
50
50
  import type { GwtCondition, Message, MessageDefinition } from './types';
51
51
 
52
52
  export class TemplateRenderError extends Error {
@@ -60,16 +60,16 @@ export class TemplateRenderError extends Error {
60
60
  }
61
61
 
62
62
  export class ScaffoldError extends Error {
63
- readonly flowName: string;
64
- readonly sliceName: string;
65
- readonly sliceType: string;
63
+ readonly sceneName: string;
64
+ readonly momentName: string;
65
+ readonly momentType: string;
66
66
 
67
- constructor(flowName: string, sliceName: string, sliceType: string, cause: unknown) {
68
- super(`Scaffold failed for ${flowName}/${sliceName} (${sliceType})`, { cause });
67
+ constructor(sceneName: string, momentName: string, momentType: string, cause: unknown) {
68
+ super(`Scaffold failed for ${sceneName}/${momentName} (${momentType})`, { cause });
69
69
  this.name = 'ScaffoldError';
70
- this.flowName = flowName;
71
- this.sliceName = sliceName;
72
- this.sliceType = sliceType;
70
+ this.sceneName = sceneName;
71
+ this.momentName = momentName;
72
+ this.momentType = momentType;
73
73
  }
74
74
  }
75
75
 
@@ -269,8 +269,8 @@ export interface FilePlan {
269
269
 
270
270
  export interface DuplicateCommandInfo {
271
271
  command: string;
272
- existingFlow: string;
273
- existingSlice: string;
272
+ existingScene: string;
273
+ existingMoment: string;
274
274
  }
275
275
 
276
276
  export interface FieldIssue {
@@ -537,19 +537,19 @@ function formatDataObject(exampleData: Record<string, unknown>, schema: Message
537
537
 
538
538
  async function generateFileForTemplate(
539
539
  templateFile: string,
540
- slice: Slice,
541
- sliceDir: string,
540
+ slice: Moment,
541
+ momentDir: string,
542
542
  templateData: Record<string, unknown>,
543
543
  unionToEnumName: Map<string, string> = new Map(),
544
544
  ): Promise<FilePlan> {
545
545
  debugFiles('Generating file from template: %s', templateFile);
546
- debugFiles(' Slice type: %s', slice.type);
547
- debugFiles(' Slice directory: %s', sliceDir);
546
+ debugFiles(' Moment type: %s', slice.type);
547
+ debugFiles(' Moment directory: %s', momentDir);
548
548
 
549
549
  const __dirname = dirname(fileURLToPath(import.meta.url));
550
550
  const templatePath = path.join(__dirname, './templates', slice.type, templateFile);
551
551
  const fileName = templateFile.replace(/\.ts\.ejs$/, '.ts');
552
- const outputPath = path.join(sliceDir, fileName);
552
+ const outputPath = path.join(momentDir, fileName);
553
553
 
554
554
  debugFiles(' Template path: %s', templatePath);
555
555
  debugFiles(' Output path: %s', outputPath);
@@ -627,8 +627,8 @@ function extractUsedErrors(gwtMapping: Record<string, (GwtCondition & { failingF
627
627
  }
628
628
 
629
629
  async function prepareTemplateData(
630
- slice: Slice,
631
- flow: Narrative,
630
+ slice: Moment,
631
+ scene: Scene,
632
632
  commands: Message[],
633
633
  events: Message[],
634
634
  states: Message[],
@@ -637,9 +637,9 @@ async function prepareTemplateData(
637
637
  projectionSingleton: boolean | undefined,
638
638
  allMessages?: MessageDefinition[],
639
639
  integrations?: Model['integrations'],
640
- flows?: Narrative[],
640
+ scenes?: Scene[],
641
641
  ): Promise<Record<string, unknown>> {
642
- debug('Preparing template data for slice: %s (flow: %s)', slice.name, flow.name);
642
+ debug('Preparing template data for moment: %s (scene: %s)', slice.name, scene.name);
643
643
  debug(' Commands: %d', commands.length);
644
644
  debug(' Events: %d', events.length);
645
645
  debug(' States: %d', states.length);
@@ -662,11 +662,11 @@ async function prepareTemplateData(
662
662
  const uniqueCommands = Array.from(new Map(commands.map((c) => [c.type, c])).values());
663
663
  debug(' Unique commands: %d', uniqueCommands.length);
664
664
 
665
- const allowedForSlice = new Set(Object.keys(gwtMapping));
665
+ const allowedForMoment = new Set(Object.keys(gwtMapping));
666
666
  const filteredCommands =
667
- allowedForSlice.size > 0 ? uniqueCommands.filter((c) => allowedForSlice.has(c.type)) : uniqueCommands;
667
+ allowedForMoment.size > 0 ? uniqueCommands.filter((c) => allowedForMoment.has(c.type)) : uniqueCommands;
668
668
 
669
- const eventImportGroups = groupEventImports({ currentSliceName: slice.name, currentFlowName: flow.name, events });
669
+ const eventImportGroups = groupEventImports({ currentMomentName: slice.name, currentSceneName: scene.name, events });
670
670
  const allEventTypesArray = getAllEventTypes(events);
671
671
  const allEventTypes = createEventUnionType(events);
672
672
  const selfImportedTypes = new Set(
@@ -683,9 +683,9 @@ async function prepareTemplateData(
683
683
  messagesByName,
684
684
  );
685
685
 
686
- const normalizedSlice = normalizeSliceForTemplate(slice, allMessages);
686
+ const normalizedMoment = normalizeMomentForTemplate(slice, allMessages);
687
687
 
688
- const normalizedRules = normalizedSlice.server?.specs?.rules ?? [];
688
+ const normalizedRules = normalizedMoment.server?.specs?.rules ?? [];
689
689
  const eventCommandPairs: Array<{ eventType: string; commandType: string }> = [];
690
690
  const seenPairKeys = new Set<string>();
691
691
  for (const rule of normalizedRules) {
@@ -703,16 +703,16 @@ async function prepareTemplateData(
703
703
  }
704
704
 
705
705
  const eventIdFieldMap =
706
- slice.type === 'query' && flows ? buildEventIdFieldMap(events, flows, findEventSource) : undefined;
706
+ slice.type === 'query' && scenes ? buildEventIdFieldMap(events, scenes, findEventSource) : undefined;
707
707
 
708
708
  const stateFieldNames = new Set((queryMessage?.fields ?? []).map((f) => f.name));
709
709
  const argToStateFieldMap =
710
710
  slice.type === 'query' ? buildArgToStateFieldMap(queryGwtMapping, stateFieldNames, slice.mappings) : undefined;
711
711
 
712
712
  return {
713
- flowName: flow.name,
714
- sliceName: slice.name,
715
- slice: normalizedSlice,
713
+ sceneName: scene.name,
714
+ momentName: slice.name,
715
+ slice: normalizedMoment,
716
716
  stream: { pattern: streamPattern, id: streamId },
717
717
  commands: filteredCommands,
718
718
  events,
@@ -724,7 +724,7 @@ async function prepareTemplateData(
724
724
  projectionIdField,
725
725
  projectionSingleton,
726
726
  projectionName,
727
- parsedRequest: parseSliceRequest(slice),
727
+ parsedRequest: parseMomentRequest(slice),
728
728
  messages: allMessages,
729
729
  message:
730
730
  slice.type === 'query' && allMessages
@@ -744,17 +744,17 @@ async function prepareTemplateData(
744
744
 
745
745
  function annotateEventSources(
746
746
  events: Message[],
747
- flows: Narrative[],
747
+ scenes: Scene[],
748
748
  fallbackFlowName: string,
749
- fallbackSliceName: string,
750
- sliceType: string,
749
+ fallbackMomentName: string,
750
+ momentType: string,
751
751
  ): void {
752
752
  debug('Annotating event sources for %d events', events.length);
753
753
  for (const event of events) {
754
- const match = findEventSource(flows, event.type);
755
- event.sourceFlowName = match?.flowName ?? fallbackFlowName;
756
- event.sourceSliceName = match?.sliceName ?? (sliceType === 'react' ? undefined : fallbackSliceName);
757
- debug(' Event %s: flow=%s, slice=%s', event.type, event.sourceFlowName, event.sourceSliceName);
754
+ const match = findEventSource(scenes, event.type);
755
+ event.sourceSceneName = match?.sceneName ?? fallbackFlowName;
756
+ event.sourceMomentName = match?.momentName ?? (momentType === 'react' ? undefined : fallbackMomentName);
757
+ debug(' Event %s: scene=%s, slice=%s', event.type, event.sourceSceneName, event.sourceMomentName);
758
758
  }
759
759
  }
760
760
 
@@ -762,113 +762,113 @@ function hasEventInGwtSpecs(gwtSpecs: GwtResult[], eventType: string): boolean {
762
762
  return gwtSpecs.some((g) => g.then.some((t) => 'eventRef' in t && t.eventRef === eventType));
763
763
  }
764
764
 
765
- function canSliceProduceEvent(slice: Slice): boolean {
765
+ function canMomentProduceEvent(slice: Moment): boolean {
766
766
  return ['command', 'react'].includes(slice.type) && 'server' in slice && Boolean(slice.server?.specs);
767
767
  }
768
768
 
769
- export function findEventSource(flows: Narrative[], eventType: string): { flowName: string; sliceName: string } | null {
770
- debugSlice('Finding source for event: %s', eventType);
769
+ export function findEventSource(scenes: Scene[], eventType: string): { sceneName: string; momentName: string } | null {
770
+ debugMoment('Finding source for event: %s', eventType);
771
771
 
772
- for (const flow of flows) {
773
- for (const slice of flow.slices) {
774
- if (canSliceProduceEvent(slice)) {
775
- const gwtSpecs = extractGwtSpecsFromSlice(slice);
772
+ for (const scene of scenes) {
773
+ for (const slice of scene.moments) {
774
+ if (canMomentProduceEvent(slice)) {
775
+ const gwtSpecs = extractGwtSpecsFromMoment(slice);
776
776
  if (hasEventInGwtSpecs(gwtSpecs, eventType)) {
777
- debugSlice(' Found event source in flow: %s, slice: %s', flow.name, slice.name);
778
- return { flowName: flow.name, sliceName: slice.name };
777
+ debugMoment(' Found event source in scene: %s, slice: %s', scene.name, slice.name);
778
+ return { sceneName: scene.name, momentName: slice.name };
779
779
  }
780
780
  }
781
781
  if ('server' in slice && slice.server?.data?.items) {
782
782
  for (const item of slice.server.data.items) {
783
783
  if (item.target.type === 'Event' && item.target.name === eventType) {
784
- debugSlice(' Found event source in data target: flow=%s, slice=%s', flow.name, slice.name);
785
- return { flowName: flow.name, sliceName: slice.name };
784
+ debugMoment(' Found event source in data target: scene=%s, slice=%s', scene.name, slice.name);
785
+ return { sceneName: scene.name, momentName: slice.name };
786
786
  }
787
787
  }
788
788
  }
789
789
  }
790
790
  }
791
791
 
792
- debugSlice(' No source found for event: %s', eventType);
792
+ debugMoment(' No source found for event: %s', eventType);
793
793
  return null;
794
794
  }
795
795
 
796
796
  function annotateCommandSources(
797
797
  commands: Message[],
798
- flows: Narrative[],
798
+ scenes: Scene[],
799
799
  fallbackFlowName: string,
800
- fallbackSliceName: string,
801
- sliceType: string,
800
+ fallbackMomentName: string,
801
+ momentType: string,
802
802
  ): void {
803
803
  debug('Annotating command sources for %d commands', commands.length);
804
804
  for (const command of commands) {
805
- const match = findCommandSource(flows, command.type);
806
- command.sourceFlowName = match?.flowName ?? fallbackFlowName;
807
- command.sourceSliceName = match?.sliceName ?? (sliceType === 'react' ? undefined : fallbackSliceName);
808
- debug(' Command %s: flow=%s, slice=%s', command.type, command.sourceFlowName, command.sourceSliceName);
805
+ const match = findCommandSource(scenes, command.type);
806
+ command.sourceSceneName = match?.sceneName ?? fallbackFlowName;
807
+ command.sourceMomentName = match?.momentName ?? (momentType === 'react' ? undefined : fallbackMomentName);
808
+ debug(' Command %s: scene=%s, slice=%s', command.type, command.sourceSceneName, command.sourceMomentName);
809
809
  }
810
810
  }
811
811
 
812
- function findCommandSource(flows: Narrative[], commandType: string): { flowName: string; sliceName: string } | null {
813
- debugSlice('Finding source for command: %s', commandType);
814
- for (const flow of flows) {
815
- for (const slice of flow.slices) {
812
+ function findCommandSource(scenes: Scene[], commandType: string): { sceneName: string; momentName: string } | null {
813
+ debugMoment('Finding source for command: %s', commandType);
814
+ for (const scene of scenes) {
815
+ for (const slice of scene.moments) {
816
816
  if (slice.type !== 'command') continue;
817
817
 
818
- const gwtSpecs = extractGwtSpecsFromSlice(slice);
818
+ const gwtSpecs = extractGwtSpecsFromMoment(slice);
819
819
  if (gwtSpecs.some((g) => !Array.isArray(g.when) && 'commandRef' in g.when && g.when.commandRef === commandType)) {
820
- debugSlice(' Found command source in flow: %s, slice: %s', flow.name, slice.name);
821
- return { flowName: flow.name, sliceName: slice.name };
820
+ debugMoment(' Found command source in scene: %s, slice: %s', scene.name, slice.name);
821
+ return { sceneName: scene.name, momentName: slice.name };
822
822
  }
823
823
  }
824
824
  }
825
- debugSlice(' No source found for command: %s', commandType);
825
+ debugMoment(' No source found for command: %s', commandType);
826
826
  return null;
827
827
  }
828
828
 
829
- async function generateFilesForSlice(
830
- slice: Slice,
831
- flow: Narrative,
832
- sliceDir: string,
829
+ async function generateFilesForMoment(
830
+ slice: Moment,
831
+ scene: Scene,
832
+ momentDir: string,
833
833
  messages: MessageDefinition[],
834
- flows: Narrative[],
834
+ scenes: Scene[],
835
835
  unionToEnumName: Map<string, string>,
836
836
  integrations?: Model['integrations'],
837
- registeredCommands?: Map<string, { flowName: string; sliceName: string }>,
837
+ registeredCommands?: Map<string, { sceneName: string; momentName: string }>,
838
838
  ): Promise<{ plans: FilePlan[]; duplicateCommands: DuplicateCommandInfo[] }> {
839
- debugSlice('Generating files for slice: %s (type: %s)', slice.name, slice.type);
840
- debugSlice(' Flow: %s', flow.name);
841
- debugSlice(' Output directory: %s', sliceDir);
839
+ debugMoment('Generating files for moment: %s (type: %s)', slice.name, slice.type);
840
+ debugMoment(' Scene: %s', scene.name);
841
+ debugMoment(' Output directory: %s', momentDir);
842
842
 
843
843
  const duplicateCommands: DuplicateCommandInfo[] = [];
844
844
 
845
845
  const templates = defaultFilesByType[slice.type];
846
846
  if (!templates?.length) {
847
- debugSlice(' No templates found for slice type: %s', slice.type);
847
+ debugMoment(' No templates found for moment type: %s', slice.type);
848
848
  return { plans: [], duplicateCommands };
849
849
  }
850
- debugSlice(' Found %d templates for slice type', templates.length);
850
+ debugMoment(' Found %d templates for moment type', templates.length);
851
851
 
852
852
  if (
853
853
  slice.type === 'react' &&
854
854
  !slice.server?.specs?.some((spec) => spec.rules?.some((rule) => rule.examples?.length > 0))
855
855
  ) {
856
- debugSlice(' Skipping react slice with no examples: %s', slice.name);
856
+ debugMoment(' Skipping react slice with no examples: %s', slice.name);
857
857
  return { plans: [], duplicateCommands };
858
858
  }
859
859
 
860
860
  const extracted = extractMessagesFromSpecs(slice, messages);
861
- debugSlice(' Extracted messages:');
862
- debugSlice(' Commands: %d', extracted.commands.length);
863
- debugSlice(' Events: %d', extracted.events.length);
864
- debugSlice(' States: %d', extracted.states.length);
865
- debugSlice(
866
- '💡 Events for slice',
861
+ debugMoment(' Extracted messages:');
862
+ debugMoment(' Commands: %d', extracted.commands.length);
863
+ debugMoment(' Events: %d', extracted.events.length);
864
+ debugMoment(' States: %d', extracted.states.length);
865
+ debugMoment(
866
+ '💡 Events for moment',
867
867
  slice.name,
868
868
  extracted.events.map((e) => e.type),
869
869
  );
870
- annotateEventSources(extracted.events, flows, flow.name, slice.name, slice.type);
871
- annotateCommandSources(extracted.commands, flows, flow.name, slice.name, slice.type);
870
+ annotateEventSources(extracted.events, scenes, scene.name, slice.name, slice.type);
871
+ annotateCommandSources(extracted.commands, scenes, scene.name, slice.name, slice.type);
872
872
 
873
873
  let filteredTemplates = templates;
874
874
 
@@ -880,33 +880,33 @@ async function generateFilesForSlice(
880
880
  for (const ct of commandTypes) {
881
881
  const existing = registeredCommands.get(ct);
882
882
  if (existing) {
883
- debugSlice(
883
+ debugMoment(
884
884
  ' Duplicate command handler detected: %s (already in %s/%s)',
885
885
  ct,
886
- existing.flowName,
887
- existing.sliceName,
886
+ existing.sceneName,
887
+ existing.momentName,
888
888
  );
889
889
  duplicateCommands.push({
890
890
  command: ct,
891
- existingFlow: existing.flowName,
892
- existingSlice: existing.sliceName,
891
+ existingScene: existing.sceneName,
892
+ existingMoment: existing.momentName,
893
893
  });
894
894
  }
895
895
  }
896
896
  filteredTemplates = templates.filter((t) => t !== 'register.ts.ejs');
897
- debugSlice(' Filtered out register.ts.ejs for duplicate command slice');
897
+ debugMoment(' Filtered out register.ts.ejs for duplicate command moment');
898
898
  }
899
899
 
900
900
  for (const ct of commandTypes) {
901
901
  if (!registeredCommands.has(ct)) {
902
- registeredCommands.set(ct, { flowName: flow.name, sliceName: slice.name });
902
+ registeredCommands.set(ct, { sceneName: scene.name, momentName: slice.name });
903
903
  }
904
904
  }
905
905
  }
906
906
 
907
907
  const templateData = await prepareTemplateData(
908
908
  slice,
909
- flow,
909
+ scene,
910
910
  extracted.commands,
911
911
  extracted.events,
912
912
  extracted.states,
@@ -915,28 +915,29 @@ async function generateFilesForSlice(
915
915
  extracted.projectionSingleton,
916
916
  messages,
917
917
  integrations,
918
- flows,
918
+ scenes,
919
919
  );
920
920
 
921
- debugSlice(' Generating %d files from templates', filteredTemplates.length);
921
+ debugMoment(' Generating %d files from templates', filteredTemplates.length);
922
922
  const plans = await Promise.all(
923
923
  filteredTemplates.map((template) =>
924
- generateFileForTemplate(template, slice, sliceDir, templateData, unionToEnumName),
924
+ generateFileForTemplate(template, slice, momentDir, templateData, unionToEnumName),
925
925
  ),
926
926
  );
927
- debugSlice(' Generated %d file plans for slice: %s', plans.length, slice.name);
927
+ debugMoment(' Generated %d file plans for moment: %s', plans.length, slice.name);
928
928
  return { plans, duplicateCommands };
929
929
  }
930
930
 
931
931
  export async function generateScaffoldFilePlans(
932
- flows: Narrative[],
932
+ scenes: Scene[],
933
933
  messages: Model['messages'],
934
934
  integrations?: Model['integrations'],
935
- baseDir = 'src/domain/flows',
936
- affectedSliceIds?: Set<string>,
935
+ baseDir = 'src/domain/narratives',
936
+ affectedMomentIds?: Set<string>,
937
+ narratives?: Model['narratives'],
937
938
  ): Promise<ScaffoldResult> {
938
939
  debug('Generating scaffold file plans');
939
- debug(' Number of flows: %d', flows.length);
940
+ debug(' Number of scenes: %d', scenes.length);
940
941
  debug(' Number of messages: %d', messages?.length ?? 0);
941
942
  debug(' Base directory: %s', baseDir);
942
943
 
@@ -969,46 +970,59 @@ export async function generateScaffoldFilePlans(
969
970
  const { enums, unionToEnumName } = extractEnumsFromMessages(sanitizedMessages);
970
971
  debug(' Extracted %d enums from messages', enums.length);
971
972
 
972
- const domainBaseDir = baseDir.replace(/\/flows$/, '');
973
+ const domainBaseDir = baseDir.replace(/\/scenes$/, '');
973
974
  await writeSharedTypes(domainBaseDir, enums);
974
975
 
975
976
  const allPlans: FilePlan[] = [];
976
977
  const allDuplicateCommands: DuplicateCommandInfo[] = [];
977
- const registeredCommands = new Map<string, { flowName: string; sliceName: string }>();
978
-
979
- for (const flow of flows) {
980
- debugFlow('Processing flow: %s', flow.name);
981
- const flowDir = ensureDirPath(baseDir, toKebabCase(flow.name));
982
- debugFlow(' Flow directory: %s', flowDir);
983
- debugFlow(' Number of slices: %d', flow.slices.length);
984
-
985
- for (const slice of flow.slices) {
986
- if (affectedSliceIds) {
987
- const sliceId = `${toKebabCase(flow.name)}/${toKebabCase(slice.name)}`;
988
- if (!affectedSliceIds.has(sliceId)) continue;
978
+ const registeredCommands = new Map<string, { sceneName: string; momentName: string }>();
979
+
980
+ const sceneToNarrative = new Map<string, string>();
981
+ if (narratives) {
982
+ for (const narrative of narratives) {
983
+ for (const sceneId of narrative.sceneIds) {
984
+ sceneToNarrative.set(sceneId, narrative.name);
985
+ }
986
+ }
987
+ }
988
+
989
+ for (const scene of scenes) {
990
+ debugScene('Processing scene: %s', scene.name);
991
+ const narrativeName = scene.id ? sceneToNarrative.get(scene.id) : undefined;
992
+ const sceneDirName = narrativeName
993
+ ? `${toKebabCase(narrativeName)}_${toKebabCase(scene.name)}`
994
+ : toKebabCase(scene.name);
995
+ const sceneDir = ensureDirPath(baseDir, sceneDirName);
996
+ debugScene(' Scene directory: %s', sceneDir);
997
+ debugScene(' Number of moments: %d', scene.moments.length);
998
+
999
+ for (const slice of scene.moments) {
1000
+ if (affectedMomentIds) {
1001
+ const sliceId = `${toKebabCase(scene.name)}/${toKebabCase(slice.name)}`;
1002
+ if (!affectedMomentIds.has(sliceId)) continue;
989
1003
  }
990
- debugFlow(' Processing slice: %s (type: %s)', slice.name, slice.type);
991
- const sliceDir = ensureDirPath(flowDir, toKebabCase(slice.name));
992
- debugFlow(' Slice directory: %s', sliceDir);
1004
+ debugScene(' Processing slice: %s (type: %s)', slice.name, slice.type);
1005
+ const momentDir = ensureDirPath(sceneDir, toKebabCase(slice.name));
1006
+ debugScene(' Moment directory: %s', momentDir);
993
1007
  try {
994
- const { plans, duplicateCommands } = await generateFilesForSlice(
1008
+ const { plans, duplicateCommands } = await generateFilesForMoment(
995
1009
  slice,
996
- flow,
997
- sliceDir,
1010
+ scene,
1011
+ momentDir,
998
1012
  sanitizedMessages,
999
- flows,
1013
+ scenes,
1000
1014
  unionToEnumName,
1001
1015
  integrations,
1002
1016
  registeredCommands,
1003
1017
  );
1004
- debugFlow(' Generated %d plans for slice', plans.length);
1018
+ debugScene(' Generated %d plans for moment', plans.length);
1005
1019
  allPlans.push(...plans);
1006
1020
  allDuplicateCommands.push(...duplicateCommands);
1007
1021
  } catch (error) {
1008
- throw new ScaffoldError(flow.name, slice.name, slice.type, error);
1022
+ throw new ScaffoldError(scene.name, slice.name, slice.type, error);
1009
1023
  }
1010
1024
  }
1011
- debugFlow(' Completed flow: %s', flow.name);
1025
+ debugScene(' Completed scene: %s', scene.name);
1012
1026
  }
1013
1027
 
1014
1028
  debug('Total file plans generated: %d', allPlans.length);
@@ -1036,16 +1050,16 @@ export async function writeScaffoldFilePlans(plans: FilePlan[]) {
1036
1050
  }
1037
1051
 
1038
1052
  export async function scaffoldFromSchema(
1039
- flows: Narrative[],
1053
+ scenes: Scene[],
1040
1054
  messages: Model['messages'],
1041
- baseDir = 'src/domain/flows',
1055
+ baseDir = 'src/domain/narratives',
1042
1056
  ): Promise<void> {
1043
1057
  debug('Starting scaffold from schema');
1044
- debug(' Flows: %d', flows.length);
1058
+ debug(' Scenes: %d', scenes.length);
1045
1059
  debug(' Messages: %d', messages?.length ?? 0);
1046
1060
  debug(' Base directory: %s', baseDir);
1047
1061
 
1048
- const { plans } = await generateScaffoldFilePlans(flows, messages, undefined, baseDir);
1062
+ const { plans } = await generateScaffoldFilePlans(scenes, messages, undefined, baseDir);
1049
1063
  debug('Generated %d file plans, writing to disk...', plans.length);
1050
1064
 
1051
1065
  await writeScaffoldFilePlans(plans);
@@ -6,10 +6,10 @@ describe('commands.ts.ejs', () => {
6
6
  it('should generate correct command file', async () => {
7
7
  const spec: SpecsSchema = {
8
8
  variant: 'specs',
9
- narratives: [
9
+ scenes: [
10
10
  {
11
11
  name: 'Host creates a listing',
12
- slices: [
12
+ moments: [
13
13
  {
14
14
  type: 'command',
15
15
  name: 'Create listing',
@@ -77,7 +77,7 @@ describe('commands.ts.ejs', () => {
77
77
  ],
78
78
  };
79
79
 
80
- const { plans } = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
80
+ const { plans } = await generateScaffoldFilePlans(spec.scenes, spec.messages, undefined, 'src/domain/narratives');
81
81
  const commandFile = plans.find((p) => p.outputPath.endsWith('commands.ts'));
82
82
 
83
83
  expect(commandFile?.contents).toMatchInlineSnapshot(`