@auto-engineer/narrative 0.11.20 → 0.12.1

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 (69) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +24 -0
  3. package/dist/src/fluent-builder.d.ts.map +1 -1
  4. package/dist/src/fluent-builder.js +9 -12
  5. package/dist/src/fluent-builder.js.map +1 -1
  6. package/dist/src/getNarratives.specs.js +43 -27
  7. package/dist/src/getNarratives.specs.js.map +1 -1
  8. package/dist/src/id/addAutoIds.specs.js +11 -25
  9. package/dist/src/id/addAutoIds.specs.js.map +1 -1
  10. package/dist/src/id/hasAllIds.specs.js +12 -12
  11. package/dist/src/index.d.ts +2 -1
  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/model-to-narrative.specs.js +110 -108
  16. package/dist/src/model-to-narrative.specs.js.map +1 -1
  17. package/dist/src/narrative-context.d.ts +4 -2
  18. package/dist/src/narrative-context.d.ts.map +1 -1
  19. package/dist/src/narrative-context.js +98 -95
  20. package/dist/src/narrative-context.js.map +1 -1
  21. package/dist/src/narrative.d.ts +7 -1
  22. package/dist/src/narrative.d.ts.map +1 -1
  23. package/dist/src/narrative.js +27 -6
  24. package/dist/src/narrative.js.map +1 -1
  25. package/dist/src/samples/items.narrative.js +7 -7
  26. package/dist/src/samples/items.narrative.js.map +1 -1
  27. package/dist/src/samples/place-order.narrative.js +4 -4
  28. package/dist/src/samples/place-order.narrative.js.map +1 -1
  29. package/dist/src/samples/questionnaires.narrative.js +16 -18
  30. package/dist/src/samples/questionnaires.narrative.js.map +1 -1
  31. package/dist/src/samples/seasonal-assistant.schema.json +37 -31
  32. package/dist/src/schema.d.ts +91 -462
  33. package/dist/src/schema.d.ts.map +1 -1
  34. package/dist/src/schema.js +22 -24
  35. package/dist/src/schema.js.map +1 -1
  36. package/dist/src/slice-builder.js +2 -2
  37. package/dist/src/slice-builder.js.map +1 -1
  38. package/dist/src/transformers/model-to-narrative/generators/flow.d.ts.map +1 -1
  39. package/dist/src/transformers/model-to-narrative/generators/flow.js +34 -10
  40. package/dist/src/transformers/model-to-narrative/generators/flow.js.map +1 -1
  41. package/dist/src/transformers/model-to-narrative/generators/imports.d.ts +1 -1
  42. package/dist/src/transformers/model-to-narrative/generators/imports.d.ts.map +1 -1
  43. package/dist/src/transformers/model-to-narrative/generators/imports.js +2 -1
  44. package/dist/src/transformers/model-to-narrative/generators/imports.js.map +1 -1
  45. package/dist/src/transformers/narrative-to-model/index.d.ts.map +1 -1
  46. package/dist/src/transformers/narrative-to-model/index.js +4 -8
  47. package/dist/src/transformers/narrative-to-model/index.js.map +1 -1
  48. package/dist/src/transformers/narrative-to-model/type-inference.specs.js +3 -3
  49. package/dist/src/transformers/narrative-to-model/type-inference.specs.js.map +1 -1
  50. package/dist/tsconfig.tsbuildinfo +1 -1
  51. package/package.json +5 -5
  52. package/src/fluent-builder.ts +9 -12
  53. package/src/getNarratives.specs.ts +43 -28
  54. package/src/id/addAutoIds.specs.ts +11 -25
  55. package/src/id/hasAllIds.specs.ts +12 -12
  56. package/src/index.ts +2 -1
  57. package/src/model-to-narrative.specs.ts +110 -108
  58. package/src/narrative-context.ts +103 -101
  59. package/src/narrative.ts +44 -6
  60. package/src/samples/items.narrative.ts +7 -7
  61. package/src/samples/place-order.narrative.ts +4 -4
  62. package/src/samples/questionnaires.narrative.ts +17 -18
  63. package/src/samples/seasonal-assistant.schema.json +37 -31
  64. package/src/schema.ts +33 -24
  65. package/src/slice-builder.ts +2 -2
  66. package/src/transformers/model-to-narrative/generators/flow.ts +53 -23
  67. package/src/transformers/model-to-narrative/generators/imports.ts +2 -1
  68. package/src/transformers/narrative-to-model/index.ts +4 -7
  69. package/src/transformers/narrative-to-model/type-inference.specs.ts +3 -3
