graphql-http 1.6.1 → 1.7.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.
- package/LICENSE.md +1 -1
- package/README.md +124 -114
- package/lib/audits/server.js +160 -106
- package/lib/audits/server.mjs +160 -106
- package/lib/audits/utils.d.mts +2 -3
- package/lib/audits/utils.d.ts +2 -3
- package/lib/audits/utils.js +3 -8
- package/lib/audits/utils.mjs +3 -8
- package/lib/handler.d.mts +26 -24
- package/lib/handler.d.ts +26 -24
- package/lib/handler.js +17 -7
- package/lib/handler.mjs +17 -7
- package/lib/use/express.d.mts +21 -0
- package/lib/use/express.d.ts +21 -0
- package/lib/use/express.js +73 -0
- package/lib/use/express.mjs +69 -0
- package/lib/use/fastify.d.mts +21 -0
- package/lib/use/fastify.d.ts +21 -0
- package/lib/use/fastify.js +69 -0
- package/lib/use/fastify.mjs +65 -0
- package/lib/use/fetch.d.mts +40 -0
- package/lib/use/fetch.d.ts +40 -0
- package/lib/use/fetch.js +81 -0
- package/lib/use/fetch.mjs +77 -0
- package/lib/use/node.d.mts +21 -0
- package/lib/use/node.d.ts +21 -0
- package/lib/use/node.js +72 -0
- package/lib/use/node.mjs +68 -0
- package/package.json +45 -21
- package/umd/graphql-http.js +0 -2
- package/umd/graphql-http.min.js +1 -1
- package/umd/graphql-http.min.js.gz +0 -0
package/lib/audits/server.mjs
CHANGED
|
@@ -16,23 +16,25 @@ export function serverAudits(opts) {
|
|
|
16
16
|
audit(
|
|
17
17
|
// TODO: convert to MUST after watershed
|
|
18
18
|
'SHOULD accept application/graphql-response+json and match the content-type', async () => {
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
const res = await fetchFn(url.toString(), {
|
|
19
|
+
const res = await fetchFn(opts.url, {
|
|
20
|
+
method: 'POST',
|
|
22
21
|
headers: {
|
|
22
|
+
'content-type': 'application/json',
|
|
23
23
|
accept: 'application/graphql-response+json',
|
|
24
24
|
},
|
|
25
|
+
body: JSON.stringify({ query: '{ __typename }' }),
|
|
25
26
|
});
|
|
26
27
|
assert('Status code', res.status).toBe(200);
|
|
27
28
|
assert('Content-Type header', res.headers.get('content-type')).toContain('application/graphql-response+json');
|
|
28
29
|
}),
|
|
29
30
|
audit('MUST accept application/json and match the content-type', async () => {
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
const res = await fetchFn(url.toString(), {
|
|
31
|
+
const res = await fetchFn(opts.url, {
|
|
32
|
+
method: 'POST',
|
|
33
33
|
headers: {
|
|
34
|
+
'content-type': 'application/json',
|
|
34
35
|
accept: 'application/json',
|
|
35
36
|
},
|
|
37
|
+
body: JSON.stringify({ query: '{ __typename }' }),
|
|
36
38
|
});
|
|
37
39
|
assert('Status code', res.status).toBe(200);
|
|
38
40
|
assert('Content-Type header', res.headers.get('content-type')).toContain('application/json');
|
|
@@ -40,12 +42,13 @@ export function serverAudits(opts) {
|
|
|
40
42
|
audit(
|
|
41
43
|
// TODO: convert to MUST after watershed
|
|
42
44
|
'SHOULD accept */* and use application/graphql-response+json for the content-type', async () => {
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
const res = await fetchFn(url.toString(), {
|
|
45
|
+
const res = await fetchFn(opts.url, {
|
|
46
|
+
method: 'POST',
|
|
46
47
|
headers: {
|
|
48
|
+
'content-type': 'application/json',
|
|
47
49
|
accept: '*/*',
|
|
48
50
|
},
|
|
51
|
+
body: JSON.stringify({ query: '{ __typename }' }),
|
|
49
52
|
});
|
|
50
53
|
assert('Status code', res.status).toBe(200);
|
|
51
54
|
assert('Content-Type header', res.headers.get('content-type')).toContain('application/graphql-response+json');
|
|
@@ -53,16 +56,24 @@ export function serverAudits(opts) {
|
|
|
53
56
|
audit(
|
|
54
57
|
// TODO: convert to MUST after watershed
|
|
55
58
|
'SHOULD assume application/graphql-response+json content-type when accept is missing', async () => {
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
+
const res = await fetchFn(opts.url, {
|
|
60
|
+
method: 'POST',
|
|
61
|
+
headers: {
|
|
62
|
+
'content-type': 'application/json',
|
|
63
|
+
},
|
|
64
|
+
body: JSON.stringify({ query: '{ __typename }' }),
|
|
65
|
+
});
|
|
59
66
|
assert('Status code', res.status).toBe(200);
|
|
60
67
|
assert('Content-Type header', res.headers.get('content-type')).toContain('application/graphql-response+json');
|
|
61
68
|
}),
|
|
62
69
|
audit('MUST use utf-8 encoding when responding', async () => {
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
70
|
+
const res = await fetchFn(opts.url, {
|
|
71
|
+
method: 'POST',
|
|
72
|
+
headers: {
|
|
73
|
+
'content-type': 'application/json',
|
|
74
|
+
},
|
|
75
|
+
body: JSON.stringify({ query: '{ __typename }' }),
|
|
76
|
+
});
|
|
66
77
|
assert('Status code', res.status).toBe(200);
|
|
67
78
|
// has charset set to utf-8
|
|
68
79
|
try {
|
|
@@ -84,24 +95,27 @@ export function serverAudits(opts) {
|
|
|
84
95
|
throw 'Body is not UTF-8 encoded';
|
|
85
96
|
}
|
|
86
97
|
}),
|
|
87
|
-
audit('MUST accept
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
const res = await fetchFn(url.toString(), {
|
|
98
|
+
audit('MUST accept utf-8 encoding', async () => {
|
|
99
|
+
const res = await fetchFn(opts.url, {
|
|
100
|
+
method: 'POST',
|
|
91
101
|
headers: {
|
|
92
|
-
|
|
102
|
+
'content-type': 'application/json; charset=utf-8',
|
|
93
103
|
},
|
|
104
|
+
body: JSON.stringify({ query: '{ __typename }' }),
|
|
94
105
|
});
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
106
|
+
assert('Status code', res.status).toBe(200);
|
|
107
|
+
assert('Content-Type header', res.headers.get('content-type')).toContain('utf-8');
|
|
108
|
+
}),
|
|
109
|
+
audit('MUST assume utf-8 if encoding is unspecified', async () => {
|
|
110
|
+
const res = await fetchFn(opts.url, {
|
|
111
|
+
method: 'POST',
|
|
112
|
+
headers: {
|
|
113
|
+
'content-type': 'application/json',
|
|
114
|
+
},
|
|
115
|
+
body: JSON.stringify({ query: '{ __typename }' }),
|
|
116
|
+
});
|
|
117
|
+
assert('Status code', res.status).toBe(200);
|
|
118
|
+
assert('Content-Type header', res.headers.get('content-type')).toContain('utf-8');
|
|
105
119
|
}),
|
|
106
120
|
// Request
|
|
107
121
|
audit('MUST accept POST requests', async () => {
|
|
@@ -119,7 +133,8 @@ export function serverAudits(opts) {
|
|
|
119
133
|
assert('Status code', res.status).toBe(200);
|
|
120
134
|
}),
|
|
121
135
|
// Request GET
|
|
122
|
-
|
|
136
|
+
// TODO: this is a MUST if the server supports GET requests
|
|
137
|
+
audit('MAY NOT allow executing mutations on GET requests', async () => {
|
|
123
138
|
const url = new URL(opts.url);
|
|
124
139
|
url.searchParams.set('query', 'mutation { __typename }');
|
|
125
140
|
const res = await fetchFn(url.toString(), {
|
|
@@ -127,15 +142,14 @@ export function serverAudits(opts) {
|
|
|
127
142
|
accept: 'application/graphql-response+json',
|
|
128
143
|
},
|
|
129
144
|
});
|
|
130
|
-
assert('Status code', res.status).
|
|
145
|
+
assert('Status code', res.status).toBeBetween(400, 499);
|
|
131
146
|
}),
|
|
132
147
|
// Request POST
|
|
133
148
|
audit('SHOULD respond with 4xx status code if content-type is not supplied on POST requests', async () => {
|
|
134
149
|
const res = await fetchFn(opts.url, {
|
|
135
150
|
method: 'POST',
|
|
136
151
|
});
|
|
137
|
-
assert('Status code', res.status).
|
|
138
|
-
assert('Status code', res.status).toBeLessThanOrEqual(499);
|
|
152
|
+
assert('Status code', res.status).toBeBetween(400, 499);
|
|
139
153
|
}),
|
|
140
154
|
audit('MUST accept application/json POST requests', async () => {
|
|
141
155
|
const res = await fetchFn(opts.url, {
|
|
@@ -360,9 +374,7 @@ export function serverAudits(opts) {
|
|
|
360
374
|
assert('Status code', res.status).toBe(200);
|
|
361
375
|
assert('Execution result', await assertBodyAsExecutionResult(res)).notToHaveProperty('errors');
|
|
362
376
|
}),
|
|
363
|
-
audit(
|
|
364
|
-
// TODO: convert to MUST after watershed
|
|
365
|
-
'SHOULD allow URL-encoded JSON string {variables} parameter in GETs when accepting application/graphql-response+json', async () => {
|
|
377
|
+
audit('MAY allow URL-encoded JSON string {variables} parameter in GETs when accepting application/graphql-response+json', async () => {
|
|
366
378
|
const url = new URL(opts.url);
|
|
367
379
|
url.searchParams.set('query', 'query Type($name: String!) { __type(name: $name) { name } }');
|
|
368
380
|
url.searchParams.set('variables', JSON.stringify({ name: 'sometype' }));
|
|
@@ -374,7 +386,7 @@ export function serverAudits(opts) {
|
|
|
374
386
|
});
|
|
375
387
|
assert('Status code', res.status).toBe(200);
|
|
376
388
|
}),
|
|
377
|
-
audit('
|
|
389
|
+
audit('MAY allow URL-encoded JSON string {variables} parameter in GETs when accepting application/json', async () => {
|
|
378
390
|
const url = new URL(opts.url);
|
|
379
391
|
url.searchParams.set('query', 'query Type($name: String!) { __type(name: $name) { name } }');
|
|
380
392
|
url.searchParams.set('variables', JSON.stringify({ name: 'sometype' }));
|
|
@@ -463,29 +475,39 @@ export function serverAudits(opts) {
|
|
|
463
475
|
assert('Status code', res.status).toBe(200);
|
|
464
476
|
}),
|
|
465
477
|
audit('SHOULD use 200 status code if parameters are invalid when accepting application/json', async () => {
|
|
466
|
-
const
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
478
|
+
const res = await fetchFn(opts.url, {
|
|
479
|
+
method: 'POST',
|
|
480
|
+
headers: {
|
|
481
|
+
'content-type': 'application/json',
|
|
482
|
+
accept: 'application/json',
|
|
483
|
+
},
|
|
484
|
+
body: JSON.stringify({
|
|
485
|
+
qeury: /* typo */ '{ __typename }',
|
|
486
|
+
}),
|
|
471
487
|
});
|
|
472
488
|
assert('Status code', res.status).toBe(200);
|
|
473
489
|
}),
|
|
474
490
|
audit('SHOULD use 200 status code on document parsing failure when accepting application/json', async () => {
|
|
475
|
-
const
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
491
|
+
const res = await fetchFn(opts.url, {
|
|
492
|
+
method: 'POST',
|
|
493
|
+
headers: {
|
|
494
|
+
'content-type': 'application/json',
|
|
495
|
+
accept: 'application/json',
|
|
496
|
+
},
|
|
497
|
+
body: JSON.stringify({ query: '{' }),
|
|
480
498
|
});
|
|
481
499
|
assert('Status code', res.status).toBe(200);
|
|
482
500
|
}),
|
|
483
501
|
audit('SHOULD use 200 status code on document validation failure when accepting application/json', async () => {
|
|
484
|
-
const
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
502
|
+
const res = await fetchFn(opts.url, {
|
|
503
|
+
method: 'POST',
|
|
504
|
+
headers: {
|
|
505
|
+
'content-type': 'application/json',
|
|
506
|
+
accept: 'application/json',
|
|
507
|
+
},
|
|
508
|
+
body: JSON.stringify({
|
|
509
|
+
query: '{ 8f31403dfe404bccbb0e835f2629c6a7 }', // making sure the field doesnt exist
|
|
510
|
+
}),
|
|
489
511
|
});
|
|
490
512
|
assert('Status code', res.status).toBe(200);
|
|
491
513
|
}),
|
|
@@ -501,8 +523,7 @@ export function serverAudits(opts) {
|
|
|
501
523
|
},
|
|
502
524
|
body: '{ "not a JSON',
|
|
503
525
|
});
|
|
504
|
-
assert('Status code', res.status).
|
|
505
|
-
assert('Status code', res.status).toBeLessThanOrEqual(599);
|
|
526
|
+
assert('Status code', res.status).toBeBetween(400, 499);
|
|
506
527
|
}),
|
|
507
528
|
audit('SHOULD use 400 status code on JSON parsing failure when accepting application/graphql-response+json', async () => {
|
|
508
529
|
const res = await fetchFn(opts.url, {
|
|
@@ -529,90 +550,123 @@ export function serverAudits(opts) {
|
|
|
529
550
|
audit(
|
|
530
551
|
// TODO: convert to MUST after watershed
|
|
531
552
|
'SHOULD use 4xx or 5xx status codes if parameters are invalid when accepting application/graphql-response+json', async () => {
|
|
532
|
-
const
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
553
|
+
const res = await fetchFn(opts.url, {
|
|
554
|
+
method: 'POST',
|
|
555
|
+
headers: {
|
|
556
|
+
'content-type': 'application/json',
|
|
557
|
+
accept: 'application/graphql-response+json',
|
|
558
|
+
},
|
|
559
|
+
body: JSON.stringify({
|
|
560
|
+
qeury /* typo */: '{ __typename }',
|
|
561
|
+
}),
|
|
537
562
|
});
|
|
538
|
-
assert('Status code', res.status).
|
|
539
|
-
assert('Status code', res.status).toBeLessThanOrEqual(599);
|
|
563
|
+
assert('Status code', res.status).toBeBetween(400, 599);
|
|
540
564
|
}),
|
|
541
565
|
audit('SHOULD use 400 status code if parameters are invalid when accepting application/graphql-response+json', async () => {
|
|
542
|
-
const
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
566
|
+
const res = await fetchFn(opts.url, {
|
|
567
|
+
method: 'POST',
|
|
568
|
+
headers: {
|
|
569
|
+
'content-type': 'application/json',
|
|
570
|
+
accept: 'application/graphql-response+json',
|
|
571
|
+
},
|
|
572
|
+
body: JSON.stringify({
|
|
573
|
+
qeury: /* typo */ '{ __typename }',
|
|
574
|
+
}),
|
|
547
575
|
});
|
|
548
576
|
assert('Status code', res.status).toBe(400);
|
|
549
577
|
}),
|
|
550
578
|
audit('SHOULD not contain the data entry if parameters are invalid when accepting application/graphql-response+json', async () => {
|
|
551
|
-
const
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
579
|
+
const res = await fetchFn(opts.url, {
|
|
580
|
+
method: 'POST',
|
|
581
|
+
headers: {
|
|
582
|
+
'content-type': 'application/json',
|
|
583
|
+
accept: 'application/graphql-response+json',
|
|
584
|
+
},
|
|
585
|
+
body: JSON.stringify({
|
|
586
|
+
qeury: /* typo */ '{ __typename }',
|
|
587
|
+
}),
|
|
556
588
|
});
|
|
557
589
|
assert('Data entry', (await assertBodyAsExecutionResult(res)).data).toBe(undefined);
|
|
558
590
|
}),
|
|
559
591
|
audit(
|
|
560
592
|
// TODO: convert to MUST after watershed
|
|
561
593
|
'SHOULD use 4xx or 5xx status codes on document parsing failure when accepting application/graphql-response+json', async () => {
|
|
562
|
-
const
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
594
|
+
const res = await fetchFn(opts.url, {
|
|
595
|
+
method: 'POST',
|
|
596
|
+
headers: {
|
|
597
|
+
'content-type': 'application/json',
|
|
598
|
+
accept: 'application/graphql-response+json',
|
|
599
|
+
},
|
|
600
|
+
body: JSON.stringify({
|
|
601
|
+
query: '{',
|
|
602
|
+
}),
|
|
567
603
|
});
|
|
568
|
-
assert('Status code', res.status).
|
|
569
|
-
assert('Status code', res.status).toBeLessThanOrEqual(599);
|
|
604
|
+
assert('Status code', res.status).toBeBetween(400, 599);
|
|
570
605
|
}),
|
|
571
606
|
audit('SHOULD use 400 status code on document parsing failure when accepting application/graphql-response+json', async () => {
|
|
572
|
-
const
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
607
|
+
const res = await fetchFn(opts.url, {
|
|
608
|
+
method: 'POST',
|
|
609
|
+
headers: {
|
|
610
|
+
'content-type': 'application/json',
|
|
611
|
+
accept: 'application/graphql-response+json',
|
|
612
|
+
},
|
|
613
|
+
body: JSON.stringify({
|
|
614
|
+
query: '{',
|
|
615
|
+
}),
|
|
577
616
|
});
|
|
578
617
|
assert('Status code', res.status).toBe(400);
|
|
579
618
|
}),
|
|
580
619
|
audit('SHOULD not contain the data entry on document parsing failure when accepting application/graphql-response+json', async () => {
|
|
581
|
-
const
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
620
|
+
const res = await fetchFn(opts.url, {
|
|
621
|
+
method: 'POST',
|
|
622
|
+
headers: {
|
|
623
|
+
'content-type': 'application/json',
|
|
624
|
+
accept: 'application/graphql-response+json',
|
|
625
|
+
},
|
|
626
|
+
body: JSON.stringify({
|
|
627
|
+
query: '{',
|
|
628
|
+
}),
|
|
586
629
|
});
|
|
587
630
|
assert('Data entry', (await assertBodyAsExecutionResult(res)).data).toBe(undefined);
|
|
588
631
|
}),
|
|
589
632
|
audit(
|
|
590
633
|
// TODO: convert to MUST after watershed
|
|
591
634
|
'SHOULD use 4xx or 5xx status codes on document validation failure when accepting application/graphql-response+json', async () => {
|
|
592
|
-
const
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
635
|
+
const res = await fetchFn(opts.url, {
|
|
636
|
+
method: 'POST',
|
|
637
|
+
headers: {
|
|
638
|
+
'content-type': 'application/json',
|
|
639
|
+
accept: 'application/graphql-response+json',
|
|
640
|
+
},
|
|
641
|
+
body: JSON.stringify({
|
|
642
|
+
query: '{ 8f31403dfe404bccbb0e835f2629c6a7 }', // making sure the field doesnt exist
|
|
643
|
+
}),
|
|
597
644
|
});
|
|
598
|
-
assert('Status code', res.status).
|
|
599
|
-
assert('Status code', res.status).toBeLessThanOrEqual(599);
|
|
645
|
+
assert('Status code', res.status).toBeBetween(400, 599);
|
|
600
646
|
}),
|
|
601
647
|
audit('SHOULD use 400 status code on document validation failure when accepting application/graphql-response+json', async () => {
|
|
602
|
-
const
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
648
|
+
const res = await fetchFn(opts.url, {
|
|
649
|
+
method: 'POST',
|
|
650
|
+
headers: {
|
|
651
|
+
'content-type': 'application/json',
|
|
652
|
+
accept: 'application/graphql-response+json',
|
|
653
|
+
},
|
|
654
|
+
body: JSON.stringify({
|
|
655
|
+
query: '{ 8f31403dfe404bccbb0e835f2629c6a7 }', // making sure the field doesnt exist
|
|
656
|
+
}),
|
|
607
657
|
});
|
|
608
658
|
assert('Status code', res.status).toBe(400);
|
|
609
659
|
}),
|
|
610
660
|
audit('SHOULD not contain the data entry on document validation failure when accepting application/graphql-response+json', async () => {
|
|
611
|
-
const
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
661
|
+
const res = await fetchFn(opts.url, {
|
|
662
|
+
method: 'POST',
|
|
663
|
+
headers: {
|
|
664
|
+
'content-type': 'application/json',
|
|
665
|
+
accept: 'application/graphql-response+json',
|
|
666
|
+
},
|
|
667
|
+
body: JSON.stringify({
|
|
668
|
+
query: '{ 8f31403dfe404bccbb0e835f2629c6a7 }', // making sure the field doesnt exist
|
|
669
|
+
}),
|
|
616
670
|
});
|
|
617
671
|
assert('Data entry', (await assertBodyAsExecutionResult(res)).data).toBe(undefined);
|
|
618
672
|
}),
|
package/lib/audits/utils.d.mts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* audit/utils
|
|
4
4
|
*
|
|
5
5
|
*/
|
|
6
|
-
import { ExecutionResult } from 'graphql';
|
|
6
|
+
import type { ExecutionResult } from 'graphql';
|
|
7
7
|
import { Audit, AuditName } from './common';
|
|
8
8
|
export * from '../utils';
|
|
9
9
|
/**
|
|
@@ -21,8 +21,7 @@ export declare function audit(name: AuditName, fn: () => Promise<void>): Audit;
|
|
|
21
21
|
*/
|
|
22
22
|
export declare function assert<T = unknown>(name: string, actual: T): {
|
|
23
23
|
toBe: (expected: T) => void;
|
|
24
|
-
|
|
25
|
-
toBeGreaterThanOrEqual: (expected: T extends number ? T : never) => void;
|
|
24
|
+
toBeBetween: (min: T extends number ? T : never, max: T extends number ? T : never) => void;
|
|
26
25
|
toContain: (expected: T extends (infer U)[] ? U : T extends string ? T : never) => void;
|
|
27
26
|
notToContain: (expected: T extends (infer U_1)[] ? U_1 : T extends string ? T : never) => void;
|
|
28
27
|
toHaveProperty: (prop: T extends Record<any, any> ? PropertyKey : never) => void;
|
package/lib/audits/utils.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* audit/utils
|
|
4
4
|
*
|
|
5
5
|
*/
|
|
6
|
-
import { ExecutionResult } from 'graphql';
|
|
6
|
+
import type { ExecutionResult } from 'graphql';
|
|
7
7
|
import { Audit, AuditName } from './common';
|
|
8
8
|
export * from '../utils';
|
|
9
9
|
/**
|
|
@@ -21,8 +21,7 @@ export declare function audit(name: AuditName, fn: () => Promise<void>): Audit;
|
|
|
21
21
|
*/
|
|
22
22
|
export declare function assert<T = unknown>(name: string, actual: T): {
|
|
23
23
|
toBe: (expected: T) => void;
|
|
24
|
-
|
|
25
|
-
toBeGreaterThanOrEqual: (expected: T extends number ? T : never) => void;
|
|
24
|
+
toBeBetween: (min: T extends number ? T : never, max: T extends number ? T : never) => void;
|
|
26
25
|
toContain: (expected: T extends (infer U)[] ? U : T extends string ? T : never) => void;
|
|
27
26
|
notToContain: (expected: T extends (infer U_1)[] ? U_1 : T extends string ? T : never) => void;
|
|
28
27
|
toHaveProperty: (prop: T extends Record<any, any> ? PropertyKey : never) => void;
|
package/lib/audits/utils.js
CHANGED
|
@@ -70,14 +70,9 @@ function assert(name, actual) {
|
|
|
70
70
|
throw `${name} ${actual} is not ${expected}`;
|
|
71
71
|
}
|
|
72
72
|
},
|
|
73
|
-
|
|
74
|
-
if (!(actual <=
|
|
75
|
-
throw `${name} ${actual} is not
|
|
76
|
-
}
|
|
77
|
-
},
|
|
78
|
-
toBeGreaterThanOrEqual: (expected) => {
|
|
79
|
-
if (!(actual >= expected)) {
|
|
80
|
-
throw `${name} ${actual} is not greater than or equal to ${expected}`;
|
|
73
|
+
toBeBetween: (min, max) => {
|
|
74
|
+
if (!(min <= actual && actual <= max)) {
|
|
75
|
+
throw `${name} ${actual} is not between ${min} and ${max}`;
|
|
81
76
|
}
|
|
82
77
|
},
|
|
83
78
|
toContain: (expected) => {
|
package/lib/audits/utils.mjs
CHANGED
|
@@ -52,14 +52,9 @@ export function assert(name, actual) {
|
|
|
52
52
|
throw `${name} ${actual} is not ${expected}`;
|
|
53
53
|
}
|
|
54
54
|
},
|
|
55
|
-
|
|
56
|
-
if (!(actual <=
|
|
57
|
-
throw `${name} ${actual} is not
|
|
58
|
-
}
|
|
59
|
-
},
|
|
60
|
-
toBeGreaterThanOrEqual: (expected) => {
|
|
61
|
-
if (!(actual >= expected)) {
|
|
62
|
-
throw `${name} ${actual} is not greater than or equal to ${expected}`;
|
|
55
|
+
toBeBetween: (min, max) => {
|
|
56
|
+
if (!(min <= actual && actual <= max)) {
|
|
57
|
+
throw `${name} ${actual} is not between ${min} and ${max}`;
|
|
63
58
|
}
|
|
64
59
|
},
|
|
65
60
|
toContain: (expected) => {
|
package/lib/handler.d.mts
CHANGED
|
@@ -8,27 +8,25 @@ import { RequestParams } from './common';
|
|
|
8
8
|
/**
|
|
9
9
|
* The incoming request headers the implementing server should provide.
|
|
10
10
|
*
|
|
11
|
-
* @category
|
|
11
|
+
* @category Server
|
|
12
12
|
*/
|
|
13
|
-
export
|
|
14
|
-
accept?: string | undefined;
|
|
15
|
-
allow?: string | undefined;
|
|
16
|
-
'content-type'?: string | undefined;
|
|
13
|
+
export declare type RequestHeaders = {
|
|
17
14
|
/**
|
|
18
15
|
* Always an array in Node. Duplicates are added to it.
|
|
19
|
-
* Not necessarily true for other environments
|
|
20
|
-
* to check the type during runtime.
|
|
16
|
+
* Not necessarily true for other environments.
|
|
21
17
|
*/
|
|
22
18
|
'set-cookie'?: string | string[] | undefined;
|
|
23
19
|
[key: string]: string | string[] | undefined;
|
|
24
|
-
}
|
|
20
|
+
} | {
|
|
21
|
+
get: (key: string) => string | null;
|
|
22
|
+
};
|
|
25
23
|
/**
|
|
26
24
|
* Server agnostic request interface containing the raw request
|
|
27
25
|
* which is server dependant.
|
|
28
26
|
*
|
|
29
|
-
* @category
|
|
27
|
+
* @category Server
|
|
30
28
|
*/
|
|
31
|
-
export interface Request<
|
|
29
|
+
export interface Request<Raw, Context> {
|
|
32
30
|
readonly method: string;
|
|
33
31
|
readonly url: string;
|
|
34
32
|
readonly headers: RequestHeaders;
|
|
@@ -45,7 +43,7 @@ export interface Request<RawRequest, Context> {
|
|
|
45
43
|
* For example: `express.Request` when using Express, or maybe
|
|
46
44
|
* `http.IncomingMessage` when just using Node with `http.createServer`.
|
|
47
45
|
*/
|
|
48
|
-
readonly raw:
|
|
46
|
+
readonly raw: Raw;
|
|
49
47
|
/**
|
|
50
48
|
* Context value about the incoming request, you're free to pass any information here.
|
|
51
49
|
*/
|
|
@@ -54,7 +52,7 @@ export interface Request<RawRequest, Context> {
|
|
|
54
52
|
/**
|
|
55
53
|
* The response headers that get returned from graphql-http.
|
|
56
54
|
*
|
|
57
|
-
* @category
|
|
55
|
+
* @category Server
|
|
58
56
|
*/
|
|
59
57
|
export declare type ResponseHeaders = {
|
|
60
58
|
accept?: string;
|
|
@@ -65,14 +63,14 @@ export declare type ResponseHeaders = {
|
|
|
65
63
|
* Server agnostic response body returned from `graphql-http` needing
|
|
66
64
|
* to be coerced to the server implementation in use.
|
|
67
65
|
*
|
|
68
|
-
* @category
|
|
66
|
+
* @category Server
|
|
69
67
|
*/
|
|
70
68
|
export declare type ResponseBody = string;
|
|
71
69
|
/**
|
|
72
70
|
* Server agnostic response options (ex. status and headers) returned from
|
|
73
71
|
* `graphql-http` needing to be coerced to the server implementation in use.
|
|
74
72
|
*
|
|
75
|
-
* @category
|
|
73
|
+
* @category Server
|
|
76
74
|
*/
|
|
77
75
|
export interface ResponseInit {
|
|
78
76
|
readonly status: number;
|
|
@@ -83,13 +81,13 @@ export interface ResponseInit {
|
|
|
83
81
|
* Server agnostic response returned from `graphql-http` containing the
|
|
84
82
|
* body and init options needing to be coerced to the server implementation in use.
|
|
85
83
|
*
|
|
86
|
-
* @category
|
|
84
|
+
* @category Server
|
|
87
85
|
*/
|
|
88
86
|
export declare type Response = readonly [body: ResponseBody | null, init: ResponseInit];
|
|
89
87
|
/**
|
|
90
88
|
* Checks whether the passed value is the `graphql-http` server agnostic response.
|
|
91
89
|
*
|
|
92
|
-
* @category
|
|
90
|
+
* @category Server
|
|
93
91
|
*/
|
|
94
92
|
export declare function isResponse(val: unknown): val is Response;
|
|
95
93
|
/**
|
|
@@ -102,9 +100,13 @@ export declare function isResponse(val: unknown): val is Response;
|
|
|
102
100
|
*
|
|
103
101
|
* @category Server
|
|
104
102
|
*/
|
|
105
|
-
export declare type
|
|
103
|
+
export declare type OperationContext = Record<PropertyKey, unknown> | symbol | number | string | boolean | undefined | null;
|
|
104
|
+
/** @category Server */
|
|
105
|
+
export declare type OperationArgs<Context extends OperationContext = undefined> = ExecutionArgs & {
|
|
106
|
+
contextValue?: Context;
|
|
107
|
+
};
|
|
106
108
|
/** @category Server */
|
|
107
|
-
export interface HandlerOptions<
|
|
109
|
+
export interface HandlerOptions<RequestRaw = unknown, RequestContext = unknown, Context extends OperationContext = undefined> {
|
|
108
110
|
/**
|
|
109
111
|
* The GraphQL schema on which the operations will
|
|
110
112
|
* be executed and validated against.
|
|
@@ -121,13 +123,13 @@ export interface HandlerOptions<RawRequest = unknown, Context = unknown> {
|
|
|
121
123
|
* you should do by returning a `Request` argument which will stop
|
|
122
124
|
* further execution.
|
|
123
125
|
*/
|
|
124
|
-
schema?: GraphQLSchema | ((req: Request<
|
|
126
|
+
schema?: GraphQLSchema | ((req: Request<RequestRaw, RequestContext>, args: Omit<OperationArgs<Context>, 'schema'>) => Promise<GraphQLSchema | Response> | GraphQLSchema | Response);
|
|
125
127
|
/**
|
|
126
128
|
* A value which is provided to every resolver and holds
|
|
127
129
|
* important contextual information like the currently
|
|
128
130
|
* logged in user, or access to a database.
|
|
129
131
|
*/
|
|
130
|
-
context?:
|
|
132
|
+
context?: Context | ((req: Request<RequestRaw, RequestContext>, params: RequestParams) => Promise<Context | Response> | Context | Response);
|
|
131
133
|
/**
|
|
132
134
|
* A custom GraphQL validate function allowing you to apply your
|
|
133
135
|
* own validation rules.
|
|
@@ -176,7 +178,7 @@ export interface HandlerOptions<RawRequest = unknown, Context = unknown> {
|
|
|
176
178
|
* you should do by returning a `Request` argument which will stop
|
|
177
179
|
* further execution.
|
|
178
180
|
*/
|
|
179
|
-
onSubscribe?: (req: Request<
|
|
181
|
+
onSubscribe?: (req: Request<RequestRaw, RequestContext>, params: RequestParams) => Promise<ExecutionResult | OperationArgs<Context> | readonly GraphQLError[] | Response | void> | ExecutionResult | OperationArgs<Context> | readonly GraphQLError[] | Response | void;
|
|
180
182
|
/**
|
|
181
183
|
* Executed after the operation call resolves.
|
|
182
184
|
*
|
|
@@ -190,7 +192,7 @@ export interface HandlerOptions<RawRequest = unknown, Context = unknown> {
|
|
|
190
192
|
* you should do by returning a `Request` argument which will stop
|
|
191
193
|
* further execution.
|
|
192
194
|
*/
|
|
193
|
-
onOperation?: (req: Request<
|
|
195
|
+
onOperation?: (req: Request<RequestRaw, RequestContext>, args: OperationArgs<Context>, result: ExecutionResult) => Promise<ExecutionResult | Response | void> | ExecutionResult | Response | void;
|
|
194
196
|
}
|
|
195
197
|
/**
|
|
196
198
|
* The ready-to-use handler. Simply plug it in your favourite HTTP framework
|
|
@@ -202,7 +204,7 @@ export interface HandlerOptions<RawRequest = unknown, Context = unknown> {
|
|
|
202
204
|
*
|
|
203
205
|
* @category Server
|
|
204
206
|
*/
|
|
205
|
-
export declare type Handler<
|
|
207
|
+
export declare type Handler<RequestRaw = unknown, RequestContext = unknown> = (req: Request<RequestRaw, RequestContext>) => Promise<Response>;
|
|
206
208
|
/**
|
|
207
209
|
* Makes a GraphQL over HTTP Protocol compliant server handler. The handler can
|
|
208
210
|
* be used with your favourite server library.
|
|
@@ -258,7 +260,7 @@ export declare type Handler<RawRequest = unknown, Context = unknown> = (req: Req
|
|
|
258
260
|
*
|
|
259
261
|
* @category Server
|
|
260
262
|
*/
|
|
261
|
-
export declare function createHandler<
|
|
263
|
+
export declare function createHandler<RequestRaw = unknown, RequestContext = unknown, Context extends OperationContext = undefined>(options: HandlerOptions<RequestRaw, RequestContext, Context>): Handler<RequestRaw, RequestContext>;
|
|
262
264
|
/**
|
|
263
265
|
* Request's Media-Type that the server accepts.
|
|
264
266
|
*
|