Compare commits
357 Commits
new-resolv
...
infinite-l
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
56c7933f3a | ||
|
|
b896a3c3e1 | ||
|
|
7be9466b94 | ||
|
|
08afd85e7b | ||
|
|
adcb08eb8f | ||
|
|
6eeefcadb4 | ||
|
|
cca9ba7ab7 | ||
|
|
ec3f871189 | ||
|
|
9cb37aac93 | ||
|
|
0222e1572d | ||
|
|
6ee30561e7 | ||
|
|
958005c186 | ||
|
|
0a14acaea9 | ||
|
|
df1d648c5b | ||
|
|
1857eb830f | ||
|
|
fcb2782b33 | ||
|
|
57b9b88380 | ||
|
|
ab10fd408a | ||
|
|
1869b9c9f5 | ||
|
|
b546754dd1 | ||
|
|
3dde672b5c | ||
|
|
848d961e1c | ||
|
|
2cf32ad352 | ||
|
|
f62360834d | ||
|
|
6a3c2fffeb | ||
|
|
df8434cd9b | ||
|
|
b7abca29ab | ||
|
|
559496af6b | ||
|
|
d15226e770 | ||
|
|
a377e6b94f | ||
|
|
177dccdcba | ||
|
|
0b06288210 | ||
|
|
36211b9968 | ||
|
|
a0c5209a94 | ||
|
|
39a720df63 | ||
|
|
c335a1d968 | ||
|
|
de286205b1 | ||
|
|
c86c0527a4 | ||
|
|
8cdd56fc40 | ||
|
|
617ab8c564 | ||
|
|
92b90ee49c | ||
|
|
ea26d8ecb8 | ||
|
|
a18c00dbc3 | ||
|
|
fdab34bee4 | ||
|
|
cf7d34764e | ||
|
|
6f8d25438d | ||
|
|
c301d296a5 | ||
|
|
b6d264c294 | ||
|
|
61d11e4c89 | ||
|
|
39e738c240 | ||
|
|
d88fc0430f | ||
|
|
6bbe9b9720 | ||
|
|
ec5cc08062 | ||
|
|
fc216c283d | ||
|
|
03ebcb911d | ||
|
|
2136ab7520 | ||
|
|
58b58d292c | ||
|
|
f861dd6683 | ||
|
|
559d20d1a1 | ||
|
|
2ae313a43a | ||
|
|
a8b3794a60 | ||
|
|
0bfd917b24 | ||
|
|
2e3116b58a | ||
|
|
b18fe2615c | ||
|
|
8de82025d0 | ||
|
|
8822a38c60 | ||
|
|
3879ecde1f | ||
|
|
5b8dab3fb3 | ||
|
|
e1276f93a0 | ||
|
|
0c2abcb9de | ||
|
|
3058d64dad | ||
|
|
973c2384ec | ||
|
|
0cba94dd2e | ||
|
|
bf7eed62bd | ||
|
|
5fc813a64b | ||
|
|
9e78d1c7f6 | ||
|
|
fa8fd64921 | ||
|
|
bc0c7287ec | ||
|
|
3c3f10b201 | ||
|
|
e1f7cf8bb4 | ||
|
|
f3d0f7f79d | ||
|
|
15ee721fc9 | ||
|
|
9a872ef14a | ||
|
|
6df0db9b87 | ||
|
|
48cca48e60 | ||
|
|
65e67ffcc3 | ||
|
|
4d42b462da | ||
|
|
a17227d853 | ||
|
|
b3facfc9a5 | ||
|
|
6527a14f28 | ||
|
|
5d473a2312 | ||
|
|
0aa5280a0f | ||
|
|
a3737aff3d | ||
|
|
d8d12be249 | ||
|
|
af539332a1 | ||
|
|
563720bb57 | ||
|
|
d242056848 | ||
|
|
8f659f8b0b | ||
|
|
63af540c87 | ||
|
|
a26fdd8162 | ||
|
|
04a157531f | ||
|
|
f099a88d38 | ||
|
|
f40c0f022e | ||
|
|
1d4695a17a | ||
|
|
a293d8bfa7 | ||
|
|
18c1b9cb86 | ||
|
|
32328b442d | ||
|
|
541b612fff | ||
|
|
cc7c72d1c1 | ||
|
|
46b6395870 | ||
|
|
9a13b4e361 | ||
|
|
3147394f86 | ||
|
|
59f6f158e2 | ||
|
|
2c548fa875 | ||
|
|
fa14a92bcb | ||
|
|
5ba52be9f5 | ||
|
|
006cae4288 | ||
|
|
2e15ba2e82 | ||
|
|
5d56db5928 | ||
|
|
4baa37ba38 | ||
|
|
03d9711983 | ||
|
|
54796b6afd | ||
|
|
e826deb503 | ||
|
|
ee5cceb9c8 | ||
|
|
1a28e5b1c0 | ||
|
|
a0b53dc149 | ||
|
|
006ef89fbe | ||
|
|
44aac3bb3a | ||
|
|
d5872d9b51 | ||
|
|
e35d17d6a3 | ||
|
|
70cc8777d6 | ||
|
|
6cc4fb6c59 | ||
|
|
d299d50364 | ||
|
|
7f7dcd381f | ||
|
|
12f533972d | ||
|
|
c574e28c1a | ||
|
|
9616836303 | ||
|
|
22cd691758 | ||
|
|
e6b34a7771 | ||
|
|
3b6a2a2f68 | ||
|
|
08f0a87088 | ||
|
|
cae123dfda | ||
|
|
c20b3a19f6 | ||
|
|
45b281acb6 | ||
|
|
85fec4f386 | ||
|
|
bfe3f90d2a | ||
|
|
8bc3a402bc | ||
|
|
fb27225445 | ||
|
|
7617579d9c | ||
|
|
09da51c544 | ||
|
|
720ee606bd | ||
|
|
7022505a60 | ||
|
|
ca7cf26891 | ||
|
|
3812b907a4 | ||
|
|
2d4f70786f | ||
|
|
5883de88b2 | ||
|
|
3ac92528f5 | ||
|
|
3b114e0b37 | ||
|
|
c79bcae95b | ||
|
|
31155ea360 | ||
|
|
e3ed233bc2 | ||
|
|
6a5df1e315 | ||
|
|
47ca5b92da | ||
|
|
c7c7e050a8 | ||
|
|
ec5c7746d5 | ||
|
|
626f9d968d | ||
|
|
ee7acd4924 | ||
|
|
2cd377e10f | ||
|
|
5d69e92082 | ||
|
|
fa10972e5a | ||
|
|
af6d81413f | ||
|
|
c699d4f021 | ||
|
|
e97ba82de8 | ||
|
|
7ff1d2d052 | ||
|
|
9568d1c56c | ||
|
|
ca7ad291e6 | ||
|
|
c21445a268 | ||
|
|
b38ab9b0f6 | ||
|
|
90b44ee54d | ||
|
|
be9d712437 | ||
|
|
cc8a10fa8c | ||
|
|
e4cebb2974 | ||
|
|
ae4ca3982f | ||
|
|
c1a72f7e8a | ||
|
|
0f9b19cbef | ||
|
|
7250a27916 | ||
|
|
31fada7325 | ||
|
|
4be4c5181e | ||
|
|
66cd76643c | ||
|
|
eaef66ba0f | ||
|
|
bcec41832d | ||
|
|
efcd1da39a | ||
|
|
2178f11210 | ||
|
|
3565c57fca | ||
|
|
e41401c744 | ||
|
|
63387ab2ee | ||
|
|
e4a8a8b5b2 | ||
|
|
b347a7d999 | ||
|
|
b34368c690 | ||
|
|
fe87534109 | ||
|
|
328035bf22 | ||
|
|
e8551a0991 | ||
|
|
50c50097aa | ||
|
|
d2e5368b5c | ||
|
|
2fcd6d88b6 | ||
|
|
ec6455920c | ||
|
|
2880fbba30 | ||
|
|
cf6a906fc1 | ||
|
|
4203d94169 | ||
|
|
34f249d259 | ||
|
|
d3254d11f8 | ||
|
|
14ac3d9f3f | ||
|
|
4c85bce781 | ||
|
|
1a078c54d4 | ||
|
|
47438c8ccf | ||
|
|
13b8f1239d | ||
|
|
5ed0f11734 | ||
|
|
48383ef363 | ||
|
|
11fc19fee2 | ||
|
|
fb4a63acba | ||
|
|
c3f8c7d07d | ||
|
|
51519166ef | ||
|
|
113a3a04f3 | ||
|
|
eba2a8081d | ||
|
|
16d2cae5b8 | ||
|
|
c2bd242d46 | ||
|
|
99966ab192 | ||
|
|
387e6e420b | ||
|
|
d39b461ddd | ||
|
|
0b7d463e7f | ||
|
|
87c4ee058c | ||
|
|
177c2b0519 | ||
|
|
31411baff5 | ||
|
|
2c4b803e7b | ||
|
|
117f0fda74 | ||
|
|
f613e08866 | ||
|
|
b696831e9f | ||
|
|
bd1781b99e | ||
|
|
9077397abe | ||
|
|
8e8fcffa2a | ||
|
|
364fb5e618 | ||
|
|
7678181bcf | ||
|
|
180d4cc4a1 | ||
|
|
2484b3a692 | ||
|
|
f1e223cd47 | ||
|
|
1a4dd4a2fe | ||
|
|
7f3fcf1489 | ||
|
|
219f069dba | ||
|
|
09bd6751fa | ||
|
|
f03fe566e5 | ||
|
|
4ed750a8f7 | ||
|
|
39eb1cef9b | ||
|
|
0a3c975b98 | ||
|
|
1b4119bb50 | ||
|
|
f0d880a4f2 | ||
|
|
11a11be20b | ||
|
|
0273e58749 | ||
|
|
75c3dc635e | ||
|
|
00e54948f2 | ||
|
|
ffcd0b4243 | ||
|
|
6bc26128ee | ||
|
|
f8b599aff4 | ||
|
|
fa09904d3a | ||
|
|
7c6c7dfdb5 | ||
|
|
ca850480ae | ||
|
|
f9006974bf | ||
|
|
5f6d296e37 | ||
|
|
5697173d5a | ||
|
|
69e5da6794 | ||
|
|
66e521e14b | ||
|
|
2a25bab41c | ||
|
|
c2fa1486df | ||
|
|
19cc7a1510 | ||
|
|
b1ed61f96d | ||
|
|
a101964603 | ||
|
|
eeeab00f1b | ||
|
|
42bf665f44 | ||
|
|
abf4795395 | ||
|
|
d48891a144 | ||
|
|
57f1e7d703 | ||
|
|
2d3e46fd79 | ||
|
|
984de2cab1 | ||
|
|
fca7791c47 | ||
|
|
c389a7ad6b | ||
|
|
a4320fd974 | ||
|
|
157c397959 | ||
|
|
d7804089e1 | ||
|
|
04b0cd356c | ||
|
|
cd615f58bd | ||
|
|
c49da040b5 | ||
|
|
da6685cd2d | ||
|
|
0f4de7761e | ||
|
|
3114e8bc4a | ||
|
|
e2383fbb5f | ||
|
|
a4ea513a76 | ||
|
|
9e1860d8b3 | ||
|
|
05673fbdb5 | ||
|
|
a2cdcf4a74 | ||
|
|
9ff56beebf | ||
|
|
b238bae52c | ||
|
|
caf3d260c8 | ||
|
|
b108c5720f | ||
|
|
8ccc744c5f | ||
|
|
b0ed47e1b8 | ||
|
|
ae43a07b4d | ||
|
|
0ba81b6474 | ||
|
|
190bc65c53 | ||
|
|
85bd4ec286 | ||
|
|
1792cc6788 | ||
|
|
dc88bc68d2 | ||
|
|
37348f2595 | ||
|
|
bec3cd6cde | ||
|
|
a9c1171a14 | ||
|
|
b46a245f08 | ||
|
|
a5208e2b96 | ||
|
|
4fefbcbbb0 | ||
|
|
258c0491dd | ||
|
|
8ec0d929d8 | ||
|
|
adfc529852 | ||
|
|
aebc79fe9c | ||
|
|
da6393c538 | ||
|
|
e51a002892 | ||
|
|
d73750be0c | ||
|
|
95781eb6ba | ||
|
|
afcd47569c | ||
|
|
f405b5b4df | ||
|
|
a0d7378642 | ||
|
|
60bba38919 | ||
|
|
901d1cad7e | ||
|
|
383034d1fe | ||
|
|
bd6f37a6b0 | ||
|
|
b1116b6052 | ||
|
|
85c91e2e2e | ||
|
|
0bad1f181b | ||
|
|
971e695b30 | ||
|
|
c0e1246dc0 | ||
|
|
bce9822d13 | ||
|
|
e41219944c | ||
|
|
fe0d958368 | ||
|
|
06248f6005 | ||
|
|
24b85b0108 | ||
|
|
7804d3ea77 | ||
|
|
6dca78a8ed | ||
|
|
92679cfa03 | ||
|
|
46cb60a962 | ||
|
|
012d146fd9 | ||
|
|
5015693ddd | ||
|
|
e2ab39ed75 | ||
|
|
64ced80f77 | ||
|
|
050c4f7127 | ||
|
|
86de3303bf | ||
|
|
31724c7b2a | ||
|
|
d7647ad2be | ||
|
|
0a3836b30b | ||
|
|
1fccb1dc19 | ||
|
|
7195aaea66 | ||
|
|
d1c4553ddd |
@@ -20,7 +20,9 @@
|
||||
"code",
|
||||
"content",
|
||||
"ideas",
|
||||
"review"
|
||||
"review",
|
||||
"test",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -365,7 +367,8 @@
|
||||
"contributions": [
|
||||
"code",
|
||||
"test",
|
||||
"maintenance"
|
||||
"maintenance",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -1245,7 +1248,8 @@
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/8682104?v=4",
|
||||
"profile": "https://github.com/konradkalemba",
|
||||
"contributions": [
|
||||
"code"
|
||||
"code",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -1587,7 +1591,8 @@
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/43112535?v=4",
|
||||
"profile": "http://Dal.Design",
|
||||
"contributions": [
|
||||
"code"
|
||||
"code",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -1605,7 +1610,8 @@
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/4396533?v=4",
|
||||
"profile": "https://github.com/sakulstra",
|
||||
"contributions": [
|
||||
"code"
|
||||
"code",
|
||||
"test"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -1659,7 +1665,9 @@
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/61833561?v=4",
|
||||
"profile": "https://tundera.dev",
|
||||
"contributions": [
|
||||
"code"
|
||||
"code",
|
||||
"test",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -1882,8 +1890,599 @@
|
||||
"design",
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "queq1890",
|
||||
"name": "Yuji Matsumoto",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/32263803?v=4",
|
||||
"profile": "http://queq1890.info",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Gim3l",
|
||||
"name": "Gimel Dick",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/46765702?v=4",
|
||||
"profile": "https://github.com/Gim3l",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "akbo",
|
||||
"name": "Andreas Bollig",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1926271?v=4",
|
||||
"profile": "https://github.com/akbo",
|
||||
"contributions": [
|
||||
"code",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "ajmarkow",
|
||||
"name": "AJ Markow",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/66390428?v=4",
|
||||
"profile": "https://ajm.codes",
|
||||
"contributions": [
|
||||
"test",
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "wafuwafu13",
|
||||
"name": "TagawaHirotaka",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/50798936?v=4",
|
||||
"profile": "https://wafuwafu13.hateblo.jp/",
|
||||
"contributions": [
|
||||
"code",
|
||||
"test"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "merodiro",
|
||||
"name": "Amr A.Mohammed",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/17033502?v=4",
|
||||
"profile": "https://github.com/merodiro",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "lcswillems",
|
||||
"name": "Lucas Willems",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/5437552?v=4",
|
||||
"profile": "http://www.lucaswillems.com",
|
||||
"contributions": [
|
||||
"doc",
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "alii",
|
||||
"name": "Alistair Smith",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/25351731?v=4",
|
||||
"profile": "https://alistair.cloud",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "rodrigoehlers",
|
||||
"name": "Rodrigo Ehlers",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/19683042?v=4",
|
||||
"profile": "https://rodrigoehlers.com",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "mtford90",
|
||||
"name": "Michael Ford",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1734057?v=4",
|
||||
"profile": "https://www.builtopen.com/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "LBrian",
|
||||
"name": "Brian Liu",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/3888780?v=4",
|
||||
"profile": "https://brianypliu.com",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "beerose",
|
||||
"name": "Aleksandra Sikora",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/9019397?v=4",
|
||||
"profile": "http://aleksandra.codes",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "JuanM04",
|
||||
"name": "JuanM04",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/16712703?v=4",
|
||||
"profile": "https://juanm04.com",
|
||||
"contributions": [
|
||||
"code",
|
||||
"doc",
|
||||
"test"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "arenddeboer",
|
||||
"name": "Arend de Boer",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/7022204?v=4",
|
||||
"profile": "https://github.com/arenddeboer",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "fmilani",
|
||||
"name": "Felipe Milani",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1580375?v=4",
|
||||
"profile": "https://github.com/fmilani",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "jxe",
|
||||
"name": "Joe Edelman",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/13018?v=4",
|
||||
"profile": "http://nxhx.org",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "garytube",
|
||||
"name": "Gary",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/3823504?v=4",
|
||||
"profile": "https://github.com/garytube",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "oliverloops",
|
||||
"name": "Oliver Lopez ",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/33361399?v=4",
|
||||
"profile": "http://oliverloops.com",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "DecadentIpsum",
|
||||
"name": "Andreas Zaralis",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/32861532?v=4",
|
||||
"profile": "https://decadentIpsum.me",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "davetorbeck",
|
||||
"name": "David Torbeck",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/5829885?v=4",
|
||||
"profile": "https://github.com/davetorbeck",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "gusgard",
|
||||
"name": "Gustavo Gard",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/2577356?v=4",
|
||||
"profile": "https://github.com/gusgard",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Immortalin",
|
||||
"name": "Immortalin",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/7126128?v=4",
|
||||
"profile": "https://narrationbox.com",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "cristianbgp",
|
||||
"name": "Cristian Granda",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/8507974?v=4",
|
||||
"profile": "https://cristianbgp.com",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "deniseyu",
|
||||
"name": "Denise Yu",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/8420094?v=4",
|
||||
"profile": "https://deniseyu.io",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "andreadellacorte",
|
||||
"name": "Andrea Della Corte",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/295683?v=4",
|
||||
"profile": "http://dellacorte.me",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "aditsachde",
|
||||
"name": "Adit Sachde",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/23707194?v=4",
|
||||
"profile": "http://aditsachde.com",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "hirenchauhan2",
|
||||
"name": "Hiren Chauhan",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/8999668?v=4",
|
||||
"profile": "https://github.com/hirenchauhan2",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "remjx",
|
||||
"name": "Mark Jackson",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/35121685?v=4",
|
||||
"profile": "http://remjx.com/",
|
||||
"contributions": [
|
||||
"doc",
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "lewisblackburn",
|
||||
"name": "Lewis Blackburn",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/51877955?v=4",
|
||||
"profile": "https://lewisb.cloud/",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "FDiskas",
|
||||
"name": "Vytenis",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/468006?v=4",
|
||||
"profile": "https://github.com/FDiskas",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "matthieu994",
|
||||
"name": "Matthieu",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/12969089?v=4",
|
||||
"profile": "https://portfolio.matthieupetit.com",
|
||||
"contributions": [
|
||||
"code",
|
||||
"test"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "mitchazj",
|
||||
"name": "Mitchell Johnson",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/15032956?v=4",
|
||||
"profile": "https://github.com/mitchazj",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Roesh",
|
||||
"name": "Roshan Manuel",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/31125563?v=4",
|
||||
"profile": "https://roshan.page/",
|
||||
"contributions": [
|
||||
"code",
|
||||
"doc",
|
||||
"test"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "kevinlangleyjr",
|
||||
"name": "Kevin Langley Jr.",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/877634?v=4",
|
||||
"profile": "https://kevinlangleyjr.com",
|
||||
"contributions": [
|
||||
"code",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "heavygabriel",
|
||||
"name": "Gabriel Picard",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/51029779?v=4",
|
||||
"profile": "https://projet-test-99df0.firebaseapp.com/",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "chenkie",
|
||||
"name": "Ryan Chenkie",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1847678?v=4",
|
||||
"profile": "http://ryanchenkie.com/",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "sbappan",
|
||||
"name": "Santhosh B. Appan",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/12586088?v=4",
|
||||
"profile": "https://github.com/sbappan",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "james2406",
|
||||
"name": "James Moran",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/10858584?v=4",
|
||||
"profile": "http://stackoverflow.com/users/5207233/james-moran",
|
||||
"contributions": [
|
||||
"code",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "bugzpodder",
|
||||
"name": "Jack Zhao",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/14841421?v=4",
|
||||
"profile": "http://fb.me/yz",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "the-red",
|
||||
"name": "Hisaki Akaza",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/4494300?v=4",
|
||||
"profile": "https://github.com/the-red",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Flavyoo",
|
||||
"name": "Flavio",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/14948074?v=4",
|
||||
"profile": "http://flavioander.com/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "pbteja1998",
|
||||
"name": "Bhanu Teja Pachipulusu",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/17903466?v=4",
|
||||
"profile": "https://bhanuteja.dev/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "pavestru",
|
||||
"name": "Pavel Struhar",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/10186479?v=4",
|
||||
"profile": "https://twitter.com/pavestru",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "reo777",
|
||||
"name": "Reo Ishiyama",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/42126368?v=4",
|
||||
"profile": "https://in-thepink.com/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "tmcw",
|
||||
"name": "Tom MacWright",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/32314?v=4",
|
||||
"profile": "https://macwright.com/",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "franky47",
|
||||
"name": "François Best",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1174092?v=4",
|
||||
"profile": "https://francoisbest.com",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "FarazPatankar",
|
||||
"name": "Faraz Patankar",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/10681116?v=4",
|
||||
"profile": "https://github.com/FarazPatankar",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "ericvicenti",
|
||||
"name": "Eric Vicenti",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1483597?v=4",
|
||||
"profile": "https://github.com/ericvicenti",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "amdolan",
|
||||
"name": "Alex Dolan",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/2552275?v=4",
|
||||
"profile": "https://github.com/amdolan",
|
||||
"contributions": [
|
||||
"doc",
|
||||
"code",
|
||||
"test"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Maastrich",
|
||||
"name": "Mathis Pinsault",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/58431775?v=4",
|
||||
"profile": "https://github.com/Maastrich",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "gstranger",
|
||||
"name": "gstranger",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/36181416?v=4",
|
||||
"profile": "https://github.com/gstranger",
|
||||
"contributions": [
|
||||
"code",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "markhughes",
|
||||
"name": "Mark Hughes",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1357323?v=4",
|
||||
"profile": "http://twitter.com/_markeh",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "andrearizzello",
|
||||
"name": "Andrea Rizzello",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/10348930?v=4",
|
||||
"profile": "www.andrearizzello.work",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "jahredhope",
|
||||
"name": "Jahred Hope",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/13903378?v=4",
|
||||
"profile": "jahred.com.au",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "simonelnahas",
|
||||
"name": "Simon El Nahas",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/29279201?v=4",
|
||||
"profile": "simonelnahas.github.io/",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Cristy94",
|
||||
"name": "Buleandra Cristian",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1384885?v=4",
|
||||
"profile": "www.usertrack.net",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "peterpalau",
|
||||
"name": "Pedro Enrique Palau Isaac",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/12257885?v=4",
|
||||
"profile": "http://palauisaac.me/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "sean-brydon",
|
||||
"name": "sean-brydon",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/55134778?v=4",
|
||||
"profile": "www.seanbrydon.me",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Dieman89",
|
||||
"name": "Alessandro",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/28837891?v=4",
|
||||
"profile": "buonerba.dev",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "laubonghaudoi",
|
||||
"name": "laubonghaudoi",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/11172180?v=4",
|
||||
"profile": "www.jyutping.org",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "TommasoBruno99",
|
||||
"name": "Tommaso Bruno",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/61512591?v=4",
|
||||
"profile": "https://github.com/TommasoBruno99",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "antonykamp",
|
||||
"name": "Antony",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/45163503?v=4",
|
||||
"profile": "antonykamp.de",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "frontsideair",
|
||||
"name": "Fatih Altinok",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/868283?v=4",
|
||||
"profile": "https://blog.6nok.org",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Mokshit06",
|
||||
"name": "Mokshit Jain",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/50412128?v=4",
|
||||
"profile": "https://mokshitjain.co/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 7,
|
||||
"skipCi": true
|
||||
}
|
||||
}
|
||||
50
.eslintignore
Normal file
50
.eslintignore
Normal file
@@ -0,0 +1,50 @@
|
||||
node_modules
|
||||
reports
|
||||
*.log
|
||||
.nyc_output
|
||||
**/coverage
|
||||
tsconfig.tsbuildinfo
|
||||
**/.blitz/**
|
||||
**/.next/**
|
||||
**/dist/**
|
||||
**/.vercel/**
|
||||
**/.test*
|
||||
/examples/auth2
|
||||
|
||||
prettier.config.*
|
||||
jest.config.*
|
||||
jest.setup.*
|
||||
babel.config.*
|
||||
eslint.config.*
|
||||
|
||||
/__mocks__
|
||||
/__fixturse
|
||||
/assets
|
||||
/patches
|
||||
/rfc-docs
|
||||
/scripts
|
||||
/types
|
||||
/recipes/*/templates
|
||||
/packages/generator/templates
|
||||
/packages/cli/lib
|
||||
|
||||
|
||||
// COPIED FROM nextjs/.eslintignore
|
||||
/nextjs/**/.next/**
|
||||
/nextjs/**/_next/**
|
||||
/nextjs/**/dist/**
|
||||
/nextjs/examples/with-typescript-eslint-jest/**
|
||||
/nextjs/examples/with-kea/**
|
||||
/nextjs/packages/next/bundles/webpack/packages/*.runtime.js
|
||||
/nextjs/packages/next/compiled/**/*
|
||||
/nextjs/packages/react-refresh-utils/**/*.js
|
||||
/nextjs/packages/react-dev-overlay/lib/**
|
||||
/nextjs/**/__tmp__/**
|
||||
/nextjs/.github/actions/next-stats-action/.work
|
||||
/nextjs/packages/next-codemod/transforms/__testfixtures__/**/*
|
||||
/nextjs/packages/next-codemod/transforms/__tests__/**/*
|
||||
/nextjs/packages/next-codemod/**/*.js
|
||||
/nextjs/packages/next-codemod/**/*.d.ts
|
||||
/nextjs/packages/next-env/**/*.d.ts
|
||||
/nextjs/test/integration/async-modules/**
|
||||
/nextjs/test-timings.json
|
||||
62
.eslintrc.js
62
.eslintrc.js
@@ -1,14 +1,19 @@
|
||||
module.exports = {
|
||||
parser: "@typescript-eslint/parser",
|
||||
parser: "babel-eslint",
|
||||
env: {
|
||||
browser: true,
|
||||
commonjs: true,
|
||||
es6: true,
|
||||
node: true,
|
||||
},
|
||||
parserOptions: {
|
||||
ecmaVersion: 6,
|
||||
sourceType: "module",
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
},
|
||||
project: `./tsconfig.json`,
|
||||
},
|
||||
plugins: ["@typescript-eslint", "import", "unicorn", "simple-import-sort"],
|
||||
plugins: ["import", "unicorn", "simple-import-sort"],
|
||||
extends: ["react-app"],
|
||||
rules: {
|
||||
"react/react-in-jsx-scope": "off", // React is always in scope with Blitz
|
||||
@@ -23,13 +28,6 @@ module.exports = {
|
||||
case: "kebabCase",
|
||||
},
|
||||
],
|
||||
"@typescript-eslint/no-floating-promises": "error",
|
||||
// note you must disable the base rule as it can report incorrect errors
|
||||
"no-use-before-define": "off",
|
||||
"@typescript-eslint/no-use-before-define": ["error"],
|
||||
// note you must disable the base rule as it can report incorrect errors
|
||||
"no-redeclare": "off",
|
||||
"@typescript-eslint/no-redeclare": ["error"],
|
||||
"simple-import-sort/imports": [
|
||||
"warn",
|
||||
{
|
||||
@@ -51,20 +49,50 @@ module.exports = {
|
||||
},
|
||||
],
|
||||
},
|
||||
ignorePatterns: [
|
||||
"packages/cli/",
|
||||
"packages/generator/templates",
|
||||
".eslintrc.js",
|
||||
"recipes/*/templates",
|
||||
],
|
||||
overrides: [
|
||||
{
|
||||
files: ["examples/**", "packages/gui/**", "recipes/**"],
|
||||
files: ["**/*.ts", "**/*.tsx"],
|
||||
parser: "@typescript-eslint/parser",
|
||||
parserOptions: {
|
||||
ecmaVersion: 6,
|
||||
sourceType: "module",
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
},
|
||||
project: `./tsconfig.eslint.json`,
|
||||
},
|
||||
plugins: ["@typescript-eslint"],
|
||||
rules: {
|
||||
"@typescript-eslint/no-floating-promises": "error",
|
||||
// note you must disable the base rule as it can report incorrect errors
|
||||
"no-use-before-define": "off",
|
||||
// "@typescript-eslint/no-use-before-define": ["error"],
|
||||
// note you must disable the base rule as it can report incorrect errors
|
||||
"no-redeclare": "off",
|
||||
"@typescript-eslint/no-redeclare": ["error"],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ["examples/**", "recipes/**"],
|
||||
rules: {
|
||||
"import/no-default-export": "off",
|
||||
"unicorn/filename-case": "off",
|
||||
"@typescript-eslint/no-floating-promises": "off",
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ["packages/cli/src/commands/**/*"],
|
||||
rules: {
|
||||
"require-await": "off",
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ["test/**", "**/__fixtures__/**"],
|
||||
rules: {
|
||||
"import/no-default-export": "off",
|
||||
"require-await": "off",
|
||||
"unicorn/filename-case": "off",
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
6
.github/CODEOWNERS
vendored
6
.github/CODEOWNERS
vendored
@@ -2,7 +2,7 @@
|
||||
|
||||
* @flybayer
|
||||
|
||||
packages/cli/**/* @aem, @flybayer
|
||||
packages/generator/**/* @aem @flybayer
|
||||
# packages/cli/**/* @aem, @flybayer
|
||||
# packages/generator/**/* @aem @flybayer
|
||||
packages/generator/templates**/* @flybayer
|
||||
packages/installer/**/* @aem @flybayer
|
||||
# packages/installer/**/* @aem @flybayer
|
||||
|
||||
23
.github/ISSUE_TEMPLATE/bug_report.md
vendored
23
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,23 +0,0 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Something is not working right. Or error messages are unclear.
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
### What is the problem?
|
||||
|
||||
### Steps to Reproduce
|
||||
|
||||
1.
|
||||
|
||||
### Versions
|
||||
|
||||
```
|
||||
output of `blitz --version --verbose`
|
||||
```
|
||||
|
||||
### Other
|
||||
|
||||
Please include applicable logs and screenshots that show your problem.
|
||||
48
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
48
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
name: Bug Report
|
||||
description: Something is not working right. Or error messages are unclear.
|
||||
labels: "kind/bug, status/triage"
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: Thanks for taking the time to file a bug report! Please fill out this form as completely as possible.
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: What is the problem?
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Paste all your error logs here:"
|
||||
value: |
|
||||
```
|
||||
PASTE_HERE (leave the ``` marks)
|
||||
```
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Paste all relevant code snippets here:"
|
||||
value: |
|
||||
```
|
||||
PASTE_HERE (leave the ``` marks)
|
||||
```
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: What are detailed steps to reproduce this?
|
||||
value: "1."
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Run `blitz -v` and paste the output here:"
|
||||
value: |
|
||||
```
|
||||
PASTE_HERE (leave the ``` marks)
|
||||
```
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Please include below any other applicable logs and screenshots that show your problem:"
|
||||
2
.github/ISSUE_TEMPLATE/feature_request.md
vendored
2
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -2,7 +2,7 @@
|
||||
name: Feature/change request
|
||||
about: Something new or better!
|
||||
title: ""
|
||||
labels: ""
|
||||
labels: "status/triage"
|
||||
assignees: ""
|
||||
---
|
||||
|
||||
|
||||
19
.github/PULL_REQUEST_TEMPLATE.md
vendored
19
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,10 +1,19 @@
|
||||
Closes: ??
|
||||
<!--
|
||||
Thanks for opening a PR! Your contribution is much appreciated.
|
||||
To make sure your PR is handled as smoothly as possible please:
|
||||
- Link issue via "Closes #[issue_number]
|
||||
- Choose & follow the right checklist for the change that you're making:
|
||||
-->
|
||||
|
||||
Closes: ?
|
||||
|
||||
### What are the changes and their implications?
|
||||
|
||||
### Checklist
|
||||
## Bug Checklist
|
||||
|
||||
- [ ] Changes covered by tests (tests added if needed)
|
||||
- [ ] PR submitted to [blitzjs.com](https://github.com/blitz-js/blitzjs.com) for any user facing changes
|
||||
- [ ] Integration test added (see [test docs](https://blitzjs.com/docs/contributing#running-tests) if needed)
|
||||
|
||||
<!-- IMPORTANT: Make sure to check the "Allow edits from maintainers" box below this window -->
|
||||
## Feature Checklist
|
||||
|
||||
- [ ] Integration test added (see [test docs](https://blitzjs.com/docs/contributing#running-tests) if needed)
|
||||
- [ ] Documentation added/updated (submit PR to [blitzjs.com repo](https://github.com/blitz-js/blitzjs.com))
|
||||
|
||||
10
.github/workflows/compressed.yml
vendored
10
.github/workflows/compressed.yml
vendored
@@ -11,16 +11,8 @@ jobs:
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node and Yarn
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: "14"
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
yarn && yarn build
|
||||
env:
|
||||
CI: true
|
||||
- name: Count size
|
||||
uses: preactjs/compressed-size-action@v2
|
||||
with:
|
||||
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
exclude: "{./nextjs/**}"
|
||||
|
||||
459
.github/workflows/main.yml
vendored
459
.github/workflows/main.yml
vendored
@@ -1,14 +1,12 @@
|
||||
# https://github.com/vercel/next.js/commits/canary/.github/workflows/build_test_deploy.yml
|
||||
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- canary
|
||||
branches: [canary]
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- canary
|
||||
types: [opened, synchronize]
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
@@ -30,32 +28,38 @@ jobs:
|
||||
path: |
|
||||
${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
**/node_modules
|
||||
/home/runner/.cache/Cypress
|
||||
C:\Users\runneradmin\AppData\Local\Cypress\Cache
|
||||
key: ${{ runner.os }}-${{ runner.node_version}}-yarn-v4-${{ hashFiles('yarn.lock') }}
|
||||
key: ${{ runner.os }}-${{ runner.node_version}}-yarn-v6-${{ hashFiles('yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ runner.node_version}}-yarn-v4-
|
||||
${{ runner.os }}-${{ runner.node_version}}-yarn-v6-
|
||||
- name: Install dependencies
|
||||
run: yarn install --frozen-lockfile --silent
|
||||
env:
|
||||
CI: true
|
||||
- name: manypkg lint
|
||||
run: yarn manypkg check
|
||||
env:
|
||||
CI: true
|
||||
- name: yarn lint
|
||||
run: yarn lint
|
||||
env:
|
||||
CI: true
|
||||
build_and_test_pkgs:
|
||||
name: Packages Tests
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
node_version: [12, 14]
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
NEXT_TELEMETRY_DISABLED: 1
|
||||
outputs:
|
||||
docsChange: ${{ steps.docs-change.outputs.DOCS_CHANGE }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 25
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: ${{ matrix.node_version }}
|
||||
node-version: "14"
|
||||
- run: git fetch --depth=1 origin +refs/tags/*:refs/tags/*
|
||||
- name: Get yarn cache directory path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
@@ -65,40 +69,65 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
/home/runner/.cache/Cypress
|
||||
C:\Users\runneradmin\AppData\Local\Cypress\Cache
|
||||
key: ${{ runner.os }}-${{ runner.node_version}}-yarn-v4-${{ hashFiles('yarn.lock') }}
|
||||
**/node_modules
|
||||
key: ${{ runner.os }}-${{ runner.node_version}}-yarn-v6-${{ hashFiles('yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ runner.node_version}}-yarn-v4-
|
||||
- name: Install dependencies
|
||||
run: yarn install --frozen-lockfile --silent
|
||||
env:
|
||||
CI: true
|
||||
${{ runner.os }}-${{ runner.node_version}}-yarn-v6-
|
||||
- run: yarn install --frozen-lockfile --check-files
|
||||
- name: Build Packages
|
||||
run: yarn build
|
||||
- run: node run-tests.js --timings --write-timings -g 1/1
|
||||
working-directory: nextjs
|
||||
- name: Check docs only change
|
||||
working-directory: nextjs
|
||||
run: echo ::set-output name=DOCS_CHANGE::$(node skip-docs-change.js echo 'not-docs-only-change')
|
||||
id: docs-change
|
||||
- run: echo ${{steps.docs-change.outputs.DOCS_CHANGE}}
|
||||
- uses: actions/cache@v2
|
||||
id: cache-build
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ github.sha }}
|
||||
|
||||
testBlitzPackages:
|
||||
name: Blitz - Test Packages
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
NEXT_TELEMETRY_DISABLED: 1
|
||||
steps:
|
||||
- uses: actions/cache@v2
|
||||
if: ${{needs.build.outputs.docsChange != 'docs only change'}}
|
||||
id: restore-build
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ github.sha }}
|
||||
- name: Setup kernel to increase watchers
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p
|
||||
- name: Build packages
|
||||
run: yarn build
|
||||
env:
|
||||
CI: true
|
||||
- name: Test Blitz Packages
|
||||
run: yarn testonly:packages
|
||||
env:
|
||||
CI: true
|
||||
build_and_test_examples:
|
||||
|
||||
testBlitzExamples:
|
||||
timeout-minutes: 30
|
||||
name: Example Apps Tests
|
||||
name: Blitz - Test Example Apps
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
node_version: [12, 14]
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
NEXT_TELEMETRY_DISABLED: 1
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 25
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: ${{ matrix.node_version }}
|
||||
node-version: "14"
|
||||
- run: git fetch --depth=1 origin +refs/tags/*:refs/tags/*
|
||||
- name: Get yarn cache directory path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
@@ -108,23 +137,359 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
/home/runner/.cache/Cypress
|
||||
C:\Users\runneradmin\AppData\Local\Cypress\Cache
|
||||
key: ${{ runner.os }}-${{ runner.node_version}}-yarn-v4-${{ hashFiles('yarn.lock') }}
|
||||
**/node_modules
|
||||
key: ${{ runner.os }}-${{ runner.node_version}}-yarn-v6-${{ hashFiles('yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ runner.node_version}}-yarn-v4-
|
||||
- name: Install dependencies
|
||||
run: yarn install --frozen-lockfile --silent
|
||||
env:
|
||||
CI: true
|
||||
${{ runner.os }}-${{ runner.node_version}}-yarn-v6-
|
||||
- run: yarn install --frozen-lockfile --check-files
|
||||
# - run: yarn cpy node_modules/.blitz packages/core/node_modules/.blitz
|
||||
# if: matrix.os == 'windows-latest'
|
||||
- name: Build Packages
|
||||
run: yarn build
|
||||
# Needed to get cypress binary
|
||||
- run: yarn cypress install
|
||||
- name: Install sass
|
||||
run: yarn install -W sass
|
||||
- name: Setup kernel to increase watchers
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p
|
||||
- name: Build packages
|
||||
run: yarn build
|
||||
env:
|
||||
CI: true
|
||||
- name: Test examples
|
||||
run: yarn testonly:examples
|
||||
run: yarn testonly:examples || yarn testonly:examples
|
||||
env:
|
||||
CI: true
|
||||
|
||||
checkPrecompiled:
|
||||
name: Check Pre-compiled
|
||||
defaults:
|
||||
run:
|
||||
working-directory: nextjs
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
env:
|
||||
NEXT_TELEMETRY_DISABLED: 1
|
||||
steps:
|
||||
- uses: actions/cache@v2
|
||||
if: ${{needs.build.outputs.docsChange != 'docs only change'}}
|
||||
id: restore-build
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ github.sha }}
|
||||
- run: ./check-pre-compiled.sh
|
||||
if: ${{needs.build.outputs.docsChange != 'docs only change'}}
|
||||
|
||||
testUnit:
|
||||
name: Nextjs - Test Unit
|
||||
defaults:
|
||||
run:
|
||||
working-directory: nextjs
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
env:
|
||||
NEXT_TELEMETRY_DISABLED: 1
|
||||
NEXT_TEST_JOB: 1
|
||||
HEADLESS: true
|
||||
steps:
|
||||
- uses: actions/cache@v2
|
||||
if: ${{needs.build.outputs.docsChange != 'docs only change'}}
|
||||
id: restore-build
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ github.sha }}
|
||||
|
||||
- run: node run-tests.js --timings --type unit -g 1/1
|
||||
if: ${{needs.build.outputs.docsChange != 'docs only change'}}
|
||||
|
||||
testIntegrationBlitz:
|
||||
name: Blitz - Test Integration
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
env:
|
||||
NEXT_TELEMETRY_DISABLED: 1
|
||||
NEXT_TEST_JOB: 1
|
||||
HEADLESS: true
|
||||
strategy:
|
||||
fail-fast: false
|
||||
steps:
|
||||
- run: echo ${{needs.build.outputs.docsChange}}
|
||||
- uses: actions/cache@v2
|
||||
if: ${{needs.build.outputs.docsChange != 'docs only change'}}
|
||||
id: restore-build
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ github.sha }}
|
||||
|
||||
# TODO: remove after we fix watchpack watching too much
|
||||
- run: echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p
|
||||
if: ${{needs.build.outputs.docsChange != 'docs only change'}}
|
||||
|
||||
- run: xvfb-run node nextjs/run-tests.js -c 3
|
||||
if: ${{needs.build.outputs.docsChange != 'docs only change'}}
|
||||
|
||||
testIntegration:
|
||||
name: Nextjs - Test Integration
|
||||
defaults:
|
||||
run:
|
||||
working-directory: nextjs
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
env:
|
||||
NEXT_TELEMETRY_DISABLED: 1
|
||||
NEXT_TEST_JOB: 1
|
||||
HEADLESS: true
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
group: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
||||
steps:
|
||||
- run: echo ${{needs.build.outputs.docsChange}}
|
||||
working-directory: ./
|
||||
- uses: actions/cache@v2
|
||||
if: ${{needs.build.outputs.docsChange != 'docs only change'}}
|
||||
id: restore-build
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ github.sha }}
|
||||
|
||||
# TODO: remove after we fix watchpack watching too much
|
||||
- run: echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p
|
||||
if: ${{needs.build.outputs.docsChange != 'docs only change'}}
|
||||
|
||||
- run: xvfb-run node run-tests.js --timings -g ${{ matrix.group }}/10 -c 3
|
||||
if: ${{needs.build.outputs.docsChange != 'docs only change'}}
|
||||
|
||||
testElectron:
|
||||
name: Nextjs - Test Electron
|
||||
defaults:
|
||||
run:
|
||||
working-directory: nextjs
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
env:
|
||||
NEXT_TELEMETRY_DISABLED: 1
|
||||
NEXT_TEST_JOB: 1
|
||||
HEADLESS: true
|
||||
TEST_ELECTRON: 1
|
||||
steps:
|
||||
- uses: actions/cache@v2
|
||||
if: ${{needs.build.outputs.docsChange != 'docs only change'}}
|
||||
id: restore-build
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ github.sha }}
|
||||
|
||||
# TODO: remove after we fix watchpack watching too much
|
||||
- run: echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p
|
||||
if: ${{needs.build.outputs.docsChange != 'docs only change'}}
|
||||
|
||||
- run: yarn add -W --dev spectron@7.0.0 electron@5.0.0
|
||||
if: ${{needs.build.outputs.docsChange != 'docs only change'}}
|
||||
working-directory: ./
|
||||
|
||||
- run: xvfb-run node run-tests.js test/integration/with-electron/test/index.test.js
|
||||
if: ${{needs.build.outputs.docsChange != 'docs only change'}}
|
||||
|
||||
testsPass:
|
||||
name: thank you, next
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
[
|
||||
checkPrecompiled,
|
||||
testIntegration,
|
||||
testIntegrationBlitz,
|
||||
testUnit,
|
||||
testBlitzPackages,
|
||||
testBlitzExamples,
|
||||
]
|
||||
steps:
|
||||
- run: exit 0
|
||||
|
||||
testFutureDependencies:
|
||||
name: Nextjs - Webpack 5 (Basic, Production, Acceptance)
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
NEXT_TELEMETRY_DISABLED: 1
|
||||
NEXT_TEST_JOB: 1
|
||||
HEADLESS: true
|
||||
NEXT_PRIVATE_TEST_WEBPACK5_MODE: 1
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 25
|
||||
|
||||
- run: echo ::set-output name=DOCS_CHANGE::$(node skip-docs-change.js echo 'not-docs-only-change')
|
||||
id: docs-change
|
||||
|
||||
- run: git fetch --depth=1 origin +refs/tags/*:refs/tags/*
|
||||
if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }}
|
||||
|
||||
- name: Use Node.js
|
||||
if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }}
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: "14"
|
||||
- run: git fetch --depth=1 origin +refs/tags/*:refs/tags/*
|
||||
if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }}
|
||||
- name: Get yarn cache directory path
|
||||
if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }}
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- name: Cache node_modules
|
||||
if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }}
|
||||
id: yarn-cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
**/node_modules
|
||||
/home/runner/.cache/Cypress
|
||||
C:\Users\runneradmin\AppData\Local\Cypress\Cache
|
||||
key: ${{ runner.os }}-${{ runner.node_version}}-yarn-v6-${{ hashFiles('yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ runner.node_version}}-yarn-v6-
|
||||
|
||||
- run: yarn install --check-files
|
||||
if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }}
|
||||
|
||||
- run: yarn build:nextjs
|
||||
if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }}
|
||||
|
||||
- run: xvfb-run node run-tests.js test/integration/{fallback-modules,link-ref,production,basic,async-modules,font-optimization,ssr-ctx}/test/index.test.js test/acceptance/*.test.js
|
||||
if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }}
|
||||
working-directory: nextjs
|
||||
|
||||
testLegacyReact:
|
||||
name: Nextjs - React 16 + Webpack 4 (Basic, Production, Acceptance)
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./
|
||||
env:
|
||||
NEXT_TELEMETRY_DISABLED: 1
|
||||
NEXT_TEST_JOB: 1
|
||||
HEADLESS: true
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 25
|
||||
|
||||
- run: echo ::set-output name=DOCS_CHANGE::$(node skip-docs-change.js echo 'not-docs-only-change')
|
||||
id: docs-change
|
||||
|
||||
- run: git fetch --depth=1 origin +refs/tags/*:refs/tags/*
|
||||
if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }}
|
||||
|
||||
- run: cat package.json | jq '.resolutions.react = "^16.14.0"' > package.json.tmp && mv package.json.tmp package.json
|
||||
if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }}
|
||||
|
||||
- run: cat package.json | jq '.resolutions."react-dom" = "^16.14.0"' > package.json.tmp && mv package.json.tmp package.json
|
||||
if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }}
|
||||
|
||||
- name: Use Node.js
|
||||
if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }}
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: "14"
|
||||
- run: git fetch --depth=1 origin +refs/tags/*:refs/tags/*
|
||||
if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }}
|
||||
- name: Get yarn cache directory path
|
||||
if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }}
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- name: Cache node_modules
|
||||
if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }}
|
||||
id: yarn-cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
**/node_modules
|
||||
/home/runner/.cache/Cypress
|
||||
C:\Users\runneradmin\AppData\Local\Cypress\Cache
|
||||
key: ${{ runner.os }}-${{ runner.node_version}}-yarn-v6-${{ hashFiles('yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ runner.node_version}}-yarn-v6-
|
||||
|
||||
- run: yarn install --check-files
|
||||
if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }}
|
||||
|
||||
- run: yarn list react react-dom
|
||||
if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }}
|
||||
|
||||
- run: yarn build:nextjs
|
||||
if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }}
|
||||
|
||||
- run: xvfb-run node run-tests.js test/integration/{link-ref,production,basic,async-modules,font-optimization,ssr-ctx,worker-loader}/test/index.test.js test/acceptance/*.test.js
|
||||
if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }}
|
||||
working-directory: nextjs
|
||||
|
||||
testFirefox:
|
||||
name: Nextjs - Test Firefox (production)
|
||||
defaults:
|
||||
run:
|
||||
working-directory: nextjs
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
env:
|
||||
HEADLESS: true
|
||||
BROWSERNAME: "firefox"
|
||||
NEXT_TELEMETRY_DISABLED: 1
|
||||
steps:
|
||||
- uses: actions/cache@v2
|
||||
if: ${{needs.build.outputs.docsChange != 'docs only change'}}
|
||||
id: restore-build
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ github.sha }}
|
||||
- run: node run-tests.js test/integration/production/test/index.test.js
|
||||
if: ${{needs.build.outputs.docsChange != 'docs only change'}}
|
||||
|
||||
testSafari:
|
||||
name: Nextjs - Test Safari (production)
|
||||
defaults:
|
||||
run:
|
||||
working-directory: nextjs
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
env:
|
||||
BROWSERSTACK: true
|
||||
BROWSERNAME: "safari"
|
||||
NEXT_TELEMETRY_DISABLED: 1
|
||||
SKIP_LOCAL_SELENIUM_SERVER: true
|
||||
BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }}
|
||||
BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }}
|
||||
steps:
|
||||
- uses: actions/cache@v2
|
||||
if: ${{needs.build.outputs.docsChange != 'docs only change'}}
|
||||
id: restore-build
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ github.sha }}
|
||||
- run: '[[ -z "$BROWSERSTACK_ACCESS_KEY" ]] && echo "Skipping for PR" || node run-tests.js test/integration/production/test/index.test.js'
|
||||
if: ${{needs.build.outputs.docsChange != 'docs only change'}}
|
||||
|
||||
testSafariOld:
|
||||
name: Nextjs - Test Safari 10.1 (nav)
|
||||
defaults:
|
||||
run:
|
||||
working-directory: nextjs
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build, testSafari]
|
||||
env:
|
||||
BROWSERSTACK: true
|
||||
LEGACY_SAFARI: true
|
||||
BROWSERNAME: "safari"
|
||||
NEXT_TELEMETRY_DISABLED: 1
|
||||
SKIP_LOCAL_SELENIUM_SERVER: true
|
||||
BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }}
|
||||
BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }}
|
||||
steps:
|
||||
- uses: actions/cache@v2
|
||||
if: ${{needs.build.outputs.docsChange != 'docs only change'}}
|
||||
id: restore-build
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ github.sha }}
|
||||
- run: '[[ -z "$BROWSERSTACK_ACCESS_KEY" ]] && echo "Skipping for PR" || node run-tests.js test/integration/production-nav/test/index.test.js'
|
||||
if: ${{needs.build.outputs.docsChange != 'docs only change'}}
|
||||
|
||||
7
.gitignore
vendored
7
.gitignore
vendored
@@ -1,7 +1,7 @@
|
||||
.log
|
||||
.DS_Store
|
||||
.idea
|
||||
.jest-*
|
||||
lib
|
||||
node_modules
|
||||
reports
|
||||
*.log
|
||||
@@ -24,3 +24,8 @@ dist
|
||||
.tsbuildinfo
|
||||
.nvmrc
|
||||
**/.test*
|
||||
examples/auth2
|
||||
.idea
|
||||
.ultra.cache.json
|
||||
db.sqlite-journal
|
||||
test/integration/**/db.json
|
||||
|
||||
1
.husky/.gitignore
vendored
Normal file
1
.husky/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
_
|
||||
5
.husky/pre-commit
Executable file
5
.husky/pre-commit
Executable file
@@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
yarn lint-staged
|
||||
yarn pretty-quick --staged
|
||||
@@ -4,7 +4,7 @@
|
||||
.log
|
||||
.DS_Store
|
||||
.jest-*
|
||||
lib
|
||||
packages/cli/lib
|
||||
node_modules
|
||||
reports
|
||||
*.log
|
||||
@@ -19,3 +19,4 @@ bin
|
||||
!packages/blitz/src/bin
|
||||
packages/generator/templates/**
|
||||
.github/ISSUE_TEMPLATE/bug_report.md
|
||||
nextjs/packages/next/compiled/**
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
# Blitz.js Governance
|
||||
|
||||
_From Brandon Bayer (@flybayer), the creator:_
|
||||
|
||||
Currently at this very early stage it's basically a [BDFL situation](https://opensource.guide/leadership-and-governance/#what-are-some-of-the-common-governance-structures-for-open-source-projects), with me having the final say in decisions.
|
||||
|
||||
However we will move away from BDFL to something that looks more like Ember.js. It's extremely important to me (Brandon) that Blitz.js is a long-term, sustainable, and community-run project.
|
||||
|
||||
I would love some mentorship from people with experience in large open-source projects on making this transition.
|
||||
|
||||
Also, it's possible I will create one or more business around Blitz, perhaps similar to how Taylor Otwell has around Laravel, but Blitz itself will always remain a separate community-run project.
|
||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 Brandon Bayer
|
||||
Copyright (c) 2021 Brandon Bayer
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
This document has moved here: https://blitzjs.com/docs/maintainers
|
||||
@@ -1,3 +0,0 @@
|
||||
# Manifesto
|
||||
|
||||
[The Manifesto has been moved to Blitzjs.com](https://blitzjs.com/docs/manifesto)
|
||||
203
README.md
203
README.md
@@ -6,7 +6,7 @@
|
||||
<img alt="" src="https://img.shields.io/badge/Join%20our%20community-6700EB.svg?style=for-the-badge&labelColor=000000&logoWidth=20&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAQ9SURBVHgB7d3dVdtAEIbhcSpICUoH0IEogQqSVBBSAU4FSSpIOoAORAfQgSghHXzZ1U/YcMD4R9rZmf2ec3y448LyiNf27iLiGIAmPLrweC9Un3DhrzG6EarLNP09nlwJ1SOZ/lQr5N80/S/p2QMVCBf5N17XCfm1Y/rBHqjAG9PPHvBsz+mf9WAP+HLA9M/YA14cOP2payH7jpj+VCtk1wnTP+vj7xCy6cTpn7EHLMLp059iD1iD8eveJbVCNsSLheX1YA/YgOWnf8YeKB3Wmf7Ud6Fy4f/FHmtpxbl3YlC4MJ/Cj0bWdwPnPbARg+L0S54XQHS32WwuxClzd4CM0z9rPfeAuTtA5ulPXYQ7wZ04Y+oOoDD9KZc9YOoOoDj9s4dwFzgXR6w1wIPoOvPWA9buAHEJ173o3gWiy3AnuBUHLEbgmYwvAk1/wuM8vAgexThzbwPDkx7/DHwVXfFOxP2GmsKd4Ab6zPeAyU8CI7AHFmH2BRCBPXAyk18GzUrqAXCTiR4ssyj0VFw/oCU8+e+RZ33AWz6KMaYbIIWxB+JSLs1bsbkeMN0AqakHvoku9oA2sAfqBvbAQdw0QArsgb25aYBUQT3QgT2gB+yBuqGcHij2UCqXDZACe2Anlw2QYg/QAOyBuoE98CL3DZDCuK4/rh/Q7oGL6U+TOvcNkJoijN8X1C48+T+g75eQDrAH/qmqAVJgDwyqaoAUe4AGYA/UDZX3QLUNkEIZPRCd5+6BahsgVUgPROwBTSijB7jpVAvGHriHvmw9wAZ4BpX1ABvgmakHtPcbRuwBTWAPULgAV9D/jKDY9YRvwvgEaurD44uQHvAol7qBW7WKluVtIHiUS7GyvA0s6CiXDnxrpQfsgbqBS7GKk/2jYHCrVlGyfxTMrVo0ALdq1Q3sgSKofh0M9oA61a+D2QM0AHugbmAPqClmSRjK2apVVQ8UsySsoK1aHdgDesCtWnUDeyCrIpeFg1u3sylyWTi3btMA7IG6gT2wuuK3hoE9sKrit4YVslWLPaAN7IG6ocKt2zmY2h4O9sDiTG0PZw/QANy6XTewBxZj9ogYVHy025LMHhEz9cBn0We6B0yfERReBLfhx0/R1YQHPx/QBPbA0VwcEwf2wNFcHBPHHjiem3MC2QPHcXdSaJjA+KfgTPQ8hhfjBzHC40mhlzJ+Xq9lK4a4PCs43AVaGTed5mZq+iOXZwWHi3AnOj2wFWNcnxYe7gTxLtBKHuamP/J+Wnh8a5irB7ZC5Yk9gPX1QuXC+usHWqGyhYvUYR0a7zboUOFCNVhnk0krZAOW7wFOvzXhom2xnEbIHizTA1wEYhWW6YFGyC6c1gOcfg9wfA80Qj7g8B7g9HuCww+haIR8wf49wOn3Cvv9k8tGyC/s7gFOv3fY3QONkH+v9MBWqB7PeqDn9FcIT//kcitUn6kHOu/T/xfWzlQy3dEHhwAAAABJRU5ErkJggg==">
|
||||
</a>
|
||||
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
||||
<a aria-label="All Contributors" href="#contributors-"><img alt="" src="https://img.shields.io/badge/all_contributors-199-17BB8A.svg?style=for-the-badge&labelColor=000000"></a>
|
||||
<a aria-label="All Contributors" href="#contributors-"><img alt="" src="https://img.shields.io/badge/all_contributors-263-17BB8A.svg?style=for-the-badge&labelColor=000000"></a>
|
||||
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
||||
<a aria-label="License" href="https://github.com/blitz-js/blitz/blob/canary/LICENSE">
|
||||
<img alt="" src="https://img.shields.io/npm/l/blitz.svg?style=for-the-badge&labelColor=000000&color=blue">
|
||||
@@ -22,15 +22,15 @@
|
||||
<h1 align="center">The Fullstack React Framework</h1>
|
||||
|
||||
<h5 align="center">"Zero-API" Data Layer — Built on Next.js — Inspired by Ruby on Rails</h3>
|
||||
<h3 align="center">Makes you far more productive than you ever dreamed was possible 😉</h3>
|
||||
<h3 align="center"><a href="https://blitzjs.com" target="_blank">Read the Documentation</a></h3>
|
||||
<br>
|
||||
|
||||
“Zero-API” data layer **lets you import server code directly into your React components** instead of having to manually add API endpoints and do client-side fetching and caching.
|
||||
|
||||
Includes everything you need for production apps. **Everything end-to-end from the database to the frontend.**
|
||||
New Blitz apps come with **all the boring stuff already set up for you!** Like ESLint, Prettier, Jest, user sign up, log in, and password reset.
|
||||
|
||||
Provides **helpful defaults and conventions** for things like routing, file structure, and authentication while also being extremely flexible.
|
||||
|
||||
Blitz brings back the **simplicity and conventions** of server-rendered frameworks like Ruby on Rails while preserving everything we love about React and client-side rendering!
|
||||
|
||||
<br>
|
||||
|
||||
@@ -48,8 +48,8 @@ _You can alternatively use [`npx`](https://www.npmjs.com/package/npx)_
|
||||
|
||||
1. `blitz new myAppName`
|
||||
2. `cd myAppName`
|
||||
3. `blitz start`
|
||||
4. View your baby app at http://localhost:3000
|
||||
3. `blitz dev`
|
||||
4. View your brand new app at http://localhost:3000
|
||||
|
||||
<br><br>
|
||||
|
||||
@@ -57,32 +57,10 @@ _You can alternatively use [`npx`](https://www.npmjs.com/package/npx)_
|
||||
<img alt="Bytes Newsletter" src="https://files-8wtskjofb.vercel.app/smarter-16x1.jpg">
|
||||
</a>
|
||||
|
||||
<br><br>
|
||||
|
||||

|
||||
|
||||
<br><br>
|
||||
|
||||
**Features:**<br>
|
||||
⚡️ Built on Next.js<br>
|
||||
⚡️ Don't have to build an API for client-side rendering<br>
|
||||
⚡️ Client-side rendering, Server-side rendering, and fully static pages all in the same app<br>
|
||||
⚡️ Full TypeScript support with static, end-to-end typing (no code generation step needed like with GraphQL)<br>
|
||||
⚡️ React Concurrent Mode enabled<br>
|
||||
⚡️ Database/ORM agnostic, but Prisma 2 is default<br>
|
||||
⚡️ CLI with code scaffolding, Rails-style console REPL, etc<br>
|
||||
⚡️ GraphQL Ready<br>
|
||||
⚡️ Deploy serverless or serverful<br>
|
||||
⚡️ Highly secure authentication <br>
|
||||
⚡️ Authorization you can use on both server and client<br>
|
||||
⚡️ Recipes for easily adding libraries like Tailwind, CSS-in-JS, etc.<br>
|
||||
|
||||
**Other key features coming:**<br>
|
||||
⚡️ Model validation you can use on both server and client<br>
|
||||
⚡️ React native support<br>
|
||||
⚡️ GUI so you don't have to use the CLI<br>
|
||||
|
||||
<br>
|
||||
|
||||
### The Foundational Principles
|
||||
|
||||
@@ -127,38 +105,56 @@ Your financial contributions help ensure Blitz continues to be developed and mai
|
||||
|
||||
### 🌱 Seedling Sponsors
|
||||
|
||||
<a aria-label="React Bricks" href="https://reactbricks.com/?utm_source=blitzjs&utm_medium=sponsorship&utm_campaign=blitzjs_sponsorship">
|
||||
<img alt="" src="https://reactbricks.com/reactbricks_icon.svg" width="30px"/>
|
||||
</a>
|
||||
<a aria-label="Andreas Asprou" href="https://andreas.fyi">
|
||||
<img alt="" src="https://raw.githubusercontent.com/blitz-js/blitz/canary/assets/andreas.jpg" width="30px"/>
|
||||
</a>
|
||||
<table>
|
||||
<tr>
|
||||
<td><a aria-label="React Bricks" href="https://reactbricks.com/?utm_source=blitzjs&utm_medium=sponsorship&utm_campaign=blitzjs_sponsorship">
|
||||
<img alt="" src="https://raw.githubusercontent.com/blitz-js/blitz/canary/assets/reactbricks_icon.svg" width="40px"/>
|
||||
</a></td>
|
||||
<td><a aria-label="Andreas Asprou" href="https://andreas.fyi">
|
||||
<img alt="" src="https://raw.githubusercontent.com/blitz-js/blitz/canary/assets/andreas.jpg" width="40px"/>
|
||||
</a></td>
|
||||
<td><a aria-label="Robert Malko" href="https://github.com/malkomalko">
|
||||
<img alt="" src="https://raw.githubusercontent.com/blitz-js/blitz/canary/assets/rob_blitz.jpg" width="40px"/>
|
||||
</a></td>
|
||||
<td><a aria-label="Digas" href="https://digsas.com">
|
||||
<img alt="" src="https://raw.githubusercontent.com/blitz-js/blitz/canary/assets/digsas.svg" width="40px"/>
|
||||
</a></td>
|
||||
<td><a aria-label="userTrack" href="https://www.usertrack.net/?ref=blitzjs">
|
||||
<img alt="" src="https://i.imgur.com/UDBeazC.png" width="40px"/>
|
||||
</a></td>
|
||||
</tr></table>
|
||||
|
||||
|
||||
### 🥉 Bronze Sponsors
|
||||
|
||||
<a aria-label="Render.com" href="https://render.com?utm_source=BlitzJS&utm_medium=sponsorship&utm_campaign=BlitzJS_Sponsorship_2020">
|
||||
<img alt="" src="https://raw.githubusercontent.com/blitz-js/blitz/canary/assets/render-logo-color2.png" width="110px">
|
||||
</a>
|
||||
<table>
|
||||
<tr>
|
||||
<td><a aria-label="Render.com" href="https://render.com?utm_source=BlitzJS&utm_medium=sponsorship&utm_campaign=BlitzJS_Sponsorship_2020">
|
||||
<img alt="" src="https://raw.githubusercontent.com/blitz-js/blitz/canary/assets/render-logo-color2.png" width="200px">
|
||||
</a></td>
|
||||
<td><a aria-label="RIT" href="https://rit-inc.co.jp/?utm_source=BlitzJS&utm_medium=sponsorship&utm_campaign=BlitzJS_Sponsorship_2021">
|
||||
<img alt="" src="https://raw.githubusercontent.com/blitz-js/blitz/canary/assets/rit_logo.png" width="200px">
|
||||
</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
### 🥈 Silver Sponsors
|
||||
|
||||
<a aria-label="Fauna" href="https://dashboard.fauna.com/accounts/register?utm_source=BlitzJS&utm_medium=sponsorship&utm_campaign=BlitzJS_Sponsorship_2020">
|
||||
<img alt="" src="https://raw.githubusercontent.com/blitz-js/blitz/canary/assets/Fauna_Logo_Blue.png" width="200px">
|
||||
<img alt="" src="https://raw.githubusercontent.com/blitz-js/blitz/canary/assets/Fauna_Logo_Blue.png" width="300px">
|
||||
</a>
|
||||
|
||||
### 🏆 Gold Sponsors
|
||||
|
||||
<div>
|
||||
<a aria-label="G2i" href="http://g2i.co/sign-up?utm_source=blitz&utm_medium=referral&utm_campaign=blitz2020">
|
||||
<img alt="" src="https://files-5oz00y7xp.vercel.app/G2i_Logo_wwords.png" width="160px">
|
||||
<a aria-label="Your Company" href="#">
|
||||
<img alt="" src="https://dummyimage.com/1000x330/efe8ff/000000.png&text=Your+Logo+Here" width="400px">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
### 💎 Diamond Sponsors
|
||||
|
||||
<a aria-label="Your Company" href="#">
|
||||
<img alt="" src="https://dummyimage.com/1000x330/efe8ff/000000.png&text=Your+Logo+Here" width="400px">
|
||||
<img alt="" src="https://dummyimage.com/1000x330/efe8ff/000000.png&text=Your+Logo+Here" width="500px">
|
||||
</a>
|
||||
|
||||
<br>
|
||||
@@ -179,15 +175,15 @@ Your financial contributions help ensure Blitz continues to be developed and mai
|
||||
|
||||
## Maintainers (Level 2) ✨
|
||||
|
||||
_Code ownership, pull request approvals and merging, etc_ (see [MAINTAINERS.md](./MAINTAINERS.md))
|
||||
_Code ownership, pull request approvals and merging, etc_ (see [Maintainers L2](https://blitzjs.com/docs/maintainers#level-2-maintainers))
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
<!-- markdownlint-disable -->
|
||||
<table>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/aem"><img src="https://avatars0.githubusercontent.com/u/1909883?v=4" width="100px;" alt=""/><br /><sub><b>Adam Markon</b></sub></a><br />CLI</td>
|
||||
<td align="center"><a href="http://robdrosenberg.com"><img src="https://avatars0.githubusercontent.com/u/20813991?v=4" width="100px;" alt=""/><br /><sub><b>Robert Rosenberg</b></sub></a><br />Website/Docs</td>
|
||||
<td align="center"><a href="http://simonknott.de"><img src="https://avatars1.githubusercontent.com/u/14912729?v=4" width="100px;" alt=""/><br /><sub><b>Simon Knott</b></sub></a><br />SuperJSON</td>
|
||||
<td align="center"><a href="https://juanm04.com"><img src="https://avatars0.githubusercontent.com/u/16712703?v=4" width="100px;" alt=""/><br /><sub><b>Juan Martín Seery</b></sub></a><br />Website/Docs</td>
|
||||
</tr>
|
||||
</table>
|
||||
<!-- markdownlint-enable -->
|
||||
@@ -197,27 +193,34 @@ _Code ownership, pull request approvals and merging, etc_ (see [MAINTAINERS.md](
|
||||
|
||||
## Maintainers (Level 1) ✨
|
||||
|
||||
_Issue triage, pull request triage, community encouragement and moderation, etc_ (see [MAINTAINERS.md](./MAINTAINERS.md))
|
||||
_Issue triage, pull request triage, community encouragement and moderation, etc_ (see [Maintainers L1](https://blitzjs.com/docs/maintainers#level-1-maintainers))
|
||||
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
<!-- markdownlint-disable -->
|
||||
<table>
|
||||
<tr>
|
||||
<td align="center"><a href="https://corey-brown.com"><img src="https://avatars1.githubusercontent.com/u/12791148?v=4" width="100px;" alt=""/><br /><sub><b>Corey Brown</b></sub></a></td>
|
||||
<td align="center"><a href="http://jeremyliberman.com/"><img src="https://avatars3.githubusercontent.com/u/2754163?v=4" width="100px;" alt=""/><br /><sub><b>Jeremy Liberman</b></td>
|
||||
<td align="center"><a href="http://jagascript.com"><img src="https://avatars0.githubusercontent.com/u/4562878?v=4" width="100px;" alt=""/><br /><sub><b>Jaga Santagostino</b></sub></a></td>
|
||||
<td align="center"><a href="https://twitter.com/nitaking_"><img src="https://avatars2.githubusercontent.com/u/10850034?v=4" width="100px;" alt=""/><br /><sub><b>Satoshi Nitawaki</b></sub></a></td>
|
||||
<td align="center"><a href="https://twitter.com/sandulat"><img src="https://avatars2.githubusercontent.com/u/7345874?v=4" width="100px;" alt=""/><br /><sub><b>Alexandru Stratulat</b></sub></a></td>
|
||||
<td align="center"><a href="https://github.com/engelkes-finstreet"><img src="https://avatars0.githubusercontent.com/u/36962022?s=460&u=34cfc4a3d6da0a87026f6068c371779c68daa3a2&v=4" width="100px;" alt=""/><br /><sub><b>Patrick Engelkes</b></sub></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://twitter.com/jdavenport97"><img src="https://avatars2.githubusercontent.com/u/1329874?v=4" width="100px;" alt=""/><br /><sub><b>Jamie Davenport</b></sub></a></td>
|
||||
<td align="center"><a href="https://twitter.com/myrondavis"><img src="https://avatars2.githubusercontent.com/u/1430136?v=4" width="100px;" alt=""/><br /><sub><b>Myron Davis</b></sub></a></td>
|
||||
<td align="center"><a href="https://flavioander.com/"><img src="https://avatars2.githubusercontent.com/u/14948074?s=460&u=31d7ea58b5c5cd9f724d684ed578f68896c4af71&v=4" width="100px;" alt=""/><br /><sub><b>Flavio Andrade</b></sub></a></td>
|
||||
<td align="center"><a href="https://twitter.com/NaReto1125_"><img src="https://avatars.githubusercontent.com/reo777" width="100px;" alt=""/><br /><sub><b>Reo Ishiyama</b></sub></a></td>
|
||||
<td align="center"><a href="https://github.com/malkomalko"><img src="https://avatars.githubusercontent.com/malkomalko" width="100px;" alt=""/><br /><sub><b>Robert Malko</b></sub></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://kevinlangleyjr.com">
|
||||
<img
|
||||
src="https://avatars.githubusercontent.com/u/877634?v=4"
|
||||
width="100px;"
|
||||
alt=""
|
||||
/><br />
|
||||
<sub>
|
||||
<b>Kevin Langley Jr.</b>
|
||||
</sub>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<!-- markdownlint-enable -->
|
||||
<!-- prettier-ignore-end -->
|
||||
@@ -234,7 +237,7 @@ Thanks to these wonderful people ([emoji key](https://allcontributors.org/docs/e
|
||||
<!-- markdownlint-disable -->
|
||||
<table>
|
||||
<tr>
|
||||
<td align="center"><a href="https://twitter.com/flybayer"><img src="https://avatars3.githubusercontent.com/u/8813276?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Brandon Bayer</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=flybayer" title="Code">💻</a> <a href="#content-flybayer" title="Content">🖋</a> <a href="#ideas-flybayer" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/blitz-js/blitz/pulls?q=is%3Apr+reviewed-by%3Aflybayer" title="Reviewed Pull Requests">👀</a></td>
|
||||
<td align="center"><a href="https://twitter.com/flybayer"><img src="https://avatars3.githubusercontent.com/u/8813276?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Brandon Bayer</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=flybayer" title="Code">💻</a> <a href="#content-flybayer" title="Content">🖋</a> <a href="#ideas-flybayer" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/blitz-js/blitz/pulls?q=is%3Apr+reviewed-by%3Aflybayer" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/blitz-js/blitz/commits?author=flybayer" title="Tests">⚠️</a> <a href="https://github.com/blitz-js/blitz/commits?author=flybayer" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://medium.com/@ryardley"><img src="https://avatars0.githubusercontent.com/u/1256409?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Rudi Yardley</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=ryardley" title="Code">💻</a> <a href="#ideas-ryardley" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/blitz-js/blitz/pulls?q=is%3Apr+reviewed-by%3Aryardley" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/blitz-js/blitz/commits?author=ryardley" title="Tests">⚠️</a></td>
|
||||
<td align="center"><a href="https://merelinguist.me"><img src="https://avatars3.githubusercontent.com/u/24858006?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Dylan Brookes</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=merelinguist" title="Code">💻</a> <a href="#ideas-merelinguist" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/blitz-js/blitz/pulls?q=is%3Apr+reviewed-by%3Amerelinguist" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/blitz-js/blitz/commits?author=merelinguist" title="Tests">⚠️</a> <a href="https://github.com/blitz-js/blitz/commits?author=merelinguist" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/aem"><img src="https://avatars0.githubusercontent.com/u/1909883?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Adam Markon</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=aem" title="Code">💻</a> <a href="#ideas-aem" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/blitz-js/blitz/pulls?q=is%3Apr+reviewed-by%3Aaem" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/blitz-js/blitz/commits?author=aem" title="Tests">⚠️</a> <a href="#maintenance-aem" title="Maintenance">🚧</a></td>
|
||||
@@ -279,7 +282,7 @@ Thanks to these wonderful people ([emoji key](https://allcontributors.org/docs/e
|
||||
<td align="center"><a href="https://github.com/ntgussoni"><img src="https://avatars0.githubusercontent.com/u/10161067?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Nicolas Torres</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=ntgussoni" title="Tests">⚠️</a> <a href="https://github.com/blitz-js/blitz/commits?author=ntgussoni" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/pulls?q=is%3Apr+reviewed-by%3Antgussoni" title="Reviewed Pull Requests">👀</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="http://simonknott.de"><img src="https://avatars1.githubusercontent.com/u/14912729?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Simon Knott</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=Skn0tt" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=Skn0tt" title="Tests">⚠️</a> <a href="#maintenance-Skn0tt" title="Maintenance">🚧</a></td>
|
||||
<td align="center"><a href="http://simonknott.de"><img src="https://avatars1.githubusercontent.com/u/14912729?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Simon Knott</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=Skn0tt" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=Skn0tt" title="Tests">⚠️</a> <a href="#maintenance-Skn0tt" title="Maintenance">🚧</a> <a href="https://github.com/blitz-js/blitz/commits?author=Skn0tt" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="http://jagascript.com"><img src="https://avatars0.githubusercontent.com/u/4562878?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jaga Santagostino</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=kandros" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=kandros" title="Documentation">📖</a> <a href="#maintenance-kandros" title="Maintenance">🚧</a></td>
|
||||
<td align="center"><a href="http://www.joaoportela.com"><img src="https://avatars0.githubusercontent.com/u/1010018?v=4?s=100" width="100px;" alt=""/><br /><sub><b>João Portela</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=jportela" title="Code">💻</a></td>
|
||||
<td align="center"><a href="http://dajin.dev"><img src="https://avatars0.githubusercontent.com/u/7122182?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Da-Jin Chu</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=dajinchu" title="Code">💻</a></td>
|
||||
@@ -399,7 +402,7 @@ Thanks to these wonderful people ([emoji key](https://allcontributors.org/docs/e
|
||||
<td align="center"><a href="https://github.com/phillippschmedt"><img src="https://avatars0.githubusercontent.com/u/16028406?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Phillipp Schmedt</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=phillippschmedt" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://haspar.us"><img src="https://avatars0.githubusercontent.com/u/15332326?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Piotr Monwid-Olechnowicz</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=hasparus" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://mizchi.dev"><img src="https://avatars2.githubusercontent.com/u/73962?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Kotaro Chikuba</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=mizchi" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=mizchi" title="Tests">⚠️</a></td>
|
||||
<td align="center"><a href="https://github.com/konradkalemba"><img src="https://avatars0.githubusercontent.com/u/8682104?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Konrad Kalemba</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=konradkalemba" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/konradkalemba"><img src="https://avatars0.githubusercontent.com/u/8682104?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Konrad Kalemba</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=konradkalemba" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=konradkalemba" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/Alucard17"><img src="https://avatars1.githubusercontent.com/u/26205172?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Alucard17</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=Alucard17" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/Dohxis"><img src="https://avatars2.githubusercontent.com/u/8768909?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Domantas Mauruča</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=Dohxis" title="Tests">⚠️</a> <a href="https://github.com/blitz-js/blitz/commits?author=Dohxis" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://sandulat.com/"><img src="https://avatars0.githubusercontent.com/u/7345874?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Stratulat Alexandru</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=sandulat" title="Code">💻</a> <a href="#maintenance-sandulat" title="Maintenance">🚧</a></td>
|
||||
@@ -446,17 +449,17 @@ Thanks to these wonderful people ([emoji key](https://allcontributors.org/docs/e
|
||||
<td align="center"><a href="http://kattcorp.com"><img src="https://avatars1.githubusercontent.com/u/459267?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Alex Johansson</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=KATT" title="Code">💻</a></td>
|
||||
<td align="center"><a href="http://davidmazza.com"><img src="https://avatars0.githubusercontent.com/u/120893?v=4?s=100" width="100px;" alt=""/><br /><sub><b>David Mazza</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=dmzza" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/rayandrews"><img src="https://avatars1.githubusercontent.com/u/4437323?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ray Andrew</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=rayandrews" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=rayandrews" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="http://Dal.Design"><img src="https://avatars3.githubusercontent.com/u/43112535?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Abdullah Mzaien</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=Mzaien" title="Code">💻</a></td>
|
||||
<td align="center"><a href="http://Dal.Design"><img src="https://avatars3.githubusercontent.com/u/43112535?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Abdullah Mzaien</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=Mzaien" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=Mzaien" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="http://kwao.io"><img src="https://avatars2.githubusercontent.com/u/8839514?v=4?s=100" width="100px;" alt=""/><br /><sub><b>William Kwao</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=williamkwao" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/sakulstra"><img src="https://avatars3.githubusercontent.com/u/4396533?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Lukas Strassel</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=sakulstra" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/sakulstra"><img src="https://avatars3.githubusercontent.com/u/4396533?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Lukas Strassel</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=sakulstra" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=sakulstra" title="Tests">⚠️</a></td>
|
||||
<td align="center"><a href="https://thibpat.com"><img src="https://avatars3.githubusercontent.com/u/494686?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Thibaut Patel</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=tpatel" title="Code">💻</a></td>
|
||||
<td align="center"><a href="http://jonstuebe.com"><img src="https://avatars0.githubusercontent.com/u/156722?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jon Stuebe</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=jonstuebe" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://ugogo.dev"><img src="https://avatars2.githubusercontent.com/u/5040476?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ugo Onali</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=ugogo" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://saintmalik.me"><img src="https://avatars1.githubusercontent.com/u/37118134?v=4?s=100" width="100px;" alt=""/><br /><sub><b>SaintMalik</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=saintmalik" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://khaledgarbaya.net"><img src="https://avatars1.githubusercontent.com/u/1156093?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Khaled Garbaya</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=Khaledgarbaya" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://tundera.dev"><img src="https://avatars0.githubusercontent.com/u/61833561?v=4?s=100" width="100px;" alt=""/><br /><sub><b>tundera</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=tundera" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://tundera.dev"><img src="https://avatars0.githubusercontent.com/u/61833561?v=4?s=100" width="100px;" alt=""/><br /><sub><b>tundera</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=tundera" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=tundera" title="Tests">⚠️</a> <a href="https://github.com/blitz-js/blitz/commits?author=tundera" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/markylaing"><img src="https://avatars2.githubusercontent.com/u/41469221?v=4?s=100" width="100px;" alt=""/><br /><sub><b>markylaing</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=markylaing" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=markylaing" title="Documentation">📖</a></td>
|
||||
@@ -489,6 +492,88 @@ Thanks to these wonderful people ([emoji key](https://allcontributors.org/docs/e
|
||||
<td align="center"><a href="https://github.com/jonasthiesen"><img src="https://avatars.githubusercontent.com/u/23408018?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jonas Thiesen</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=jonasthiesen" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://thakkaryash94.github.io/"><img src="https://avatars.githubusercontent.com/u/7349778?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Yash Thakkar</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=thakkaryash94" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/rince"><img src="https://avatars.githubusercontent.com/u/933895?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Kazuma Suzuki</b></sub></a><br /><a href="#design-rince" title="Design">🎨</a> <a href="https://github.com/blitz-js/blitz/commits?author=rince" title="Code">💻</a></td>
|
||||
<td align="center"><a href="http://queq1890.info"><img src="https://avatars.githubusercontent.com/u/32263803?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Yuji Matsumoto</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=queq1890" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/Gim3l"><img src="https://avatars.githubusercontent.com/u/46765702?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Gimel Dick</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=Gim3l" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/akbo"><img src="https://avatars.githubusercontent.com/u/1926271?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Andreas Bollig</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=akbo" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=akbo" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://ajm.codes"><img src="https://avatars.githubusercontent.com/u/66390428?v=4?s=100" width="100px;" alt=""/><br /><sub><b>AJ Markow</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=ajmarkow" title="Tests">⚠️</a> <a href="https://github.com/blitz-js/blitz/commits?author=ajmarkow" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://wafuwafu13.hateblo.jp/"><img src="https://avatars.githubusercontent.com/u/50798936?v=4?s=100" width="100px;" alt=""/><br /><sub><b>TagawaHirotaka</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=wafuwafu13" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=wafuwafu13" title="Tests">⚠️</a></td>
|
||||
<td align="center"><a href="https://github.com/merodiro"><img src="https://avatars.githubusercontent.com/u/17033502?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Amr A.Mohammed</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=merodiro" title="Code">💻</a></td>
|
||||
<td align="center"><a href="http://www.lucaswillems.com"><img src="https://avatars.githubusercontent.com/u/5437552?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Lucas Willems</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=lcswillems" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=lcswillems" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://alistair.cloud"><img src="https://avatars.githubusercontent.com/u/25351731?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Alistair Smith</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=alii" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://rodrigoehlers.com"><img src="https://avatars.githubusercontent.com/u/19683042?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Rodrigo Ehlers</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=rodrigoehlers" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://www.builtopen.com/"><img src="https://avatars.githubusercontent.com/u/1734057?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Michael Ford</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=mtford90" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://brianypliu.com"><img src="https://avatars.githubusercontent.com/u/3888780?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Brian Liu</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=LBrian" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="http://aleksandra.codes"><img src="https://avatars.githubusercontent.com/u/9019397?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Aleksandra Sikora</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=beerose" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://juanm04.com"><img src="https://avatars.githubusercontent.com/u/16712703?v=4?s=100" width="100px;" alt=""/><br /><sub><b>JuanM04</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=JuanM04" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=JuanM04" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=JuanM04" title="Tests">⚠️</a></td>
|
||||
<td align="center"><a href="https://github.com/arenddeboer"><img src="https://avatars.githubusercontent.com/u/7022204?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Arend de Boer</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=arenddeboer" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/fmilani"><img src="https://avatars.githubusercontent.com/u/1580375?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Felipe Milani</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=fmilani" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="http://nxhx.org"><img src="https://avatars.githubusercontent.com/u/13018?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Joe Edelman</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=jxe" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/garytube"><img src="https://avatars.githubusercontent.com/u/3823504?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Gary</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=garytube" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="http://oliverloops.com"><img src="https://avatars.githubusercontent.com/u/33361399?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Oliver Lopez </b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=oliverloops" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://decadentIpsum.me"><img src="https://avatars.githubusercontent.com/u/32861532?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Andreas Zaralis</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=DecadentIpsum" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/davetorbeck"><img src="https://avatars.githubusercontent.com/u/5829885?v=4?s=100" width="100px;" alt=""/><br /><sub><b>David Torbeck</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=davetorbeck" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/gusgard"><img src="https://avatars.githubusercontent.com/u/2577356?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Gustavo Gard</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=gusgard" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://narrationbox.com"><img src="https://avatars.githubusercontent.com/u/7126128?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Immortalin</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=Immortalin" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://cristianbgp.com"><img src="https://avatars.githubusercontent.com/u/8507974?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Cristian Granda</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=cristianbgp" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://deniseyu.io"><img src="https://avatars.githubusercontent.com/u/8420094?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Denise Yu</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=deniseyu" title="Code">💻</a></td>
|
||||
<td align="center"><a href="http://dellacorte.me"><img src="https://avatars.githubusercontent.com/u/295683?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Andrea Della Corte</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=andreadellacorte" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="http://aditsachde.com"><img src="https://avatars.githubusercontent.com/u/23707194?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Adit Sachde</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=aditsachde" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/hirenchauhan2"><img src="https://avatars.githubusercontent.com/u/8999668?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Hiren Chauhan</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=hirenchauhan2" title="Code">💻</a></td>
|
||||
<td align="center"><a href="http://remjx.com/"><img src="https://avatars.githubusercontent.com/u/35121685?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Mark Jackson</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=remjx" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=remjx" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://lewisb.cloud/"><img src="https://avatars.githubusercontent.com/u/51877955?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Lewis Blackburn</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=lewisblackburn" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/FDiskas"><img src="https://avatars.githubusercontent.com/u/468006?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Vytenis</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=FDiskas" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://portfolio.matthieupetit.com"><img src="https://avatars.githubusercontent.com/u/12969089?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Matthieu</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=matthieu994" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=matthieu994" title="Tests">⚠️</a></td>
|
||||
<td align="center"><a href="https://github.com/mitchazj"><img src="https://avatars.githubusercontent.com/u/15032956?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Mitchell Johnson</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=mitchazj" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://roshan.page/"><img src="https://avatars.githubusercontent.com/u/31125563?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Roshan Manuel</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=Roesh" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=Roesh" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=Roesh" title="Tests">⚠️</a></td>
|
||||
<td align="center"><a href="https://kevinlangleyjr.com"><img src="https://avatars.githubusercontent.com/u/877634?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Kevin Langley Jr.</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=kevinlangleyjr" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=kevinlangleyjr" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://projet-test-99df0.firebaseapp.com/"><img src="https://avatars.githubusercontent.com/u/51029779?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Gabriel Picard</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=heavygabriel" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="http://ryanchenkie.com/"><img src="https://avatars.githubusercontent.com/u/1847678?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ryan Chenkie</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=chenkie" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/sbappan"><img src="https://avatars.githubusercontent.com/u/12586088?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Santhosh B. Appan</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=sbappan" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="http://stackoverflow.com/users/5207233/james-moran"><img src="https://avatars.githubusercontent.com/u/10858584?v=4?s=100" width="100px;" alt=""/><br /><sub><b>James Moran</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=james2406" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=james2406" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="http://fb.me/yz"><img src="https://avatars.githubusercontent.com/u/14841421?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jack Zhao</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=bugzpodder" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/the-red"><img src="https://avatars.githubusercontent.com/u/4494300?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Hisaki Akaza</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=the-red" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="http://flavioander.com/"><img src="https://avatars.githubusercontent.com/u/14948074?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Flavio</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=Flavyoo" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://bhanuteja.dev/"><img src="https://avatars.githubusercontent.com/u/17903466?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Bhanu Teja Pachipulusu</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=pbteja1998" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://twitter.com/pavestru"><img src="https://avatars.githubusercontent.com/u/10186479?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Pavel Struhar</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=pavestru" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://in-thepink.com/"><img src="https://avatars.githubusercontent.com/u/42126368?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Reo Ishiyama</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=reo777" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://macwright.com/"><img src="https://avatars.githubusercontent.com/u/32314?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Tom MacWright</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=tmcw" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://francoisbest.com"><img src="https://avatars.githubusercontent.com/u/1174092?v=4?s=100" width="100px;" alt=""/><br /><sub><b>François Best</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=franky47" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/FarazPatankar"><img src="https://avatars.githubusercontent.com/u/10681116?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Faraz Patankar</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=FarazPatankar" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/ericvicenti"><img src="https://avatars.githubusercontent.com/u/1483597?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Eric Vicenti</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=ericvicenti" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/amdolan"><img src="https://avatars.githubusercontent.com/u/2552275?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Alex Dolan</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=amdolan" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=amdolan" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=amdolan" title="Tests">⚠️</a></td>
|
||||
<td align="center"><a href="https://github.com/Maastrich"><img src="https://avatars.githubusercontent.com/u/58431775?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Mathis Pinsault</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=Maastrich" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/gstranger"><img src="https://avatars.githubusercontent.com/u/36181416?v=4?s=100" width="100px;" alt=""/><br /><sub><b>gstranger</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=gstranger" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=gstranger" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="http://twitter.com/_markeh"><img src="https://avatars.githubusercontent.com/u/1357323?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Mark Hughes</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=markhughes" title="Code">💻</a></td>
|
||||
<td align="center"><a href="www.andrearizzello.work"><img src="https://avatars.githubusercontent.com/u/10348930?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Andrea Rizzello</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=andrearizzello" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="jahred.com.au"><img src="https://avatars.githubusercontent.com/u/13903378?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jahred Hope</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=jahredhope" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="simonelnahas.github.io/"><img src="https://avatars.githubusercontent.com/u/29279201?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Simon El Nahas</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=simonelnahas" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="www.usertrack.net"><img src="https://avatars.githubusercontent.com/u/1384885?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Buleandra Cristian</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=Cristy94" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="http://palauisaac.me/"><img src="https://avatars.githubusercontent.com/u/12257885?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Pedro Enrique Palau Isaac</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=peterpalau" title="Code">💻</a></td>
|
||||
<td align="center"><a href="www.seanbrydon.me"><img src="https://avatars.githubusercontent.com/u/55134778?v=4?s=100" width="100px;" alt=""/><br /><sub><b>sean-brydon</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=sean-brydon" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="buonerba.dev"><img src="https://avatars.githubusercontent.com/u/28837891?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Alessandro</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=Dieman89" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="www.jyutping.org"><img src="https://avatars.githubusercontent.com/u/11172180?v=4?s=100" width="100px;" alt=""/><br /><sub><b>laubonghaudoi</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=laubonghaudoi" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/TommasoBruno99"><img src="https://avatars.githubusercontent.com/u/61512591?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Tommaso Bruno</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=TommasoBruno99" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="antonykamp.de"><img src="https://avatars.githubusercontent.com/u/45163503?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Antony</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=antonykamp" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://blog.6nok.org"><img src="https://avatars.githubusercontent.com/u/868283?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Fatih Altinok</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=frontsideair" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://mokshitjain.co/"><img src="https://avatars.githubusercontent.com/u/50412128?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Mokshit Jain</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=Mokshit06" title="Code">💻</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
10
SECURITY.md
Normal file
10
SECURITY.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
TODO
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Email Brandon Bayer at b@bayer.ws to report a vulnerablity, and he will follow up with you asap.
|
||||
|
||||
24
assets/digsas.svg
Normal file
24
assets/digsas.svg
Normal file
@@ -0,0 +1,24 @@
|
||||
<svg width="128" height="128" viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0)">
|
||||
<path d="M79.4831 1.95572C70.2813 -0.81923 55.2811 -0.617415 46.1549 2.4098L18.6125 11.5167C9.48627 14.5313 9.38542 19.7784 18.3856 23.1588L49.9743 35.028C58.9744 38.4084 73.6217 38.194 82.5084 34.5487L110.883 22.9192C119.782 19.2739 119.53 14.0267 110.316 11.2518L79.4831 1.95572Z" fill="url(#paint0_linear)"/>
|
||||
<path d="M84.235 38.6101C75.3357 42.2554 68.0625 53.1029 68.0625 62.727V113.635C68.0625 123.259 74.9071 127.245 83.2644 122.489L109.282 107.706C117.639 102.951 124.912 91.208 125.442 81.6092L127.837 37.8281C128.366 28.2167 121.509 23.3479 112.609 26.9932L84.235 38.6101Z" fill="url(#paint1_linear)"/>
|
||||
<path d="M0.0071345 37.8409C-0.257575 28.2295 6.877 23.1211 15.8771 26.5015L47.4658 38.3707C56.466 41.7511 63.8274 52.3842 63.8274 62.0082V112.916C63.8274 122.54 56.882 126.715 48.386 122.212L17.0998 105.6C8.60392 101.085 1.44415 89.5306 1.16683 79.9192L0.0071345 37.8409Z" fill="url(#paint2_linear)"/>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear" x1="63.9326" y1="0" x2="63.9326" y2="124.497" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#036EB8"/>
|
||||
<stop offset="1" stop-color="#469196"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear" x1="63.9326" y1="0" x2="63.9326" y2="124.497" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#036EB8"/>
|
||||
<stop offset="1" stop-color="#469196"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint2_linear" x1="63.9326" y1="0" x2="63.9326" y2="124.497" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#036EB8"/>
|
||||
<stop offset="1" stop-color="#469196"/>
|
||||
</linearGradient>
|
||||
<clipPath id="clip0">
|
||||
<rect width="128" height="128" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
1
assets/reactbricks_icon.svg
Normal file
1
assets/reactbricks_icon.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 980 979.97"><path d="M139.38,0c-185.82-3.54-185.82,277,0,273.46H251.44C437.25,277,437.25-3.5,251.44,0ZM729.25,353.24c-184.92-2.62-184.92,276.1,0,273.48H841.31c184.92,2.62,184.92-276.1,0-273.48ZM138.69,706.46c-184.92-2.61-184.92,276.1,0,273.49H250.75c184.91,2.61,184.91-276.1,0-273.49Z" fill="#b43278"/><path d="M583.47,0c-185.82-3.54-185.82,277,0,273.46H840.61c185.81,3.54,185.81-277,0-273.46ZM138.7,353.24c-184.92-2.62-184.92,276.1,0,273.48H395.85c184.92,2.62,184.92-276.1,0-273.48ZM584.13,706.46c-184.91-2.61-184.91,276.1,0,273.49H841.3c184.92,2.61,184.92-276.1,0-273.49Z" fill="#f65a8e"/></svg>
|
||||
|
After Width: | Height: | Size: 650 B |
BIN
assets/rit_logo.png
Normal file
BIN
assets/rit_logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.8 KiB |
BIN
assets/rob_blitz.jpg
Normal file
BIN
assets/rob_blitz.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.8 MiB |
64
babel.config.js
Normal file
64
babel.config.js
Normal file
@@ -0,0 +1,64 @@
|
||||
module.exports = {
|
||||
presets: [
|
||||
"@babel/preset-typescript",
|
||||
"@babel/preset-react",
|
||||
[
|
||||
"@babel/preset-env",
|
||||
{
|
||||
modules: false,
|
||||
loose: true,
|
||||
exclude: [
|
||||
"@babel/plugin-transform-async-to-generator",
|
||||
"@babel/plugin-transform-regenerator",
|
||||
],
|
||||
},
|
||||
],
|
||||
],
|
||||
plugins: [
|
||||
"babel-plugin-annotate-pure-calls",
|
||||
"babel-plugin-dev-expression",
|
||||
["@babel/plugin-proposal-class-properties", {loose: true}],
|
||||
"babel-plugin-macros",
|
||||
[
|
||||
"transform-inline-environment-variables",
|
||||
{
|
||||
include: ["BLITZ_PROD_BUILD"],
|
||||
},
|
||||
],
|
||||
],
|
||||
overrides: [
|
||||
{
|
||||
test: "./test/**/*",
|
||||
presets: [
|
||||
[
|
||||
"@babel/preset-env",
|
||||
{
|
||||
modules: false,
|
||||
exclude: [
|
||||
"@babel/plugin-transform-async-to-generator",
|
||||
"@babel/plugin-transform-regenerator",
|
||||
],
|
||||
},
|
||||
],
|
||||
"blitz/babel",
|
||||
],
|
||||
plugins: [],
|
||||
},
|
||||
{
|
||||
test: "./nextjs/test/**/*",
|
||||
presets: [
|
||||
[
|
||||
"@babel/preset-env",
|
||||
{
|
||||
modules: false,
|
||||
exclude: [
|
||||
"@babel/plugin-transform-async-to-generator",
|
||||
"@babel/plugin-transform-regenerator",
|
||||
],
|
||||
},
|
||||
],
|
||||
"blitz/babel",
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
@@ -14,13 +14,13 @@ model Project {
|
||||
2. DB migrate
|
||||
|
||||
```
|
||||
blitz prisma migrate dev --preview-feature
|
||||
blitz prisma migrate dev
|
||||
```
|
||||
|
||||
3. Start the dev server
|
||||
|
||||
```
|
||||
blitz start
|
||||
blitz dev
|
||||
```
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
|
||||
@@ -3,6 +3,7 @@ import db from "db"
|
||||
import {Strategy as TwitterStrategy} from "passport-twitter"
|
||||
import {Strategy as GitHubStrategy} from "passport-github2"
|
||||
import {Strategy as Auth0Strategy} from "passport-auth0"
|
||||
import {Role} from "types"
|
||||
|
||||
function assert(condition: any, message: string): asserts condition {
|
||||
if (!condition) throw new Error(message)
|
||||
@@ -21,14 +22,14 @@ assert(process.env.AUTH0_DOMAIN, "You must provide the AUTH0_DOMAIN env variable
|
||||
assert(process.env.AUTH0_CLIENT_ID, "You must provide the AUTH0_CLIENT_ID env variable")
|
||||
assert(process.env.AUTH0_CLIENT_SECRET, "You must provide the AUTH0_CLIENT_SECRET env variable")
|
||||
|
||||
export default passportAuth({
|
||||
export default passportAuth((ctx) => ({
|
||||
successRedirectUrl: "/",
|
||||
strategies: [
|
||||
{
|
||||
strategy: new TwitterStrategy(
|
||||
{
|
||||
consumerKey: process.env.TWITTER_CONSUMER_KEY,
|
||||
consumerSecret: process.env.TWITTER_CONSUMER_SECRET,
|
||||
consumerKey: process.env.TWITTER_CONSUMER_KEY as string,
|
||||
consumerSecret: process.env.TWITTER_CONSUMER_SECRET as string,
|
||||
callbackURL:
|
||||
process.env.NODE_ENV === "production"
|
||||
? "https://auth-example-flybayer.blitzjs.vercel.app/api/auth/twitter/callback"
|
||||
@@ -43,6 +44,8 @@ export default passportAuth({
|
||||
return done(new Error("Twitter OAuth response doesn't have email."))
|
||||
}
|
||||
|
||||
console.log(ctx.session.userId)
|
||||
|
||||
const user = await db.user.upsert({
|
||||
where: {email},
|
||||
create: {
|
||||
@@ -52,7 +55,7 @@ export default passportAuth({
|
||||
update: {email},
|
||||
})
|
||||
|
||||
const publicData = {userId: user.id, roles: [user.role], source: "twitter"}
|
||||
const publicData = {userId: user.id, role: user.role, source: "twitter"}
|
||||
done(null, {publicData})
|
||||
},
|
||||
),
|
||||
@@ -60,8 +63,8 @@ export default passportAuth({
|
||||
{
|
||||
strategy: new GitHubStrategy(
|
||||
{
|
||||
clientID: process.env.GITHUB_CLIENT_ID,
|
||||
clientSecret: process.env.GITHUB_CLIENT_SECRET,
|
||||
clientID: process.env.GITHUB_CLIENT_ID as string,
|
||||
clientSecret: process.env.GITHUB_CLIENT_SECRET as string,
|
||||
callbackURL:
|
||||
process.env.NODE_ENV === "production"
|
||||
? "https://auth-example-flybayer.blitzjs.vercel.app/api/auth/github/callback"
|
||||
@@ -91,7 +94,7 @@ export default passportAuth({
|
||||
|
||||
const publicData = {
|
||||
userId: user.id,
|
||||
roles: [user.role],
|
||||
role: user.role as Role,
|
||||
source: "github",
|
||||
githubUsername: profile.username,
|
||||
}
|
||||
@@ -103,9 +106,9 @@ export default passportAuth({
|
||||
authenticateOptions: {scope: "openid email profile"},
|
||||
strategy: new Auth0Strategy(
|
||||
{
|
||||
domain: process.env.AUTH0_DOMAIN,
|
||||
clientID: process.env.AUTH0_CLIENT_ID,
|
||||
clientSecret: process.env.AUTH0_CLIENT_SECRET,
|
||||
domain: process.env.AUTH0_DOMAIN as string,
|
||||
clientID: process.env.AUTH0_CLIENT_ID as string,
|
||||
clientSecret: process.env.AUTH0_CLIENT_SECRET as string,
|
||||
callbackURL:
|
||||
process.env.NODE_ENV === "production"
|
||||
? "https://auth-example-flybayer.blitzjs.vercel.app/api/auth/auth0/callback"
|
||||
@@ -130,7 +133,7 @@ export default passportAuth({
|
||||
|
||||
const publicData = {
|
||||
userId: user.id,
|
||||
roles: [user.role],
|
||||
role: user.role,
|
||||
source: "auth0",
|
||||
githubUsername: profile.username,
|
||||
}
|
||||
@@ -139,4 +142,4 @@ export default passportAuth({
|
||||
),
|
||||
},
|
||||
],
|
||||
})
|
||||
}))
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import {Link, useMutation, AuthenticationError} from "blitz"
|
||||
import {LabeledTextField} from "app/components/LabeledTextField"
|
||||
import {Form, FORM_ERROR} from "app/components/Form"
|
||||
import login, {LoginInput} from "app/auth/mutations/login"
|
||||
import {AuthenticationError, Link, useMutation} from "blitz"
|
||||
import {LabeledTextField} from "app/core/components/LabeledTextField"
|
||||
import {Form, FORM_ERROR} from "app/core/components/Form"
|
||||
import login from "app/auth/mutations/login"
|
||||
import {Login} from "app/auth/validations"
|
||||
|
||||
import {Routes} from ".blitz"
|
||||
|
||||
type LoginFormProps = {
|
||||
onSuccess?: () => void
|
||||
@@ -9,17 +12,19 @@ type LoginFormProps = {
|
||||
|
||||
export const LoginForm = (props: LoginFormProps) => {
|
||||
const [loginMutation] = useMutation(login)
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>Login</h1>
|
||||
|
||||
<Form
|
||||
submitText="Login"
|
||||
schema={LoginInput}
|
||||
initialValues={{email: undefined, password: undefined}}
|
||||
schema={Login}
|
||||
initialValues={{email: "", password: ""}}
|
||||
onSubmit={async (values) => {
|
||||
try {
|
||||
await loginMutation(values)
|
||||
props.onSuccess && props.onSuccess()
|
||||
props.onSuccess?.()
|
||||
} catch (error) {
|
||||
if (error instanceof AuthenticationError) {
|
||||
return {[FORM_ERROR]: "Sorry, those credentials are invalid"}
|
||||
@@ -34,9 +39,15 @@ export const LoginForm = (props: LoginFormProps) => {
|
||||
>
|
||||
<LabeledTextField name="email" label="Email" placeholder="Email" />
|
||||
<LabeledTextField name="password" label="Password" placeholder="Password" type="password" />
|
||||
<div>
|
||||
<Link href={Routes.ForgotPasswordPage()}>
|
||||
<a>Forgot your password?</a>
|
||||
</Link>
|
||||
</div>
|
||||
</Form>
|
||||
|
||||
<div style={{marginTop: "1rem"}}>
|
||||
Or <Link href="/signup">Sign Up</Link>
|
||||
Or <Link href={Routes.SignupPage()}>Sign Up</Link>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
44
examples/auth/app/auth/components/SignupForm.tsx
Normal file
44
examples/auth/app/auth/components/SignupForm.tsx
Normal file
@@ -0,0 +1,44 @@
|
||||
import React from "react"
|
||||
import {useMutation} from "blitz"
|
||||
import {LabeledTextField} from "app/core/components/LabeledTextField"
|
||||
import {Form, FORM_ERROR} from "app/core/components/Form"
|
||||
import signup from "app/auth/mutations/signup"
|
||||
import {Signup} from "app/auth/validations"
|
||||
|
||||
type SignupFormProps = {
|
||||
onSuccess?: () => void
|
||||
}
|
||||
|
||||
export const SignupForm = (props: SignupFormProps) => {
|
||||
const [signupMutation] = useMutation(signup)
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>Create an Account</h1>
|
||||
|
||||
<Form
|
||||
submitText="Create Account"
|
||||
schema={Signup}
|
||||
initialValues={{email: "", password: ""}}
|
||||
onSubmit={async (values) => {
|
||||
try {
|
||||
await signupMutation(values)
|
||||
props.onSuccess?.()
|
||||
} catch (error) {
|
||||
if (error.code === "P2002" && error.meta?.target?.includes("email")) {
|
||||
// This error comes from Prisma
|
||||
return {email: "This email is already being used"}
|
||||
} else {
|
||||
return {[FORM_ERROR]: error.toString()}
|
||||
}
|
||||
}
|
||||
}}
|
||||
>
|
||||
<LabeledTextField name="email" label="Email" placeholder="Email" />
|
||||
<LabeledTextField name="password" label="Password" placeholder="Password" type="password" />
|
||||
</Form>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default SignupForm
|
||||
23
examples/auth/app/auth/mutations/changePassword.ts
Normal file
23
examples/auth/app/auth/mutations/changePassword.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import {NotFoundError, SecurePassword, resolver} from "blitz"
|
||||
import db from "db"
|
||||
import {authenticateUser} from "./login"
|
||||
import {ChangePassword} from "../validations"
|
||||
|
||||
export default resolver.pipe(
|
||||
resolver.zod(ChangePassword),
|
||||
resolver.authorize(),
|
||||
async ({currentPassword, newPassword}, ctx) => {
|
||||
const user = await db.user.findFirst({where: {id: ctx.session.userId!}})
|
||||
if (!user) throw new NotFoundError()
|
||||
|
||||
await authenticateUser(user.email, currentPassword)
|
||||
|
||||
const hashedPassword = await SecurePassword.hash(newPassword)
|
||||
await db.user.update({
|
||||
where: {id: user.id},
|
||||
data: {hashedPassword},
|
||||
})
|
||||
|
||||
return true
|
||||
},
|
||||
)
|
||||
56
examples/auth/app/auth/mutations/forgotPassword.test.ts
Normal file
56
examples/auth/app/auth/mutations/forgotPassword.test.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import {hash256, Ctx} from "blitz"
|
||||
import forgotPassword from "./forgotPassword"
|
||||
import db from "db"
|
||||
import previewEmail from "preview-email"
|
||||
|
||||
beforeEach(async () => {
|
||||
await db.$reset()
|
||||
})
|
||||
|
||||
const generatedToken = "plain-token"
|
||||
jest.mock("@blitzjs/core/server", () => ({
|
||||
...jest.requireActual<object>("@blitzjs/core/server")!,
|
||||
generateToken: () => generatedToken,
|
||||
}))
|
||||
jest.mock("preview-email", () => jest.fn())
|
||||
|
||||
describe("forgotPassword mutation", () => {
|
||||
it("does not throw error if user doesn't exist", async () => {
|
||||
await expect(forgotPassword({email: "no-user@email.com"}, {} as Ctx)).resolves.not.toThrow()
|
||||
})
|
||||
|
||||
it("works correctly", async () => {
|
||||
// Create test user
|
||||
const user = await db.user.create({
|
||||
data: {
|
||||
email: "user@example.com",
|
||||
tokens: {
|
||||
// Create old token to ensure it's deleted
|
||||
create: {
|
||||
type: "RESET_PASSWORD",
|
||||
hashedToken: "token",
|
||||
expiresAt: new Date(),
|
||||
sentTo: "user@example.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
include: {tokens: true},
|
||||
})
|
||||
|
||||
// Invoke the mutation
|
||||
await forgotPassword({email: user.email}, {} as Ctx)
|
||||
|
||||
const tokens = await db.token.findMany({where: {userId: user.id}})
|
||||
const token = tokens[0]
|
||||
|
||||
// delete's existing tokens
|
||||
expect(tokens.length).toBe(1)
|
||||
|
||||
expect(token.id).not.toBe(user.tokens[0].id)
|
||||
expect(token.type).toBe("RESET_PASSWORD")
|
||||
expect(token.sentTo).toBe(user.email)
|
||||
expect(token.hashedToken).toBe(hash256(generatedToken))
|
||||
expect(token.expiresAt > new Date()).toBe(true)
|
||||
expect(previewEmail).toBeCalled()
|
||||
})
|
||||
})
|
||||
41
examples/auth/app/auth/mutations/forgotPassword.ts
Normal file
41
examples/auth/app/auth/mutations/forgotPassword.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import {resolver, generateToken, hash256} from "blitz"
|
||||
import db from "db"
|
||||
import {forgotPasswordMailer} from "mailers/forgotPasswordMailer"
|
||||
import {ForgotPassword} from "../validations"
|
||||
|
||||
const RESET_PASSWORD_TOKEN_EXPIRATION_IN_HOURS = 4
|
||||
|
||||
export default resolver.pipe(resolver.zod(ForgotPassword), async ({email}) => {
|
||||
// 1. Get the user
|
||||
const user = await db.user.findFirst({where: {email: email.toLowerCase()}})
|
||||
|
||||
// 2. Generate the token and expiration date.
|
||||
const token = generateToken()
|
||||
const hashedToken = hash256(token)
|
||||
const expiresAt = new Date()
|
||||
expiresAt.setHours(expiresAt.getHours() + RESET_PASSWORD_TOKEN_EXPIRATION_IN_HOURS)
|
||||
|
||||
// 3. If user with this email was found
|
||||
if (user) {
|
||||
// 4. Delete any existing password reset tokens
|
||||
await db.token.deleteMany({where: {type: "RESET_PASSWORD", userId: user.id}})
|
||||
// 5. Save this new token in the database.
|
||||
await db.token.create({
|
||||
data: {
|
||||
user: {connect: {id: user.id}},
|
||||
type: "RESET_PASSWORD",
|
||||
expiresAt,
|
||||
hashedToken,
|
||||
sentTo: user.email,
|
||||
},
|
||||
})
|
||||
// 6. Send the email
|
||||
await forgotPasswordMailer({to: user.email, token}).send()
|
||||
} else {
|
||||
// 7. If no user found wait the same time so attackers can't tell the difference
|
||||
await new Promise((resolve) => setTimeout(resolve, 750))
|
||||
}
|
||||
|
||||
// 8. Return the same result whether a password reset email was sent or not
|
||||
return
|
||||
})
|
||||
@@ -1,6 +1,7 @@
|
||||
import {resolver, SecurePassword, AuthenticationError} from "blitz"
|
||||
import db from "db"
|
||||
import * as z from "zod"
|
||||
import {Login} from "../validations"
|
||||
import {Role} from "types"
|
||||
|
||||
export const authenticateUser = async (email: string, password: string) => {
|
||||
const user = await db.user.findFirst({where: {email}})
|
||||
@@ -18,16 +19,11 @@ export const authenticateUser = async (email: string, password: string) => {
|
||||
return rest
|
||||
}
|
||||
|
||||
export const LoginInput = z.object({
|
||||
email: z.string().email(),
|
||||
password: z.string(),
|
||||
})
|
||||
|
||||
export default resolver.pipe(resolver.zod(LoginInput), async ({email, password}, {session}) => {
|
||||
export default resolver.pipe(resolver.zod(Login), async ({email, password}, ctx) => {
|
||||
// This throws an error if credentials are invalid
|
||||
const user = await authenticateUser(email, password)
|
||||
|
||||
await session.$create({userId: user.id, roles: [user.role]})
|
||||
await ctx.session.$create({userId: user.id, role: user.role as Role})
|
||||
|
||||
return user
|
||||
})
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {Ctx} from "blitz"
|
||||
|
||||
export default async function logout(_: any, {session}: Ctx) {
|
||||
return await session.$revoke()
|
||||
export default async function logout(_: any, ctx: Ctx) {
|
||||
return await ctx.session.$revoke()
|
||||
}
|
||||
|
||||
82
examples/auth/app/auth/mutations/resetPassword.test.ts
Normal file
82
examples/auth/app/auth/mutations/resetPassword.test.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
import resetPassword from "./resetPassword"
|
||||
import db from "db"
|
||||
import {hash256, SecurePassword} from "blitz"
|
||||
|
||||
beforeEach(async () => {
|
||||
await db.$reset()
|
||||
})
|
||||
|
||||
const mockCtx: any = {
|
||||
session: {
|
||||
$create: jest.fn,
|
||||
},
|
||||
}
|
||||
|
||||
describe("resetPassword mutation", () => {
|
||||
it("works correctly", async () => {
|
||||
expect(true).toBe(true)
|
||||
|
||||
// Create test user
|
||||
const goodToken = "randomPasswordResetToken"
|
||||
const expiredToken = "expiredRandomPasswordResetToken"
|
||||
const future = new Date()
|
||||
future.setHours(future.getHours() + 4)
|
||||
const past = new Date()
|
||||
past.setHours(past.getHours() - 4)
|
||||
|
||||
const user = await db.user.create({
|
||||
data: {
|
||||
email: "user@example.com",
|
||||
tokens: {
|
||||
// Create old token to ensure it's deleted
|
||||
create: [
|
||||
{
|
||||
type: "RESET_PASSWORD",
|
||||
hashedToken: hash256(expiredToken),
|
||||
expiresAt: past,
|
||||
sentTo: "user@example.com",
|
||||
},
|
||||
{
|
||||
type: "RESET_PASSWORD",
|
||||
hashedToken: hash256(goodToken),
|
||||
expiresAt: future,
|
||||
sentTo: "user@example.com",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
include: {tokens: true},
|
||||
})
|
||||
|
||||
const newPassword = "newPassword"
|
||||
|
||||
// Non-existent token
|
||||
await expect(
|
||||
resetPassword({token: "no-token", password: "", passwordConfirmation: ""}, mockCtx),
|
||||
).rejects.toThrowError()
|
||||
|
||||
// Expired token
|
||||
await expect(
|
||||
resetPassword(
|
||||
{token: expiredToken, password: newPassword, passwordConfirmation: newPassword},
|
||||
mockCtx,
|
||||
),
|
||||
).rejects.toThrowError()
|
||||
|
||||
// Good token
|
||||
await resetPassword(
|
||||
{token: goodToken, password: newPassword, passwordConfirmation: newPassword},
|
||||
mockCtx,
|
||||
)
|
||||
|
||||
// Delete's the token
|
||||
const numberOfTokens = await db.token.count({where: {userId: user.id}})
|
||||
expect(numberOfTokens).toBe(0)
|
||||
|
||||
// Updates user's password
|
||||
const updatedUser = await db.user.findFirst({where: {id: user.id}})
|
||||
expect(await SecurePassword.verify(updatedUser!.hashedPassword, newPassword)).toBe(
|
||||
SecurePassword.VALID,
|
||||
)
|
||||
})
|
||||
})
|
||||
47
examples/auth/app/auth/mutations/resetPassword.ts
Normal file
47
examples/auth/app/auth/mutations/resetPassword.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import {resolver, SecurePassword, hash256} from "blitz"
|
||||
import db from "db"
|
||||
import {ResetPassword} from "../validations"
|
||||
import login from "./login"
|
||||
|
||||
export class ResetPasswordError extends Error {
|
||||
name = "ResetPasswordError"
|
||||
message = "Reset password link is invalid or it has expired."
|
||||
}
|
||||
|
||||
export default resolver.pipe(resolver.zod(ResetPassword), async ({password, token}, ctx) => {
|
||||
// 1. Try to find this token in the database
|
||||
const hashedToken = hash256(token)
|
||||
const possibleToken = await db.token.findFirst({
|
||||
where: {hashedToken, type: "RESET_PASSWORD"},
|
||||
include: {user: true},
|
||||
})
|
||||
|
||||
// 2. If token not found, error
|
||||
if (!possibleToken) {
|
||||
throw new ResetPasswordError()
|
||||
}
|
||||
const savedToken = possibleToken
|
||||
|
||||
// 3. Delete token so it can't be used again
|
||||
await db.token.delete({where: {id: savedToken.id}})
|
||||
|
||||
// 4. If token has expired, error
|
||||
if (savedToken.expiresAt < new Date()) {
|
||||
throw new ResetPasswordError()
|
||||
}
|
||||
|
||||
// 5. Since token is valid, now we can update the user's password
|
||||
const hashedPassword = await SecurePassword.hash(password)
|
||||
const user = await db.user.update({
|
||||
where: {id: savedToken.userId},
|
||||
data: {hashedPassword},
|
||||
})
|
||||
|
||||
// 6. Revoke all existing login sessions for this user
|
||||
await db.session.deleteMany({where: {userId: user.id}})
|
||||
|
||||
// 7. Now log the user in with the new credentials
|
||||
await login({email: user.email, password}, ctx)
|
||||
|
||||
return true
|
||||
})
|
||||
@@ -1,20 +1,15 @@
|
||||
import {resolver, SecurePassword} from "blitz"
|
||||
import db from "db"
|
||||
import * as z from "zod"
|
||||
import {Signup} from "app/auth/validations"
|
||||
import {Role} from "types"
|
||||
|
||||
export const SignupInput = z.object({
|
||||
email: z.string().email(),
|
||||
password: z.string().min(10).max(100),
|
||||
})
|
||||
|
||||
export default resolver.pipe(resolver.zod(SignupInput), async ({email, password}, {session}) => {
|
||||
export default resolver.pipe(resolver.zod(Signup), async ({email, password}, ctx) => {
|
||||
const hashedPassword = await SecurePassword.hash(password)
|
||||
const user = await db.user.create({
|
||||
data: {email, hashedPassword, role: "user"},
|
||||
data: {email: email.toLowerCase(), hashedPassword, role: "user"},
|
||||
select: {id: true, name: true, email: true, role: true},
|
||||
})
|
||||
|
||||
await session.$create({userId: user.id, roles: [user.role]})
|
||||
|
||||
await ctx.session.$create({userId: user.id, role: user.role as Role})
|
||||
return user
|
||||
})
|
||||
|
||||
47
examples/auth/app/auth/pages/forgot-password.tsx
Normal file
47
examples/auth/app/auth/pages/forgot-password.tsx
Normal file
@@ -0,0 +1,47 @@
|
||||
import {BlitzPage, useMutation} from "blitz"
|
||||
import Layout from "app/core/layouts/Layout"
|
||||
import {LabeledTextField} from "app/core/components/LabeledTextField"
|
||||
import {Form, FORM_ERROR} from "app/core/components/Form"
|
||||
import {ForgotPassword} from "app/auth/validations"
|
||||
import forgotPassword from "app/auth/mutations/forgotPassword"
|
||||
|
||||
const ForgotPasswordPage: BlitzPage = () => {
|
||||
const [forgotPasswordMutation, {isSuccess}] = useMutation(forgotPassword)
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>Forgot your password?</h1>
|
||||
|
||||
{isSuccess ? (
|
||||
<div>
|
||||
<h2>Request Submitted</h2>
|
||||
<p>
|
||||
If your email is in our system, you will receive instructions to reset your password
|
||||
shortly.
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<Form
|
||||
submitText="Send Reset Password Instructions"
|
||||
schema={ForgotPassword}
|
||||
initialValues={{email: ""}}
|
||||
onSubmit={async (values) => {
|
||||
try {
|
||||
await forgotPasswordMutation(values)
|
||||
} catch (error) {
|
||||
return {
|
||||
[FORM_ERROR]: "Sorry, we had an unexpected error. Please try again.",
|
||||
}
|
||||
}
|
||||
}}
|
||||
>
|
||||
<LabeledTextField name="email" label="Email" placeholder="Email" />
|
||||
</Form>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
ForgotPasswordPage.getLayout = (page) => <Layout title="Forgot Your Password?">{page}</Layout>
|
||||
|
||||
export default ForgotPasswordPage
|
||||
@@ -1,21 +1,25 @@
|
||||
import {Head, useRouter, BlitzPage} from "blitz"
|
||||
import React from "react"
|
||||
import {useRouter, BlitzPage} from "blitz"
|
||||
import Layout from "app/core/layouts/Layout"
|
||||
import {LoginForm} from "app/auth/components/LoginForm"
|
||||
import {Routes} from ".blitz"
|
||||
|
||||
const SignupPage: BlitzPage = () => {
|
||||
const LoginPage: BlitzPage = () => {
|
||||
const router = useRouter()
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>Login</title>
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
</Head>
|
||||
|
||||
<div>
|
||||
<LoginForm onSuccess={() => router.push("/")} />
|
||||
</div>
|
||||
</>
|
||||
<div>
|
||||
<LoginForm
|
||||
onSuccess={() => {
|
||||
const next = router.query.next ? decodeURIComponent(router.query.next as string) : "/"
|
||||
router.push(next)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default SignupPage
|
||||
LoginPage.redirectAuthenticatedTo = Routes.Home().pathname
|
||||
LoginPage.getLayout = (page) => <Layout title="Log In">{page}</Layout>
|
||||
|
||||
export default LoginPage
|
||||
|
||||
60
examples/auth/app/auth/pages/reset-password.tsx
Normal file
60
examples/auth/app/auth/pages/reset-password.tsx
Normal file
@@ -0,0 +1,60 @@
|
||||
import {BlitzPage, useRouterQuery, Link, useMutation} from "blitz"
|
||||
import Layout from "app/core/layouts/Layout"
|
||||
import {LabeledTextField} from "app/core/components/LabeledTextField"
|
||||
import {Form, FORM_ERROR} from "app/core/components/Form"
|
||||
import {ResetPassword} from "app/auth/validations"
|
||||
import resetPassword from "app/auth/mutations/resetPassword"
|
||||
|
||||
import {Routes} from ".blitz"
|
||||
|
||||
const ResetPasswordPage: BlitzPage = () => {
|
||||
const query = useRouterQuery()
|
||||
const [resetPasswordMutation, {isSuccess}] = useMutation(resetPassword)
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>Set a New Password</h1>
|
||||
|
||||
{isSuccess ? (
|
||||
<div>
|
||||
<h2>Password Reset Successfully</h2>
|
||||
<p>
|
||||
Go to the <Link href={Routes.Home()}>homepage</Link>
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<Form
|
||||
submitText="Reset Password"
|
||||
schema={ResetPassword}
|
||||
initialValues={{password: "", passwordConfirmation: "", token: query.token as string}}
|
||||
onSubmit={async (values) => {
|
||||
try {
|
||||
await resetPasswordMutation(values)
|
||||
} catch (error) {
|
||||
if (error.name === "ResetPasswordError") {
|
||||
return {
|
||||
[FORM_ERROR]: error.message,
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
[FORM_ERROR]: "Sorry, we had an unexpected error. Please try again.",
|
||||
}
|
||||
}
|
||||
}
|
||||
}}
|
||||
>
|
||||
<LabeledTextField name="password" label="New Password" type="password" />
|
||||
<LabeledTextField
|
||||
name="passwordConfirmation"
|
||||
label="Confirm New Password"
|
||||
type="password"
|
||||
/>
|
||||
</Form>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
ResetPasswordPage.getLayout = (page) => <Layout title="Reset Your Password">{page}</Layout>
|
||||
|
||||
export default ResetPasswordPage
|
||||
@@ -1,53 +1,17 @@
|
||||
import {Head, useRouter, BlitzPage, useMutation} from "blitz"
|
||||
import {Form, FORM_ERROR} from "app/components/Form"
|
||||
import {LabeledTextField} from "app/components/LabeledTextField"
|
||||
import signup, {SignupInput} from "app/auth/mutations/signup"
|
||||
import {useRouter, BlitzPage} from "blitz"
|
||||
import Layout from "app/core/layouts/Layout"
|
||||
import {SignupForm} from "app/auth/components/SignupForm"
|
||||
|
||||
const SignupPage: BlitzPage = () => {
|
||||
const router = useRouter()
|
||||
const [signupMutation] = useMutation(signup)
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>Sign Up</title>
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
</Head>
|
||||
|
||||
<div>
|
||||
<h1>Create an Account</h1>
|
||||
|
||||
<Form
|
||||
submitText="Create Account"
|
||||
schema={SignupInput}
|
||||
onSubmit={async (values) => {
|
||||
try {
|
||||
await signupMutation(values)
|
||||
router.push("/")
|
||||
} catch (error) {
|
||||
if (error.code === "P2002" && error.meta?.target?.includes("email")) {
|
||||
// This error comes from Prisma
|
||||
return {email: "This email is already being used"}
|
||||
} else {
|
||||
return {
|
||||
[FORM_ERROR]:
|
||||
"Sorry, we had an unexpected error. Please try again. - " + error.toString(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}}
|
||||
>
|
||||
<LabeledTextField name="email" label="Email" placeholder="Email" />
|
||||
<LabeledTextField
|
||||
name="password"
|
||||
label="Password"
|
||||
placeholder="Password"
|
||||
type="password"
|
||||
/>
|
||||
</Form>
|
||||
</div>
|
||||
</>
|
||||
<div>
|
||||
<SignupForm onSuccess={() => router.push("/")} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
SignupPage.getLayout = (page) => <Layout title="Sign Up">{page}</Layout>
|
||||
|
||||
export default SignupPage
|
||||
|
||||
33
examples/auth/app/auth/validations.ts
Normal file
33
examples/auth/app/auth/validations.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import * as z from "zod"
|
||||
|
||||
const password = z.string().min(10).max(100)
|
||||
|
||||
export const Signup = z.object({
|
||||
email: z.string().email(),
|
||||
password,
|
||||
})
|
||||
|
||||
export const Login = z.object({
|
||||
email: z.string().email(),
|
||||
password: z.string(),
|
||||
})
|
||||
|
||||
export const ForgotPassword = z.object({
|
||||
email: z.string().email(),
|
||||
})
|
||||
|
||||
export const ResetPassword = z
|
||||
.object({
|
||||
password: password,
|
||||
passwordConfirmation: password,
|
||||
token: z.string(),
|
||||
})
|
||||
.refine((data) => data.password === data.passwordConfirmation, {
|
||||
message: "Passwords don't match",
|
||||
path: ["passwordConfirmation"], // set the path of the error
|
||||
})
|
||||
|
||||
export const ChangePassword = z.object({
|
||||
currentPassword: z.string(),
|
||||
newPassword: password,
|
||||
})
|
||||
@@ -1,17 +1,18 @@
|
||||
import {ReactNode, PropsWithoutRef} from "react"
|
||||
import React, {ReactNode, PropsWithoutRef} from "react"
|
||||
import {Form as FinalForm, FormProps as FinalFormProps} from "react-final-form"
|
||||
import * as z from "zod"
|
||||
export {FORM_ERROR} from "final-form"
|
||||
|
||||
type FormProps<S extends z.ZodType<any, any>> = {
|
||||
export interface FormProps<S extends z.ZodType<any, any>>
|
||||
extends Omit<PropsWithoutRef<JSX.IntrinsicElements["form"]>, "onSubmit"> {
|
||||
/** All your form fields */
|
||||
children: ReactNode
|
||||
children?: ReactNode
|
||||
/** Text to display in the submit button */
|
||||
submitText?: string
|
||||
schema?: S
|
||||
onSubmit: FinalFormProps<z.infer<S>>["onSubmit"]
|
||||
initialValues?: FinalFormProps<z.infer<S>>["initialValues"]
|
||||
schema?: S
|
||||
} & Omit<PropsWithoutRef<JSX.IntrinsicElements["form"]>, "onSubmit">
|
||||
}
|
||||
|
||||
export function Form<S extends z.ZodType<any, any>>({
|
||||
children,
|
||||
@@ -1,4 +1,4 @@
|
||||
import {forwardRef, PropsWithoutRef} from "react"
|
||||
import React, {PropsWithoutRef} from "react"
|
||||
import {useField} from "react-final-form"
|
||||
|
||||
export interface LabeledTextFieldProps extends PropsWithoutRef<JSX.IntrinsicElements["input"]> {
|
||||
@@ -11,12 +11,16 @@ export interface LabeledTextFieldProps extends PropsWithoutRef<JSX.IntrinsicElem
|
||||
outerProps?: PropsWithoutRef<JSX.IntrinsicElements["div"]>
|
||||
}
|
||||
|
||||
export const LabeledTextField = forwardRef<HTMLInputElement, LabeledTextFieldProps>(
|
||||
export const LabeledTextField = React.forwardRef<HTMLInputElement, LabeledTextFieldProps>(
|
||||
({name, label, outerProps, ...props}, ref) => {
|
||||
const {
|
||||
input,
|
||||
meta: {touched, error, submitError, submitting},
|
||||
} = useField(name)
|
||||
} = useField(name, {
|
||||
parse: props.type === "number" ? Number : undefined,
|
||||
})
|
||||
|
||||
const normalizedError = Array.isArray(error) ? error.join(", ") : error || submitError
|
||||
|
||||
return (
|
||||
<div {...outerProps}>
|
||||
@@ -25,9 +29,9 @@ export const LabeledTextField = forwardRef<HTMLInputElement, LabeledTextFieldPro
|
||||
<input {...input} disabled={submitting} {...props} ref={ref} />
|
||||
</label>
|
||||
|
||||
{touched && (error || submitError) && (
|
||||
{touched && normalizedError && (
|
||||
<div role="alert" style={{color: "red"}}>
|
||||
{error || submitError}
|
||||
{normalizedError}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -2,7 +2,7 @@ import {useSession, useRouter, useMutation, Head} from "blitz"
|
||||
import logout from "app/auth/mutations/logout"
|
||||
|
||||
export default function Layout({title, children}: {title?: string; children: React.ReactNode}) {
|
||||
const session = useSession()
|
||||
const session = useSession({suspense: false})
|
||||
const router = useRouter()
|
||||
const [logoutMutation] = useMutation(logout)
|
||||
return (
|
||||
|
||||
@@ -5,29 +5,28 @@ import {
|
||||
AuthenticationError,
|
||||
AuthorizationError,
|
||||
ErrorFallbackProps,
|
||||
useQueryErrorResetBoundary,
|
||||
} from "blitz"
|
||||
import {ErrorBoundary} from "react-error-boundary"
|
||||
import {queryCache} from "react-query"
|
||||
import LoginForm from "app/auth/components/LoginForm"
|
||||
|
||||
if (typeof window !== "undefined") {
|
||||
;(window as any)["DEBUG_BLITZ"] = 1
|
||||
}
|
||||
import {ReactQueryDevtools} from "react-query/devtools"
|
||||
|
||||
export default function App({Component, pageProps}: AppProps) {
|
||||
const getLayout = Component.getLayout || ((page) => page)
|
||||
const router = useRouter()
|
||||
const {reset} = useQueryErrorResetBoundary()
|
||||
|
||||
return (
|
||||
<ErrorBoundary
|
||||
FallbackComponent={RootErrorFallback}
|
||||
resetKeys={[router.asPath]}
|
||||
onReset={() => {
|
||||
// This ensures the Blitz useQuery hooks will automatically refetch
|
||||
// data any time you reset the error boundary
|
||||
queryCache.resetErrorBoundaries()
|
||||
}}
|
||||
>
|
||||
<Component {...pageProps} />
|
||||
</ErrorBoundary>
|
||||
<>
|
||||
<ErrorBoundary
|
||||
FallbackComponent={RootErrorFallback}
|
||||
resetKeys={[router.asPath]}
|
||||
onReset={reset}
|
||||
>
|
||||
{getLayout(<Component {...pageProps} />)}
|
||||
</ErrorBoundary>
|
||||
<ReactQueryDevtools />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -43,7 +42,10 @@ function RootErrorFallback({error, resetErrorBoundary}: ErrorFallbackProps) {
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<ErrorComponent statusCode={error.statusCode || 400} title={error.message || error.name} />
|
||||
<ErrorComponent
|
||||
statusCode={(error as any)?.statusCode || 400}
|
||||
title={error.message || error.name}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,27 @@
|
||||
import {render} from "test/utils"
|
||||
|
||||
import Home from "./index"
|
||||
import {useCurrentUser} from "app/hooks/useCurrentUser"
|
||||
|
||||
jest.mock("app/hooks/useCurrentUser")
|
||||
const mockUseCurrentUser = useCurrentUser as jest.MockedFunction<typeof useCurrentUser>
|
||||
jest.mock("@blitzjs/core", () => ({
|
||||
...jest.requireActual<object>("@blitzjs/core")!,
|
||||
useQuery: () => [
|
||||
{
|
||||
id: 1,
|
||||
name: "User",
|
||||
email: "user@email.com",
|
||||
role: "user",
|
||||
},
|
||||
],
|
||||
}))
|
||||
|
||||
test("renders blitz documentation link", () => {
|
||||
mockUseCurrentUser.mockReturnValue({
|
||||
id: 1,
|
||||
name: "User",
|
||||
email: "user@email.com",
|
||||
role: "user",
|
||||
})
|
||||
// This is an example of how to ensure a specific item is in the document
|
||||
// But it's disabled by default (by test.skip) so the test doesn't fail
|
||||
// when you remove the the default content from the page
|
||||
|
||||
// This is an example on how to mock api hooks when testing
|
||||
|
||||
const {getByText} = render(<Home />)
|
||||
const element = getByText(/powered by blitz/i)
|
||||
// @ts-ignore
|
||||
expect(element).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
@@ -1,39 +1,41 @@
|
||||
import {Suspense} from "react"
|
||||
import {Head, Link, useSession, useRouterQuery, useMutation, invoke} from "blitz"
|
||||
import {
|
||||
Head,
|
||||
Link,
|
||||
useSession,
|
||||
useRouterQuery,
|
||||
useMutation,
|
||||
invoke,
|
||||
useQuery,
|
||||
BlitzPage,
|
||||
} from "blitz"
|
||||
import getUser from "app/users/queries/getUser"
|
||||
import trackView from "app/users/mutations/trackView"
|
||||
import Layout from "app/core/layouts/Layout"
|
||||
import {useCurrentUser} from "app/hooks/useCurrentUser"
|
||||
// import getUsers from "app/users/queries/getUsers"
|
||||
|
||||
import {Routes} from ".blitz"
|
||||
|
||||
const CurrentUserInfo = () => {
|
||||
const currentUser = useCurrentUser()
|
||||
const session = useSession()
|
||||
const [currentUser] = useQuery(getUser, {where: {id: session.userId!}})
|
||||
|
||||
return <pre>{JSON.stringify(currentUser, null, 2)}</pre>
|
||||
}
|
||||
|
||||
// const Users = () => {
|
||||
// const [users] = useQuery(getUsers, {})
|
||||
//
|
||||
// return <pre style={{maxWidth: "30rem"}}>{JSON.stringify(users, null, 2)}</pre>
|
||||
// }
|
||||
|
||||
const UserStuff = () => {
|
||||
const session = useSession()
|
||||
const query = useRouterQuery()
|
||||
const [trackViewMutation] = useMutation(trackView)
|
||||
|
||||
if (session.isLoading) return <div>Loading...</div>
|
||||
|
||||
return (
|
||||
<div>
|
||||
{!session.userId && (
|
||||
<>
|
||||
<div style={{marginTop: "1rem"}}>
|
||||
<Link href="/signup">Sign Up</Link>
|
||||
<Link href={Routes.SignupPage()}>Sign Up</Link>
|
||||
</div>
|
||||
<div>
|
||||
<Link href="/login">Login</Link>
|
||||
<Link href={Routes.LoginPage()}>Login</Link>
|
||||
</div>
|
||||
<a href="/api/auth/twitter" style={{display: "block"}}>
|
||||
Login with Twitter
|
||||
@@ -48,11 +50,6 @@ const UserStuff = () => {
|
||||
<Suspense fallback="Loading...">
|
||||
<CurrentUserInfo />
|
||||
</Suspense>
|
||||
{/*
|
||||
<Suspense fallback="Loading...">
|
||||
<Users />
|
||||
</Suspense>
|
||||
*/}
|
||||
<button
|
||||
onClick={async () => {
|
||||
try {
|
||||
@@ -81,7 +78,7 @@ const UserStuff = () => {
|
||||
)
|
||||
}
|
||||
|
||||
const Home = () => (
|
||||
const Home: BlitzPage = () => (
|
||||
<Layout>
|
||||
<div className="container">
|
||||
<Head>
|
||||
@@ -94,7 +91,9 @@ const Home = () => (
|
||||
<img src="/logo.png" alt="blitz.js" />
|
||||
</div>
|
||||
|
||||
<UserStuff />
|
||||
<Suspense fallback={"Loading..."}>
|
||||
<UserStuff />
|
||||
</Suspense>
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
@@ -238,4 +237,6 @@ const Home = () => (
|
||||
</Layout>
|
||||
)
|
||||
|
||||
Home.suppressFirstRenderFlicker = true
|
||||
|
||||
export default Home
|
||||
|
||||
@@ -1,36 +1,47 @@
|
||||
import {Suspense} from "react"
|
||||
import {Link, useRouter, useQuery, useParam, BlitzPage, useMutation} from "blitz"
|
||||
import {Head, Link, useRouter, useQuery, useParam, BlitzPage, useMutation} from "blitz"
|
||||
import Layout from "app/core/layouts/Layout"
|
||||
import getProject from "app/projects/queries/getProject"
|
||||
import deleteProject from "app/projects/mutations/deleteProject"
|
||||
|
||||
import {Routes} from ".blitz"
|
||||
|
||||
export const Project = () => {
|
||||
const router = useRouter()
|
||||
const projectId = useParam("projectId", "number")
|
||||
const [project] = useQuery(getProject, {where: {id: projectId}})
|
||||
const [deleteProjectMutation] = useMutation(deleteProject)
|
||||
const [deleteProjectMutation, {isSuccess}] = useMutation(deleteProject)
|
||||
const [project] = useQuery(getProject, {id: projectId}, {enabled: !isSuccess})
|
||||
|
||||
if (!project) return null
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>Project {project.id}</h1>
|
||||
<pre>{JSON.stringify(project, null, 2)}</pre>
|
||||
<>
|
||||
<Head>
|
||||
<title>Project {project.id}</title>
|
||||
</Head>
|
||||
|
||||
<Link href={`/projects/${project.id}/edit`}>
|
||||
<a>Edit</a>
|
||||
</Link>
|
||||
<div>
|
||||
<h1>Project {project.id}</h1>
|
||||
<pre>{JSON.stringify(project, null, 2)}</pre>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={async () => {
|
||||
if (window.confirm("This will be deleted")) {
|
||||
await deleteProjectMutation({where: {id: project.id}})
|
||||
router.push("/projects")
|
||||
}
|
||||
}}
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
</div>
|
||||
<Link href={Routes.EditProjectPage({projectId: project.id})}>
|
||||
<a>Edit</a>
|
||||
</Link>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={async () => {
|
||||
if (window.confirm("This will be deleted")) {
|
||||
await deleteProjectMutation({id: project.id})
|
||||
router.push("/projects")
|
||||
}
|
||||
}}
|
||||
style={{marginLeft: "0.5rem"}}
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -38,7 +49,7 @@ const ShowProjectPage: BlitzPage = () => {
|
||||
return (
|
||||
<div>
|
||||
<p>
|
||||
<Link href="/projects">
|
||||
<Link href={Routes.ProjectsPage()}>
|
||||
<a>Projects</a>
|
||||
</Link>
|
||||
</p>
|
||||
@@ -50,6 +61,7 @@ const ShowProjectPage: BlitzPage = () => {
|
||||
)
|
||||
}
|
||||
|
||||
ShowProjectPage.getLayout = (page) => <Layout title={"Project"}>{page}</Layout>
|
||||
ShowProjectPage.authenticate = true
|
||||
ShowProjectPage.getLayout = (page) => <Layout>{page}</Layout>
|
||||
|
||||
export default ShowProjectPage
|
||||
|
||||
@@ -1,39 +1,53 @@
|
||||
import {Suspense} from "react"
|
||||
import {Link, useRouter, useQuery, useMutation, useParam, BlitzPage} from "blitz"
|
||||
import {Head, Link, useRouter, useQuery, useMutation, useParam, BlitzPage} from "blitz"
|
||||
import Layout from "app/core/layouts/Layout"
|
||||
import getProject from "app/projects/queries/getProject"
|
||||
import updateProject from "app/projects/mutations/updateProject"
|
||||
import ProjectForm from "app/projects/components/ProjectForm"
|
||||
import {ProjectForm, FORM_ERROR} from "app/projects/components/ProjectForm"
|
||||
|
||||
import {Routes} from ".blitz"
|
||||
|
||||
export const EditProject = () => {
|
||||
const router = useRouter()
|
||||
const projectId = useParam("projectId", "number")
|
||||
const [project, {setQueryData}] = useQuery(getProject, {where: {id: projectId}})
|
||||
const [project, {setQueryData}] = useQuery(getProject, {id: projectId})
|
||||
const [updateProjectMutation] = useMutation(updateProject)
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>Edit Project {project.id}</h1>
|
||||
<pre>{JSON.stringify(project)}</pre>
|
||||
<>
|
||||
<Head>
|
||||
<title>Edit Project {project.id}</title>
|
||||
</Head>
|
||||
|
||||
<ProjectForm
|
||||
initialValues={project}
|
||||
onSubmit={async () => {
|
||||
try {
|
||||
const updated = await updateProjectMutation({
|
||||
where: {id: project.id},
|
||||
data: {name: "MyNewName"},
|
||||
})
|
||||
await setQueryData(updated)
|
||||
alert("Success!" + JSON.stringify(updated))
|
||||
router.push(`/projects/${updated.id}`)
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
alert("Error editing project " + JSON.stringify(error, null, 2))
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<h1>Edit Project {project.id}</h1>
|
||||
<pre>{JSON.stringify(project)}</pre>
|
||||
|
||||
<ProjectForm
|
||||
submitText="Update Project"
|
||||
// TODO use a zod schema for form validation
|
||||
// - Tip: extract mutation's schema into a shared `validations.ts` file and
|
||||
// then import and use it here
|
||||
// schema={UpdateProject}
|
||||
initialValues={project}
|
||||
onSubmit={async (values) => {
|
||||
try {
|
||||
const updated = await updateProjectMutation({
|
||||
id: project.id,
|
||||
...values,
|
||||
})
|
||||
await setQueryData(updated)
|
||||
router.push(`/projects/${updated.id}`)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
return {
|
||||
[FORM_ERROR]: error.toString(),
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -45,7 +59,7 @@ const EditProjectPage: BlitzPage = () => {
|
||||
</Suspense>
|
||||
|
||||
<p>
|
||||
<Link href="/projects">
|
||||
<Link href={Routes.ProjectsPage()}>
|
||||
<a>Projects</a>
|
||||
</Link>
|
||||
</p>
|
||||
@@ -53,6 +67,7 @@ const EditProjectPage: BlitzPage = () => {
|
||||
)
|
||||
}
|
||||
|
||||
EditProjectPage.getLayout = (page) => <Layout title={"Edit Project"}>{page}</Layout>
|
||||
EditProjectPage.authenticate = true
|
||||
EditProjectPage.getLayout = (page) => <Layout>{page}</Layout>
|
||||
|
||||
export default EditProjectPage
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import {Suspense} from "react"
|
||||
import {Link, usePaginatedQuery, useRouter, BlitzPage} from "blitz"
|
||||
import {Head, Link, usePaginatedQuery, useRouter, BlitzPage} from "blitz"
|
||||
import Layout from "app/core/layouts/Layout"
|
||||
import getProjects from "app/projects/queries/getProjects"
|
||||
|
||||
import {Routes} from ".blitz"
|
||||
|
||||
const ITEMS_PER_PAGE = 100
|
||||
|
||||
export const ProjectsList = () => {
|
||||
@@ -22,7 +24,7 @@ export const ProjectsList = () => {
|
||||
<ul>
|
||||
{projects.map((project) => (
|
||||
<li key={project.id}>
|
||||
<Link href={`/projects/${project.id}`}>
|
||||
<Link href={Routes.ShowProjectPage({projectId: project.id})}>
|
||||
<a>{project.name}</a>
|
||||
</Link>
|
||||
</li>
|
||||
@@ -41,20 +43,27 @@ export const ProjectsList = () => {
|
||||
|
||||
const ProjectsPage: BlitzPage = () => {
|
||||
return (
|
||||
<div>
|
||||
<p>
|
||||
<Link href="/projects/new">
|
||||
<a>Create Project</a>
|
||||
</Link>
|
||||
</p>
|
||||
<>
|
||||
<Head>
|
||||
<title>Projects</title>
|
||||
</Head>
|
||||
|
||||
<Suspense fallback={<div>Loading...</div>}>
|
||||
<ProjectsList />
|
||||
</Suspense>
|
||||
</div>
|
||||
<div>
|
||||
<p>
|
||||
<Link href={Routes.NewProjectPage()}>
|
||||
<a>Create Project</a>
|
||||
</Link>
|
||||
</p>
|
||||
|
||||
<Suspense fallback={<div>Loading...</div>}>
|
||||
<ProjectsList />
|
||||
</Suspense>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
ProjectsPage.getLayout = (page) => <Layout title={"Projects"}>{page}</Layout>
|
||||
ProjectsPage.authenticate = {redirectTo: "/login"}
|
||||
ProjectsPage.getLayout = (page) => <Layout>{page}</Layout>
|
||||
|
||||
export default ProjectsPage
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import {Link, useRouter, useMutation, BlitzPage} from "blitz"
|
||||
import Layout from "app/core/layouts/Layout"
|
||||
import createProject from "app/projects/mutations/createProject"
|
||||
import ProjectForm from "app/projects/components/ProjectForm"
|
||||
import {ProjectForm, FORM_ERROR} from "app/projects/components/ProjectForm"
|
||||
|
||||
import {Routes} from ".blitz"
|
||||
|
||||
const NewProjectPage: BlitzPage = () => {
|
||||
const router = useRouter()
|
||||
@@ -12,20 +14,27 @@ const NewProjectPage: BlitzPage = () => {
|
||||
<h1>Create New Project</h1>
|
||||
|
||||
<ProjectForm
|
||||
initialValues={{}}
|
||||
onSubmit={async () => {
|
||||
submitText="Create Project"
|
||||
// TODO use a zod schema for form validation
|
||||
// - Tip: extract mutation's schema into a shared `validations.ts` file and
|
||||
// then import and use it here
|
||||
// schema={CreateProject}
|
||||
// initialValues={{}}
|
||||
onSubmit={async (values) => {
|
||||
try {
|
||||
const project = await createProjectMutation({name: "MyName"})
|
||||
alert("Success!" + JSON.stringify(project))
|
||||
const project = await createProjectMutation(values)
|
||||
router.push(`/projects/${project.id}`)
|
||||
} catch (error) {
|
||||
alert("Error creating project " + JSON.stringify(error, null, 2))
|
||||
console.error(error)
|
||||
return {
|
||||
[FORM_ERROR]: error.toString(),
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
<p>
|
||||
<Link href="/projects">
|
||||
<Link href={Routes.ProjectsPage()}>
|
||||
<a>Projects</a>
|
||||
</Link>
|
||||
</p>
|
||||
@@ -33,6 +42,7 @@ const NewProjectPage: BlitzPage = () => {
|
||||
)
|
||||
}
|
||||
|
||||
NewProjectPage.authenticate = true
|
||||
NewProjectPage.getLayout = (page) => <Layout title={"Create New Project"}>{page}</Layout>
|
||||
|
||||
export default NewProjectPage
|
||||
|
||||
@@ -1,36 +1,28 @@
|
||||
import {FC} from "react"
|
||||
import {getSessionContext} from "@blitzjs/server"
|
||||
import {
|
||||
getSession,
|
||||
invokeWithMiddleware,
|
||||
useRouter,
|
||||
GetServerSideProps,
|
||||
PromiseReturnType,
|
||||
ErrorComponent as ErrorPage,
|
||||
useMutation,
|
||||
AuthenticationError,
|
||||
AuthorizationError,
|
||||
GetServerSideProps,
|
||||
InferGetServerSidePropsType,
|
||||
BlitzPage,
|
||||
} from "blitz"
|
||||
import getUser from "app/users/queries/getUser"
|
||||
import logout from "app/auth/mutations/logout"
|
||||
import path from "path"
|
||||
|
||||
type PageProps = {
|
||||
user?: PromiseReturnType<typeof getUser>
|
||||
error?: {
|
||||
statusCode: number
|
||||
message: string
|
||||
}
|
||||
}
|
||||
|
||||
export const getServerSideProps: GetServerSideProps<PageProps> = async ({req, res}) => {
|
||||
export const getServerSideProps: GetServerSideProps = async ({req, res}) => {
|
||||
// Ensure these files are not eliminated by trace-based tree-shaking (like Vercel)
|
||||
// https://github.com/blitz-js/blitz/issues/794
|
||||
path.resolve("next.config.js")
|
||||
path.resolve("blitz.config.js")
|
||||
path.resolve(".next/__db.js")
|
||||
path.resolve(".next/blitz/db.js")
|
||||
// End anti-tree-shaking
|
||||
|
||||
const session = await getSessionContext(req, res)
|
||||
const session = await getSession(req, res)
|
||||
console.log("Session id:", session.userId)
|
||||
try {
|
||||
const user = await invokeWithMiddleware(
|
||||
@@ -62,7 +54,7 @@ export const getServerSideProps: GetServerSideProps<PageProps> = async ({req, re
|
||||
}
|
||||
}
|
||||
|
||||
const Test: FC<PageProps> = ({user, error}: PageProps) => {
|
||||
const Test: BlitzPage<InferGetServerSidePropsType<typeof getServerSideProps>> = ({user, error}) => {
|
||||
const router = useRouter()
|
||||
const [logoutMutation] = useMutation(logout)
|
||||
|
||||
|
||||
13
examples/auth/app/pages/test.tsx
Normal file
13
examples/auth/app/pages/test.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import {useRouter} from "next/router"
|
||||
import React from "react"
|
||||
|
||||
export default function Test() {
|
||||
const {replace} = useRouter()
|
||||
|
||||
const handleChange = (event: any) => {
|
||||
// replace({ query }, undefined, { shallow: true })
|
||||
replace(`/test?p=${event.target.value}`)
|
||||
}
|
||||
|
||||
return <input onChange={handleChange} />
|
||||
}
|
||||
@@ -1,23 +1,12 @@
|
||||
import React from "react"
|
||||
import {Form, FormProps} from "app/core/components/Form"
|
||||
import {LabeledTextField} from "app/core/components/LabeledTextField"
|
||||
import * as z from "zod"
|
||||
export {FORM_ERROR} from "app/core/components/Form"
|
||||
|
||||
type ProjectFormProps = {
|
||||
initialValues: any
|
||||
onSubmit: React.FormEventHandler<HTMLFormElement>
|
||||
}
|
||||
|
||||
const ProjectForm = ({initialValues, onSubmit}: ProjectFormProps) => {
|
||||
export function ProjectForm<S extends z.ZodType<any, any>>(props: FormProps<S>) {
|
||||
return (
|
||||
<form
|
||||
onSubmit={(event) => {
|
||||
event.preventDefault()
|
||||
onSubmit(event)
|
||||
}}
|
||||
>
|
||||
<div>Put your form fields here. But for now, just click submit</div>
|
||||
<div>{JSON.stringify(initialValues)}</div>
|
||||
<button>Submit</button>
|
||||
</form>
|
||||
<Form<S> {...props}>
|
||||
<LabeledTextField name="name" label="Name" placeholder="Name" />
|
||||
</Form>
|
||||
)
|
||||
}
|
||||
|
||||
export default ProjectForm
|
||||
|
||||
@@ -2,24 +2,15 @@ import {resolver} from "blitz"
|
||||
import db from "db"
|
||||
import * as z from "zod"
|
||||
|
||||
export const CreateProject = z.object({
|
||||
name: z.string(),
|
||||
dueDate: z.date().optional(),
|
||||
const CreateProject = z
|
||||
.object({
|
||||
name: z.string(),
|
||||
})
|
||||
.nonstrict()
|
||||
|
||||
export default resolver.pipe(resolver.zod(CreateProject), resolver.authorize(), async (input) => {
|
||||
// TODO: in multi-tenant app, you must add validation to ensure correct tenant
|
||||
const project = await db.project.create({data: input})
|
||||
|
||||
return project
|
||||
})
|
||||
|
||||
export default resolver.pipe(
|
||||
resolver.zod(CreateProject),
|
||||
(input, _ctx) => ({extraFieldForIntegrationTesting: _ctx.session.userId, ...input}),
|
||||
resolver.authorize(),
|
||||
// How to set a default input value
|
||||
(input, _ctx) => ({dueDate: new Date(), ...input}),
|
||||
async (input, _ctx) => {
|
||||
console.log("Creating project...")
|
||||
const project = await db.project.create({
|
||||
data: input,
|
||||
})
|
||||
console.log("Created project")
|
||||
|
||||
return project
|
||||
},
|
||||
)
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
import {Ctx} from "blitz"
|
||||
import db, {Prisma} from "db"
|
||||
import {resolver} from "blitz"
|
||||
import db from "db"
|
||||
import * as z from "zod"
|
||||
|
||||
type DeleteProjectInput = Pick<Prisma.ProjectDeleteArgs, "where">
|
||||
const DeleteProject = z
|
||||
.object({
|
||||
id: z.number(),
|
||||
})
|
||||
.nonstrict()
|
||||
|
||||
export default async function deleteProject({where}: DeleteProjectInput, ctx: Ctx) {
|
||||
ctx.session.$authorize()
|
||||
|
||||
const project = await db.project.delete({where})
|
||||
export default resolver.pipe(resolver.zod(DeleteProject), resolver.authorize(), async ({id}) => {
|
||||
// TODO: in multi-tenant app, you must add validation to ensure correct tenant
|
||||
const project = await db.project.delete({where: {id}})
|
||||
|
||||
return project
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,12 +1,21 @@
|
||||
import {Ctx} from "blitz"
|
||||
import db, {Prisma} from "db"
|
||||
import {resolver} from "blitz"
|
||||
import db from "db"
|
||||
import * as z from "zod"
|
||||
|
||||
type UpdateProjectInput = Pick<Prisma.ProjectUpdateArgs, "where" | "data">
|
||||
const UpdateProject = z
|
||||
.object({
|
||||
id: z.number(),
|
||||
name: z.string(),
|
||||
})
|
||||
.nonstrict()
|
||||
|
||||
export default async function updateProject({where, data}: UpdateProjectInput, ctx: Ctx) {
|
||||
ctx.session.$authorize()
|
||||
export default resolver.pipe(
|
||||
resolver.zod(UpdateProject),
|
||||
resolver.authorize(),
|
||||
async ({id, ...data}) => {
|
||||
// TODO: in multi-tenant app, you must add validation to ensure correct tenant
|
||||
const project = await db.project.update({where: {id}, data})
|
||||
|
||||
const project = await db.project.update({where, data})
|
||||
|
||||
return project
|
||||
}
|
||||
return project
|
||||
},
|
||||
)
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
import {Ctx, NotFoundError} from "blitz"
|
||||
import db, {Prisma} from "db"
|
||||
import {resolver, NotFoundError} from "blitz"
|
||||
import db from "db"
|
||||
import * as z from "zod"
|
||||
|
||||
type GetProjectInput = Pick<Prisma.ProjectFindFirstArgs, "where">
|
||||
const GetProject = z.object({
|
||||
// This accepts type of undefined, but is required at runtime
|
||||
id: z.number().optional().refine(Boolean, "Required"),
|
||||
})
|
||||
|
||||
export default async function getProject({where}: GetProjectInput, ctx: Ctx) {
|
||||
ctx.session.$authorize()
|
||||
|
||||
const project = await db.project.findFirst({where})
|
||||
export default resolver.pipe(resolver.zod(GetProject), resolver.authorize(), async ({id}) => {
|
||||
// TODO: in multi-tenant app, you must add validation to ensure correct tenant
|
||||
const project = await db.project.findFirst({where: {id}})
|
||||
|
||||
if (!project) throw new NotFoundError()
|
||||
|
||||
return project
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,29 +1,25 @@
|
||||
import {Ctx} from "blitz"
|
||||
import {paginate, resolver} from "blitz"
|
||||
import db, {Prisma} from "db"
|
||||
|
||||
type GetProjectsInput = Pick<Prisma.ProjectFindManyArgs, "where" | "orderBy" | "skip" | "take">
|
||||
interface GetProjectsInput
|
||||
extends Pick<Prisma.ProjectFindManyArgs, "where" | "orderBy" | "skip" | "take"> {}
|
||||
|
||||
export default async function getProjects(
|
||||
{where, orderBy, skip = 0, take}: GetProjectsInput,
|
||||
ctx: Ctx,
|
||||
) {
|
||||
ctx.session.$authorize()
|
||||
export default resolver.pipe(
|
||||
resolver.authorize(),
|
||||
async ({where, orderBy, skip = 0, take = 100}: GetProjectsInput) => {
|
||||
// TODO: in multi-tenant app, you must add validation to ensure correct tenant
|
||||
const {items: projects, hasMore, nextPage, count} = await paginate({
|
||||
skip,
|
||||
take,
|
||||
count: () => db.project.count({where}),
|
||||
query: (paginateArgs) => db.project.findMany({...paginateArgs, where, orderBy}),
|
||||
})
|
||||
|
||||
const projects = await db.project.findMany({
|
||||
where,
|
||||
orderBy,
|
||||
take,
|
||||
skip,
|
||||
})
|
||||
|
||||
const count = await db.project.count()
|
||||
const hasMore = typeof take === "number" ? skip + take < count : false
|
||||
const nextPage = hasMore ? {take, skip: skip + take!} : null
|
||||
|
||||
return {
|
||||
projects,
|
||||
nextPage,
|
||||
hasMore,
|
||||
count,
|
||||
}
|
||||
}
|
||||
return {
|
||||
projects,
|
||||
nextPage,
|
||||
hasMore,
|
||||
count,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import {Ctx} from "blitz"
|
||||
import db from "db"
|
||||
|
||||
export default async function getCurrentUser(_ = null, ctx: Ctx) {
|
||||
if (!ctx.session.userId) return null
|
||||
export default async function getCurrentUser(_ = null, {session}: Ctx) {
|
||||
if (!session.userId) return null
|
||||
|
||||
const user = await db.user.findFirst({
|
||||
where: {id: ctx.session.userId},
|
||||
where: {id: session.userId},
|
||||
select: {id: true, name: true, email: true, role: true},
|
||||
})
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ type GetUserInput = {
|
||||
}
|
||||
|
||||
export default async function getUser({where}: GetUserInput, ctx: Ctx) {
|
||||
ctx.session.$authorize()
|
||||
if (!ctx.session.userId) return null
|
||||
|
||||
const user = await db.user.findFirst({where})
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const {sessionMiddleware, simpleRolesIsAuthorized} = require("@blitzjs/server")
|
||||
const {sessionMiddleware, simpleRolesIsAuthorized} = require("blitz")
|
||||
const withBundleAnalyzer = require("@next/bundle-analyzer")({
|
||||
enabled: process.env.ANALYZE === "true",
|
||||
})
|
||||
@@ -7,14 +7,19 @@ module.exports = withBundleAnalyzer({
|
||||
middleware: [
|
||||
sessionMiddleware({
|
||||
isAuthorized: simpleRolesIsAuthorized,
|
||||
sessionExpiryMinutes: 4,
|
||||
// sessionExpiryMinutes: 4,
|
||||
}),
|
||||
],
|
||||
cli: {
|
||||
clearConsoleOnBlitzDev: false,
|
||||
},
|
||||
log: {
|
||||
// level: "trace",
|
||||
},
|
||||
experimental: {
|
||||
isomorphicResolverImports: true,
|
||||
initServer() {
|
||||
console.log("Hello world from initServer")
|
||||
},
|
||||
},
|
||||
/*
|
||||
webpack: (config, {buildId, dev, isServer, defaultLoaders, webpack}) => {
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
{
|
||||
"baseUrl": "http://localhost:3099",
|
||||
"defaultCommandTimeout": 10000,
|
||||
"retries": {
|
||||
"runMode": 1
|
||||
},
|
||||
"video": false,
|
||||
"chromeWebSecurity": false
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ describe("index page", () => {
|
||||
const user = createRandomUser()
|
||||
|
||||
cy.signup(user)
|
||||
cy.wait(1000)
|
||||
|
||||
cy.location("pathname").should("equal", "/")
|
||||
cy.contains("button", "Logout")
|
||||
@@ -28,8 +29,10 @@ describe("index page", () => {
|
||||
const user = createRandomUser()
|
||||
|
||||
cy.signup(user)
|
||||
cy.wait(1000)
|
||||
|
||||
cy.contains("button", "Logout").click()
|
||||
cy.wait(1000)
|
||||
cy.contains("a", /login/i).click()
|
||||
|
||||
cy.contains("Email").find("input").type(user.email)
|
||||
@@ -37,6 +40,7 @@ describe("index page", () => {
|
||||
cy.contains("button", /login/i).click()
|
||||
|
||||
cy.location("pathname").should("equal", "/")
|
||||
cy.wait(1000)
|
||||
cy.contains("button", "Logout")
|
||||
})
|
||||
|
||||
@@ -44,10 +48,12 @@ describe("index page", () => {
|
||||
const user = createRandomUser()
|
||||
|
||||
cy.signup(user)
|
||||
cy.wait(1000)
|
||||
|
||||
cy.contains("button", "Logout").click()
|
||||
|
||||
cy.location("pathname").should("equal", "/")
|
||||
cy.wait(1000)
|
||||
cy.contains("a", /login/i)
|
||||
})
|
||||
|
||||
@@ -57,10 +63,13 @@ describe("index page", () => {
|
||||
const user = createRandomUser()
|
||||
|
||||
cy.contains("button", "Track view").click()
|
||||
cy.wait(500)
|
||||
cy.contains("button", "Track view").click()
|
||||
cy.wait(1000)
|
||||
cy.contains('"views": 2')
|
||||
|
||||
cy.signup(user)
|
||||
cy.wait(1000)
|
||||
|
||||
cy.contains('"views": 2')
|
||||
})
|
||||
|
||||
7
examples/auth/cypress/tsconfig.json
Normal file
7
examples/auth/cypress/tsconfig.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": ["dom", "es5"],
|
||||
"types": ["cypress"]
|
||||
}
|
||||
}
|
||||
0
examples/auth/db/migrations/.keep
Normal file
0
examples/auth/db/migrations/.keep
Normal file
@@ -0,0 +1,15 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "Token" (
|
||||
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" DATETIME NOT NULL,
|
||||
"hashedToken" TEXT NOT NULL,
|
||||
"type" TEXT NOT NULL,
|
||||
"expiresAt" DATETIME NOT NULL,
|
||||
"sentTo" TEXT NOT NULL,
|
||||
"userId" INTEGER NOT NULL,
|
||||
FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Token.hashedToken_type_unique" ON "Token"("hashedToken", "type");
|
||||
@@ -20,18 +20,20 @@ generator client {
|
||||
// --------------------------------------
|
||||
|
||||
model User {
|
||||
id Int @default(autoincrement()) @id
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
id Int @id @default(autoincrement())
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
name String?
|
||||
email String @unique
|
||||
email String @unique
|
||||
hashedPassword String?
|
||||
role String @default("user")
|
||||
sessions Session[]
|
||||
role String @default("user")
|
||||
|
||||
sessions Session[]
|
||||
tokens Token[]
|
||||
}
|
||||
|
||||
model Session {
|
||||
id Int @default(autoincrement()) @id
|
||||
id Int @id @default(autoincrement())
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
expiresAt DateTime?
|
||||
@@ -44,10 +46,25 @@ model Session {
|
||||
privateData String?
|
||||
}
|
||||
|
||||
model Token {
|
||||
id Int @id @default(autoincrement())
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
hashedToken String
|
||||
type String
|
||||
expiresAt DateTime
|
||||
sentTo String
|
||||
|
||||
user User @relation(fields: [userId], references: [id])
|
||||
userId Int
|
||||
|
||||
@@unique([hashedToken, type])
|
||||
}
|
||||
|
||||
model Project {
|
||||
id Int @default(autoincrement()) @id
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
id Int @id @default(autoincrement())
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
name String
|
||||
dueDate DateTime?
|
||||
}
|
||||
|
||||
0
examples/auth/mailers/.keep
Normal file
0
examples/auth/mailers/.keep
Normal file
45
examples/auth/mailers/forgotPasswordMailer.ts
Normal file
45
examples/auth/mailers/forgotPasswordMailer.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
/* TODO - You need to add a mailer integration in `integrations/` and import here.
|
||||
*
|
||||
* The integration file can be very simple. Instantiate the email client
|
||||
* and then export it. That way you can import here and anywhere else
|
||||
* and use it straight away.
|
||||
*/
|
||||
import previewEmail from "preview-email"
|
||||
|
||||
type ResetPasswordMailer = {
|
||||
to: string
|
||||
token: string
|
||||
}
|
||||
|
||||
export function forgotPasswordMailer({to, token}: ResetPasswordMailer) {
|
||||
// In production, set APP_ORIGIN to your production server origin
|
||||
const origin = process.env.APP_ORIGIN || process.env.BLITZ_DEV_SERVER_ORIGIN
|
||||
const resetUrl = `${origin}/reset-password?token=${token}`
|
||||
|
||||
const msg = {
|
||||
from: "TODO@example.com",
|
||||
to,
|
||||
subject: "Your Password Reset Instructions",
|
||||
html: `
|
||||
<h1>Reset Your Password</h1>
|
||||
<h3>NOTE: You must set up a production email integration in mailers/forgotPasswordMailer.ts</h3>
|
||||
|
||||
<a href="${resetUrl}">
|
||||
Click here to set a new password
|
||||
</a>
|
||||
`,
|
||||
}
|
||||
|
||||
return {
|
||||
async send() {
|
||||
if (process.env.NODE_ENV === "production") {
|
||||
// TODO - send the production email, like this:
|
||||
// await postmark.sendEmail(msg)
|
||||
throw new Error("No production email implementation in mailers/forgotPasswordMailer")
|
||||
} else {
|
||||
// Preview email in the browser
|
||||
await previewEmail(msg)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,18 @@
|
||||
{
|
||||
"name": "@examples/auth",
|
||||
"version": "0.30.0-canary.4",
|
||||
"version": "0.34.0-canary.0",
|
||||
"scripts": {
|
||||
"dev": "blitz dev",
|
||||
"build": "blitz build",
|
||||
"start": "blitz start",
|
||||
"studio": "blitz prisma studio",
|
||||
"build": "blitz build",
|
||||
"lint": "eslint --ignore-path .gitignore --ext .js,.ts,.tsx .",
|
||||
"analyze": "cross-env ANALYZE=true blitz build",
|
||||
"cy:open": "cypress open",
|
||||
"cy:run": "cypress run || cypress run",
|
||||
"test": "prisma generate && yarn test:jest && yarn test:e2e",
|
||||
"cy:run": "cypress run --browser chrome",
|
||||
"test": "prisma generate && blitz codegen && yarn test:jest && yarn test:e2e",
|
||||
"test:jest": "jest",
|
||||
"test:server": "blitz prisma migrate deploy --preview-feature && blitz build && blitz start --production -p 3099",
|
||||
"test:server": "cross-env NODE_ENV=test blitz prisma migrate deploy && blitz build && cross-env NODE_ENV=test blitz start -p 3099",
|
||||
"test:e2e": "cross-env NODE_ENV=test start-server-and-test test:server http://localhost:3099 cy:run"
|
||||
},
|
||||
"browserslist": [
|
||||
@@ -26,49 +27,40 @@
|
||||
"bracketSpacing": false,
|
||||
"trailingComma": "all"
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "lint-staged && pretty-quick --staged",
|
||||
"pre-push": "blitz test"
|
||||
}
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,ts,tsx}": [
|
||||
"eslint --fix"
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@prisma/cli": "2.15.0",
|
||||
"@prisma/client": "2.15.0",
|
||||
"blitz": "0.30.0-canary.4",
|
||||
"@prisma/client": "2.19.0",
|
||||
"blitz": "0.35.0-canary.0",
|
||||
"final-form": "4.20.1",
|
||||
"passport-auth0": "1.4.0",
|
||||
"passport-github2": "0.1.12",
|
||||
"passport-twitter": "1.0.4",
|
||||
"react": "0.0.0-experimental-3310209d0",
|
||||
"react-dom": "0.0.0-experimental-3310209d0",
|
||||
"react-error-boundary": "3.1.0",
|
||||
"prisma": "2.19.0",
|
||||
"react": "0.0.0-experimental-6a589ad71",
|
||||
"react-dom": "0.0.0-experimental-6a589ad71",
|
||||
"react-error-boundary": "3.1.1",
|
||||
"react-final-form": "6.5.2",
|
||||
"zod": "1.11.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@cypress/skip-test": "2.6.0",
|
||||
"@next/bundle-analyzer": "^10.0.5",
|
||||
"@testing-library/react": "11.2.3",
|
||||
"@testing-library/react-hooks": "4.0.1",
|
||||
"@next/bundle-analyzer": "^10.0.6",
|
||||
"@testing-library/react": "11.2.5",
|
||||
"@testing-library/react-hooks": "^4.0.1",
|
||||
"@types/passport-auth0": "1.0.4",
|
||||
"@types/passport-github2": "1.2.4",
|
||||
"@types/passport-twitter": "1.0.36",
|
||||
"@types/react": "17.0.0",
|
||||
"@types/preview-email": "2.0.0",
|
||||
"@types/react": "17.0.2",
|
||||
"cross-env": "7.0.3",
|
||||
"cypress": "6.2.1",
|
||||
"eslint": "7.17.0",
|
||||
"husky": "4.3.7",
|
||||
"lint-staged": "10.5.3",
|
||||
"eslint": "7.21.0",
|
||||
"husky": "5.1.2",
|
||||
"lint-staged": "10.5.4",
|
||||
"prettier": "2.2.1",
|
||||
"pretty-quick": "3.1.0",
|
||||
"preview-email": "3.0.3",
|
||||
"start-server-and-test": "1.11.7",
|
||||
"typescript": "4.1.3"
|
||||
"typescript": "4.1.5"
|
||||
},
|
||||
"private": true
|
||||
}
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
// This is the jest 'setupFilesAfterEnv' setup file
|
||||
// It's a good place to set globals, add global before/after hooks, etc
|
||||
|
||||
export {} // so TS doesn't complain
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {RouterContext, BlitzRouter} from "blitz"
|
||||
import {RouterContext, BlitzRouter, BlitzProvider} from "blitz"
|
||||
import {render as defaultRender} from "@testing-library/react"
|
||||
import {renderHook as defaultRenderHook} from "@testing-library/react-hooks"
|
||||
|
||||
@@ -13,14 +13,6 @@ export * from "@testing-library/react"
|
||||
// This is the place to add any other context providers you need while testing.
|
||||
// --------------------------------------------------------------------------------
|
||||
|
||||
type DefaultParams = Parameters<typeof defaultRender>
|
||||
type RenderUI = DefaultParams[0]
|
||||
type RenderOptions = DefaultParams[1] & {router?: Partial<BlitzRouter>}
|
||||
|
||||
type DefaultHookParams = Parameters<typeof defaultRenderHook>
|
||||
type RenderHook = DefaultHookParams[0]
|
||||
type RenderHookOptions = DefaultHookParams[1] & {router?: Partial<BlitzRouter>}
|
||||
|
||||
// --------------------------------------------------
|
||||
// render()
|
||||
// --------------------------------------------------
|
||||
@@ -32,11 +24,18 @@ type RenderHookOptions = DefaultHookParams[1] & {router?: Partial<BlitzRouter>}
|
||||
// router: { pathname: '/my-custom-pathname' },
|
||||
// });
|
||||
// --------------------------------------------------
|
||||
export function render(ui: RenderUI, {wrapper, router, ...options}: RenderOptions = {}) {
|
||||
export function render(
|
||||
ui: RenderUI,
|
||||
{wrapper, router, dehydratedState, ...options}: RenderOptions = {},
|
||||
) {
|
||||
if (!wrapper) {
|
||||
// Add a default context wrapper if one isn't supplied from the test
|
||||
wrapper = ({children}) => (
|
||||
<RouterContext.Provider value={{...mockRouter, ...router}}>{children}</RouterContext.Provider>
|
||||
<BlitzProvider dehydratedState={dehydratedState}>
|
||||
<RouterContext.Provider value={{...mockRouter, ...router}}>
|
||||
{children}
|
||||
</RouterContext.Provider>
|
||||
</BlitzProvider>
|
||||
)
|
||||
}
|
||||
return defaultRender(ui, {wrapper, ...options})
|
||||
@@ -55,12 +54,16 @@ export function render(ui: RenderUI, {wrapper, router, ...options}: RenderOption
|
||||
// --------------------------------------------------
|
||||
export function renderHook(
|
||||
hook: RenderHook,
|
||||
{wrapper, router, ...options}: RenderHookOptions = {},
|
||||
{wrapper, router, dehydratedState, ...options}: RenderHookOptions = {},
|
||||
) {
|
||||
if (!wrapper) {
|
||||
// Add a default context wrapper if one isn't supplied from the test
|
||||
wrapper = ({children}) => (
|
||||
<RouterContext.Provider value={{...mockRouter, ...router}}>{children}</RouterContext.Provider>
|
||||
<BlitzProvider dehydratedState={dehydratedState}>
|
||||
<RouterContext.Provider value={{...mockRouter, ...router}}>
|
||||
{children}
|
||||
</RouterContext.Provider>
|
||||
</BlitzProvider>
|
||||
)
|
||||
}
|
||||
return defaultRenderHook(hook, {wrapper, ...options})
|
||||
@@ -74,6 +77,8 @@ export const mockRouter: BlitzRouter = {
|
||||
params: {},
|
||||
query: {},
|
||||
isReady: true,
|
||||
isLocaleDomain: false,
|
||||
isPreview: false,
|
||||
push: jest.fn(),
|
||||
replace: jest.fn(),
|
||||
reload: jest.fn(),
|
||||
@@ -87,3 +92,17 @@ export const mockRouter: BlitzRouter = {
|
||||
},
|
||||
isFallback: false,
|
||||
}
|
||||
|
||||
type DefaultParams = Parameters<typeof defaultRender>
|
||||
type RenderUI = DefaultParams[0]
|
||||
type RenderOptions = DefaultParams[1] & {
|
||||
router?: Partial<BlitzRouter>
|
||||
dehydratedState?: unknown
|
||||
}
|
||||
|
||||
type DefaultHookParams = Parameters<typeof defaultRenderHook>
|
||||
type RenderHook = DefaultHookParams[0]
|
||||
type RenderHookOptions = DefaultHookParams[1] & {
|
||||
router?: Partial<BlitzRouter>
|
||||
dehydratedState?: unknown
|
||||
}
|
||||
|
||||
@@ -14,8 +14,9 @@
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"downlevelIteration": true,
|
||||
"jsx": "preserve"
|
||||
},
|
||||
"exclude": ["node_modules"],
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"]
|
||||
"exclude": ["node_modules", "cypress"],
|
||||
"include": ["**/*.ts", "**/*.tsx"]
|
||||
}
|
||||
|
||||
1
examples/auth/types
Symbolic link
1
examples/auth/types
Symbolic link
@@ -0,0 +1 @@
|
||||
../../types
|
||||
@@ -1,17 +1,26 @@
|
||||
import {DefaultCtx, SessionContext} from "blitz"
|
||||
import {simpleRolesIsAuthorized} from "@blitzjs/server"
|
||||
import {DefaultCtx, SessionContext, SimpleRolesIsAuthorized} from "blitz"
|
||||
import {User} from "db"
|
||||
|
||||
export type Role = "ADMIN" | "USER"
|
||||
|
||||
declare module "blitz" {
|
||||
export interface Ctx extends DefaultCtx {
|
||||
session: SessionContext
|
||||
}
|
||||
export interface Session {
|
||||
isAuthorized: typeof simpleRolesIsAuthorized
|
||||
isAuthorized: SimpleRolesIsAuthorized<Role>
|
||||
PublicData: {
|
||||
userId: User["id"]
|
||||
roles: string[]
|
||||
role: Role
|
||||
views?: number
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This should not be needed. Usually it isn't but for some reason in this example it is
|
||||
declare module "react" {
|
||||
interface StyleHTMLAttributes<T> extends React.HTMLAttributes<T> {
|
||||
jsx?: boolean
|
||||
global?: boolean
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,15 +8,15 @@
|
||||
```
|
||||
2. Migrate
|
||||
```sh
|
||||
blitz prisma migrate dev --preview-feature
|
||||
blitz prisma migrate dev
|
||||
```
|
||||
3. Start the dev server
|
||||
|
||||
```sh
|
||||
blitz start
|
||||
blitz dev
|
||||
|
||||
// Or if you want hot-reloading of server.js, use:
|
||||
yarn start
|
||||
yarn dev
|
||||
```
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
@@ -25,5 +25,5 @@ Open [http://localhost:3000](http://localhost:3000) with your browser to see the
|
||||
|
||||
```sh
|
||||
blitz build
|
||||
blitz start --production
|
||||
blitz start
|
||||
```
|
||||
|
||||
@@ -9,7 +9,7 @@ export default async function login(input: LoginInputType, {session}: Ctx) {
|
||||
// This throws an error if credentials are invalid
|
||||
const user = await authenticateUser(email, password)
|
||||
|
||||
await session.$create({userId: user.id, roles: [user.role]})
|
||||
await session.$create({userId: user.id})
|
||||
|
||||
return user
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ export default async function signup(input: SignupInputType, {session}: Ctx) {
|
||||
select: {id: true, name: true, email: true, role: true},
|
||||
})
|
||||
|
||||
await session.$create({userId: user.id, roles: [user.role]})
|
||||
await session.$create({userId: user.id})
|
||||
|
||||
return user
|
||||
}
|
||||
|
||||
@@ -1,21 +1,24 @@
|
||||
import {AppProps, ErrorComponent, useRouter, AuthenticationError, AuthorizationError} from "blitz"
|
||||
import {
|
||||
AppProps,
|
||||
ErrorComponent,
|
||||
useRouter,
|
||||
AuthenticationError,
|
||||
AuthorizationError,
|
||||
useQueryErrorResetBoundary,
|
||||
} from "blitz"
|
||||
import {ErrorBoundary, FallbackProps} from "react-error-boundary"
|
||||
import {queryCache} from "react-query"
|
||||
import LoginForm from "app/auth/components/LoginForm"
|
||||
|
||||
export default function App({Component, pageProps}: AppProps) {
|
||||
const getLayout = Component.getLayout || ((page) => page)
|
||||
const router = useRouter()
|
||||
const {reset} = useQueryErrorResetBoundary()
|
||||
|
||||
return (
|
||||
<ErrorBoundary
|
||||
FallbackComponent={RootErrorFallback}
|
||||
resetKeys={[router.asPath]}
|
||||
onReset={() => {
|
||||
// This ensures the Blitz useQuery hooks will automatically refetch
|
||||
// data any time you reset the error boundary
|
||||
queryCache.resetErrorBoundaries()
|
||||
}}
|
||||
onReset={reset}
|
||||
>
|
||||
{getLayout(<Component {...pageProps} />)}
|
||||
</ErrorBoundary>
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
import React from "react"
|
||||
import {render} from "test/utils"
|
||||
|
||||
import Home from "./index"
|
||||
import {useCurrentUser} from "app/hooks/useCurrentUser"
|
||||
|
||||
jest.mock("app/hooks/useCurrentUser")
|
||||
const mockUseCurrentUser = useCurrentUser as jest.MockedFunction<typeof useCurrentUser>
|
||||
|
||||
test.skip("renders blitz documentation link", () => {
|
||||
// This is an example of how to ensure a specific item is in the document
|
||||
// But it's disabled by default (by test.skip) so the test doesn't fail
|
||||
// when you remove the the default content from the page
|
||||
|
||||
// This is an example on how to mock api hooks when testing
|
||||
mockUseCurrentUser.mockReturnValue({
|
||||
id: 1,
|
||||
name: "User",
|
||||
email: "user@email.com",
|
||||
role: "user",
|
||||
})
|
||||
|
||||
const {getByText} = render(<Home />)
|
||||
const linkElement = getByText(/Documentation/i)
|
||||
expect(linkElement).toBeInTheDocument()
|
||||
})
|
||||
@@ -74,7 +74,7 @@ const Home: BlitzPage = () => {
|
||||
<code>blitz generate all project name:string</code>
|
||||
</pre>
|
||||
<pre>
|
||||
<code>blitz prisma migrate dev --preview-feature</code>
|
||||
<code>blitz prisma migrate dev</code>
|
||||
</pre>
|
||||
<div>
|
||||
<p>
|
||||
@@ -84,7 +84,7 @@ const Home: BlitzPage = () => {
|
||||
<code>Ctrl + c</code>
|
||||
</pre>
|
||||
<pre>
|
||||
<code>blitz start</code>
|
||||
<code>blitz dev</code>
|
||||
</pre>
|
||||
<p>
|
||||
and go to{" "}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const {sessionMiddleware, simpleRolesIsAuthorized} = require("@blitzjs/server")
|
||||
const {sessionMiddleware, simpleRolesIsAuthorized} = require("blitz")
|
||||
|
||||
module.exports = {
|
||||
middleware: [
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
{
|
||||
"name": "@examples/custom-server",
|
||||
"version": "0.30.0-canary.4",
|
||||
"version": "0.34.0-canary.0",
|
||||
"scripts": {
|
||||
"start": "nodemon --watch server.js --exec 'blitz start'",
|
||||
"dev": "blitz dev",
|
||||
"build": "blitz build",
|
||||
"start": "blitz start",
|
||||
"studio": "blitz prisma studio",
|
||||
"lint": "eslint --ignore-path .gitignore --ext .js,.ts,.tsx .",
|
||||
"test-watch": "jest --watch",
|
||||
"cy-open": "cypress open",
|
||||
"cy-run": "cypress run",
|
||||
"test:migrate": "prisma generate && blitz prisma migrate deploy --preview-feature",
|
||||
"test:jest": "jest",
|
||||
"test-server": "blitz build && blitz start --production",
|
||||
"test:migrate": "prisma generate && blitz prisma migrate deploy",
|
||||
"test:jest": "jest --passWithNoTests",
|
||||
"test-server": "blitz build && blitz start",
|
||||
"test:e2e": "cross-env NODE_ENV=test PORT=3099 start-server-and-test test-server http://localhost:3099 cy-run",
|
||||
"test": "run-s test:*"
|
||||
},
|
||||
@@ -27,62 +28,34 @@
|
||||
"bracketSpacing": false,
|
||||
"trailingComma": "all"
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "lint-staged && pretty-quick --staged",
|
||||
"pre-push": "tsc && npm run lint && npm run test"
|
||||
}
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,ts,tsx}": [
|
||||
"eslint --fix"
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@prisma/cli": "2.15.0",
|
||||
"@prisma/client": "2.15.0",
|
||||
"blitz": "0.30.0-canary.4",
|
||||
"@prisma/client": "2.19.0",
|
||||
"blitz": "0.35.0-canary.0",
|
||||
"final-form": "4.20.1",
|
||||
"react": "0.0.0-experimental-4ead6b530",
|
||||
"react-dom": "0.0.0-experimental-4ead6b530",
|
||||
"react-error-boundary": "2.3.2",
|
||||
"prisma": "2.19.0",
|
||||
"react": "0.0.0-experimental-6a589ad71",
|
||||
"react-dom": "0.0.0-experimental-6a589ad71",
|
||||
"react-error-boundary": "3.1.1",
|
||||
"react-final-form": "6.5.2",
|
||||
"secure-password": "4.0.0",
|
||||
"typescript": "4.1.3",
|
||||
"typescript": "4.1.5",
|
||||
"zod": "1.11.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@cypress/skip-test": "2.6.0",
|
||||
"@testing-library/jest-dom": "5.11.6",
|
||||
"@testing-library/react": "11.2.3",
|
||||
"@testing-library/react-hooks": "4.0.1",
|
||||
"@types/jest": "26.0.20",
|
||||
"@types/react": "16.14.1",
|
||||
"@testing-library/react": "11.2.5",
|
||||
"@testing-library/react-hooks": "^4.0.1",
|
||||
"@types/react": "17.0.2",
|
||||
"@types/secure-password": "3.1.0",
|
||||
"@typescript-eslint/eslint-plugin": "4.12.0",
|
||||
"@typescript-eslint/parser": "4.12.0",
|
||||
"babel-eslint": "10.1.0",
|
||||
"cypress": "6.2.1",
|
||||
"eslint": "7.17.0",
|
||||
"eslint-config-react-app": "5.2.1",
|
||||
"eslint-plugin-cypress": "2.11.1",
|
||||
"eslint-plugin-flowtype": "5.2.0",
|
||||
"eslint-plugin-import": "2.22.1",
|
||||
"eslint-plugin-jsx-a11y": "6.4.1",
|
||||
"eslint-plugin-react": "7.21.5",
|
||||
"eslint-plugin-react-hooks": "4.2.0",
|
||||
"husky": "4.3.7",
|
||||
"jest": "26.6.3",
|
||||
"jest-environment-jsdom-fourteen": "1.0.1",
|
||||
"jest-watch-typeahead": "0.6.1",
|
||||
"lint-staged": "10.5.1",
|
||||
"eslint": "7.21.0",
|
||||
"husky": "5.1.2",
|
||||
"lint-staged": "10.5.4",
|
||||
"nodemon": "2.0.7",
|
||||
"npm-run-all": "4.1.5",
|
||||
"prettier": "2.2.0",
|
||||
"prettier": "2.2.1",
|
||||
"pretty-quick": "3.1.0",
|
||||
"react-test-renderer": "16.14.0",
|
||||
"start-server-and-test": "1.11.2",
|
||||
"ts-jest": "26.4.4"
|
||||
"start-server-and-test": "1.11.7"
|
||||
},
|
||||
"private": true
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const {createServer} = require("http")
|
||||
const {parse} = require("url")
|
||||
const blitz = require("@blitzjs/server")
|
||||
const {log} = require("@blitzjs/display")
|
||||
import blitz from "blitz/custom-server"
|
||||
import {createServer} from "http"
|
||||
import {parse} from "url"
|
||||
import {log} from "@blitzjs/display"
|
||||
|
||||
const {PORT = "3000"} = process.env
|
||||
const dev = process.env.NODE_ENV !== "production"
|
||||
@@ -10,7 +10,7 @@ const handle = app.getRequestHandler()
|
||||
|
||||
app.prepare().then(() => {
|
||||
createServer((req, res) => {
|
||||
const parsedUrl = parse(req.url, true)
|
||||
const parsedUrl = parse(req.url!, true)
|
||||
const {pathname} = parsedUrl
|
||||
|
||||
if (pathname === "/hello") {
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from "react"
|
||||
import {RouterContext, BlitzRouter} from "blitz"
|
||||
import {RouterContext, BlitzRouter, BlitzProvider} from "blitz"
|
||||
import {render as defaultRender} from "@testing-library/react"
|
||||
import {renderHook as defaultRenderHook} from "@testing-library/react-hooks"
|
||||
|
||||
@@ -25,11 +25,18 @@ export * from "@testing-library/react"
|
||||
// router: { pathname: '/my-custom-pathname' },
|
||||
// });
|
||||
// --------------------------------------------------
|
||||
export function render(ui: RenderUI, {wrapper, router, ...options}: RenderOptions = {}) {
|
||||
export function render(
|
||||
ui: RenderUI,
|
||||
{wrapper, router, dehydratedState, ...options}: RenderOptions = {},
|
||||
) {
|
||||
if (!wrapper) {
|
||||
// Add a default context wrapper if one isn't supplied from the test
|
||||
wrapper = ({children}) => (
|
||||
<RouterContext.Provider value={{...mockRouter, ...router}}>{children}</RouterContext.Provider>
|
||||
<BlitzProvider dehydratedState={dehydratedState}>
|
||||
<RouterContext.Provider value={{...mockRouter, ...router}}>
|
||||
{children}
|
||||
</RouterContext.Provider>
|
||||
</BlitzProvider>
|
||||
)
|
||||
}
|
||||
return defaultRender(ui, {wrapper, ...options})
|
||||
@@ -48,12 +55,16 @@ export function render(ui: RenderUI, {wrapper, router, ...options}: RenderOption
|
||||
// --------------------------------------------------
|
||||
export function renderHook(
|
||||
hook: RenderHook,
|
||||
{wrapper, router, ...options}: RenderHookOptions = {},
|
||||
{wrapper, router, dehydratedState, ...options}: RenderHookOptions = {},
|
||||
) {
|
||||
if (!wrapper) {
|
||||
// Add a default context wrapper if one isn't supplied from the test
|
||||
wrapper = ({children}) => (
|
||||
<RouterContext.Provider value={{...mockRouter, ...router}}>{children}</RouterContext.Provider>
|
||||
<BlitzProvider dehydratedState={dehydratedState}>
|
||||
<RouterContext.Provider value={{...mockRouter, ...router}}>
|
||||
{children}
|
||||
</RouterContext.Provider>
|
||||
</BlitzProvider>
|
||||
)
|
||||
}
|
||||
return defaultRenderHook(hook, {wrapper, ...options})
|
||||
@@ -67,6 +78,8 @@ export const mockRouter: BlitzRouter = {
|
||||
params: {},
|
||||
query: {},
|
||||
isReady: true,
|
||||
isLocaleDomain: false,
|
||||
isPreview: false,
|
||||
push: jest.fn(),
|
||||
replace: jest.fn(),
|
||||
reload: jest.fn(),
|
||||
@@ -83,8 +96,14 @@ export const mockRouter: BlitzRouter = {
|
||||
|
||||
type DefaultParams = Parameters<typeof defaultRender>
|
||||
type RenderUI = DefaultParams[0]
|
||||
type RenderOptions = DefaultParams[1] & {router?: Partial<BlitzRouter>}
|
||||
type RenderOptions = DefaultParams[1] & {
|
||||
router?: Partial<BlitzRouter>
|
||||
dehydratedState?: unknown
|
||||
}
|
||||
|
||||
type DefaultHookParams = Parameters<typeof defaultRenderHook>
|
||||
type RenderHook = DefaultHookParams[0]
|
||||
type RenderHookOptions = DefaultHookParams[1] & {router?: Partial<BlitzRouter>}
|
||||
type RenderHookOptions = DefaultHookParams[1] & {
|
||||
router?: Partial<BlitzRouter>
|
||||
dehydratedState?: unknown
|
||||
}
|
||||
|
||||
1
examples/custom-server/types
Symbolic link
1
examples/custom-server/types
Symbolic link
@@ -0,0 +1 @@
|
||||
../../types
|
||||
@@ -1,5 +1,4 @@
|
||||
import {DefaultCtx, SessionContext} from "blitz"
|
||||
import {simpleRolesIsAuthorized} from "@blitzjs/server"
|
||||
import {DefaultCtx, SessionContext, SimpleRolesIsAuthorized} from "blitz"
|
||||
import React from "react"
|
||||
|
||||
declare module "blitz" {
|
||||
@@ -7,10 +6,9 @@ declare module "blitz" {
|
||||
session: SessionContext
|
||||
}
|
||||
export interface Session {
|
||||
isAuthorized: typeof simpleRolesIsAuthorized
|
||||
isAuthorized: SimpleRolesIsAuthorized
|
||||
PublicData: {
|
||||
userId: number
|
||||
roles: string[]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
save-exact=true
|
||||
legacy-peer-deps=true
|
||||
|
||||
@@ -31,7 +31,7 @@ FAUNA_SECRET=YOUR_AUTH_KEY
|
||||
2. Start the dev server
|
||||
|
||||
```
|
||||
yarn blitz start
|
||||
yarn blitz dev
|
||||
```
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
|
||||
@@ -9,7 +9,7 @@ export default async function login(input: LoginInputType, { session }: Ctx) {
|
||||
// This throws an error if credentials are invalid
|
||||
const user = await authenticateUser(email, password)
|
||||
|
||||
await session.$create({ userId: user.id, roles: [user.role] })
|
||||
await session.$create({ userId: user.id })
|
||||
|
||||
return user
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ export default async function signup(input: SignupInputType, { session }: Ctx) {
|
||||
)
|
||||
console.log("Create user result:", user)
|
||||
|
||||
await session.$create({ userId: user.id, roles: [user.role] })
|
||||
await session.$create({ userId: user.id })
|
||||
|
||||
return user
|
||||
}
|
||||
|
||||
@@ -1,21 +1,17 @@
|
||||
import { AppProps, ErrorComponent, useRouter } from "blitz"
|
||||
import { AppProps, ErrorComponent, useRouter, useQueryErrorResetBoundary } from "blitz"
|
||||
import { ErrorBoundary, FallbackProps } from "react-error-boundary"
|
||||
import { queryCache } from "react-query"
|
||||
import LoginForm from "app/auth/components/LoginForm"
|
||||
|
||||
export default function App({ Component, pageProps }: AppProps) {
|
||||
const getLayout = Component.getLayout || ((page) => page)
|
||||
const router = useRouter()
|
||||
const { reset } = useQueryErrorResetBoundary()
|
||||
|
||||
return (
|
||||
<ErrorBoundary
|
||||
FallbackComponent={RootErrorFallback}
|
||||
resetKeys={[router.asPath]}
|
||||
onReset={() => {
|
||||
// This ensures the Blitz useQuery hooks will automatically refetch
|
||||
// data any time you reset the error boundary
|
||||
queryCache.resetErrorBoundaries()
|
||||
}}
|
||||
onReset={reset}
|
||||
>
|
||||
{getLayout(<Component {...pageProps} />)}
|
||||
</ErrorBoundary>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const { sessionMiddleware, simpleRolesIsAuthorized } = require("@blitzjs/server")
|
||||
const { sessionMiddleware, simpleRolesIsAuthorized } = require("blitz")
|
||||
const { GraphQLClient, gql } = require("graphql-request")
|
||||
|
||||
const graphQLClient = new GraphQLClient("https://graphql.fauna.com/graphql", {
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
{
|
||||
"name": "@examples/fauna",
|
||||
"version": "0.30.0-canary.4",
|
||||
"version": "0.34.0-canary.0",
|
||||
"scripts": {
|
||||
"dev": "blitz dev",
|
||||
"build": "blitz build",
|
||||
"start": "blitz start",
|
||||
"studio": "blitz prisma studio",
|
||||
"build": "blitz build",
|
||||
"lint": "eslint --ignore-path .gitignore --ext .js,.ts,.tsx .",
|
||||
"test": "echo \"No tests yet\""
|
||||
},
|
||||
@@ -27,44 +28,33 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"blitz": "0.30.0-canary.4",
|
||||
"blitz": "0.35.0-canary.0",
|
||||
"final-form": "4.20.1",
|
||||
"graphql": "15.4.0",
|
||||
"graphql": "15.5.0",
|
||||
"graphql-request": "3.4.0",
|
||||
"react": "0.0.0-experimental-3310209d0",
|
||||
"react-dom": "0.0.0-experimental-3310209d0",
|
||||
"react-error-boundary": "3.1.0",
|
||||
"react": "0.0.0-experimental-6a589ad71",
|
||||
"react-dom": "0.0.0-experimental-6a589ad71",
|
||||
"react-error-boundary": "3.1.1",
|
||||
"react-final-form": "6.5.2",
|
||||
"secure-password": "4.0.0",
|
||||
"zod": "1.11.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@testing-library/jest-dom": "5.11.8",
|
||||
"@testing-library/react": "11.2.3",
|
||||
"@testing-library/react-hooks": "4.0.1",
|
||||
"@types/jest": "26.0.20",
|
||||
"@types/react": "17.0.0",
|
||||
"@testing-library/react": "11.2.5",
|
||||
"@testing-library/react-hooks": "^4.0.1",
|
||||
"@types/react": "17.0.2",
|
||||
"@types/secure-password": "3.1.0",
|
||||
"@typescript-eslint/eslint-plugin": "4.12.0",
|
||||
"@typescript-eslint/parser": "4.12.0",
|
||||
"babel-eslint": "10.1.0",
|
||||
"eslint": "7.17.0",
|
||||
"eslint-config-react-app": "6.0.0",
|
||||
"eslint-plugin-flowtype": "5.2.0",
|
||||
"eslint-plugin-import": "2.22.1",
|
||||
"eslint-plugin-jsx-a11y": "6.4.1",
|
||||
"eslint-plugin-react": "7.22.0",
|
||||
"eslint-plugin-react-hooks": "4.2.0",
|
||||
"husky": "4.3.7",
|
||||
"jest": "26.6.3",
|
||||
"jest-environment-jsdom-fourteen": "1.0.1",
|
||||
"jest-watch-typeahead": "0.6.1",
|
||||
"lint-staged": "10.5.3",
|
||||
"prettier": "2.2.1",
|
||||
"pretty-quick": "3.1.0",
|
||||
"babel-eslint": "~10.1.0",
|
||||
"eslint": "7.21.0",
|
||||
"eslint-config-react-app": "~6.0.0",
|
||||
"eslint-plugin-flowtype": "~5.2.0",
|
||||
"eslint-plugin-import": "~2.22.1",
|
||||
"eslint-plugin-jsx-a11y": "~6.4.1",
|
||||
"eslint-plugin-react": "~7.22.0",
|
||||
"eslint-plugin-react-hooks": "~4.2.0",
|
||||
"husky": "5.1.2",
|
||||
"start-server-and-test": "1.11.7",
|
||||
"ts-jest": "26.4.4",
|
||||
"typescript": "4.1.3"
|
||||
"typescript": "4.1.5"
|
||||
},
|
||||
"private": true
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { RouterContext, BlitzRouter } from "blitz"
|
||||
import { RouterContext, BlitzRouter, BlitzProvider } from "blitz"
|
||||
import { render as defaultRender } from "@testing-library/react"
|
||||
import { renderHook as defaultRenderHook } from "@testing-library/react-hooks"
|
||||
|
||||
@@ -15,11 +15,17 @@ export * from "@testing-library/react"
|
||||
|
||||
type DefaultParams = Parameters<typeof defaultRender>
|
||||
type RenderUI = DefaultParams[0]
|
||||
type RenderOptions = DefaultParams[1] & { router?: Partial<BlitzRouter> }
|
||||
type RenderOptions = DefaultParams[1] & {
|
||||
router?: Partial<BlitzRouter>
|
||||
dehydratedState?: unknown
|
||||
}
|
||||
|
||||
type DefaultHookParams = Parameters<typeof defaultRenderHook>
|
||||
type RenderHook = DefaultHookParams[0]
|
||||
type RenderHookOptions = DefaultHookParams[1] & { router?: Partial<BlitzRouter> }
|
||||
type RenderHookOptions = DefaultHookParams[1] & {
|
||||
router?: Partial<BlitzRouter>
|
||||
dehydratedState?: unknown
|
||||
}
|
||||
|
||||
// --------------------------------------------------
|
||||
// render()
|
||||
@@ -32,13 +38,18 @@ type RenderHookOptions = DefaultHookParams[1] & { router?: Partial<BlitzRouter>
|
||||
// router: { pathname: '/my-custom-pathname' },
|
||||
// });
|
||||
// --------------------------------------------------
|
||||
export function render(ui: RenderUI, { wrapper, router, ...options }: RenderOptions = {}) {
|
||||
export function render(
|
||||
ui: RenderUI,
|
||||
{ wrapper, router, dehydratedState, ...options }: RenderOptions = {}
|
||||
) {
|
||||
if (!wrapper) {
|
||||
// Add a default context wrapper if one isn't supplied from the test
|
||||
wrapper = ({ children }) => (
|
||||
<RouterContext.Provider value={{ ...mockRouter, ...router }}>
|
||||
{children}
|
||||
</RouterContext.Provider>
|
||||
<BlitzProvider dehydratedState={dehydratedState}>
|
||||
<RouterContext.Provider value={{ ...mockRouter, ...router }}>
|
||||
{children}
|
||||
</RouterContext.Provider>
|
||||
</BlitzProvider>
|
||||
)
|
||||
}
|
||||
return defaultRender(ui, { wrapper, ...options })
|
||||
@@ -57,14 +68,16 @@ export function render(ui: RenderUI, { wrapper, router, ...options }: RenderOpti
|
||||
// --------------------------------------------------
|
||||
export function renderHook(
|
||||
hook: RenderHook,
|
||||
{ wrapper, router, ...options }: RenderHookOptions = {}
|
||||
{ wrapper, router, dehydratedState, ...options }: RenderHookOptions = {}
|
||||
) {
|
||||
if (!wrapper) {
|
||||
// Add a default context wrapper if one isn't supplied from the test
|
||||
wrapper = ({ children }) => (
|
||||
<RouterContext.Provider value={{ ...mockRouter, ...router }}>
|
||||
{children}
|
||||
</RouterContext.Provider>
|
||||
<BlitzProvider dehydratedState={dehydratedState}>
|
||||
<RouterContext.Provider value={{ ...mockRouter, ...router }}>
|
||||
{children}
|
||||
</RouterContext.Provider>
|
||||
</BlitzProvider>
|
||||
)
|
||||
}
|
||||
return defaultRenderHook(hook, { wrapper, ...options })
|
||||
@@ -78,6 +91,8 @@ export const mockRouter: BlitzRouter = {
|
||||
params: {},
|
||||
query: {},
|
||||
isReady: true,
|
||||
isLocaleDomain: false,
|
||||
isPreview: false,
|
||||
push: jest.fn(),
|
||||
replace: jest.fn(),
|
||||
reload: jest.fn(),
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user