@auto-engineer/narrative 0.15.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 (124) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +28 -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 +5 -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.json +1 -1
  88. package/tsconfig.test.json +2 -1
  89. package/dist/src/fluent-builder.specs.d.ts +0 -2
  90. package/dist/src/fluent-builder.specs.d.ts.map +0 -1
  91. package/dist/src/fluent-builder.specs.js +0 -28
  92. package/dist/src/fluent-builder.specs.js.map +0 -1
  93. package/dist/src/getNarratives.cache.specs.d.ts +0 -2
  94. package/dist/src/getNarratives.cache.specs.d.ts.map +0 -1
  95. package/dist/src/getNarratives.cache.specs.js +0 -234
  96. package/dist/src/getNarratives.cache.specs.js.map +0 -1
  97. package/dist/src/getNarratives.specs.d.ts +0 -2
  98. package/dist/src/getNarratives.specs.d.ts.map +0 -1
  99. package/dist/src/getNarratives.specs.js +0 -1307
  100. package/dist/src/getNarratives.specs.js.map +0 -1
  101. package/dist/src/id/addAutoIds.specs.d.ts +0 -2
  102. package/dist/src/id/addAutoIds.specs.d.ts.map +0 -1
  103. package/dist/src/id/addAutoIds.specs.js +0 -602
  104. package/dist/src/id/addAutoIds.specs.js.map +0 -1
  105. package/dist/src/id/hasAllIds.specs.d.ts +0 -2
  106. package/dist/src/id/hasAllIds.specs.d.ts.map +0 -1
  107. package/dist/src/id/hasAllIds.specs.js +0 -424
  108. package/dist/src/id/hasAllIds.specs.js.map +0 -1
  109. package/dist/src/model-to-narrative.specs.d.ts +0 -2
  110. package/dist/src/model-to-narrative.specs.d.ts.map +0 -1
  111. package/dist/src/model-to-narrative.specs.js +0 -2437
  112. package/dist/src/model-to-narrative.specs.js.map +0 -1
  113. package/dist/src/narrative-context.specs.d.ts +0 -2
  114. package/dist/src/narrative-context.specs.d.ts.map +0 -1
  115. package/dist/src/narrative-context.specs.js +0 -260
  116. package/dist/src/narrative-context.specs.js.map +0 -1
  117. package/dist/src/transformers/model-to-narrative/generators/gwt.specs.d.ts +0 -2
  118. package/dist/src/transformers/model-to-narrative/generators/gwt.specs.d.ts.map +0 -1
  119. package/dist/src/transformers/model-to-narrative/generators/gwt.specs.js +0 -142
  120. package/dist/src/transformers/model-to-narrative/generators/gwt.specs.js.map +0 -1
  121. package/dist/src/transformers/narrative-to-model/type-inference.specs.d.ts +0 -2
  122. package/dist/src/transformers/narrative-to-model/type-inference.specs.d.ts.map +0 -1
  123. package/dist/src/transformers/narrative-to-model/type-inference.specs.js +0 -177
  124. package/dist/src/transformers/narrative-to-model/type-inference.specs.js.map +0 -1
