@auto-engineer/server-generator-apollo-emmett 1.88.0 → 1.89.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.
- package/.turbo/turbo-build.log +1 -1
- package/.turbo/turbo-test.log +5 -5
- package/.turbo/turbo-type-check.log +1 -1
- package/CHANGELOG.md +72 -0
- package/dist/src/codegen/extract/slice-normalizer.d.ts.map +1 -1
- package/dist/src/codegen/extract/slice-normalizer.js +14 -0
- package/dist/src/codegen/extract/slice-normalizer.js.map +1 -1
- package/dist/src/codegen/extract/type-helpers.d.ts +10 -0
- package/dist/src/codegen/extract/type-helpers.d.ts.map +1 -1
- package/dist/src/codegen/extract/type-helpers.js +17 -0
- package/dist/src/codegen/extract/type-helpers.js.map +1 -1
- package/dist/src/codegen/scaffoldFromSchema.d.ts.map +1 -1
- package/dist/src/codegen/scaffoldFromSchema.js +6 -4
- package/dist/src/codegen/scaffoldFromSchema.js.map +1 -1
- package/dist/src/codegen/templates/command/decide.specs.specs.ts +293 -34
- package/dist/src/codegen/templates/command/decide.specs.ts +34 -14
- package/dist/src/codegen/templates/command/decide.specs.ts.ejs +47 -14
- package/dist/src/codegen/templates/command/decide.ts.ejs +32 -4
- package/dist/src/codegen/templates/command/mutation.resolver.specs.ts +72 -1
- package/dist/src/codegen/templates/command/mutation.resolver.ts.ejs +1 -1
- package/dist/src/codegen/templates/query/projection.specs.specs.ts +124 -0
- package/dist/src/codegen/templates/query/projection.specs.ts +20 -0
- package/dist/src/codegen/templates/query/projection.specs.ts.ejs +5 -1
- package/dist/src/codegen/templates/query/projection.ts.ejs +5 -0
- package/dist/src/codegen/templates/query/query.resolver.ts.ejs +1 -1
- package/dist/src/codegen/templates/react/react.specs.specs.ts +115 -0
- package/dist/src/codegen/templates/react/react.specs.ts +9 -2
- package/dist/src/codegen/templates/react/react.specs.ts.ejs +1 -3
- package/dist/src/codegen/templates/react/react.ts.ejs +22 -9
- package/dist/src/codegen/templates/react/react.ts.specs.ts +253 -0
- package/dist/src/codegen/templates/react/register.specs.ts +27 -23
- package/dist/src/codegen/templates/react/register.ts.ejs +5 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/ketchup-plan.md +12 -3
- package/package.json +4 -4
- package/src/codegen/extract/slice-normalizer.specs.ts +83 -0
- package/src/codegen/extract/slice-normalizer.ts +15 -0
- package/src/codegen/extract/type-helpers.specs.ts +77 -1
- package/src/codegen/extract/type-helpers.ts +23 -0
- package/src/codegen/formatTsValueSimple.specs.ts +8 -0
- package/src/codegen/scaffoldFromSchema.ts +7 -3
- package/src/codegen/templates/command/decide.specs.specs.ts +293 -34
- package/src/codegen/templates/command/decide.specs.ts +34 -14
- package/src/codegen/templates/command/decide.specs.ts.ejs +47 -14
- package/src/codegen/templates/command/decide.ts.ejs +32 -4
- package/src/codegen/templates/command/mutation.resolver.specs.ts +72 -1
- package/src/codegen/templates/command/mutation.resolver.ts.ejs +1 -1
- package/src/codegen/templates/query/projection.specs.specs.ts +124 -0
- package/src/codegen/templates/query/projection.specs.ts +20 -0
- package/src/codegen/templates/query/projection.specs.ts.ejs +5 -1
- package/src/codegen/templates/query/projection.ts.ejs +5 -0
- package/src/codegen/templates/query/query.resolver.ts.ejs +1 -1
- package/src/codegen/templates/react/react.specs.specs.ts +115 -0
- package/src/codegen/templates/react/react.specs.ts +9 -2
- package/src/codegen/templates/react/react.specs.ts.ejs +1 -3
- package/src/codegen/templates/react/react.ts.ejs +22 -9
- package/src/codegen/templates/react/react.ts.specs.ts +253 -0
- package/src/codegen/templates/react/register.specs.ts +27 -23
- package/src/codegen/templates/react/register.ts.ejs +5 -1
- package/dist/src/codegen/extract/graphql.d.ts +0 -14
- package/dist/src/codegen/extract/graphql.d.ts.map +0 -1
- package/dist/src/codegen/extract/graphql.js +0 -81
- package/dist/src/codegen/extract/graphql.js.map +0 -1
- package/src/codegen/extract/graphql.ts +0 -103
|
@@ -194,6 +194,8 @@ describe('react.ts.ejs', () => {
|
|
|
194
194
|
expect(reactFile?.contents).toContain('commandSender.send({');
|
|
195
195
|
expect(reactFile?.contents).toContain('event.data.orderId');
|
|
196
196
|
expect(reactFile?.contents).toContain('undefined, // TODO: source unknown');
|
|
197
|
+
expect(reactFile?.contents).toContain('NEVER hardcode values copied from test assertions');
|
|
198
|
+
expect(reactFile?.contents).toContain('Preserve all import paths');
|
|
197
199
|
expect(reactFile?.contents).not.toContain('throw new IllegalStateError');
|
|
198
200
|
});
|
|
199
201
|
|
|
@@ -551,6 +553,8 @@ export type BarberNotified = Event<
|
|
|
551
553
|
const reactFile = plans.find((p) => p.outputPath.endsWith('notify-barber-of-cancellation/react.ts'));
|
|
552
554
|
|
|
553
555
|
expect(reactFile?.contents).toContain('aggregateStream');
|
|
556
|
+
expect(reactFile?.contents).toContain('Record<string, unknown>');
|
|
557
|
+
expect(reactFile?.contents).toContain('Do NOT modify or remove aggregateStream');
|
|
554
558
|
expect(reactFile?.contents).toContain('event.data.barberId');
|
|
555
559
|
expect(reactFile?.contents).toContain('appointment');
|
|
556
560
|
expect(reactFile?.contents).not.toContain('readable from database');
|
|
@@ -562,4 +566,253 @@ export type BarberNotified = Event<
|
|
|
562
566
|
expect(reactFile?.contents).not.toContain('throw new IllegalStateError');
|
|
563
567
|
expect(reactFile?.contents).not.toContain('// Example:');
|
|
564
568
|
});
|
|
569
|
+
|
|
570
|
+
it('should ignore event And-steps in react Then, using only commands', async () => {
|
|
571
|
+
const spec: SpecsSchema = {
|
|
572
|
+
variant: 'specs',
|
|
573
|
+
narratives: [
|
|
574
|
+
{
|
|
575
|
+
name: 'notification flow',
|
|
576
|
+
slices: [
|
|
577
|
+
{
|
|
578
|
+
type: 'command',
|
|
579
|
+
name: 'earn points',
|
|
580
|
+
client: { specs: [] },
|
|
581
|
+
server: {
|
|
582
|
+
description: '',
|
|
583
|
+
specs: [
|
|
584
|
+
{
|
|
585
|
+
type: 'gherkin',
|
|
586
|
+
feature: 'Earn points',
|
|
587
|
+
rules: [
|
|
588
|
+
{
|
|
589
|
+
name: 'Should earn',
|
|
590
|
+
examples: [
|
|
591
|
+
{
|
|
592
|
+
name: 'Points earned',
|
|
593
|
+
steps: [
|
|
594
|
+
{ keyword: 'When', text: 'EarnPoints', docString: { memberId: 'm1', amount: 10 } },
|
|
595
|
+
{ keyword: 'Then', text: 'PointsEarned', docString: { memberId: 'm1', amount: 10 } },
|
|
596
|
+
],
|
|
597
|
+
},
|
|
598
|
+
],
|
|
599
|
+
},
|
|
600
|
+
],
|
|
601
|
+
},
|
|
602
|
+
],
|
|
603
|
+
},
|
|
604
|
+
},
|
|
605
|
+
{
|
|
606
|
+
type: 'react',
|
|
607
|
+
name: 'notify milestone',
|
|
608
|
+
server: {
|
|
609
|
+
description: 'Notifies on milestone',
|
|
610
|
+
data: {
|
|
611
|
+
items: [
|
|
612
|
+
{
|
|
613
|
+
target: { type: 'Command', name: 'SendNotification' },
|
|
614
|
+
destination: { type: 'stream', pattern: 'notif-${memberId}' },
|
|
615
|
+
},
|
|
616
|
+
],
|
|
617
|
+
},
|
|
618
|
+
specs: [
|
|
619
|
+
{
|
|
620
|
+
type: 'gherkin',
|
|
621
|
+
feature: 'Milestone notification',
|
|
622
|
+
rules: [
|
|
623
|
+
{
|
|
624
|
+
name: 'Should notify',
|
|
625
|
+
examples: [
|
|
626
|
+
{
|
|
627
|
+
name: 'Milestone reached',
|
|
628
|
+
steps: [
|
|
629
|
+
{ keyword: 'When', text: 'PointsEarned', docString: { memberId: 'm1', amount: 10 } },
|
|
630
|
+
{ keyword: 'Then', text: 'MilestoneNotified', docString: { memberId: 'm1' } },
|
|
631
|
+
{ keyword: 'And', text: 'SendNotification', docString: { memberId: 'm1' } },
|
|
632
|
+
],
|
|
633
|
+
},
|
|
634
|
+
],
|
|
635
|
+
},
|
|
636
|
+
],
|
|
637
|
+
},
|
|
638
|
+
],
|
|
639
|
+
},
|
|
640
|
+
},
|
|
641
|
+
],
|
|
642
|
+
},
|
|
643
|
+
],
|
|
644
|
+
messages: [
|
|
645
|
+
{
|
|
646
|
+
type: 'command',
|
|
647
|
+
name: 'EarnPoints',
|
|
648
|
+
fields: [
|
|
649
|
+
{ name: 'memberId', type: 'string', required: true },
|
|
650
|
+
{ name: 'amount', type: 'number', required: true },
|
|
651
|
+
],
|
|
652
|
+
},
|
|
653
|
+
{
|
|
654
|
+
type: 'event',
|
|
655
|
+
name: 'PointsEarned',
|
|
656
|
+
source: 'internal',
|
|
657
|
+
fields: [
|
|
658
|
+
{ name: 'memberId', type: 'string', required: true },
|
|
659
|
+
{ name: 'amount', type: 'number', required: true },
|
|
660
|
+
],
|
|
661
|
+
},
|
|
662
|
+
{
|
|
663
|
+
type: 'command',
|
|
664
|
+
name: 'SendNotification',
|
|
665
|
+
fields: [{ name: 'memberId', type: 'string', required: true }],
|
|
666
|
+
},
|
|
667
|
+
{
|
|
668
|
+
type: 'event',
|
|
669
|
+
name: 'MilestoneNotified',
|
|
670
|
+
source: 'internal',
|
|
671
|
+
fields: [{ name: 'memberId', type: 'string', required: true }],
|
|
672
|
+
},
|
|
673
|
+
],
|
|
674
|
+
};
|
|
675
|
+
|
|
676
|
+
const { plans } = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
|
|
677
|
+
const reactFile = plans.find((p) => p.outputPath.endsWith('notify-milestone/react.ts'));
|
|
678
|
+
|
|
679
|
+
expect(reactFile?.contents).toContain("type: 'SendNotification'");
|
|
680
|
+
expect(reactFile?.contents).not.toContain('MilestoneNotified');
|
|
681
|
+
});
|
|
682
|
+
|
|
683
|
+
it('should use primitive linking field, skipping array/object fields', async () => {
|
|
684
|
+
const spec: SpecsSchema = {
|
|
685
|
+
variant: 'specs',
|
|
686
|
+
narratives: [
|
|
687
|
+
{
|
|
688
|
+
name: 'fitness flow',
|
|
689
|
+
slices: [
|
|
690
|
+
{
|
|
691
|
+
type: 'command',
|
|
692
|
+
name: 'log workout',
|
|
693
|
+
client: { specs: [] },
|
|
694
|
+
server: {
|
|
695
|
+
description: '',
|
|
696
|
+
specs: [
|
|
697
|
+
{
|
|
698
|
+
type: 'gherkin',
|
|
699
|
+
feature: 'Log workout',
|
|
700
|
+
rules: [
|
|
701
|
+
{
|
|
702
|
+
name: 'Should log workout',
|
|
703
|
+
examples: [
|
|
704
|
+
{
|
|
705
|
+
name: 'Workout logged',
|
|
706
|
+
steps: [
|
|
707
|
+
{
|
|
708
|
+
keyword: 'When',
|
|
709
|
+
text: 'LogWorkout',
|
|
710
|
+
docString: { memberId: 'm1', exercises: [{ exercise: 'squat', sets: 3 }] },
|
|
711
|
+
},
|
|
712
|
+
{
|
|
713
|
+
keyword: 'Then',
|
|
714
|
+
text: 'WorkoutLogged',
|
|
715
|
+
docString: { memberId: 'm1', exercises: [{ exercise: 'squat', sets: 3 }] },
|
|
716
|
+
},
|
|
717
|
+
],
|
|
718
|
+
},
|
|
719
|
+
],
|
|
720
|
+
},
|
|
721
|
+
],
|
|
722
|
+
},
|
|
723
|
+
],
|
|
724
|
+
},
|
|
725
|
+
},
|
|
726
|
+
{
|
|
727
|
+
type: 'react',
|
|
728
|
+
name: 'check record updates',
|
|
729
|
+
server: {
|
|
730
|
+
description: 'Updates records after workout',
|
|
731
|
+
data: {
|
|
732
|
+
items: [
|
|
733
|
+
{
|
|
734
|
+
target: { type: 'Command', name: 'UpdateRecord' },
|
|
735
|
+
destination: { type: 'stream', pattern: 'records-${memberId}' },
|
|
736
|
+
},
|
|
737
|
+
],
|
|
738
|
+
},
|
|
739
|
+
specs: [
|
|
740
|
+
{
|
|
741
|
+
type: 'gherkin',
|
|
742
|
+
feature: 'Check record updates',
|
|
743
|
+
rules: [
|
|
744
|
+
{
|
|
745
|
+
name: 'Should update records',
|
|
746
|
+
examples: [
|
|
747
|
+
{
|
|
748
|
+
name: 'Record updated',
|
|
749
|
+
steps: [
|
|
750
|
+
{
|
|
751
|
+
keyword: 'Given',
|
|
752
|
+
text: 'WorkoutSummary',
|
|
753
|
+
docString: { memberId: 'm1', exercises: [{ exercise: 'squat', sets: 3 }] },
|
|
754
|
+
},
|
|
755
|
+
{
|
|
756
|
+
keyword: 'When',
|
|
757
|
+
text: 'WorkoutLogged',
|
|
758
|
+
docString: { memberId: 'm1', exercises: [{ exercise: 'squat', sets: 3 }] },
|
|
759
|
+
},
|
|
760
|
+
{
|
|
761
|
+
keyword: 'Then',
|
|
762
|
+
text: 'UpdateRecord',
|
|
763
|
+
docString: { memberId: 'm1' },
|
|
764
|
+
},
|
|
765
|
+
],
|
|
766
|
+
},
|
|
767
|
+
],
|
|
768
|
+
},
|
|
769
|
+
],
|
|
770
|
+
},
|
|
771
|
+
],
|
|
772
|
+
},
|
|
773
|
+
},
|
|
774
|
+
],
|
|
775
|
+
},
|
|
776
|
+
],
|
|
777
|
+
messages: [
|
|
778
|
+
{
|
|
779
|
+
type: 'command',
|
|
780
|
+
name: 'LogWorkout',
|
|
781
|
+
fields: [
|
|
782
|
+
{ name: 'memberId', type: 'string', required: true },
|
|
783
|
+
{ name: 'exercises', type: '{exercise:string,sets:number}[]', required: true },
|
|
784
|
+
],
|
|
785
|
+
},
|
|
786
|
+
{
|
|
787
|
+
type: 'event',
|
|
788
|
+
name: 'WorkoutLogged',
|
|
789
|
+
source: 'internal',
|
|
790
|
+
fields: [
|
|
791
|
+
{ name: 'memberId', type: 'string', required: true },
|
|
792
|
+
{ name: 'exercises', type: '{exercise:string,sets:number}[]', required: true },
|
|
793
|
+
],
|
|
794
|
+
},
|
|
795
|
+
{
|
|
796
|
+
type: 'command',
|
|
797
|
+
name: 'UpdateRecord',
|
|
798
|
+
fields: [{ name: 'memberId', type: 'string', required: true }],
|
|
799
|
+
},
|
|
800
|
+
{
|
|
801
|
+
type: 'state',
|
|
802
|
+
name: 'WorkoutSummary',
|
|
803
|
+
fields: [
|
|
804
|
+
{ name: 'memberId', type: 'string', required: true },
|
|
805
|
+
{ name: 'exercises', type: '{exercise:string,sets:number}[]', required: true },
|
|
806
|
+
],
|
|
807
|
+
},
|
|
808
|
+
],
|
|
809
|
+
};
|
|
810
|
+
|
|
811
|
+
const { plans } = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
|
|
812
|
+
const reactFile = plans.find((p) => p.outputPath.endsWith('check-record-updates/react.ts'));
|
|
813
|
+
|
|
814
|
+
expect(reactFile?.contents).toContain("'WorkoutSummary-' + event.data.memberId");
|
|
815
|
+
expect(reactFile?.contents).toContain('aggregateStream');
|
|
816
|
+
expect(reactFile?.contents).not.toContain('event.data.exercises');
|
|
817
|
+
});
|
|
565
818
|
});
|
|
@@ -232,31 +232,35 @@ describe('register.ts.ejs (react slice)', () => {
|
|
|
232
232
|
const registerFile = plans.find((p) => p.outputPath.endsWith('send-notification-to-host/register.ts'));
|
|
233
233
|
|
|
234
234
|
expect(registerFile?.contents).toMatchInlineSnapshot(`
|
|
235
|
-
|
|
236
|
-
|
|
235
|
+
"import { type CommandSender, type EventSubscription, type EventStore } from '@event-driven-io/emmett';
|
|
236
|
+
import type { BookingRequested } from '../guest-submits-booking-request/events';
|
|
237
237
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
238
|
+
export async function register(messageBus: CommandSender & EventSubscription, eventStore: EventStore) {
|
|
239
|
+
messageBus.subscribe(async (event: BookingRequested) => {
|
|
240
|
+
/**
|
|
241
|
+
* ## IMPLEMENTATION INSTRUCTIONS ##
|
|
242
|
+
*
|
|
243
|
+
* - Replace the placeholder logic with the real implementation.
|
|
244
|
+
* - Send one or more commands via: messageBus.send({...})
|
|
245
|
+
* - If calling eventStore.aggregateStream(), type the evolve callback:
|
|
246
|
+
* evolve: (s: Record<string, unknown>, e: { type: string; data: Record<string, unknown> }) => ...
|
|
247
|
+
* - NEVER hardcode values copied from test assertions.
|
|
248
|
+
* - Preserve all import paths above — they are generated from the model.
|
|
249
|
+
*/
|
|
246
250
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
251
|
+
// await messageBus.send({
|
|
252
|
+
// type: 'NotifyHost',
|
|
253
|
+
// kind: 'Command',
|
|
254
|
+
// data: {
|
|
255
|
+
// // Map event fields to command fields here
|
|
256
|
+
// // e.g., userId: event.data.userId,
|
|
257
|
+
// },
|
|
258
|
+
// });
|
|
255
259
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
260
|
+
return;
|
|
261
|
+
}, 'BookingRequested');
|
|
262
|
+
}
|
|
263
|
+
"
|
|
264
|
+
`);
|
|
261
265
|
});
|
|
262
266
|
});
|
|
@@ -32,8 +32,12 @@ async (event: <%= pascalCase(eventType) %>) => {
|
|
|
32
32
|
/**
|
|
33
33
|
* ## IMPLEMENTATION INSTRUCTIONS ##
|
|
34
34
|
*
|
|
35
|
-
* - Replace the placeholder logic with the
|
|
35
|
+
* - Replace the placeholder logic with the real implementation.
|
|
36
36
|
* - Send one or more commands via: messageBus.send({...})
|
|
37
|
+
* - If calling eventStore.aggregateStream(), type the evolve callback:
|
|
38
|
+
* evolve: (s: Record<string, unknown>, e: { type: string; data: Record<string, unknown> }) => ...
|
|
39
|
+
* - NEVER hardcode values copied from test assertions.
|
|
40
|
+
* - Preserve all import paths above — they are generated from the model.
|
|
37
41
|
*/
|
|
38
42
|
|
|
39
43
|
// await messageBus.send({
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
export interface ParsedArg {
|
|
2
|
-
name: string;
|
|
3
|
-
tsType: string;
|
|
4
|
-
graphqlType: string;
|
|
5
|
-
nullable: boolean;
|
|
6
|
-
}
|
|
7
|
-
export interface ParsedGraphQlQuery {
|
|
8
|
-
queryName: string;
|
|
9
|
-
args: ParsedArg[];
|
|
10
|
-
returnType: string;
|
|
11
|
-
tsReturnType: string;
|
|
12
|
-
}
|
|
13
|
-
export declare function parseGraphQlRequest(request: string): ParsedGraphQlQuery;
|
|
14
|
-
//# sourceMappingURL=graphql.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"graphql.d.ts","sourceRoot":"","sources":["../../../../src/codegen/extract/graphql.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,SAAS,EAAE,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;CACtB;AA8CD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,kBAAkB,CAsCvE"}
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import { parse, print } from 'graphql';
|
|
2
|
-
function getTypeName(typeNode) {
|
|
3
|
-
if (typeNode.kind === 'NamedType') {
|
|
4
|
-
return { graphqlType: typeNode.name.value, nullable: true };
|
|
5
|
-
}
|
|
6
|
-
else if (typeNode.kind === 'NonNullType') {
|
|
7
|
-
const inner = getTypeName(typeNode.type);
|
|
8
|
-
return { ...inner, nullable: false };
|
|
9
|
-
}
|
|
10
|
-
else {
|
|
11
|
-
return getTypeName(typeNode.type);
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
function graphqlToTs(type) {
|
|
15
|
-
switch (type) {
|
|
16
|
-
case 'String':
|
|
17
|
-
return 'string';
|
|
18
|
-
case 'Int':
|
|
19
|
-
case 'Float':
|
|
20
|
-
case 'Number':
|
|
21
|
-
return 'number';
|
|
22
|
-
case 'Boolean':
|
|
23
|
-
return 'boolean';
|
|
24
|
-
case 'Date':
|
|
25
|
-
return 'Date';
|
|
26
|
-
default:
|
|
27
|
-
return type;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
function convertJsonAstToSdl(request) {
|
|
31
|
-
// Handle JSON-serialized AST
|
|
32
|
-
if (request.startsWith('{') && request.includes('"kind"')) {
|
|
33
|
-
try {
|
|
34
|
-
const ast = JSON.parse(request);
|
|
35
|
-
if (typeof ast === 'object' && ast !== null && 'kind' in ast && ast.kind === 'Document') {
|
|
36
|
-
// Convert AST to SDL string - cast is safe here as we've validated it's a Document
|
|
37
|
-
return print(ast);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
catch {
|
|
41
|
-
// If parsing fails, assume it's already a GraphQL string
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
return request;
|
|
45
|
-
}
|
|
46
|
-
export function parseGraphQlRequest(request) {
|
|
47
|
-
const sdlRequest = convertJsonAstToSdl(request);
|
|
48
|
-
const ast = parse(sdlRequest);
|
|
49
|
-
const op = ast.definitions.find((d) => d.kind === 'OperationDefinition' && d.operation === 'query');
|
|
50
|
-
if (!op)
|
|
51
|
-
throw new Error('No query operation found');
|
|
52
|
-
const queryName = op.name?.value;
|
|
53
|
-
if (queryName == null)
|
|
54
|
-
throw new Error('Query must have a name');
|
|
55
|
-
const args = (op.variableDefinitions ?? []).map((def) => {
|
|
56
|
-
const varName = def.variable.name.value;
|
|
57
|
-
const { graphqlType, nullable } = getTypeName(def.type);
|
|
58
|
-
return {
|
|
59
|
-
name: varName,
|
|
60
|
-
graphqlType,
|
|
61
|
-
tsType: graphqlToTs(graphqlType),
|
|
62
|
-
nullable,
|
|
63
|
-
};
|
|
64
|
-
});
|
|
65
|
-
const field = op.selectionSet.selections[0];
|
|
66
|
-
if (field?.kind !== 'Field' || !field.name.value) {
|
|
67
|
-
throw new Error('Query selection must be a field');
|
|
68
|
-
}
|
|
69
|
-
const baseName = field.name.value;
|
|
70
|
-
const returnType = `${pascalCase(baseName)}View`;
|
|
71
|
-
return {
|
|
72
|
-
queryName: baseName,
|
|
73
|
-
args,
|
|
74
|
-
returnType,
|
|
75
|
-
tsReturnType: `${returnType}[]`,
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
function pascalCase(input) {
|
|
79
|
-
return input.replace(/(^\w|_\w)/g, (match) => match.replace('_', '').toUpperCase());
|
|
80
|
-
}
|
|
81
|
-
//# sourceMappingURL=graphql.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"graphql.js","sourceRoot":"","sources":["../../../../src/codegen/extract/graphql.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
import { type OperationDefinitionNode, parse, print, type TypeNode } from 'graphql';
|
|
2
|
-
|
|
3
|
-
export interface ParsedArg {
|
|
4
|
-
name: string;
|
|
5
|
-
tsType: string;
|
|
6
|
-
graphqlType: string;
|
|
7
|
-
nullable: boolean;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export interface ParsedGraphQlQuery {
|
|
11
|
-
queryName: string;
|
|
12
|
-
args: ParsedArg[];
|
|
13
|
-
returnType: string;
|
|
14
|
-
tsReturnType: string;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function getTypeName(typeNode: TypeNode): { graphqlType: string; nullable: boolean } {
|
|
18
|
-
if (typeNode.kind === 'NamedType') {
|
|
19
|
-
return { graphqlType: typeNode.name.value, nullable: true };
|
|
20
|
-
} else if (typeNode.kind === 'NonNullType') {
|
|
21
|
-
const inner = getTypeName(typeNode.type);
|
|
22
|
-
return { ...inner, nullable: false };
|
|
23
|
-
} else {
|
|
24
|
-
return getTypeName(typeNode.type);
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function graphqlToTs(type: string): string {
|
|
29
|
-
switch (type) {
|
|
30
|
-
case 'String':
|
|
31
|
-
return 'string';
|
|
32
|
-
case 'Int':
|
|
33
|
-
case 'Float':
|
|
34
|
-
case 'Number':
|
|
35
|
-
return 'number';
|
|
36
|
-
case 'Boolean':
|
|
37
|
-
return 'boolean';
|
|
38
|
-
case 'Date':
|
|
39
|
-
return 'Date';
|
|
40
|
-
default:
|
|
41
|
-
return type;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function convertJsonAstToSdl(request: string): string {
|
|
46
|
-
// Handle JSON-serialized AST
|
|
47
|
-
if (request.startsWith('{') && request.includes('"kind"')) {
|
|
48
|
-
try {
|
|
49
|
-
const ast = JSON.parse(request) as unknown;
|
|
50
|
-
if (typeof ast === 'object' && ast !== null && 'kind' in ast && ast.kind === 'Document') {
|
|
51
|
-
// Convert AST to SDL string - cast is safe here as we've validated it's a Document
|
|
52
|
-
return print(ast as Parameters<typeof print>[0]);
|
|
53
|
-
}
|
|
54
|
-
} catch {
|
|
55
|
-
// If parsing fails, assume it's already a GraphQL string
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
return request;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export function parseGraphQlRequest(request: string): ParsedGraphQlQuery {
|
|
62
|
-
const sdlRequest = convertJsonAstToSdl(request);
|
|
63
|
-
|
|
64
|
-
const ast = parse(sdlRequest);
|
|
65
|
-
const op = ast.definitions.find(
|
|
66
|
-
(d): d is OperationDefinitionNode => d.kind === 'OperationDefinition' && d.operation === 'query',
|
|
67
|
-
);
|
|
68
|
-
|
|
69
|
-
if (!op) throw new Error('No query operation found');
|
|
70
|
-
|
|
71
|
-
const queryName = op.name?.value;
|
|
72
|
-
if (queryName == null) throw new Error('Query must have a name');
|
|
73
|
-
|
|
74
|
-
const args: ParsedArg[] = (op.variableDefinitions ?? []).map((def) => {
|
|
75
|
-
const varName = def.variable.name.value;
|
|
76
|
-
const { graphqlType, nullable } = getTypeName(def.type);
|
|
77
|
-
return {
|
|
78
|
-
name: varName,
|
|
79
|
-
graphqlType,
|
|
80
|
-
tsType: graphqlToTs(graphqlType),
|
|
81
|
-
nullable,
|
|
82
|
-
};
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
const field = op.selectionSet.selections[0];
|
|
86
|
-
if (field?.kind !== 'Field' || !field.name.value) {
|
|
87
|
-
throw new Error('Query selection must be a field');
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const baseName = field.name.value;
|
|
91
|
-
const returnType = `${pascalCase(baseName)}View`;
|
|
92
|
-
|
|
93
|
-
return {
|
|
94
|
-
queryName: baseName,
|
|
95
|
-
args,
|
|
96
|
-
returnType,
|
|
97
|
-
tsReturnType: `${returnType}[]`,
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
function pascalCase(input: string): string {
|
|
102
|
-
return input.replace(/(^\w|_\w)/g, (match) => match.replace('_', '').toUpperCase());
|
|
103
|
-
}
|