@auto-engineer/server-generator-nestjs 0.11.16
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.
- package/.turbo/turbo-build.log +5 -0
- package/LICENSE +10 -0
- package/README.md +290 -0
- package/dist/src/codegen/entity-consolidation.d.ts +19 -0
- package/dist/src/codegen/entity-consolidation.d.ts.map +1 -0
- package/dist/src/codegen/entity-consolidation.js +134 -0
- package/dist/src/codegen/entity-consolidation.js.map +1 -0
- package/dist/src/codegen/extract/commands.d.ts +25 -0
- package/dist/src/codegen/extract/commands.d.ts.map +1 -0
- package/dist/src/codegen/extract/commands.js +67 -0
- package/dist/src/codegen/extract/commands.js.map +1 -0
- package/dist/src/codegen/extract/data-sink.d.ts +6 -0
- package/dist/src/codegen/extract/data-sink.d.ts.map +1 -0
- package/dist/src/codegen/extract/data-sink.js +78 -0
- package/dist/src/codegen/extract/data-sink.js.map +1 -0
- package/dist/src/codegen/extract/events.d.ts +10 -0
- package/dist/src/codegen/extract/events.d.ts.map +1 -0
- package/dist/src/codegen/extract/events.js +42 -0
- package/dist/src/codegen/extract/events.js.map +1 -0
- package/dist/src/codegen/extract/fields.d.ts +3 -0
- package/dist/src/codegen/extract/fields.d.ts.map +1 -0
- package/dist/src/codegen/extract/fields.js +9 -0
- package/dist/src/codegen/extract/fields.js.map +1 -0
- package/dist/src/codegen/extract/graphql.d.ts +14 -0
- package/dist/src/codegen/extract/graphql.d.ts.map +1 -0
- package/dist/src/codegen/extract/graphql.js +81 -0
- package/dist/src/codegen/extract/graphql.js.map +1 -0
- package/dist/src/codegen/extract/gwt.d.ts +6 -0
- package/dist/src/codegen/extract/gwt.d.ts.map +1 -0
- package/dist/src/codegen/extract/gwt.js +64 -0
- package/dist/src/codegen/extract/gwt.js.map +1 -0
- package/dist/src/codegen/extract/imports.d.ts +29 -0
- package/dist/src/codegen/extract/imports.d.ts.map +1 -0
- package/dist/src/codegen/extract/imports.js +55 -0
- package/dist/src/codegen/extract/imports.js.map +1 -0
- package/dist/src/codegen/extract/index.d.ts +10 -0
- package/dist/src/codegen/extract/index.d.ts.map +1 -0
- package/dist/src/codegen/extract/index.js +10 -0
- package/dist/src/codegen/extract/index.js.map +1 -0
- package/dist/src/codegen/extract/messages.d.ts +17 -0
- package/dist/src/codegen/extract/messages.d.ts.map +1 -0
- package/dist/src/codegen/extract/messages.js +172 -0
- package/dist/src/codegen/extract/messages.js.map +1 -0
- package/dist/src/codegen/extract/projection.d.ts +5 -0
- package/dist/src/codegen/extract/projection.d.ts.map +1 -0
- package/dist/src/codegen/extract/projection.js +44 -0
- package/dist/src/codegen/extract/projection.js.map +1 -0
- package/dist/src/codegen/extract/query.d.ts +14 -0
- package/dist/src/codegen/extract/query.d.ts.map +1 -0
- package/dist/src/codegen/extract/query.js +17 -0
- package/dist/src/codegen/extract/query.js.map +1 -0
- package/dist/src/codegen/extract/states.d.ts +5 -0
- package/dist/src/codegen/extract/states.d.ts.map +1 -0
- package/dist/src/codegen/extract/states.js +48 -0
- package/dist/src/codegen/extract/states.js.map +1 -0
- package/dist/src/codegen/extract/step-converter.d.ts +17 -0
- package/dist/src/codegen/extract/step-converter.d.ts.map +1 -0
- package/dist/src/codegen/extract/step-converter.js +82 -0
- package/dist/src/codegen/extract/step-converter.js.map +1 -0
- package/dist/src/codegen/extract/step-types.d.ts +29 -0
- package/dist/src/codegen/extract/step-types.d.ts.map +1 -0
- package/dist/src/codegen/extract/step-types.js +16 -0
- package/dist/src/codegen/extract/step-types.js.map +1 -0
- package/dist/src/codegen/extract/type-helpers.d.ts +13 -0
- package/dist/src/codegen/extract/type-helpers.d.ts.map +1 -0
- package/dist/src/codegen/extract/type-helpers.js +98 -0
- package/dist/src/codegen/extract/type-helpers.js.map +1 -0
- package/dist/src/codegen/scaffoldFromSchema.d.ts +9 -0
- package/dist/src/codegen/scaffoldFromSchema.d.ts.map +1 -0
- package/dist/src/codegen/scaffoldFromSchema.js +389 -0
- package/dist/src/codegen/scaffoldFromSchema.js.map +1 -0
- package/dist/src/codegen/templates/command/command.ts.ejs +16 -0
- package/dist/src/codegen/templates/command/handler.specs.ts.ejs +50 -0
- package/dist/src/codegen/templates/command/handler.ts.ejs +30 -0
- package/dist/src/codegen/templates/command/input.ts.ejs +23 -0
- package/dist/src/codegen/templates/command/resolver.ts.ejs +56 -0
- package/dist/src/codegen/templates/entity/entity.ts.ejs +35 -0
- package/dist/src/codegen/templates/entity/index.ts.ejs +6 -0
- package/dist/src/codegen/templates/module/app-module.ts.ejs +65 -0
- package/dist/src/codegen/templates/module/domain-module.ts.ejs +46 -0
- package/dist/src/codegen/templates/query/handler.ts.ejs +25 -0
- package/dist/src/codegen/templates/query/query.ts.ejs +1 -0
- package/dist/src/codegen/templates/query/resolver.ts.ejs +20 -0
- package/dist/src/codegen/templates/query/type.ts.ejs +24 -0
- package/dist/src/codegen/types.d.ts +38 -0
- package/dist/src/codegen/types.d.ts.map +1 -0
- package/dist/src/codegen/types.js +2 -0
- package/dist/src/codegen/types.js.map +1 -0
- package/dist/src/codegen/utils/path.d.ts +4 -0
- package/dist/src/codegen/utils/path.d.ts.map +1 -0
- package/dist/src/codegen/utils/path.js +18 -0
- package/dist/src/codegen/utils/path.js.map +1 -0
- package/dist/src/commands/generate-server.d.ts +28 -0
- package/dist/src/commands/generate-server.d.ts.map +1 -0
- package/dist/src/commands/generate-server.js +421 -0
- package/dist/src/commands/generate-server.js.map +1 -0
- package/dist/src/index.d.ts +3 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +3 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/shared/graphql-types.ts +19 -0
- package/dist/src/shared/main.ts +27 -0
- package/dist/src/shared/mikro-orm.config.ts +18 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +70 -0
- package/src/codegen/entity-consolidation.ts +213 -0
- package/src/codegen/extract/commands.ts +108 -0
- package/src/codegen/extract/data-sink.ts +93 -0
- package/src/codegen/extract/events.ts +63 -0
- package/src/codegen/extract/fields.ts +17 -0
- package/src/codegen/extract/graphql.ts +103 -0
- package/src/codegen/extract/gwt.ts +79 -0
- package/src/codegen/extract/imports.ts +71 -0
- package/src/codegen/extract/index.ts +9 -0
- package/src/codegen/extract/messages.ts +232 -0
- package/src/codegen/extract/projection.ts +62 -0
- package/src/codegen/extract/query.ts +28 -0
- package/src/codegen/extract/states.ts +69 -0
- package/src/codegen/extract/step-converter.ts +124 -0
- package/src/codegen/extract/step-types.ts +51 -0
- package/src/codegen/extract/type-helpers.ts +102 -0
- package/src/codegen/scaffoldFromSchema.ts +559 -0
- package/src/codegen/templates/command/command.ts.ejs +16 -0
- package/src/codegen/templates/command/handler.specs.ts.ejs +50 -0
- package/src/codegen/templates/command/handler.ts.ejs +30 -0
- package/src/codegen/templates/command/input.ts.ejs +23 -0
- package/src/codegen/templates/command/resolver.ts.ejs +56 -0
- package/src/codegen/templates/entity/entity.ts.ejs +35 -0
- package/src/codegen/templates/entity/index.ts.ejs +6 -0
- package/src/codegen/templates/module/app-module.ts.ejs +65 -0
- package/src/codegen/templates/module/domain-module.ts.ejs +46 -0
- package/src/codegen/templates/query/handler.ts.ejs +25 -0
- package/src/codegen/templates/query/query.ts.ejs +1 -0
- package/src/codegen/templates/query/resolver.ts.ejs +20 -0
- package/src/codegen/templates/query/type.ts.ejs +24 -0
- package/src/codegen/types.ts +39 -0
- package/src/codegen/utils/path.ts +20 -0
- package/src/commands/generate-server.ts +551 -0
- package/src/index.ts +10 -0
- package/src/shared/graphql-types.ts +19 -0
- package/src/shared/main.ts +27 -0
- package/src/shared/mikro-orm.config.ts +18 -0
- package/tsconfig.json +12 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { Slice } from '@auto-engineer/narrative';
|
|
2
|
+
|
|
3
|
+
interface ProjectionOrigin {
|
|
4
|
+
type: 'projection';
|
|
5
|
+
idField?: string;
|
|
6
|
+
name?: string;
|
|
7
|
+
singleton?: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
interface HasOrigin {
|
|
11
|
+
origin: unknown;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function hasOrigin(dataSource: unknown): dataSource is HasOrigin {
|
|
15
|
+
return typeof dataSource === 'object' && dataSource !== null && 'origin' in dataSource;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function isProjectionOrigin(origin: unknown): origin is ProjectionOrigin {
|
|
19
|
+
if (typeof origin !== 'object' || origin === null) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const obj = origin as Record<string, unknown>;
|
|
24
|
+
return obj.type === 'projection';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function extractProjectionField<K extends keyof ProjectionOrigin>(slice: Slice, fieldName: K): string | undefined {
|
|
28
|
+
if (!('server' in slice)) return undefined;
|
|
29
|
+
const dataSource = slice.server?.data?.[0];
|
|
30
|
+
if (!hasOrigin(dataSource)) return undefined;
|
|
31
|
+
|
|
32
|
+
const origin = dataSource.origin;
|
|
33
|
+
if (isProjectionOrigin(origin)) {
|
|
34
|
+
const value = origin[fieldName];
|
|
35
|
+
if (typeof value === 'string') {
|
|
36
|
+
return value;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function extractProjectionIdField(slice: Slice): string | undefined {
|
|
44
|
+
return extractProjectionField(slice, 'idField');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function extractProjectionName(slice: Slice): string | undefined {
|
|
48
|
+
return extractProjectionField(slice, 'name');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function extractProjectionSingleton(slice: Slice): boolean {
|
|
52
|
+
if (!('server' in slice)) return false;
|
|
53
|
+
const dataSource = slice.server?.data?.[0];
|
|
54
|
+
if (!hasOrigin(dataSource)) return false;
|
|
55
|
+
|
|
56
|
+
const origin = dataSource.origin;
|
|
57
|
+
if (isProjectionOrigin(origin)) {
|
|
58
|
+
return origin.singleton === true;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Slice } from '@auto-engineer/narrative';
|
|
2
|
+
import { extractGwtSpecsFromSlice } from './step-converter';
|
|
3
|
+
import type { EventExample, StateExample } from './step-types';
|
|
4
|
+
|
|
5
|
+
interface QueryGwtCondition {
|
|
6
|
+
description: string;
|
|
7
|
+
given?: EventExample[];
|
|
8
|
+
when: EventExample[];
|
|
9
|
+
then: Array<{ stateRef: string; exampleData: Record<string, unknown> }>;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function buildQueryGwtMapping(slice: Slice): QueryGwtCondition[] {
|
|
13
|
+
if (slice.type !== 'query') return [];
|
|
14
|
+
|
|
15
|
+
const gwtSpecs = extractGwtSpecsFromSlice(slice);
|
|
16
|
+
|
|
17
|
+
return gwtSpecs.map((gwt) => {
|
|
18
|
+
const givenEvents = Array.isArray(gwt.given) ? gwt.given.filter((i): i is EventExample => 'eventRef' in i) : [];
|
|
19
|
+
const whenEvents = Array.isArray(gwt.when) ? gwt.when.filter((i): i is EventExample => 'eventRef' in i) : [];
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
description: gwt.description,
|
|
23
|
+
given: givenEvents.length > 0 ? givenEvents : undefined,
|
|
24
|
+
when: whenEvents,
|
|
25
|
+
then: gwt.then.filter((i): i is StateExample => 'stateRef' in i),
|
|
26
|
+
};
|
|
27
|
+
});
|
|
28
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { Slice } from '@auto-engineer/narrative';
|
|
2
|
+
import type { Message, MessageDefinition } from '../types';
|
|
3
|
+
import { extractFieldsFromMessage } from './fields';
|
|
4
|
+
|
|
5
|
+
interface DataItem {
|
|
6
|
+
origin?: unknown;
|
|
7
|
+
target?: {
|
|
8
|
+
name?: string;
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function createStateMessage(stateName: string, allMessages: MessageDefinition[]): Message {
|
|
13
|
+
return {
|
|
14
|
+
type: stateName,
|
|
15
|
+
fields: extractFieldsFromMessage(stateName, 'state', allMessages),
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function hasServerData(slice: Slice): slice is Slice & { server: { data: unknown[] } } {
|
|
20
|
+
return (
|
|
21
|
+
'server' in slice &&
|
|
22
|
+
Boolean(slice.server) &&
|
|
23
|
+
'data' in slice.server &&
|
|
24
|
+
Array.isArray(slice.server.data) &&
|
|
25
|
+
slice.server.data.length > 0
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function extractStatesFromTarget(slice: Slice, allMessages: MessageDefinition[]): Message[] {
|
|
30
|
+
if (!hasServerData(slice)) {
|
|
31
|
+
return [];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const targets = slice.server.data
|
|
35
|
+
.map((d) => (d as DataItem).target?.name)
|
|
36
|
+
.filter((name): name is string => typeof name === 'string');
|
|
37
|
+
const uniqueTargets = Array.from(new Set(targets));
|
|
38
|
+
|
|
39
|
+
return uniqueTargets.map((name) => createStateMessage(name, allMessages));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function extractStatesFromData(slice: Slice, allMessages: MessageDefinition[]): Message[] {
|
|
43
|
+
if (!hasServerData(slice)) {
|
|
44
|
+
return [];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const states: Message[] = [];
|
|
48
|
+
const seenStates = new Set<string>();
|
|
49
|
+
|
|
50
|
+
for (const dataItem of slice.server.data) {
|
|
51
|
+
const item = dataItem as DataItem;
|
|
52
|
+
if (!('origin' in item) || typeof item.target?.name !== 'string') {
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const stateName = item.target.name;
|
|
57
|
+
if (seenStates.has(stateName)) {
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const fields = extractFieldsFromMessage(stateName, 'state', allMessages);
|
|
62
|
+
if (fields.length > 0) {
|
|
63
|
+
states.push(createStateMessage(stateName, allMessages));
|
|
64
|
+
seenStates.add(stateName);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return states;
|
|
69
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import type { Example, Slice, Spec } from '@auto-engineer/narrative';
|
|
2
|
+
import {
|
|
3
|
+
type CommandExample,
|
|
4
|
+
type ErrorRef,
|
|
5
|
+
type EventExample,
|
|
6
|
+
getSliceType,
|
|
7
|
+
isStepWithDocString,
|
|
8
|
+
isStepWithError,
|
|
9
|
+
type MajorKeyword,
|
|
10
|
+
resolveMajorKeyword,
|
|
11
|
+
type SliceType,
|
|
12
|
+
type StateExample,
|
|
13
|
+
} from './step-types';
|
|
14
|
+
|
|
15
|
+
export type { CommandExample, ErrorRef, EventExample, SliceType, StateExample };
|
|
16
|
+
export { getSliceType };
|
|
17
|
+
|
|
18
|
+
export interface GwtResult {
|
|
19
|
+
given: EventExample[];
|
|
20
|
+
when: CommandExample | EventExample[];
|
|
21
|
+
then: Array<EventExample | StateExample | CommandExample | ErrorRef>;
|
|
22
|
+
description: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface ParsedSteps {
|
|
26
|
+
given: EventExample[];
|
|
27
|
+
whenItems: Array<{ text: string; exampleData: Record<string, unknown> }>;
|
|
28
|
+
thenItems: Array<{ text: string; exampleData: Record<string, unknown> }>;
|
|
29
|
+
thenErrors: ErrorRef[];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function parseSteps(example: Example): ParsedSteps {
|
|
33
|
+
const given: EventExample[] = [];
|
|
34
|
+
const whenItems: Array<{ text: string; exampleData: Record<string, unknown> }> = [];
|
|
35
|
+
const thenItems: Array<{ text: string; exampleData: Record<string, unknown> }> = [];
|
|
36
|
+
const thenErrors: ErrorRef[] = [];
|
|
37
|
+
|
|
38
|
+
let effectiveKeyword: MajorKeyword = 'Given';
|
|
39
|
+
|
|
40
|
+
for (const step of example.steps) {
|
|
41
|
+
if (isStepWithError(step)) {
|
|
42
|
+
thenErrors.push({ errorType: step.error.type, message: step.error.message });
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (!isStepWithDocString(step)) continue;
|
|
47
|
+
|
|
48
|
+
effectiveKeyword = resolveMajorKeyword(step.keyword, effectiveKeyword);
|
|
49
|
+
const exampleData = step.docString ?? {};
|
|
50
|
+
|
|
51
|
+
if (effectiveKeyword === 'Given') {
|
|
52
|
+
given.push({ eventRef: step.text, exampleData });
|
|
53
|
+
} else if (effectiveKeyword === 'When') {
|
|
54
|
+
whenItems.push({ text: step.text, exampleData });
|
|
55
|
+
} else if (effectiveKeyword === 'Then') {
|
|
56
|
+
thenItems.push({ text: step.text, exampleData });
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return { given, whenItems, thenItems, thenErrors };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function buildGwtResult(sliceType: SliceType, parsed: ParsedSteps, description: string): GwtResult {
|
|
64
|
+
const { given, whenItems, thenItems, thenErrors } = parsed;
|
|
65
|
+
|
|
66
|
+
if (sliceType === 'command') {
|
|
67
|
+
return {
|
|
68
|
+
given,
|
|
69
|
+
when: { commandRef: whenItems[0]?.text ?? '', exampleData: whenItems[0]?.exampleData ?? {} },
|
|
70
|
+
then: [...thenItems.map((t) => ({ eventRef: t.text, exampleData: t.exampleData })), ...thenErrors],
|
|
71
|
+
description,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (sliceType === 'react') {
|
|
76
|
+
return {
|
|
77
|
+
given,
|
|
78
|
+
when: whenItems.map((w) => ({ eventRef: w.text, exampleData: w.exampleData })),
|
|
79
|
+
then: [...thenItems.map((t) => ({ commandRef: t.text, exampleData: t.exampleData })), ...thenErrors],
|
|
80
|
+
description,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
given,
|
|
86
|
+
when: whenItems.map((w) => ({ eventRef: w.text, exampleData: w.exampleData })),
|
|
87
|
+
then: [...thenItems.map((t) => ({ stateRef: t.text, exampleData: t.exampleData })), ...thenErrors],
|
|
88
|
+
description,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function stepsToGwt(example: Example, sliceType: SliceType): GwtResult {
|
|
93
|
+
const parsed = parseSteps(example);
|
|
94
|
+
return buildGwtResult(sliceType, parsed, example.name);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export interface GwtConditionWithRule extends GwtResult {
|
|
98
|
+
ruleDescription: string;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function extractGwtFromSpecs(specs: Spec[], sliceType: SliceType): GwtConditionWithRule[] {
|
|
102
|
+
const results: GwtConditionWithRule[] = [];
|
|
103
|
+
|
|
104
|
+
for (const spec of specs) {
|
|
105
|
+
for (const rule of spec.rules) {
|
|
106
|
+
for (const example of rule.examples) {
|
|
107
|
+
const gwt = stepsToGwt(example, sliceType);
|
|
108
|
+
results.push({
|
|
109
|
+
...gwt,
|
|
110
|
+
ruleDescription: rule.name,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return results;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function extractGwtSpecsFromSlice(slice: Slice): GwtConditionWithRule[] {
|
|
120
|
+
if (!('server' in slice)) return [];
|
|
121
|
+
const specs = slice.server?.specs;
|
|
122
|
+
if (!Array.isArray(specs) || specs.length === 0) return [];
|
|
123
|
+
return extractGwtFromSpecs(specs, getSliceType(slice));
|
|
124
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { Slice, Step, StepWithDocStringSchema, StepWithErrorSchema } from '@auto-engineer/narrative';
|
|
2
|
+
import type { z } from 'zod';
|
|
3
|
+
|
|
4
|
+
export type StepWithDocString = z.infer<typeof StepWithDocStringSchema>;
|
|
5
|
+
export type StepWithError = z.infer<typeof StepWithErrorSchema>;
|
|
6
|
+
|
|
7
|
+
export type StepKeyword = StepWithDocString['keyword'];
|
|
8
|
+
export type MajorKeyword = Exclude<StepKeyword, 'And'>;
|
|
9
|
+
|
|
10
|
+
export type ErrorType = StepWithError['error']['type'];
|
|
11
|
+
|
|
12
|
+
export type SliceType = Extract<Slice['type'], 'command' | 'query' | 'react'>;
|
|
13
|
+
|
|
14
|
+
export function getSliceType(slice: Slice): SliceType {
|
|
15
|
+
if (slice.type === 'command' || slice.type === 'query' || slice.type === 'react') {
|
|
16
|
+
return slice.type;
|
|
17
|
+
}
|
|
18
|
+
return 'command';
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function isStepWithDocString(step: Step): step is StepWithDocString {
|
|
22
|
+
return 'text' in step;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function isStepWithError(step: Step): step is StepWithError {
|
|
26
|
+
return 'error' in step;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function resolveMajorKeyword(keyword: StepKeyword, current: MajorKeyword): MajorKeyword {
|
|
30
|
+
return keyword === 'And' ? current : keyword;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface CommandExample {
|
|
34
|
+
commandRef: string;
|
|
35
|
+
exampleData: Record<string, unknown>;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface EventExample {
|
|
39
|
+
eventRef: string;
|
|
40
|
+
exampleData: Record<string, unknown>;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface StateExample {
|
|
44
|
+
stateRef: string;
|
|
45
|
+
exampleData: Record<string, unknown>;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface ErrorRef {
|
|
49
|
+
errorType: ErrorType;
|
|
50
|
+
message?: string;
|
|
51
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
export function isInlineObject(ts: string): boolean {
|
|
2
|
+
return /^\{[\s\S]*\}$/.test((ts ?? '').trim());
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export function isInlineObjectArray(ts: string): boolean {
|
|
6
|
+
const t = (ts ?? '').trim();
|
|
7
|
+
return /^Array<\{[\s\S]*\}>$/.test(t) || /^\{[\s\S]*\}\[\]$/.test(t);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function baseTs(ts: string): string {
|
|
11
|
+
return (ts ?? 'string').replace(/\s*\|\s*null\b/g, '').trim();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function createIsEnumType(toTsFieldType: (ts: string) => string) {
|
|
15
|
+
return (tsType: string): boolean => {
|
|
16
|
+
const converted = toTsFieldType(tsType);
|
|
17
|
+
const base = converted
|
|
18
|
+
.replace(/\s*\|\s*null\b/g, '')
|
|
19
|
+
.replace(/\[\]$/, '')
|
|
20
|
+
.trim();
|
|
21
|
+
return (
|
|
22
|
+
/^[A-Z][a-zA-Z0-9]*$/.test(base) &&
|
|
23
|
+
![
|
|
24
|
+
'String',
|
|
25
|
+
'Number',
|
|
26
|
+
'Boolean',
|
|
27
|
+
'Date',
|
|
28
|
+
'ID',
|
|
29
|
+
'Int',
|
|
30
|
+
'Float',
|
|
31
|
+
'GraphQLISODateTime',
|
|
32
|
+
'GraphQLJSON',
|
|
33
|
+
'JSON',
|
|
34
|
+
].includes(base)
|
|
35
|
+
);
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function createFieldUsesDate(graphqlType: (ts: string) => string) {
|
|
40
|
+
return (ts: string): boolean => {
|
|
41
|
+
const b = baseTs(ts);
|
|
42
|
+
const gqlType = graphqlType(b);
|
|
43
|
+
if (gqlType.includes('GraphQLISODateTime')) return true;
|
|
44
|
+
if (isInlineObject(b) || isInlineObjectArray(b)) return /:\s*Date\b/.test(b);
|
|
45
|
+
return false;
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function createFieldUsesJSON(graphqlType: (ts: string) => string) {
|
|
50
|
+
return (ts: string): boolean => {
|
|
51
|
+
const b = baseTs(ts);
|
|
52
|
+
const gqlType = graphqlType(b);
|
|
53
|
+
if (gqlType.includes('GraphQLJSON') || gqlType.includes('JSON')) return true;
|
|
54
|
+
if (isInlineObject(b) || isInlineObjectArray(b)) return /:\s*(unknown|any|object)\b/.test(b);
|
|
55
|
+
return false;
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function createFieldUsesFloat(graphqlType: (ts: string) => string) {
|
|
60
|
+
return (ts: string): boolean => {
|
|
61
|
+
const b = baseTs(ts);
|
|
62
|
+
const gqlType = graphqlType(b);
|
|
63
|
+
if (gqlType.includes('Float')) return true;
|
|
64
|
+
if (isInlineObject(b) || isInlineObjectArray(b)) {
|
|
65
|
+
const inner = b.trim().startsWith('Array<')
|
|
66
|
+
? b
|
|
67
|
+
.trim()
|
|
68
|
+
.replace(/^Array<\{/, '{')
|
|
69
|
+
.replace(/}>$/, '}')
|
|
70
|
+
: b.trim().replace(/\[\]$/, '');
|
|
71
|
+
const match = inner.match(/^\{([\s\S]*)\}$/);
|
|
72
|
+
const body = match ? match[1] : '';
|
|
73
|
+
const rawFields = body.split(/[,;]\s*/).filter(Boolean);
|
|
74
|
+
return rawFields.some((f) => {
|
|
75
|
+
const parts = f.split(':');
|
|
76
|
+
const type = parts.slice(1).join(':').trim();
|
|
77
|
+
return type.length > 0 && graphqlType(type).includes('Float');
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
return false;
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function extractEnumName(tsType: string, toTsFieldType: (ts: string) => string): string {
|
|
85
|
+
return toTsFieldType(tsType)
|
|
86
|
+
.replace(/\s*\|\s*null\b/g, '')
|
|
87
|
+
.replace(/\[\]$/, '')
|
|
88
|
+
.trim();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function createCollectEnumNames(isEnumType: (tsType: string) => boolean, toTsFieldType: (ts: string) => string) {
|
|
92
|
+
return (fields: Array<{ type?: string; tsType?: string }>): string[] => {
|
|
93
|
+
const enumNames = new Set<string>();
|
|
94
|
+
for (const field of fields) {
|
|
95
|
+
const fieldType = field.type ?? field.tsType;
|
|
96
|
+
if (fieldType !== undefined && fieldType !== null && fieldType.length > 0 && isEnumType(fieldType)) {
|
|
97
|
+
enumNames.add(extractEnumName(fieldType, toTsFieldType));
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return Array.from(enumNames).sort();
|
|
101
|
+
};
|
|
102
|
+
}
|