@@ -0,0 +1,450 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import type { Model } from '../../index';
3
+ import { computeCrossModuleImports, resolveRelativeImport } from './cross-module-imports';
4
+
5
+ describe('resolveRelativeImport', () => {
6
+ it('resolves same directory imports', () => {
7
+ const result = resolveRelativeImport('src/orders.ts', 'src/types.ts');
8
+
9
+ expect(result).toBe('./types');
10
+ });
11
+
12
+ it('resolves parent directory imports with ../', () => {
13
+ const result = resolveRelativeImport('src/features/orders.ts', 'src/shared/types.ts');
14
+
15
+ expect(result).toBe('../shared/types');
16
+ });
17
+
18
+ it('resolves deeply nested imports', () => {
19
+ const result = resolveRelativeImport('src/a/b/c/file.ts', 'src/x/y/target.ts');
20
+
21
+ expect(result).toBe('../../../x/y/target');
22
+ });
23
+
24
+ it('strips file extension from target', () => {
25
+ const result = resolveRelativeImport('src/orders.narrative.ts', 'src/types.narrative.ts');
26
+
27
+ expect(result).toBe('./types.narrative');
28
+ });
29
+
30
+ it('resolves from child to parent directory', () => {
31
+ const result = resolveRelativeImport('src/features/orders.ts', 'src/types.ts');
32
+
33
+ expect(result).toBe('../types');
34
+ });
35
+
36
+ it('resolves from root to nested directory', () => {
37
+ const result = resolveRelativeImport('index.ts', 'src/types.ts');
38
+
39
+ expect(result).toBe('./src/types');
40
+ });
41
+ });
42
+
43
+ describe('computeCrossModuleImports', () => {
44
+ it('returns empty array for derived modules', () => {
45
+ const model: Model = {
46
+ variant: 'specs',
47
+ narratives: [],
48
+ messages: [],
49
+ integrations: [],
50
+ modules: [
51
+ {
52
+ id: 'derived.ts',
53
+ sourceFile: 'derived.ts',
54
+ isDerived: true,
55
+ contains: { narrativeIds: [] },
56
+ declares: { messages: [] },
57
+ },
58
+ ],
59
+ };
60
+
61
+ const imports = computeCrossModuleImports(model.modules[0], model.modules, model);
62
+
63
+ expect(imports).toEqual([]);
64
+ });
65
+
66
+ it('returns empty array when module declares all needed types', () => {
67
+ const model: Model = {
68
+ variant: 'specs',
69
+ narratives: [
70
+ {
71
+ name: 'Test',
72
+ id: 'test-1',
73
+ slices: [
74
+ {
75
+ name: 'test',
76
+ type: 'command',
77
+ client: { specs: [] },
78
+ server: {
79
+ description: 'Test',
80
+ specs: [
81
+ {
82
+ type: 'gherkin',
83
+ feature: 'Test',
84
+ rules: [
85
+ {
86
+ name: 'rule',
87
+ examples: [
88
+ {
89
+ name: 'example',
90
+ steps: [{ keyword: 'When', text: 'DoSomething' }],
91
+ },
92
+ ],
93
+ },
94
+ ],
95
+ },
96
+ ],
97
+ },
98
+ },
99
+ ],
100
+ },
101
+ ],
102
+ messages: [{ type: 'command', name: 'DoSomething', fields: [] }],
103
+ integrations: [],
104
+ modules: [
105
+ {
106
+ id: 'self-contained',
107
+ sourceFile: 'self-contained.ts',
108
+ isDerived: false,
109
+ contains: { narrativeIds: ['test-1'] },
110
+ declares: { messages: [{ kind: 'command', name: 'DoSomething' }] },
111
+ },
112
+ ],
113
+ };
114
+
115
+ const imports = computeCrossModuleImports(model.modules[0], model.modules, model);
116
+
117
+ expect(imports).toEqual([]);
118
+ });
119
+
120
+ it('returns empty array when needed type is not declared by any module', () => {
121
+ const model: Model = {
122
+ variant: 'specs',
123
+ narratives: [
124
+ {
125
+ name: 'Test',
126
+ id: 'test-1',
127
+ slices: [
128
+ {
129
+ name: 'test',
130
+ type: 'command',
131
+ client: { specs: [] },
132
+ server: {
133
+ description: 'Test',
134
+ specs: [
135
+ {
136
+ type: 'gherkin',
137
+ feature: 'Test',
138
+ rules: [
139
+ {
140
+ name: 'rule',
141
+ examples: [
142
+ {
143
+ name: 'example',
144
+ steps: [{ keyword: 'When', text: 'MissingCommand' }],
145
+ },
146
+ ],
147
+ },
148
+ ],
149
+ },
150
+ ],
151
+ },
152
+ },
153
+ ],
154
+ },
155
+ ],
156
+ messages: [{ type: 'command', name: 'MissingCommand', fields: [] }],
157
+ integrations: [],
158
+ modules: [
159
+ {
160
+ id: 'consumer',
161
+ sourceFile: 'consumer.ts',
162
+ isDerived: false,
163
+ contains: { narrativeIds: ['test-1'] },
164
+ declares: { messages: [] },
165
+ },
166
+ ],
167
+ };
168
+
169
+ const imports = computeCrossModuleImports(model.modules[0], model.modules, model);
170
+
171
+ expect(imports).toEqual([]);
172
+ });
173
+
174
+ it('generates import when type is declared by another authored module', () => {
175
+ const model: Model = {
176
+ variant: 'specs',
177
+ narratives: [
178
+ { name: 'Shared', id: 'shared-1', slices: [] },
179
+ {
180
+ name: 'Consumer',
181
+ id: 'consumer-1',
182
+ slices: [
183
+ {
184
+ name: 'test',
185
+ type: 'command',
186
+ client: { specs: [] },
187
+ server: {
188
+ description: 'Test',
189
+ specs: [
190
+ {
191
+ type: 'gherkin',
192
+ feature: 'Test',
193
+ rules: [
194
+ {
195
+ name: 'rule',
196
+ examples: [
197
+ {
198
+ name: 'example',
199
+ steps: [{ keyword: 'Then', text: 'SharedEvent' }],
200
+ },
201
+ ],
202
+ },
203
+ ],
204
+ },
205
+ ],
206
+ },
207
+ },
208
+ ],
209
+ },
210
+ ],
211
+ messages: [{ type: 'event', source: 'internal', name: 'SharedEvent', fields: [] }],
212
+ integrations: [],
213
+ modules: [
214
+ {
215
+ id: 'shared',
216
+ sourceFile: 'shared/types.ts',
217
+ isDerived: false,
218
+ contains: { narrativeIds: ['shared-1'] },
219
+ declares: { messages: [{ kind: 'event', name: 'SharedEvent' }] },
220
+ },
221
+ {
222
+ id: 'consumer',
223
+ sourceFile: 'features/consumer.ts',
224
+ isDerived: false,
225
+ contains: { narrativeIds: ['consumer-1'] },
226
+ declares: { messages: [] },
227
+ },
228
+ ],
229
+ };
230
+
231
+ const consumerModule = model.modules[1];
232
+ const imports = computeCrossModuleImports(consumerModule, model.modules, model);
233
+
234
+ expect(imports).toEqual([{ fromPath: '../shared/types', typeNames: ['SharedEvent'] }]);
235
+ });
236
+
237
+ it('groups multiple types from same module into single import', () => {
238
+ const model: Model = {
239
+ variant: 'specs',
240
+ narratives: [
241
+ { name: 'Shared', id: 'shared-1', slices: [] },
242
+ {
243
+ name: 'Consumer',
244
+ id: 'consumer-1',
245
+ slices: [
246
+ {
247
+ name: 'test',
248
+ type: 'command',
249
+ client: { specs: [] },
250
+ server: {
251
+ description: 'Test',
252
+ specs: [
253
+ {
254
+ type: 'gherkin',
255
+ feature: 'Test',
256
+ rules: [
257
+ {
258
+ name: 'rule',
259
+ examples: [
260
+ {
261
+ name: 'example',
262
+ steps: [
263
+ { keyword: 'Given', text: 'EventA' },
264
+ { keyword: 'Then', text: 'EventB' },
265
+ ],
266
+ },
267
+ ],
268
+ },
269
+ ],
270
+ },
271
+ ],
272
+ },
273
+ },
274
+ ],
275
+ },
276
+ ],
277
+ messages: [
278
+ { type: 'event', source: 'internal', name: 'EventA', fields: [] },
279
+ { type: 'event', source: 'internal', name: 'EventB', fields: [] },
280
+ ],
281
+ integrations: [],
282
+ modules: [
283
+ {
284
+ id: 'shared',
285
+ sourceFile: 'shared.ts',
286
+ isDerived: false,
287
+ contains: { narrativeIds: ['shared-1'] },
288
+ declares: {
289
+ messages: [
290
+ { kind: 'event', name: 'EventA' },
291
+ { kind: 'event', name: 'EventB' },
292
+ ],
293
+ },
294
+ },
295
+ {
296
+ id: 'consumer',
297
+ sourceFile: 'consumer.ts',
298
+ isDerived: false,
299
+ contains: { narrativeIds: ['consumer-1'] },
300
+ declares: { messages: [] },
301
+ },
302
+ ],
303
+ };
304
+
305
+ const consumerModule = model.modules[1];
306
+ const imports = computeCrossModuleImports(consumerModule, model.modules, model);
307
+
308
+ expect(imports).toHaveLength(1);
309
+ expect(imports[0].typeNames.sort()).toEqual(['EventA', 'EventB']);
310
+ });
311
+
312
+ it('ignores types declared by derived modules', () => {
313
+ const model: Model = {
314
+ variant: 'specs',
315
+ narratives: [
316
+ {
317
+ name: 'Consumer',
318
+ id: 'consumer-1',
319
+ slices: [
320
+ {
321
+ name: 'test',
322
+ type: 'command',
323
+ client: { specs: [] },
324
+ server: {
325
+ description: 'Test',
326
+ specs: [
327
+ {
328
+ type: 'gherkin',
329
+ feature: 'Test',
330
+ rules: [
331
+ {
332
+ name: 'rule',
333
+ examples: [
334
+ {
335
+ name: 'example',
336
+ steps: [{ keyword: 'Then', text: 'DerivedEvent' }],
337
+ },
338
+ ],
339
+ },
340
+ ],
341
+ },
342
+ ],
343
+ },
344
+ },
345
+ ],
346
+ },
347
+ ],
348
+ messages: [{ type: 'event', source: 'internal', name: 'DerivedEvent', fields: [] }],
349
+ integrations: [],
350
+ modules: [
351
+ {
352
+ id: 'derived.ts',
353
+ sourceFile: 'derived.ts',
354
+ isDerived: true,
355
+ contains: { narrativeIds: [] },
356
+ declares: { messages: [{ kind: 'event', name: 'DerivedEvent' }] },
357
+ },
358
+ {
359
+ id: 'consumer',
360
+ sourceFile: 'consumer.ts',
361
+ isDerived: false,
362
+ contains: { narrativeIds: ['consumer-1'] },
363
+ declares: { messages: [] },
364
+ },
365
+ ],
366
+ };
367
+
368
+ const consumerModule = model.modules[1];
369
+ const imports = computeCrossModuleImports(consumerModule, model.modules, model);
370
+
371
+ expect(imports).toEqual([]);
372
+ });
373
+
374
+ it('sorts imports by source path', () => {
375
+ const model: Model = {
376
+ variant: 'specs',
377
+ narratives: [
378
+ { name: 'Types1', id: 'types-1', slices: [] },
379
+ { name: 'Types2', id: 'types-2', slices: [] },
380
+ {
381
+ name: 'Consumer',
382
+ id: 'consumer-1',
383
+ slices: [
384
+ {
385
+ name: 'test',
386
+ type: 'command',
387
+ client: { specs: [] },
388
+ server: {
389
+ description: 'Test',
390
+ specs: [
391
+ {
392
+ type: 'gherkin',
393
+ feature: 'Test',
394
+ rules: [
395
+ {
396
+ name: 'rule',
397
+ examples: [
398
+ {
399
+ name: 'example',
400
+ steps: [
401
+ { keyword: 'Given', text: 'ZEvent' },
402
+ { keyword: 'Then', text: 'AEvent' },
403
+ ],
404
+ },
405
+ ],
406
+ },
407
+ ],
408
+ },
409
+ ],
410
+ },
411
+ },
412
+ ],
413
+ },
414
+ ],
415
+ messages: [
416
+ { type: 'event', source: 'internal', name: 'ZEvent', fields: [] },
417
+ { type: 'event', source: 'internal', name: 'AEvent', fields: [] },
418
+ ],
419
+ integrations: [],
420
+ modules: [
421
+ {
422
+ id: 'z-types',
423
+ sourceFile: 'z-types.ts',
424
+ isDerived: false,
425
+ contains: { narrativeIds: ['types-1'] },
426
+ declares: { messages: [{ kind: 'event', name: 'ZEvent' }] },
427
+ },
428
+ {
429
+ id: 'a-types',
430
+ sourceFile: 'a-types.ts',
431
+ isDerived: false,
432
+ contains: { narrativeIds: ['types-2'] },
433
+ declares: { messages: [{ kind: 'event', name: 'AEvent' }] },
434
+ },
435
+ {
436
+ id: 'consumer',
437
+ sourceFile: 'consumer.ts',
438
+ isDerived: false,
439
+ contains: { narrativeIds: ['consumer-1'] },
440
+ declares: { messages: [] },
441
+ },
442
+ ],
443
+ };
444
+
445
+ const consumerModule = model.modules[2];
446
+ const imports = computeCrossModuleImports(consumerModule, model.modules, model);
447
+
448
+ expect(imports.map((i) => i.fromPath)).toEqual(['./a-types', './z-types']);
449
+ });
450
+ });
@@ -0,0 +1,83 @@
1
+ import path from 'node:path';
2
+ import type { Model, Module } from '../../index';
3
+ import { parseMessageKey, sortImportsBySource, toMessageKey } from './ordering';
4
+ import { collectMessageKeysFromNarratives } from './spec-traversal';
5
+ import type { CrossModuleImport } from './types';
6
+
7
+ export type { CrossModuleImport };
8
+
9
+ export function computeCrossModuleImports(module: Module, allModules: Module[], model: Model): CrossModuleImport[] {
10
+ if (module.isDerived) {
11
+ return [];
12
+ }
13
+
14
+ const declaredKeys = new Set(module.declares.messages.map((m) => toMessageKey(m.kind, m.name)));
15
+ const usedKeys = collectUsedMessageKeysForModule(module, model);
16
+ const neededKeys = new Set([...usedKeys].filter((k) => !declaredKeys.has(k)));
17
+
18
+ if (neededKeys.size === 0) {
19
+ return [];
20
+ }
21
+
22
+ const importsByModule = new Map<string, string[]>();
23
+
24
+ for (const msgKey of neededKeys) {
25
+ const { name } = parseMessageKey(msgKey);
26
+ const declaringModule = findDeclaringModule(msgKey, allModules, module);
27
+
28
+ if (declaringModule) {
29
+ const modulePath = declaringModule.sourceFile;
30
+ if (!importsByModule.has(modulePath)) {
31
+ importsByModule.set(modulePath, []);
32
+ }
33
+ importsByModule.get(modulePath)!.push(name);
34
+ }
35
+ }
36
+
37
+ const imports: CrossModuleImport[] = [];
38
+ for (const [modulePath, typeNames] of importsByModule) {
39
+ const relativePath = resolveRelativeImport(module.sourceFile, modulePath);
40
+ imports.push({ fromPath: relativePath, typeNames });
41
+ }
42
+
43
+ return sortImportsBySource(imports);
44
+ }
45
+
46
+ export function resolveRelativeImport(fromPath: string, toPath: string): string {
47
+ const fromDir = path.dirname(fromPath);
48
+ const toDir = path.dirname(toPath);
49
+ const toFile = path.basename(toPath, path.extname(toPath));
50
+
51
+ const relativePath = path.relative(fromDir, toDir);
52
+ if (relativePath === '') {
53
+ return `./${toFile}`;
54
+ }
55
+ if (!relativePath.startsWith('.')) {
56
+ return `./${relativePath}/${toFile}`.replace(/\\/g, '/');
57
+ }
58
+
59
+ return path.join(relativePath, toFile).replace(/\\/g, '/');
60
+ }
61
+
62
+ function collectUsedMessageKeysForModule(module: Module, model: Model): Set<string> {
63
+ const narrativeIds = new Set(module.contains.narrativeIds);
64
+ const narratives = model.narratives.filter((n) => n.id && narrativeIds.has(n.id));
65
+
66
+ const usedKeys = collectMessageKeysFromNarratives(narratives);
67
+
68
+ const modelKeys = new Set(model.messages.map((m) => toMessageKey(m.type, m.name)));
69
+ return new Set([...usedKeys].filter((k) => modelKeys.has(k)));
70
+ }
71
+
72
+ function findDeclaringModule(messageKey: string, allModules: Module[], currentModule: Module): Module | undefined {
73
+ const authoredModules = allModules.filter((m) => !m.isDerived && m.id !== currentModule.id);
74
+
75
+ for (const mod of authoredModules) {
76
+ const declares = mod.declares.messages.some((m) => toMessageKey(m.kind, m.name) === messageKey);
77
+ if (declares) {
78
+ return mod;
79
+ }
80
+ }
81
+
82
+ return undefined;
83
+ }
@@ -1,27 +1,28 @@
1
1
  import type tsNS from 'typescript';