package/src/schema.ts CHANGED
@@ -215,17 +215,38 @@ const SpecSchema = z
215
215
  })
216
216
  .describe('Specification with business rules');
217
217
 
218
+ const ItNode = z
219
+ .object({
220
+ type: z.literal('it'),
221
+ id: z.string().optional(),
222
+ title: z.string(),
223
+ })
224
+ .strict();
225
+
226
+ type ClientSpecNode =
227
+ | { type: 'it'; id?: string; title: string }
228
+ | { type: 'describe'; id?: string; title?: string; children?: ClientSpecNode[] };
229
+
230
+ export const ClientSpecNodeSchema: z.ZodType<ClientSpecNode> = z.lazy(() =>
231
+ z.union([
232
+ ItNode,
233
+ z
234
+ .object({
235
+ type: z.literal('describe'),
236
+ id: z.string().optional(),
237
+ title: z.string().optional(),
238
+ children: z.array(ClientSpecNodeSchema).default([]),
239
+ })
240
+ .strict(),
241
+ ]),
242
+ );
243
+
244
+ export const ClientSpecSchema = z.array(ClientSpecNodeSchema).default([]);
245
+
218
246
  const CommandSliceSchema = BaseSliceSchema.extend({
219
247
  type: z.literal('command'),
220
248
  client: z.object({
221
- description: z.string(),
222
- specs: z
223
- .object({
224
- name: z.string().describe('Spec group name'),
225
- rules: z.array(z.string()).describe('UI specifications (should statements)'),
226
- })
227
- .optional()
228
- .describe('Client-side specifications'),
249
+ specs: ClientSpecSchema,
229
250
  }),
230
251
  request: z.string().describe('Command request (GraphQL, REST endpoint, or other query format)').optional(),
231
252
  server: z.object({
@@ -238,14 +259,7 @@ const CommandSliceSchema = BaseSliceSchema.extend({
238
259
  const QuerySliceSchema = BaseSliceSchema.extend({
239
260
  type: z.literal('query'),
240
261
  client: z.object({
241
- description: z.string(),
242
- specs: z
243
- .object({
244
- name: z.string().describe('Spec group name'),
245
- rules: z.array(z.string()).describe('UI specifications (should statements)'),
246
- })
247
- .optional()
248
- .describe('Client-side specifications'),
262
+ specs: ClientSpecSchema,
249
263
  }),
250
264
  request: z.string().describe('Query request (GraphQL, REST endpoint, or other query format)').optional(),
251
265
  server: z.object({
@@ -270,14 +284,7 @@ const ReactSliceSchema = BaseSliceSchema.extend({
270
284
  const ExperienceSliceSchema = BaseSliceSchema.extend({
271
285
  type: z.literal('experience'),
272
286
  client: z.object({
273
- description: z.string().optional(),
274
- specs: z
275
- .object({
276
- name: z.string().describe('Spec group name'),
277
- rules: z.array(z.string()).describe('UI specifications (should statements)'),
278
- })
279
- .optional()
280
- .describe('Client-side specifications'),
287
+ specs: ClientSpecSchema,
281
288
  }),
282
289
  }).describe('Experience slice for user interactions and UI behavior');
283
290
 
@@ -387,6 +394,8 @@ export const modelSchema = z
387
394
  })
388
395
  .describe('Complete system specification with all implementation details');
389
396
 
397
+ export type { ClientSpecNode };
398
+
390
399
  export {
391
400
  StateExampleSchema,
392
401
  MessageFieldSchema,
@@ -47,7 +47,7 @@ export const createSliceBuilder = (config: SliceConfig = {}): SliceBuilder => ({
47
47
  const slice: CommandSlice = {
48
48
  type: 'command',
49
49
  name,
50
- client: { description: '', specs: undefined },
50
+ client: { specs: [] },
51
51
  server: { description: '', specs: { name: '', rules: [] }, data: undefined },
52
52
  // Optional fields
53
53
  ...(config.eventStream != null && { stream: config.eventStream }),
@@ -66,7 +66,7 @@ export const createSliceBuilder = (config: SliceConfig = {}): SliceBuilder => ({
66
66
  const slice: QuerySlice = {
67
67
  type: 'query',
68
68
  name,
69
- client: { description: '', specs: undefined },
69
+ client: { specs: [] },
70
70
  server: { description: '', specs: { name: '', rules: [] }, data: undefined },
71
71
  // Optional fields
72
72
  ...(config.eventStream != null && { stream: config.eventStream }),
@@ -13,6 +13,7 @@ import {
13
13
  DataSourceSchema,
14
14
  DestinationSchema,
15
15
  OriginSchema,
16
+ type ClientSpecNode,
16
17
  } from '../../../schema';
17
18
 
18
19
  type CommandSlice = z.infer<typeof CommandSliceSchema>;
@@ -27,33 +28,55 @@ type Destination = z.infer<typeof DestinationSchema>;
27
28
  type Origin = z.infer<typeof OriginSchema>;
28
29
  type Slice = CommandSlice | QuerySlice | ReactSlice | ExperienceSlice;
29
30
 
30
- function buildClientSpecs(
31
+ function buildClientSpecNode(
31
32
  ts: typeof import('typescript'),
32
33
  f: tsNS.NodeFactory,
33
- specs: { name: string; rules: string[] },
34
- ) {
35
- const shouldCalls = specs.rules.map((txt) =>
36
- f.createExpressionStatement(
37
- f.createCallExpression(f.createIdentifier('should'), undefined, [f.createStringLiteral(txt)]),
38
- ),
39
- );
34
+ node: ClientSpecNode,
35
+ ): tsNS.Statement {
36
+ if (node.type === 'it') {
37
+ const args: tsNS.Expression[] = [];
40
38
 
41
- const arrowFunction = f.createArrowFunction(
42
- undefined,
43
- undefined,
44
- [],
45
- undefined,
46
- f.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
47
- f.createBlock(shouldCalls, true),
48
- );
39
+ if (node.id !== undefined && node.id !== '') {
40
+ args.push(f.createStringLiteral(node.id));
41
+ args.push(f.createStringLiteral(node.title));
42
+ } else {
43
+ args.push(f.createStringLiteral(node.title));
44
+ }
45
+
46
+ return f.createExpressionStatement(f.createCallExpression(f.createIdentifier('it'), undefined, args));
47
+ } else {
48
+ const childStatements = (node.children || []).map((child) => buildClientSpecNode(ts, f, child));
49
+
50
+ const args: tsNS.Expression[] = [];
51
+
52
+ if (node.id !== undefined && node.id !== '') {
53
+ args.push(f.createStringLiteral(node.id));
54
+ args.push(f.createStringLiteral(node.title ?? ''));
55
+ } else {
56
+ args.push(f.createStringLiteral(node.title ?? ''));
57
+ }
58
+
59
+ args.push(
60
+ f.createArrowFunction(
61
+ undefined,
62
+ undefined,
63
+ [],
64
+ undefined,
65
+ f.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
66
+ f.createBlock(childStatements, true),
67
+ ),
68
+ );
49
69
 
50
- const args: tsNS.Expression[] = [];
51
- if (specs.name && specs.name.trim() !== '') {
52
- args.push(f.createStringLiteral(specs.name));
70
+ return f.createExpressionStatement(f.createCallExpression(f.createIdentifier('describe'), undefined, args));
53
71
  }
54
- args.push(arrowFunction);
72
+ }
55
73
 
56
- return f.createExpressionStatement(f.createCallExpression(f.createIdentifier('specs'), undefined, args));
74
+ function buildClientSpecs(
75
+ ts: typeof import('typescript'),
76
+ f: tsNS.NodeFactory,
77
+ specs: ClientSpecNode[],
78
+ ): tsNS.Statement[] {
79
+ return specs.map((node) => buildClientSpecNode(ts, f, node));
57
80
  }
58
81
 
59
82
  function buildInitialChain(
@@ -347,7 +370,14 @@ function addClientToChain(
347
370
  chain: tsNS.Expression,
348
371
  slice: CommandSlice | QuerySlice | ReactSlice | ExperienceSlice,
349
372
  ): tsNS.Expression {
350
- if ('client' in slice && slice.client !== null && slice.client !== undefined && slice.client.specs) {
373
+ if (
374
+ 'client' in slice &&
375
+ slice.client !== null &&
376
+ slice.client !== undefined &&
377
+ 'specs' in slice.client &&
378
+ slice.client.specs !== undefined &&
379
+ slice.client.specs.length > 0
380
+ ) {
351
381
  return f.createCallExpression(f.createPropertyAccessExpression(chain, f.createIdentifier('client')), undefined, [
352
382
  f.createArrowFunction(
353
383
  undefined,
@@ -355,7 +385,7 @@ function addClientToChain(
355
385
  [],
356
386
  undefined,
357
387
  f.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
358
- f.createBlock([buildClientSpecs(ts, f, slice.client.specs)], true),
388
+ f.createBlock(buildClientSpecs(ts, f, slice.client.specs), true),
359
389
  ),
360
390
  ]);
361
391
  }
@@ -6,7 +6,8 @@ export const ALL_FLOW_FUNCTION_NAMES = [
6
6
  'react',
7
7
  'experience',
8
8
  'narrative',
9
- 'should',
9
+ 'describe',
10
+ 'it',
10
11
  'specs',
11
12
  'rule',
12
13
  'example',
@@ -129,11 +129,9 @@ function createTypeResolver(
129
129
  };
130
130
  }
131
131
 
132
- function getSliceSpecs(slice: Slice) {
132
+ function getServerSpecs(slice: Slice) {
133
133
  if ('server' in slice && slice.server?.specs !== undefined) {
134
134
  return slice.server.specs;
135
- } else if ('client' in slice && slice.client?.specs !== undefined) {
136
- return slice.client.specs;
137
135
  }
138
136
  return undefined;
139
137
  }
@@ -144,11 +142,10 @@ function processSliceSpecs(
144
142
  messages: Map<string, Message>,
145
143
  exampleShapeHints: ExampleShapeHints,
146
144
  ): void {
147
- const spec = getSliceSpecs(slice);
145
+ const serverSpec = getServerSpecs(slice);
148
146
 
149
- if (spec !== undefined && Array.isArray(spec.rules)) {
150
- spec.rules.forEach((rule: unknown) => {
151
- // Only process rule objects (server specs), not string rules (client specs)
147
+ if (serverSpec !== undefined && Array.isArray(serverSpec.rules)) {
148
+ serverSpec.rules.forEach((rule: unknown) => {
152
149
  if (
153
150
  typeof rule === 'object' &&
154
151
  rule !== null &&
@@ -13,7 +13,7 @@ describe('Type inference in narrative-to-model transformer', () => {
13
13
  id: 'SLICE-001',
14
14
  type: 'command',
15
15
  name: 'Submit Answer Command',
16
- client: { description: 'Submit answer client' },
16
+ client: { specs: [] },
17
17
  server: {
18
18
  description: 'Submit answer server',
19
19
  specs: {
@@ -59,7 +59,7 @@ describe('Type inference in narrative-to-model transformer', () => {
59
59
  id: 'SLICE-002',
60
60
  type: 'command',
61
61
  name: 'Submit Questionnaire Command',
62
- client: { description: 'Submit questionnaire client' },
62
+ client: { specs: [] },
63
63
  server: {
64
64
  description: 'Submit questionnaire server',
65
65
  specs: {
@@ -132,7 +132,7 @@ describe('Type inference in narrative-to-model transformer', () => {
132
132
  id: 'SLICE-001',
133
133
  type: 'command',
134
134
  name: 'Single Object Command',
135
- client: { description: 'Single object client' },
135
+ client: { specs: [] },
136
136
  server: {
137
137
  description: 'Single object server',
138
138
  specs: {