2
2
  import type { z } from 'zod';
3
+ import type {
4
+ CommandSlice as CommandSliceType,
5
+ ExperienceSlice as ExperienceSliceType,
6
+ Narrative,
7
+ QuerySlice as QuerySliceType,
8
+ ReactSlice as ReactSliceType,
9
+ } from '../../../index';
3
10
  import type {
4
11
  ClientSpecNode,
5
- CommandSliceSchema,
6
12
  DataSinkSchema,
7
13
  DataSourceSchema,
8
14
  DestinationSchema,
9
15
  ExampleSchema,
10
- ExperienceSliceSchema,
11
- NarrativeSchema,
12
16
  OriginSchema,
13
- QuerySliceSchema,
14
- ReactSliceSchema,
15
17
  } from '../../../schema';
16
18
  import { jsonToExpr } from '../ast/emit-helpers';
17
19
  import { buildConsolidatedGwtSpecBlock, type GWTBlock } from './gwt';
18
20
 
19
- type CommandSlice = z.infer<typeof CommandSliceSchema>;
20
- type QuerySlice = z.infer<typeof QuerySliceSchema>;
21
- type ReactSlice = z.infer<typeof ReactSliceSchema>;
22
- type ExperienceSlice = z.infer<typeof ExperienceSliceSchema>;
21
+ type CommandSlice = CommandSliceType;
22
+ type QuerySlice = QuerySliceType;
23
+ type ReactSlice = ReactSliceType;
24
+ type ExperienceSlice = ExperienceSliceType;
23
25
  type Example = z.infer<typeof ExampleSchema>;
24
- type Narrative = z.infer<typeof NarrativeSchema>;
25
26
  type DataSinkItem = z.infer<typeof DataSinkSchema>;
26
27
  type DataSourceItem = z.infer<typeof DataSourceSchema>;
27
28
  type Destination = z.infer<typeof DestinationSchema>;