Compare commits
35 Commits
react18
...
@blitzjs/a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c6e8df5efd | ||
|
|
46a34c7b3a | ||
|
|
18d4ef74a9 | ||
|
|
212a1cb941 | ||
|
|
151dcc308e | ||
|
|
fd90cbedb4 | ||
|
|
10d27c74af | ||
|
|
9810d984f1 | ||
|
|
10574b7359 | ||
|
|
c28684b8e5 | ||
|
|
a590820c14 | ||
|
|
ff3ef58a47 | ||
|
|
e33c81a70f | ||
|
|
dc41f8eedc | ||
|
|
f49aa37892 | ||
|
|
e218d5f84d | ||
|
|
2eb4f791a8 | ||
|
|
fa0850d0a3 | ||
|
|
2d3c6cbfb8 | ||
|
|
6ff7e99d15 | ||
|
|
77ca03af96 | ||
|
|
f56ef9b2c5 | ||
|
|
991b4a9db0 | ||
|
|
0c8edbb8b2 | ||
|
|
2d75a2b7c7 | ||
|
|
b878c6845e | ||
|
|
cb0da6a0cb | ||
|
|
a47c643145 | ||
|
|
945c66a53c | ||
|
|
50421a19ed | ||
|
|
926e5cf10f | ||
|
|
6228366ea3 | ||
|
|
bcb56eb79d | ||
|
|
80f0c81130 | ||
|
|
0847896968 |
@@ -1519,7 +1519,8 @@
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/11509865?v=4",
|
||||
"profile": "http://taloranderson.com",
|
||||
"contributions": [
|
||||
"code"
|
||||
"code",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -1546,7 +1547,8 @@
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/244174?v=4",
|
||||
"profile": "https://github.com/piotrski",
|
||||
"contributions": [
|
||||
"code"
|
||||
"code",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -2003,7 +2005,9 @@
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/9019397?v=4",
|
||||
"profile": "http://aleksandra.codes",
|
||||
"contributions": [
|
||||
"code"
|
||||
"code",
|
||||
"doc",
|
||||
"test"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -2849,7 +2853,8 @@
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/2716058?v=4",
|
||||
"profile": "http://anolilab.de",
|
||||
"contributions": [
|
||||
"code"
|
||||
"code",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -2870,6 +2875,721 @@
|
||||
"test",
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "ricardo-rp",
|
||||
"name": "Ricardo Romero",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/30808767?v=4",
|
||||
"profile": "https://github.com/ricardo-rp",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "anothernode",
|
||||
"name": "Moritz Reiter",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/3286144?v=4",
|
||||
"profile": "exocortex.anothernode.com",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "msichterman",
|
||||
"name": "Matt Sichterman",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/38794918?v=4",
|
||||
"profile": "https://msich.dev",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "medihack",
|
||||
"name": "Kai Schlamp",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/120626?v=4",
|
||||
"profile": "https://github.com/medihack",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "muyiwaolu",
|
||||
"name": "Muyiwa Olu",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/6832244?v=4",
|
||||
"profile": "https://muyiwa.me",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "rabbihossain",
|
||||
"name": "Rabbi Hossain",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/4346154?v=4",
|
||||
"profile": "http://2hr.me/",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "bravo-kernel",
|
||||
"name": "bravo-kernel",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/230500?v=4",
|
||||
"profile": "https://github.com/bravo-kernel",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "sam3d",
|
||||
"name": "Sam Holmes",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/8385528?v=4",
|
||||
"profile": "https://samholmes.net",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "doncicuto",
|
||||
"name": "Miguel Cabrerizo",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/30386061?v=4",
|
||||
"profile": "https://doncicuto.medium.com",
|
||||
"contributions": [
|
||||
"code",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "zenhob",
|
||||
"name": "Zack Hobson",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/12092?v=4",
|
||||
"profile": "http://zackhobson.com/",
|
||||
"contributions": [
|
||||
"code",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "m5r",
|
||||
"name": "Mokhtar",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/13026820?v=4",
|
||||
"profile": "https://www.mokhtar.dev",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "kenkuan",
|
||||
"name": "Ken Kuan",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1924968?v=4",
|
||||
"profile": "https://github.com/kenkuan",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "meehawk",
|
||||
"name": "meehawk",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/80167324?v=4",
|
||||
"profile": "https://github.com/meehawk",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "ravindranrahul",
|
||||
"name": "Rahul Ravindran",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/10168946?v=4",
|
||||
"profile": "rahulravindran.in",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "s-r-x",
|
||||
"name": "Ilya",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/41614937?v=4",
|
||||
"profile": "https://github.com/s-r-x",
|
||||
"contributions": [
|
||||
"code",
|
||||
"doc",
|
||||
"test"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "hashimwarren",
|
||||
"name": "Hashim Warren",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/6027587?v=4",
|
||||
"profile": "https://github.com/hashimwarren",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "damilolarandolph",
|
||||
"name": "Damilola Randolph",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/43427949?v=4",
|
||||
"profile": "https://damilolarandolph.com",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "mwcampbell",
|
||||
"name": "Matt Campbell",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/214820?v=4",
|
||||
"profile": "https://github.com/mwcampbell",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "ratson",
|
||||
"name": "(◕ᴥ◕)",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/2682937?v=4",
|
||||
"profile": "https://github.com/ratson",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "maciejmyslinski",
|
||||
"name": "Mat Milbury",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/11421186?v=4",
|
||||
"profile": "maciejmyslinski.com",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "andreasasprou",
|
||||
"name": "Andreas Asprou",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/8077469?v=4",
|
||||
"profile": "https://andreas.fyi",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "kotx",
|
||||
"name": "Kot",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/33439542?v=4",
|
||||
"profile": "https://github.com/kotx",
|
||||
"contributions": [
|
||||
"code",
|
||||
"test",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "isaka1022",
|
||||
"name": "Amane",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/28589716?v=4",
|
||||
"profile": "https://github.com/isaka1022",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "fuzzthink",
|
||||
"name": "John Leung",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/20699?v=4",
|
||||
"profile": "johnleung.com",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "bcye",
|
||||
"name": "Bruce",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/29666239?v=4",
|
||||
"profile": "roettgers.co",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "emilygracekz",
|
||||
"name": "Emily",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/57361805?v=4",
|
||||
"profile": "https://github.com/emilygracekz",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "npverni",
|
||||
"name": "Nathan Verni",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/3537?v=4",
|
||||
"profile": "https://github.com/npverni",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "davyengone",
|
||||
"name": "Davy Engone",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/4896002?v=4",
|
||||
"profile": "https://davyengone.io",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Fedeorlandau",
|
||||
"name": "Federico Joel Orlandau",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/10283686?v=4",
|
||||
"profile": "https://fedeorlandau.dev/",
|
||||
"contributions": [
|
||||
"doc",
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "johnmurphy01",
|
||||
"name": "John Murphy",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/2939548?v=4",
|
||||
"profile": "https://github.com/johnmurphy01",
|
||||
"contributions": [
|
||||
"doc",
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "martinsaxa",
|
||||
"name": "martinsaxa",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/33789474?v=4",
|
||||
"profile": "https://github.com/martinsaxa",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "ajwgeek",
|
||||
"name": "Austin Walhof",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/2135600?v=4",
|
||||
"profile": "https://github.com/ajwgeek",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "g3offrey",
|
||||
"name": "Geoffrey",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/11151445?v=4",
|
||||
"profile": "g3offrey.dev",
|
||||
"contributions": [
|
||||
"code",
|
||||
"doc",
|
||||
"test"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "keevan",
|
||||
"name": "Kevin Pham",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/9924643?v=4",
|
||||
"profile": "https://github.com/keevan",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "kimngan-bui",
|
||||
"name": "kimngan-bui",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/20723478?v=4",
|
||||
"profile": "https://github.com/kimngan-bui",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "9j",
|
||||
"name": "Bahk Chanhee",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/11691670?v=4",
|
||||
"profile": "world.hey.com/bach",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Vandivier",
|
||||
"name": "John Vandivier",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/5559355?v=4",
|
||||
"profile": "http://www.afterecon.com/",
|
||||
"contributions": [
|
||||
"code",
|
||||
"test",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "namirsab",
|
||||
"name": "Namir",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/6980777?v=4",
|
||||
"profile": "http://namirsab.github.io",
|
||||
"contributions": [
|
||||
"doc",
|
||||
"code",
|
||||
"test"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "scttcper",
|
||||
"name": "Scott Cooper",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1400464?v=4",
|
||||
"profile": "https://twitter.com/scttcper",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Abduttayyeb",
|
||||
"name": "Abduttayyeb M.r",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/55306260?v=4",
|
||||
"profile": "abduttayyeb.github.io",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "maybebored",
|
||||
"name": "Mayuran",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/20951181?v=4",
|
||||
"profile": "https://github.com/maybebored",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "MuckHub",
|
||||
"name": "Aleksei Vesselko",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/54979136?v=4",
|
||||
"profile": "https://github.com/MuckHub",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "p-siriphanthong",
|
||||
"name": "Punn Siriphanthong",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/29949429?v=4",
|
||||
"profile": "https://p-siriphanthong.github.io/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "shawn-fetanat",
|
||||
"name": "Shawn Fetanat",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/83679827?v=4",
|
||||
"profile": "https://my-portfolio-292eb.web.app",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "mochi-sann",
|
||||
"name": "Moyuru",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/44772513?v=4",
|
||||
"profile": "https://github.com/mochi-sann",
|
||||
"contributions": [
|
||||
"code",
|
||||
"test",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "camsloanftc",
|
||||
"name": "Cam Sloan",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/16295659?v=4",
|
||||
"profile": "https://github.com/camsloanftc",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "sitek94",
|
||||
"name": "Maciek Sitkowski",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/58401630?v=4",
|
||||
"profile": "https://macieksitkowski.com",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "vivek7405",
|
||||
"name": "Vivek",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/24492244?v=4",
|
||||
"profile": "https://github.com/vivek7405",
|
||||
"contributions": [
|
||||
"doc",
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "cj",
|
||||
"name": "CJ Lazell",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1819?v=4",
|
||||
"profile": "http://cj.io",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "RobertBroersma",
|
||||
"name": "Robert",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/4519828?v=4",
|
||||
"profile": "robertbroersma.com",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "cbejensen",
|
||||
"name": "Christian Jensen",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/12374723?v=4",
|
||||
"profile": "https://christianjensen.netlify.com",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "dvnrsn",
|
||||
"name": "Devin Rasmussen",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/9112811?v=4",
|
||||
"profile": "https://github.com/dvnrsn",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "devtombiz",
|
||||
"name": "Thomas Brenneur",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/23282613?v=4",
|
||||
"profile": "www.linkedin.com/in/devtom",
|
||||
"contributions": [
|
||||
"code",
|
||||
"doc",
|
||||
"test"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "lucasvazq",
|
||||
"name": "Lucas Vazquez",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/38964964?v=4",
|
||||
"profile": "https://lucasvazq.github.io/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "chrisj-back2work",
|
||||
"name": "Chris Johnson",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/68551954?v=4",
|
||||
"profile": "https://github.com/chrisj-back2work",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "thisdotrob",
|
||||
"name": "Rob Stevenson",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/12902589?v=4",
|
||||
"profile": "https://github.com/thisdotrob",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "lovethebomb",
|
||||
"name": "Lucas Heymès",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1363056?v=4",
|
||||
"profile": "www.lucas.computer",
|
||||
"contributions": [
|
||||
"code",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "NorfeldtAbtion",
|
||||
"name": "Lasse Norfeldt",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/53769763?v=4",
|
||||
"profile": "https://github.com/NorfeldtAbtion",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "netwarex",
|
||||
"name": "Péter Nyári",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/6048614?v=4",
|
||||
"profile": "https://nyaripeter.hu/",
|
||||
"contributions": [
|
||||
"doc",
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "5minpause",
|
||||
"name": "Holger Frohloff",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/84148?v=4",
|
||||
"profile": "https://www.holgerfrohloff.de",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "basilk76",
|
||||
"name": "Basil Khan",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/45275512?v=4",
|
||||
"profile": "https://github.com/basilk76",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "danestves",
|
||||
"name": "Daniel Esteves",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/31737273?v=4",
|
||||
"profile": "https://danestves.com/",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "coryhouse",
|
||||
"name": "Cory House",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1688997?v=4",
|
||||
"profile": "http://www.bitnative.com",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "rockmanvnx6",
|
||||
"name": "Austin (Thang Pham)",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/16440123?v=4",
|
||||
"profile": "https://auspham.dev/",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "noxify",
|
||||
"name": "Marcus Reinhardt",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/521777?v=4",
|
||||
"profile": "https://jammeryhq.com",
|
||||
"contributions": [
|
||||
"doc",
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "davidchristie",
|
||||
"name": "David Christie",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/12044333?v=4",
|
||||
"profile": "https://github.com/davidchristie",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "ajanth97",
|
||||
"name": "Ajanth",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/50458502?v=4",
|
||||
"profile": "https://github.com/ajanth97",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "divpreet",
|
||||
"name": "Div",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/2805650?v=4",
|
||||
"profile": "https://github.com/divpreet",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "david-arteaga",
|
||||
"name": "David Arteaga",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/7199015?v=4",
|
||||
"profile": "https://github.com/david-arteaga",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "MukulKolpe",
|
||||
"name": "Mukul Kolpe",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/78664749?v=4",
|
||||
"profile": "https://github.com/MukulKolpe",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "skotchpine",
|
||||
"name": "tyler",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/13043909?v=4",
|
||||
"profile": "https://github.com/skotchpine",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "SofianeDjellouli",
|
||||
"name": "Sofiane Djellouli",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/38258952?v=4",
|
||||
"profile": "https://github.com/SofianeDjellouli",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "kreako",
|
||||
"name": "kreako",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/65113001?v=4",
|
||||
"profile": "https://github.com/kreako",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "sarahdayan",
|
||||
"name": "Sarah Dayan",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/5370675?v=4",
|
||||
"profile": "https://sarahdayan.dev",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "c-ciobanu",
|
||||
"name": "Cristi Ciobanu",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/33382714?v=4",
|
||||
"profile": "https://github.com/c-ciobanu",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "arpitdalal",
|
||||
"name": "Arpit Dalal",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/61059807?v=4",
|
||||
"profile": "https://arpitdalal.dev",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "robertrisch",
|
||||
"name": "robertrisch",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/73828816?v=4",
|
||||
"profile": "https://github.com/robertrisch",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 7,
|
||||
|
||||
8
.changeset/README.md
Normal file
8
.changeset/README.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# Changesets
|
||||
|
||||
Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
|
||||
with multi-package repos, or single-package repos to help you version and publish your code. You can
|
||||
find the full documentation for it [in our repository](https://github.com/changesets/changesets)
|
||||
|
||||
We have a quick list of common questions to get you started engaging with this project in
|
||||
[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
|
||||
11
.changeset/config.json
Normal file
11
.changeset/config.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"$schema": "https://unpkg.com/@changesets/config@2.0.0/schema.json",
|
||||
"changelog": "@changesets/cli/changelog",
|
||||
"commit": false,
|
||||
"fixed": [["blitz"], ["@blitzjs/*"]],
|
||||
"linked": [],
|
||||
"access": "restricted",
|
||||
"baseBranch": "main",
|
||||
"updateInternalDependencies": "patch",
|
||||
"ignore": ["web", "test-*"]
|
||||
}
|
||||
9
.changeset/ninety-pets-heal.md
Normal file
9
.changeset/ninety-pets-heal.md
Normal file
@@ -0,0 +1,9 @@
|
||||
---
|
||||
"blitz": patch
|
||||
"@blitzjs/auth": patch
|
||||
"@blitzjs/next": patch
|
||||
"@blitzjs/rpc": patch
|
||||
"@blitzjs/generator": patch
|
||||
---
|
||||
|
||||
initial publish
|
||||
18
.changeset/pre.json
Normal file
18
.changeset/pre.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"mode": "pre",
|
||||
"tag": "alpha",
|
||||
"initialVersions": {
|
||||
"web": "0.0.0",
|
||||
"test-auth": "0.0.0",
|
||||
"test-rpc": "0.0.0",
|
||||
"test-utils": "0.0.0",
|
||||
"blitz": "2.0.0-alpha.0",
|
||||
"@blitzjs/auth": "2.0.0-alpha.0",
|
||||
"@blitzjs/next": "2.0.0-alpha.0",
|
||||
"@blitzjs/rpc": "2.0.0-alpha.0",
|
||||
"@blitzjs/config": "0.0.0",
|
||||
"@blitzjs/generator": "2.0.0-alpha.0",
|
||||
"template": "0.0.0"
|
||||
},
|
||||
"changesets": ["ninety-pets-heal"]
|
||||
}
|
||||
@@ -1,53 +1,2 @@
|
||||
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
|
||||
/packages/babel-preset/src/fix-node-file-trace/tests/**
|
||||
/test/integration/**/out/**
|
||||
/nextjs/packages/create-next-app
|
||||
|
||||
|
||||
// COPIED FROM nextjs/.eslintignore
|
||||
/nextjs/**/.next/**
|
||||
/nextjs/**/_next/**
|
||||
/nextjs/**/dist/**
|
||||
/nextjs/examples/**
|
||||
/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/integration/eslint/**
|
||||
/nextjs/test-timings.json
|
||||
**/package.json
|
||||
*.d.ts
|
||||
|
||||
111
.eslintrc.js
111
.eslintrc.js
@@ -1,111 +0,0 @@
|
||||
module.exports = {
|
||||
parser: "babel-eslint",
|
||||
env: {
|
||||
browser: true,
|
||||
commonjs: true,
|
||||
es6: true,
|
||||
node: true,
|
||||
},
|
||||
parserOptions: {
|
||||
ecmaVersion: 6,
|
||||
sourceType: "module",
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
},
|
||||
},
|
||||
plugins: ["import", "unicorn", "simple-import-sort"],
|
||||
extends: ["react-app"],
|
||||
rules: {
|
||||
"react/react-in-jsx-scope": "off", // React is always in scope with Blitz
|
||||
"jsx-a11y/anchor-is-valid": "off", //Doesn't play well with Blitz/Next <Link> usage
|
||||
"import/first": "off",
|
||||
"import/no-default-export": "error",
|
||||
"require-await": "error",
|
||||
"no-async-promise-executor": "error",
|
||||
"unicorn/filename-case": [
|
||||
"error",
|
||||
{
|
||||
case: "kebabCase",
|
||||
},
|
||||
],
|
||||
"simple-import-sort/imports": [
|
||||
"warn",
|
||||
{
|
||||
groups: [
|
||||
[
|
||||
// Side effect imports.
|
||||
"^\\u0000",
|
||||
// Packages.
|
||||
// Things that start with a letter (or digit or underscore), or `@` followed by a letter.
|
||||
"^@?\\w",
|
||||
// Absolute imports and other imports such as Vue-style `@/foo`.
|
||||
// Anything that does not start with a dot.
|
||||
"^[^.]",
|
||||
// Relative imports.
|
||||
// Anything that starts with a dot.
|
||||
"^\\.",
|
||||
],
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
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: ["examples/**"],
|
||||
plugins: ["cypress"],
|
||||
parserOptions: {
|
||||
project: null,
|
||||
},
|
||||
env: {
|
||||
"cypress/globals": true,
|
||||
},
|
||||
rules: {
|
||||
"simple-import-sort/imports": "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",
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
5
.github/CODEOWNERS
vendored
5
.github/CODEOWNERS
vendored
@@ -1,8 +1,5 @@
|
||||
# https://help.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners
|
||||
|
||||
* @flybayer
|
||||
* @flybayer @beerose
|
||||
|
||||
# packages/cli/**/* @aem, @flybayer
|
||||
# packages/generator/**/* @aem @flybayer
|
||||
packages/generator/templates**/* @flybayer
|
||||
# packages/installer/**/* @aem @flybayer
|
||||
|
||||
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -16,4 +16,4 @@ Closes: ?
|
||||
## 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))
|
||||
- [ ] Documentation added/updated (submit PR to [blitzjs.com repo `canary` branch](https://github.com/blitz-js/blitzjs.com/tree/canary))
|
||||
|
||||
18
.github/checkInstallTime.js
vendored
18
.github/checkInstallTime.js
vendored
@@ -1,18 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const fs = require("fs")
|
||||
const yarnOut = fs.readFileSync(0, {encoding: "utf8"})
|
||||
|
||||
const [installTimeString] = /(?<=^Done in )\d+\.\d+(?=s\.$)/m.exec(yarnOut)
|
||||
const installTime = Number(installTimeString)
|
||||
|
||||
console.log(`Install time: ${installTime}s`)
|
||||
|
||||
if (installTime < 30) {
|
||||
console.log("We're below 30 secs. That's awesome!")
|
||||
} else if (installTime < 50) {
|
||||
console.log("We're below 50 secs. That's fine!")
|
||||
} else {
|
||||
console.log("We're above 50 secs. That's not great!")
|
||||
process.exit(1)
|
||||
}
|
||||
22
.github/workflows/compressed.yml
vendored
22
.github/workflows/compressed.yml
vendored
@@ -1,22 +0,0 @@
|
||||
name: Size Check
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [master, canary]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Compressed Size
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Count size
|
||||
uses: preactjs/compressed-size-action@v2
|
||||
with:
|
||||
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
exclude: "{./nextjs/**}"
|
||||
377
.github/workflows/main.yml
vendored
377
.github/workflows/main.yml
vendored
@@ -11,369 +11,28 @@ concurrency:
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
name: Lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: "14"
|
||||
- name: Get yarn cache directory path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- name: Cache node_modules
|
||||
id: yarn-cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
**/node_modules
|
||||
key: ${{ runner.os }}-${{ runner.node_version}}-yarn-v13-${{ hashFiles('yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ runner.node_version}}-yarn-v13-
|
||||
- name: Install dependencies
|
||||
run: yarn install --frozen-lockfile --silent
|
||||
env:
|
||||
CI: true
|
||||
- name: manypkg lint
|
||||
run: yarn manypkg check
|
||||
env:
|
||||
CI: true
|
||||
- name: Build next.js
|
||||
run: yarn build:nextjs
|
||||
env:
|
||||
CI: true
|
||||
- name: yarn lint
|
||||
run: yarn lint
|
||||
env:
|
||||
CI: true
|
||||
|
||||
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: "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)"
|
||||
- name: Cache node_modules
|
||||
id: yarn-cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
**/node_modules
|
||||
key: ${{ runner.os }}-${{ runner.node_version}}-yarn-v13-${{ hashFiles('yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ runner.node_version}}-yarn-v13-
|
||||
- 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: Test Blitz Packages
|
||||
run: yarn testonly:packages
|
||||
env:
|
||||
CI: true
|
||||
|
||||
testBlitzExamples:
|
||||
timeout-minutes: 30
|
||||
name: Blitz - Test Example Apps
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
NEXT_TELEMETRY_DISABLED: 1
|
||||
strategy:
|
||||
matrix:
|
||||
os:
|
||||
- ubuntu-latest
|
||||
node_version:
|
||||
- 16
|
||||
name: Node ${{ matrix.node_version }} - ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: pnpm/action-setup@646cdf48217256a3d0b80361c5a50727664284f2
|
||||
with:
|
||||
fetch-depth: 25
|
||||
- name: Use Node.js
|
||||
version: 6.32.6
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
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)"
|
||||
- name: Cache node_modules
|
||||
id: yarn-cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
**/node_modules
|
||||
key: ${{ runner.os }}-${{ runner.node_version}}-yarn-v13-${{ hashFiles('yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ runner.node_version}}-yarn-v13-
|
||||
- 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: Test examples
|
||||
run: 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, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
|
||||
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 }}/20 -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: cd test/integration/with-electron/app && yarn
|
||||
if: ${{needs.build.outputs.docsChange != 'docs only change'}}
|
||||
|
||||
- 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
|
||||
|
||||
testFirefox:
|
||||
name: Nextjs - Test Firefox (production)
|
||||
defaults:
|
||||
run:
|
||||
working-directory: nextjs
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
env:
|
||||
HEADLESS: true
|
||||
BROWSER_NAME: "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 -c 1 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
|
||||
BROWSER_NAME: "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 -c 1 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'}}
|
||||
node-version: ${{ matrix.node_version }}
|
||||
cache: "pnpm"
|
||||
- run: pnpm install --frozen-lockfile
|
||||
- run: pnpm manypkg check
|
||||
- run: pnpm build
|
||||
- run: pnpm lint
|
||||
- run: pnpm build:apps
|
||||
- run: pnpm test
|
||||
|
||||
37
.gitignore
vendored
37
.gitignore
vendored
@@ -1,3 +1,37 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
node_modules
|
||||
.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
coverage
|
||||
|
||||
.next/
|
||||
out/
|
||||
build
|
||||
dist
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# local env files
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
# turbo
|
||||
.turbo
|
||||
|
||||
.log
|
||||
.DS_Store
|
||||
.idea
|
||||
@@ -30,4 +64,7 @@ examples/auth2
|
||||
db.sqlite-journal
|
||||
test/integration/**/db.json
|
||||
test/**/*/out
|
||||
test/**/blitz-env.d.ts
|
||||
examples/**/blitz-env.d.ts
|
||||
.blitz**
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
yarn lint-staged
|
||||
yarn pretty-quick --staged
|
||||
pnpm manypkg check
|
||||
pnpm lint
|
||||
pnpm pretty-quick --staged
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
# .kodiak.toml
|
||||
# Minimal config. version is the only required field.
|
||||
version = 1
|
||||
merge.automerge_label = "0 - <(^_^)> - merge it! ✌️"
|
||||
approve.auto_approve_usernames = ["flybayer", "depfu"]
|
||||
@@ -1 +1 @@
|
||||
12.20.0
|
||||
16.13.2
|
||||
|
||||
16
.npmignore
16
.npmignore
@@ -1,16 +0,0 @@
|
||||
.DS_Store
|
||||
.prettierrc
|
||||
.nyc_output
|
||||
.travis.yml
|
||||
coverage
|
||||
coverage.lcov
|
||||
bench
|
||||
docs
|
||||
src
|
||||
examples
|
||||
babel.config.js
|
||||
test
|
||||
CONTRIBUTING.md
|
||||
CODE_OF_CONDUCT.md
|
||||
*.ts
|
||||
!*.d.ts
|
||||
6
.npmrc
6
.npmrc
@@ -1 +1,7 @@
|
||||
save-exact=true
|
||||
|
||||
public-hoist-pattern[]=secure-password
|
||||
public-hoist-pattern[]=*types*
|
||||
public-hoist-pattern[]=*eslint*
|
||||
public-hoist-pattern[]=@prettier/plugin-*
|
||||
public-hoist-pattern[]=*prettier-plugin-*
|
||||
|
||||
@@ -16,10 +16,5 @@ reports
|
||||
tsconfig.tsbuildinfo
|
||||
dist
|
||||
bin
|
||||
!packages/blitz/src/bin
|
||||
packages/generator/templates/**
|
||||
.github/ISSUE_TEMPLATE/bug_report.md
|
||||
nextjs/packages/next/compiled/**
|
||||
|
||||
// Because file from nextjs upstream isn't formatted properly
|
||||
nextjs/packages/next/build/webpack-config.ts
|
||||
packages/generator/templates/**
|
||||
|
||||
@@ -2,34 +2,18 @@
|
||||
|
||||
[Read the Contributing Guide at Blitzjs.com](https://blitzjs.com/docs/contributing)
|
||||
|
||||
## Notes For Core Team
|
||||
## To run tests
|
||||
|
||||
### Syncing Next.js Fork
|
||||
Make sure you have `chromedriver` installed for your Chrome version. You can install it with
|
||||
|
||||
1. Run `yarn push-nextjs`
|
||||
- If it fails with an error of `git-subrepo: Can't commit: 'subrepo/nextjs' doesn't contain upstream HEAD:`, then run `yarn push-nextjs --force` (see https://github.com/ingydotnet/git-subrepo/issues/530)
|
||||
2. Create new git branch for the upgrade
|
||||
3. In the forked repo (https://github.com/blitz-js/next.js), run:
|
||||
1. `git pull`
|
||||
2. `git fetch --all`
|
||||
3. `git merge v10.2.0` (change the version to be the version you are updating to)
|
||||
4. Run `rm -rf examples && git add examples`
|
||||
5. To resolve conflict with their version for a path, like docs, run this:
|
||||
- `git checkout --theirs docs && git add docs`
|
||||
6. Resolve all merge conflicts and complete merge
|
||||
7. Run `yarn` and make sure all builds complete
|
||||
8. Run `yarn lint` and fix any issues
|
||||
9. `git push`
|
||||
4. Run `yarn pull next-nextjs`
|
||||
5. Run `yarn`
|
||||
6. Run `yarn manypkg check` and optionally `yarn manypkg fix` to fix any issues
|
||||
7. Under `nextjs/`, run `./check-pre-compiled.sh` and commit the changes
|
||||
8. Run `yarn build:nextjs`
|
||||
9. Run `yarn lint` - fix any issues
|
||||
10. Run `yarn build` - fix any issues
|
||||
11. Run `yarn test:nextjs-size` and update tests if there are any failures
|
||||
12. Open PR and fix any failing tests
|
||||
13. Update any references to nextjs in new code including imports like `next/image`, etc.
|
||||
14. Any doc updates needed?
|
||||
15. Merge PR
|
||||
16. `yarn push-nextjs`
|
||||
- `brew install --cask chromedriver` on Mac OS X
|
||||
- `chocolatey install chromedriver` on Windows
|
||||
- Or manually download the version that matches your installed chrome version (if there's no match, download a version under it, but not above) from the [chromedriver repo](https://chromedriver.storage.googleapis.com/index.html) and add the binary to `<next-repo>/node_modules/.bin`
|
||||
|
||||
You may also have to [install Rust](https://www.rust-lang.org/tools/install) and build our native packages to see all tests pass locally. We check in binaries for the most common targets and those required for CI so that most people don't have to, but if you do not see a binary for your target in `packages/next/native`, you can build it by running `yarn --cwd packages/next build-native`. If you are working on the Rust code and you need to build the binaries for ci, you can manually trigger [the workflow](https://github.com/vercel/next.js/actions/workflows/build_native.yml) to build and commit with the "Run workflow" button.
|
||||
|
||||
Running all tests:
|
||||
|
||||
```sh
|
||||
pnpm test
|
||||
```
|
||||
|
||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 Brandon Bayer
|
||||
Copyright (c) 2022 Blitz Revolution Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
124
MEETING_NOTES.md
124
MEETING_NOTES.md
@@ -1,124 +0,0 @@
|
||||
# 2020-08-17 Blitz Contributor Call
|
||||
|
||||
- Attending: Brandon Bayer, Adam Markon, Kellen Mace, Myron Davis, Dwight Watson
|
||||
- Brandon:
|
||||
- Auth out, set up by default in canary release
|
||||
- Need to work on a potential CSRF bug
|
||||
- Next major release will include auth by default and allow you to choose your form library
|
||||
- Next auth features are email confirmation
|
||||
- After that, logging and plugins are next
|
||||
- Adam:
|
||||
- Overhauled the recipe infrastructure. Now using jscodeshift instead of recast
|
||||
- Added support for conditional JSX in templates
|
||||
- Going to work on custom templates next
|
||||
- Dwight
|
||||
- Has been opening issues for problems
|
||||
- Made a few PRs for some issues
|
||||
|
||||
# 2020-07-07 Blitz Contributor Call
|
||||
|
||||
- Attending: Brandon Bayer, Robert Rosenburg, Jeremy Liberman
|
||||
- Brandon:
|
||||
- Finishing up session managment code
|
||||
- Waiting for review of session managment code from Rishabh
|
||||
- Will be working on actual auth code (vs session management) like password hashing, password reset, social login, etc.
|
||||
- Benefits of our session managment vs rails
|
||||
- Don’t have to redirect to login page
|
||||
- Using top level error component that catches authentication errors
|
||||
- You can login from anywhere, during sign up
|
||||
- Robert:
|
||||
- Waiting on Kirstina’s website designs. Desktop design is finished, but she's working on mobile design
|
||||
- Code snippet / sandbox of blitz code for the website
|
||||
|
||||
# 2020-06-23 Blitz Contributor Call
|
||||
|
||||
- Attending: Brandon Bayer, Robert Rosenberg, Justin Hall, Adam Markon
|
||||
- Brandon:
|
||||
- Server side session management code is mostly set up. Still need to integrate client side of RPC calls to expose session information
|
||||
- Identity verification/Oauth integrations still need to be firmed up
|
||||
- Once auth is wrapped up we should be ready to start on plugins
|
||||
- Adam:
|
||||
- Been experimenting with smart page generation based on the current schema model
|
||||
- MDX installer recipes
|
||||
- Robert:
|
||||
- Waiting on designs from Kristina
|
||||
- Prism component from theme ui customization? If not try and grab source code from docusaurus line highlighting component
|
||||
- Justin:
|
||||
- Updated the tutorial
|
||||
- Helped with various bug fixes
|
||||
|
||||
# 2020-06-17 Blitz Contributor Call
|
||||
|
||||
- Attending: Brandon Bayer, Fran Zekan, Sigurd Wahl
|
||||
- Brandon:
|
||||
- Spent the past week implementing HTTP middlware which is now released!
|
||||
- Now working on implementing session management!
|
||||
- Fran:
|
||||
- Finishing up the PR for adding `blitz db seed`
|
||||
- Sigurd:
|
||||
- New to the community, slowly learning the codebase
|
||||
- Exicited to get a first PR in here sometime soon :)
|
||||
|
||||
# 2020-06-09 Blitz Contributor Call
|
||||
|
||||
- Attending: Brandon Bayer, Rudi Yardley, Fran Zekan, Adam Markon, Robert Rosenberg, Kristina Matuska
|
||||
- Brandon:
|
||||
- blitzjs.com published, docs + marketing site v0.1 live
|
||||
- For now most of the docs copied from react-query and Next, we should eventually clean them up so they're stylistically similar to ours, but really easy to start
|
||||
- HTTP middleware is almost done, just fixing a few edge cases
|
||||
- Authentication is up next after that, translating pseudocode into actual code
|
||||
- Kristina:
|
||||
- Homepage design mostly wrapped up right now, have to finish up mobile and light mode and then ready to move on to the rest of the docs
|
||||
- Syntax highlighting, we can customize colors
|
||||
- Sidebar inspiration from tailwindcss
|
||||
- Robert:
|
||||
- Decided to wait to convert existing components to theme UI until the final design is done
|
||||
- More docs content:
|
||||
- More guides
|
||||
- Improve the tutorial to incorporate relationship generation
|
||||
- Add branches for canary and master
|
||||
- If you add a feature you can add your documentation to the canary branch
|
||||
- Adam:
|
||||
- blitz generate model finished!
|
||||
- Installer rewrite complete
|
||||
- At similar place to what Gatsby has for installing stuff
|
||||
- Next up:
|
||||
- Support gatsby MDX recipes
|
||||
- Make all code generators aware of actual model attributes
|
||||
- Fran:
|
||||
- Working on a package for getting the blitz config anywhere - getConfig()
|
||||
- Prevent app from "warming" the server when deployed as server rather than serverless
|
||||
- Testing examples - e2e with cypress and unit tests with Jest so we can link to a testing setup in the docs/getting started guide
|
||||
- Rudi:
|
||||
- Extracted out the @blitzjs/file-pipeline (previously synchronizer)
|
||||
- Extracted out the @blitzjs/display package
|
||||
- Working on various Next.js compatibility issues
|
||||
- Debugging a bug in blitz start where it gets stuck at \_manifest.json
|
||||
|
||||
# 2020-05-26 Blitz Contributor Call
|
||||
|
||||
- Attending: Brandon Bayer, Robert Rosenberg, Adam Markon, Simon Debbarma
|
||||
- Brandon:
|
||||
- Kitze livestream last week went great — recording on youtube
|
||||
- Codebase walkthrough yesterday went great — recording on youtube
|
||||
- Website overhaul, installed Theme UI
|
||||
- Adam:
|
||||
- Opened PR for Prisma model generation from the CLI
|
||||
- Working on Installer stuff and prepping for integration with Gatsby recipes
|
||||
- Simon
|
||||
- Working on custom illustrations for the web
|
||||
- Robert
|
||||
- Misc work on website
|
||||
- Website
|
||||
- Most website components are owned by us now instead of docusaurus, we'll need to be weary of api updates and any other important component updates
|
||||
- Make sidebar like tailwind docs sidebar
|
||||
- Dark theme needs to be fixed
|
||||
- Theme switcher inconsistent
|
||||
- Live code sandbox examples
|
||||
- Code comparison between blitz and rails
|
||||
- Auth
|
||||
- Rishabh continuing to work on pseudo code for the session management library
|
||||
- Brandon planning to build http middleware support this week
|
||||
- CLI
|
||||
- Adam working on new features, including generating prisma models and making existing templates aware of actual model attributes
|
||||
- Plugin ideas / discussion once recipes are farther along. Will post an RFC for plugins at some point
|
||||
168
README.md
168
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-304-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-381-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,7 +22,7 @@
|
||||
<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"><a href="https://blitzjs.com" target="_blank">Read the Documentation</a></h3>
|
||||
<h3 align="center"><a href="https://blitzjs.com/docs/get-started" 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.
|
||||
@@ -107,20 +107,17 @@ Your financial contributions help ensure Blitz continues to be developed and mai
|
||||
|
||||
<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://raw.githubusercontent.com/blitz-js/blitz/canary/assets/usertrack.png" width="40px"/>
|
||||
</a></td>
|
||||
<td><a aria-label="MeetKai" href="https://meetkai.com/?ref=blitzjs">
|
||||
<img alt="" src="https://raw.githubusercontent.com/blitz-js/blitz/canary/assets/meetkai.png" width="40px"/>
|
||||
</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -135,16 +132,26 @@ Your financial contributions help ensure Blitz continues to be developed and mai
|
||||
</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>
|
||||
<td><a aria-label="Boostry" href="https://boostry.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/boostry.svg" width="200px">
|
||||
</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
### 🥈 Silver Sponsors
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<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="300px">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
### 🏆 Gold Sponsors
|
||||
|
||||
@@ -166,6 +173,8 @@ Your financial contributions help ensure Blitz continues to be developed and mai
|
||||
<table>
|
||||
<tr>
|
||||
<td align="center"><a href="https://twitter.com/flybayer"><img src="https://avatars3.githubusercontent.com/u/8813276?v=4" width="100px;" alt=""/><br /><sub><b>Brandon Bayer</b></sub></a><br />Creator</td>
|
||||
<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 />Lead Maintainer</td>
|
||||
|
||||
</tr>
|
||||
</table>
|
||||
<!-- markdownlint-enable -->
|
||||
@@ -182,7 +191,6 @@ _Code ownership, pull request approvals and merging, etc_ (see [Maintainers L2](
|
||||
<!-- markdownlint-disable -->
|
||||
<table>
|
||||
<tr>
|
||||
<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>
|
||||
@@ -202,25 +210,6 @@ _Issue triage, pull request triage, community encouragement and moderation, etc_
|
||||
<table>
|
||||
<tr>
|
||||
<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="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://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>
|
||||
<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://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>
|
||||
<td align="center">
|
||||
<a href="https://mina.ca">
|
||||
<img src="https://avatars.githubusercontent.com/mabadir" width="100px;" alt="Mina Abadir avatar" /><br />
|
||||
@@ -229,6 +218,22 @@ _Issue triage, pull request triage, community encouragement and moderation, etc_
|
||||
</sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://builtforfifty.com">
|
||||
<img src="https://avatars.githubusercontent.com/abuuzayr" width="100px;" alt="Abu Uzayr avatar" /><br />
|
||||
<sub>
|
||||
<b>Abu Uzayr</b>
|
||||
</sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://damilolarandolph.com/">
|
||||
<img src="https://avatars.githubusercontent.com/damilolarandolph" width="100px;" alt="Damilola Randolph avatar" /><br />
|
||||
<sub>
|
||||
<b>Damilola Randolph</b>
|
||||
</sub>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<!-- markdownlint-enable -->
|
||||
@@ -448,12 +453,12 @@ Thanks to these wonderful people ([emoji key](https://allcontributors.org/docs/e
|
||||
<td align="center"><a href="https://github.com/leggsimon"><img src="https://avatars2.githubusercontent.com/u/11544418?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Simon Legg</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=leggsimon" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://robsoriano.com"><img src="https://avatars3.githubusercontent.com/u/13049130?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Robert Soriano</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=wobsoriano" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/benediktms"><img src="https://avatars2.githubusercontent.com/u/48836135?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Benedikt Schnatterbeck</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=benediktms" title="Code">💻</a></td>
|
||||
<td align="center"><a href="http://taloranderson.com"><img src="https://avatars2.githubusercontent.com/u/11509865?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Talor Anderson</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=Talor-A" title="Code">💻</a></td>
|
||||
<td align="center"><a href="http://taloranderson.com"><img src="https://avatars2.githubusercontent.com/u/11509865?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Talor Anderson</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=Talor-A" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=Talor-A" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/akirabaruah"><img src="https://avatars2.githubusercontent.com/u/6751517?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Akira Baruah</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=akirabaruah" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://chriswray.dev/"><img src="https://avatars0.githubusercontent.com/u/53663762?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Christopher Wray</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=cwray-tech" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/piotrski"><img src="https://avatars0.githubusercontent.com/u/244174?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Piotrek Tomczewski</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=piotrski" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/piotrski"><img src="https://avatars0.githubusercontent.com/u/244174?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Piotrek Tomczewski</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=piotrski" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=piotrski" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="http://raph.site"><img src="https://avatars3.githubusercontent.com/u/1575946?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Raphaël Huchet</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=rap2hpoutre" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=rap2hpoutre" title="Tests">⚠️</a> <a href="https://github.com/blitz-js/blitz/commits?author=rap2hpoutre" title="Code">💻</a></td>
|
||||
<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>
|
||||
@@ -516,7 +521,7 @@ Thanks to these wonderful people ([emoji key](https://allcontributors.org/docs/e
|
||||
<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="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> <a href="https://github.com/blitz-js/blitz/commits?author=beerose" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=beerose" title="Tests">⚠️</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>
|
||||
@@ -633,9 +638,108 @@ Thanks to these wonderful people ([emoji key](https://allcontributors.org/docs/e
|
||||
<td align="center"><a href="https://www.kevinpeters.net/about/"><img src="https://avatars.githubusercontent.com/u/12736734?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Kevin Peters</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=igeligel" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="http://anolilab.de"><img src="https://avatars.githubusercontent.com/u/2716058?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Daniel Bannert</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=prisis" title="Code">💻</a></td>
|
||||
<td align="center"><a href="http://anolilab.de"><img src="https://avatars.githubusercontent.com/u/2716058?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Daniel Bannert</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=prisis" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=prisis" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://benjakugler96.github.io/"><img src="https://avatars.githubusercontent.com/u/53273645?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Benja Kugler</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=benjakugler96" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://semeniuc.ml/"><img src="https://avatars.githubusercontent.com/u/3838856?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Eric Semeniuc</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=esemeniuc" title="Tests">⚠️</a> <a href="https://github.com/blitz-js/blitz/commits?author=esemeniuc" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/ricardo-rp"><img src="https://avatars.githubusercontent.com/u/30808767?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ricardo Romero</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=ricardo-rp" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="exocortex.anothernode.com"><img src="https://avatars.githubusercontent.com/u/3286144?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Moritz Reiter</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=anothernode" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://msich.dev"><img src="https://avatars.githubusercontent.com/u/38794918?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Matt Sichterman</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=msichterman" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/medihack"><img src="https://avatars.githubusercontent.com/u/120626?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Kai Schlamp</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=medihack" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://muyiwa.me"><img src="https://avatars.githubusercontent.com/u/6832244?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Muyiwa Olu</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=muyiwaolu" title="Code">💻</a></td>
|
||||
<td align="center"><a href="http://2hr.me/"><img src="https://avatars.githubusercontent.com/u/4346154?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Rabbi Hossain</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=rabbihossain" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/bravo-kernel"><img src="https://avatars.githubusercontent.com/u/230500?v=4?s=100" width="100px;" alt=""/><br /><sub><b>bravo-kernel</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=bravo-kernel" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://samholmes.net"><img src="https://avatars.githubusercontent.com/u/8385528?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Sam Holmes</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=sam3d" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://doncicuto.medium.com"><img src="https://avatars.githubusercontent.com/u/30386061?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Miguel Cabrerizo</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=doncicuto" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=doncicuto" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="http://zackhobson.com/"><img src="https://avatars.githubusercontent.com/u/12092?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Zack Hobson</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=zenhob" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=zenhob" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://www.mokhtar.dev"><img src="https://avatars.githubusercontent.com/u/13026820?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Mokhtar</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=m5r" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/kenkuan"><img src="https://avatars.githubusercontent.com/u/1924968?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ken Kuan</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=kenkuan" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/meehawk"><img src="https://avatars.githubusercontent.com/u/80167324?v=4?s=100" width="100px;" alt=""/><br /><sub><b>meehawk</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=meehawk" title="Code">💻</a></td>
|
||||
<td align="center"><a href="rahulravindran.in"><img src="https://avatars.githubusercontent.com/u/10168946?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Rahul Ravindran</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=ravindranrahul" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/s-r-x"><img src="https://avatars.githubusercontent.com/u/41614937?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ilya</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=s-r-x" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=s-r-x" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=s-r-x" title="Tests">⚠️</a></td>
|
||||
<td align="center"><a href="https://github.com/hashimwarren"><img src="https://avatars.githubusercontent.com/u/6027587?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Hashim Warren</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=hashimwarren" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://damilolarandolph.com"><img src="https://avatars.githubusercontent.com/u/43427949?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Damilola Randolph</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=damilolarandolph" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/mwcampbell"><img src="https://avatars.githubusercontent.com/u/214820?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Matt Campbell</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=mwcampbell" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/ratson"><img src="https://avatars.githubusercontent.com/u/2682937?v=4?s=100" width="100px;" alt=""/><br /><sub><b>(◕ᴥ◕)</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=ratson" title="Code">💻</a></td>
|
||||
<td align="center"><a href="maciejmyslinski.com"><img src="https://avatars.githubusercontent.com/u/11421186?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Mat Milbury</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=maciejmyslinski" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://andreas.fyi"><img src="https://avatars.githubusercontent.com/u/8077469?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Andreas Asprou</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=andreasasprou" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/kotx"><img src="https://avatars.githubusercontent.com/u/33439542?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Kot</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=kotx" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=kotx" title="Tests">⚠️</a> <a href="https://github.com/blitz-js/blitz/commits?author=kotx" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/isaka1022"><img src="https://avatars.githubusercontent.com/u/28589716?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Amane</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=isaka1022" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="johnleung.com"><img src="https://avatars.githubusercontent.com/u/20699?v=4?s=100" width="100px;" alt=""/><br /><sub><b>John Leung</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=fuzzthink" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="roettgers.co"><img src="https://avatars.githubusercontent.com/u/29666239?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Bruce</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=bcye" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/emilygracekz"><img src="https://avatars.githubusercontent.com/u/57361805?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Emily</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=emilygracekz" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/npverni"><img src="https://avatars.githubusercontent.com/u/3537?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Nathan Verni</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=npverni" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://davyengone.io"><img src="https://avatars.githubusercontent.com/u/4896002?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Davy Engone</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=davyengone" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://fedeorlandau.dev/"><img src="https://avatars.githubusercontent.com/u/10283686?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Federico Joel Orlandau</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=Fedeorlandau" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=Fedeorlandau" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/johnmurphy01"><img src="https://avatars.githubusercontent.com/u/2939548?v=4?s=100" width="100px;" alt=""/><br /><sub><b>John Murphy</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=johnmurphy01" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=johnmurphy01" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/martinsaxa"><img src="https://avatars.githubusercontent.com/u/33789474?v=4?s=100" width="100px;" alt=""/><br /><sub><b>martinsaxa</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=martinsaxa" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/ajwgeek"><img src="https://avatars.githubusercontent.com/u/2135600?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Austin Walhof</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=ajwgeek" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="g3offrey.dev"><img src="https://avatars.githubusercontent.com/u/11151445?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Geoffrey</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=g3offrey" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=g3offrey" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=g3offrey" title="Tests">⚠️</a></td>
|
||||
<td align="center"><a href="https://github.com/keevan"><img src="https://avatars.githubusercontent.com/u/9924643?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Kevin Pham</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=keevan" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/kimngan-bui"><img src="https://avatars.githubusercontent.com/u/20723478?v=4?s=100" width="100px;" alt=""/><br /><sub><b>kimngan-bui</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=kimngan-bui" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="world.hey.com/bach"><img src="https://avatars.githubusercontent.com/u/11691670?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Bahk Chanhee</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=9j" title="Code">💻</a></td>
|
||||
<td align="center"><a href="http://www.afterecon.com/"><img src="https://avatars.githubusercontent.com/u/5559355?v=4?s=100" width="100px;" alt=""/><br /><sub><b>John Vandivier</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=Vandivier" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=Vandivier" title="Tests">⚠️</a> <a href="https://github.com/blitz-js/blitz/commits?author=Vandivier" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="http://namirsab.github.io"><img src="https://avatars.githubusercontent.com/u/6980777?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Namir</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=namirsab" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=namirsab" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=namirsab" title="Tests">⚠️</a></td>
|
||||
<td align="center"><a href="https://twitter.com/scttcper"><img src="https://avatars.githubusercontent.com/u/1400464?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Scott Cooper</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=scttcper" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="abduttayyeb.github.io"><img src="https://avatars.githubusercontent.com/u/55306260?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Abduttayyeb M.r</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=Abduttayyeb" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/maybebored"><img src="https://avatars.githubusercontent.com/u/20951181?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Mayuran</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=maybebored" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/MuckHub"><img src="https://avatars.githubusercontent.com/u/54979136?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Aleksei Vesselko</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=MuckHub" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://p-siriphanthong.github.io/"><img src="https://avatars.githubusercontent.com/u/29949429?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Punn Siriphanthong</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=p-siriphanthong" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://my-portfolio-292eb.web.app"><img src="https://avatars.githubusercontent.com/u/83679827?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Shawn Fetanat</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=shawn-fetanat" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/mochi-sann"><img src="https://avatars.githubusercontent.com/u/44772513?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Moyuru</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=mochi-sann" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=mochi-sann" title="Tests">⚠️</a> <a href="https://github.com/blitz-js/blitz/commits?author=mochi-sann" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/camsloanftc"><img src="https://avatars.githubusercontent.com/u/16295659?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Cam Sloan</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=camsloanftc" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://macieksitkowski.com"><img src="https://avatars.githubusercontent.com/u/58401630?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Maciek Sitkowski</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=sitek94" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/vivek7405"><img src="https://avatars.githubusercontent.com/u/24492244?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Vivek</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=vivek7405" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=vivek7405" title="Code">💻</a></td>
|
||||
<td align="center"><a href="http://cj.io"><img src="https://avatars.githubusercontent.com/u/1819?v=4?s=100" width="100px;" alt=""/><br /><sub><b>CJ Lazell</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=cj" title="Code">💻</a></td>
|
||||
<td align="center"><a href="robertbroersma.com"><img src="https://avatars.githubusercontent.com/u/4519828?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Robert</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=RobertBroersma" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://christianjensen.netlify.com"><img src="https://avatars.githubusercontent.com/u/12374723?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Christian Jensen</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=cbejensen" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/dvnrsn"><img src="https://avatars.githubusercontent.com/u/9112811?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Devin Rasmussen</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=dvnrsn" title="Code">💻</a></td>
|
||||
<td align="center"><a href="www.linkedin.com/in/devtom"><img src="https://avatars.githubusercontent.com/u/23282613?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Thomas Brenneur</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=devtombiz" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=devtombiz" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=devtombiz" title="Tests">⚠️</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://lucasvazq.github.io/"><img src="https://avatars.githubusercontent.com/u/38964964?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Lucas Vazquez</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=lucasvazq" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/chrisj-back2work"><img src="https://avatars.githubusercontent.com/u/68551954?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Chris Johnson</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=chrisj-back2work" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/thisdotrob"><img src="https://avatars.githubusercontent.com/u/12902589?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Rob Stevenson</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=thisdotrob" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="www.lucas.computer"><img src="https://avatars.githubusercontent.com/u/1363056?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Lucas Heymès</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=lovethebomb" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=lovethebomb" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/NorfeldtAbtion"><img src="https://avatars.githubusercontent.com/u/53769763?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Lasse Norfeldt</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=NorfeldtAbtion" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://nyaripeter.hu/"><img src="https://avatars.githubusercontent.com/u/6048614?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Péter Nyári</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=netwarex" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=netwarex" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://www.holgerfrohloff.de"><img src="https://avatars.githubusercontent.com/u/84148?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Holger Frohloff</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=5minpause" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/basilk76"><img src="https://avatars.githubusercontent.com/u/45275512?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Basil Khan</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=basilk76" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://danestves.com/"><img src="https://avatars.githubusercontent.com/u/31737273?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Daniel Esteves</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=danestves" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="http://www.bitnative.com"><img src="https://avatars.githubusercontent.com/u/1688997?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Cory House</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=coryhouse" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://auspham.dev/"><img src="https://avatars.githubusercontent.com/u/16440123?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Austin (Thang Pham)</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=rockmanvnx6" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://jammeryhq.com"><img src="https://avatars.githubusercontent.com/u/521777?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Marcus Reinhardt</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=noxify" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=noxify" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/davidchristie"><img src="https://avatars.githubusercontent.com/u/12044333?v=4?s=100" width="100px;" alt=""/><br /><sub><b>David Christie</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=davidchristie" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/ajanth97"><img src="https://avatars.githubusercontent.com/u/50458502?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ajanth</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=ajanth97" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/divpreet"><img src="https://avatars.githubusercontent.com/u/2805650?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Div</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=divpreet" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/david-arteaga"><img src="https://avatars.githubusercontent.com/u/7199015?v=4?s=100" width="100px;" alt=""/><br /><sub><b>David Arteaga</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=david-arteaga" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/MukulKolpe"><img src="https://avatars.githubusercontent.com/u/78664749?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Mukul Kolpe</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=MukulKolpe" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/skotchpine"><img src="https://avatars.githubusercontent.com/u/13043909?v=4?s=100" width="100px;" alt=""/><br /><sub><b>tyler</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=skotchpine" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/SofianeDjellouli"><img src="https://avatars.githubusercontent.com/u/38258952?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Sofiane Djellouli</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=SofianeDjellouli" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/kreako"><img src="https://avatars.githubusercontent.com/u/65113001?v=4?s=100" width="100px;" alt=""/><br /><sub><b>kreako</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=kreako" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://sarahdayan.dev"><img src="https://avatars.githubusercontent.com/u/5370675?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Sarah Dayan</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=sarahdayan" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/c-ciobanu"><img src="https://avatars.githubusercontent.com/u/33382714?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Cristi Ciobanu</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=c-ciobanu" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://arpitdalal.dev"><img src="https://avatars.githubusercontent.com/u/61059807?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Arpit Dalal</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=arpitdalal" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/robertrisch"><img src="https://avatars.githubusercontent.com/u/73828816?v=4?s=100" width="100px;" alt=""/><br /><sub><b>robertrisch</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=robertrisch" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
@@ -7,4 +7,3 @@ TODO
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Email Brandon Bayer at b@bayer.ws to report a vulnerablity, and he will follow up with you asap.
|
||||
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
const {fs} = require("memfs")
|
||||
|
||||
module.exports = fs
|
||||
1
apps/web/.eslintrc.js
Normal file
1
apps/web/.eslintrc.js
Normal file
@@ -0,0 +1 @@
|
||||
module.exports = require("@blitzjs/config/eslint")
|
||||
4
apps/web/.gitignore
vendored
Normal file
4
apps/web/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
node_modules
|
||||
# Keep environment variables out of version control
|
||||
.env
|
||||
*.sqlite
|
||||
30
apps/web/README.md
Normal file
30
apps/web/README.md
Normal file
@@ -0,0 +1,30 @@
|
||||
## Getting Started
|
||||
|
||||
First, run the development server:
|
||||
|
||||
```bash
|
||||
yarn dev
|
||||
```
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
|
||||
You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file.
|
||||
|
||||
[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.js`.
|
||||
|
||||
The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.
|
||||
|
||||
## Learn More
|
||||
|
||||
To learn more about Next.js, take a look at the following resources:
|
||||
|
||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
||||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||||
|
||||
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
|
||||
|
||||
## Deploy on Vercel
|
||||
|
||||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_source=github.com&utm_medium=referral&utm_campaign=turborepo-readme) from the creators of Next.js.
|
||||
|
||||
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
|
||||
20
apps/web/app/blitz-client.ts
Normal file
20
apps/web/app/blitz-client.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import {AuthClientPlugin} from "@blitzjs/auth"
|
||||
import {setupClient} from "@blitzjs/next"
|
||||
import {BlitzRpcPlugin} from "@blitzjs/rpc"
|
||||
|
||||
const {withBlitz} = setupClient({
|
||||
plugins: [
|
||||
AuthClientPlugin({
|
||||
cookiePrefix: "webapp-cookie-prefix",
|
||||
}),
|
||||
BlitzRpcPlugin({
|
||||
reactQueryOptions: {
|
||||
queries: {
|
||||
staleTime: 7000,
|
||||
},
|
||||
},
|
||||
}),
|
||||
],
|
||||
})
|
||||
|
||||
export {withBlitz}
|
||||
17
apps/web/app/blitz-server.ts
Normal file
17
apps/web/app/blitz-server.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import {setupBlitz} from "@blitzjs/next"
|
||||
import {AuthServerPlugin, PrismaStorage} from "@blitzjs/auth"
|
||||
import {prisma as db} from "../prisma/index"
|
||||
import {simpleRolesIsAuthorized} from "@blitzjs/auth"
|
||||
|
||||
const {gSSP, gSP, api} = setupBlitz({
|
||||
plugins: [
|
||||
AuthServerPlugin({
|
||||
cookiePrefix: "webapp-cookie-prefix",
|
||||
// TODO fix type
|
||||
storage: PrismaStorage(db as any),
|
||||
isAuthorized: simpleRolesIsAuthorized,
|
||||
}),
|
||||
],
|
||||
})
|
||||
|
||||
export {gSSP, gSP, api}
|
||||
14
apps/web/app/mutations/createUser.ts
Normal file
14
apps/web/app/mutations/createUser.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import {Ctx} from "blitz"
|
||||
import {prisma} from "../../prisma"
|
||||
import {User} from "prisma"
|
||||
|
||||
export default async function createUser(
|
||||
input: {name: string; email: string},
|
||||
ctx: Ctx,
|
||||
): Promise<User> {
|
||||
ctx.session.$authorize()
|
||||
|
||||
const user = await prisma.user.create({data: {name: input.name, email: input.email}})
|
||||
|
||||
return user
|
||||
}
|
||||
4
apps/web/app/mutations/setBasic.ts
Normal file
4
apps/web/app/mutations/setBasic.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export default function setBasic(input, ctx) {
|
||||
console.log("SET BASIC input", input)
|
||||
return
|
||||
}
|
||||
5
apps/web/app/queries/getBasic.ts
Normal file
5
apps/web/app/queries/getBasic.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export default async function getBasic(input, ctx) {
|
||||
console.log("INPUT", input)
|
||||
|
||||
return "basic-result"
|
||||
}
|
||||
11
apps/web/app/queries/getUsers.ts
Normal file
11
apps/web/app/queries/getUsers.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import {Ctx} from "blitz"
|
||||
import {prisma} from "../../prisma"
|
||||
import {User} from "prisma"
|
||||
|
||||
export default async function getUsers(_input: {}, ctx: Ctx): Promise<User[]> {
|
||||
ctx.session.$authorize()
|
||||
|
||||
const users = await prisma.user.findMany()
|
||||
|
||||
return users
|
||||
}
|
||||
5
apps/web/app/queries/v2/getV2Basic.ts
Normal file
5
apps/web/app/queries/v2/getV2Basic.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export default function getV2Basic(input, ctx) {
|
||||
console.log("INPUT", input)
|
||||
|
||||
return "basic-result"
|
||||
}
|
||||
11
apps/web/jest.config.js
Normal file
11
apps/web/jest.config.js
Normal file
@@ -0,0 +1,11 @@
|
||||
const nextJest = require("@blitzjs/next/jest")
|
||||
|
||||
const createJestConfig = nextJest({
|
||||
dir: "./",
|
||||
})
|
||||
|
||||
const customJestConfig = {
|
||||
testEnvironment: "jest-environment-jsdom",
|
||||
}
|
||||
|
||||
module.exports = createJestConfig(customJestConfig)
|
||||
5
apps/web/next-env.d.ts
vendored
Normal file
5
apps/web/next-env.d.ts
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
||||
10
apps/web/next.config.js
Normal file
10
apps/web/next.config.js
Normal file
@@ -0,0 +1,10 @@
|
||||
const withBundleAnalyzer = require("@next/bundle-analyzer")({
|
||||
enabled: process.env.ANALYZE === "true",
|
||||
})
|
||||
const {withBlitz} = require("@blitzjs/next")
|
||||
|
||||
module.exports = withBlitz(
|
||||
withBundleAnalyzer({
|
||||
reactStrictMode: true,
|
||||
}),
|
||||
)
|
||||
36
apps/web/package.json
Normal file
36
apps/web/package.json
Normal file
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"name": "web",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start:dev": "pnpm run prisma:start && next dev",
|
||||
"buildapp": "prisma generate && next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint",
|
||||
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf .next",
|
||||
"prisma:start": "prisma generate && prisma migrate deploy",
|
||||
"prisma:studio": "prisma studio",
|
||||
"test": "jest"
|
||||
},
|
||||
"dependencies": {
|
||||
"@blitzjs/auth": "workspace:*",
|
||||
"@blitzjs/config": "workspace:*",
|
||||
"@blitzjs/next": "workspace:*",
|
||||
"@blitzjs/rpc": "workspace:*",
|
||||
"@prisma/client": "3.9.0",
|
||||
"@types/jest": "27.4.1",
|
||||
"blitz": "workspace:*",
|
||||
"jest": "27.5.1",
|
||||
"next": "12.1.1",
|
||||
"prisma": "3.9.0",
|
||||
"react": "18.0.0",
|
||||
"react-dom": "18.0.0",
|
||||
"ts-node": "10.7.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@next/bundle-analyzer": "12.0.8",
|
||||
"@types/react": "17.0.43",
|
||||
"eslint": "7.32.0",
|
||||
"typescript": "^4.5.3"
|
||||
}
|
||||
}
|
||||
37
apps/web/pages/_app.tsx
Normal file
37
apps/web/pages/_app.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
import {ErrorFallbackProps, ErrorComponent, ErrorBoundary} from "@blitzjs/next"
|
||||
import {AuthenticationError, AuthorizationError} from "blitz"
|
||||
import type {AppProps} from "next/app"
|
||||
import React, {Suspense} from "react"
|
||||
import {withBlitz} from "app/blitz-client"
|
||||
|
||||
function RootErrorFallback({error}: ErrorFallbackProps) {
|
||||
if (error instanceof AuthenticationError) {
|
||||
return <div>Error: You are not authenticated</div>
|
||||
} else if (error instanceof AuthorizationError) {
|
||||
return (
|
||||
<ErrorComponent
|
||||
statusCode={error.statusCode}
|
||||
title="Sorry, you are not authorized to access this"
|
||||
/>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<ErrorComponent
|
||||
statusCode={(error as any)?.statusCode || 400}
|
||||
title={error.message || error.name}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
function MyApp({Component, pageProps}: AppProps) {
|
||||
return (
|
||||
<ErrorBoundary FallbackComponent={RootErrorFallback}>
|
||||
<Suspense fallback="Loading...">
|
||||
<Component {...pageProps} />
|
||||
</Suspense>
|
||||
</ErrorBoundary>
|
||||
)
|
||||
}
|
||||
|
||||
export default withBlitz(MyApp)
|
||||
19
apps/web/pages/api/get-user.ts
Normal file
19
apps/web/pages/api/get-user.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import {api} from "app/blitz-server"
|
||||
import {SessionContext} from "@blitzjs/auth"
|
||||
import {prisma} from "../../prisma/index"
|
||||
|
||||
export default api(async (_req, res, ctx) => {
|
||||
const blitzContext = ctx
|
||||
|
||||
const publicData = blitzContext.session.$publicData
|
||||
|
||||
const sessions = await prisma.session.findMany({})
|
||||
const sessionsCount = await prisma.session.count({})
|
||||
|
||||
res.status(200).json({
|
||||
userId: blitzContext.session.userId,
|
||||
publicData: {...publicData},
|
||||
activeSessions: sessions,
|
||||
sessionsCount,
|
||||
})
|
||||
})
|
||||
11
apps/web/pages/api/is-authorized.ts
Normal file
11
apps/web/pages/api/is-authorized.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import {api} from "app/blitz-server"
|
||||
|
||||
export default api(async (_req, res, ctx) => {
|
||||
const {session} = ctx
|
||||
|
||||
const authorized = session.$isAuthorized()
|
||||
|
||||
res.status(200).json({
|
||||
authorized,
|
||||
})
|
||||
})
|
||||
9
apps/web/pages/api/logout.ts
Normal file
9
apps/web/pages/api/logout.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import {api} from "app/blitz-server"
|
||||
|
||||
export default api(async (_req, res, ctx) => {
|
||||
const blitzContext = ctx
|
||||
|
||||
await blitzContext.session.$revoke()
|
||||
|
||||
res.status(200).json({userId: blitzContext.session.userId})
|
||||
})
|
||||
21
apps/web/pages/api/multiply.ts
Normal file
21
apps/web/pages/api/multiply.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import {NextApiRequest, NextApiResponse} from "next"
|
||||
import {prisma} from "../../prisma/index"
|
||||
|
||||
export default async function handle(req: NextApiRequest, res: NextApiResponse) {
|
||||
const session = await prisma.session.findFirst({
|
||||
where: {
|
||||
handle: "test",
|
||||
},
|
||||
})
|
||||
|
||||
const isAuthorized = !!session
|
||||
|
||||
if (!isAuthorized) {
|
||||
res.status(404).json({message: "No access"})
|
||||
return
|
||||
}
|
||||
|
||||
const x = parseInt(req.query.x as string)
|
||||
const y = parseInt(req.query.y as string)
|
||||
res.json({result: x * y})
|
||||
}
|
||||
12
apps/web/pages/api/revoke-all-sessions.ts
Normal file
12
apps/web/pages/api/revoke-all-sessions.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import {api} from "app/blitz-server"
|
||||
import {prisma} from "../../prisma/index"
|
||||
|
||||
export default api(async (_req, res) => {
|
||||
const sessions = await prisma.session.deleteMany()
|
||||
const sessionsCount = await prisma.session.count()
|
||||
|
||||
res.status(200).json({
|
||||
activeSessions: sessions,
|
||||
sessionsCount,
|
||||
})
|
||||
})
|
||||
4
apps/web/pages/api/rpc/[[...blitz]].ts
Normal file
4
apps/web/pages/api/rpc/[[...blitz]].ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import {rpcHandler} from "@blitzjs/rpc"
|
||||
import {api} from "app/blitz-server"
|
||||
|
||||
export default api(rpcHandler({onError: console.log}))
|
||||
17
apps/web/pages/api/set-public-data.ts
Normal file
17
apps/web/pages/api/set-public-data.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import {setPublicDataForUser} from "@blitzjs/auth"
|
||||
import {api} from "app/blitz-server"
|
||||
import {prisma} from "../../prisma/index"
|
||||
|
||||
export default api(async (req, res, ctx) => {
|
||||
if (ctx.session.$thisIsAuthorized()) {
|
||||
ctx.session.$publicData
|
||||
|
||||
await prisma.user.update({
|
||||
where: {id: ctx.session.userId as number},
|
||||
data: {role: req.query.role as string},
|
||||
})
|
||||
await setPublicDataForUser(ctx.session.userId, {role: req.query.role as string})
|
||||
}
|
||||
|
||||
res.status(200).json({userId: ctx.session.userId, role: req.query.role as string})
|
||||
})
|
||||
31
apps/web/pages/api/signin.ts
Normal file
31
apps/web/pages/api/signin.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import {api} from "app/blitz-server"
|
||||
import {prisma} from "../../prisma/index"
|
||||
import {SecurePassword} from "@blitzjs/auth"
|
||||
|
||||
export const authenticateUser = async (email: string, password: string) => {
|
||||
const user = await prisma.user.findFirst({where: {email}})
|
||||
if (!user) throw new Error("Authentication Error")
|
||||
|
||||
const result = await SecurePassword.verify(user.hashedPassword, password)
|
||||
|
||||
if (result === SecurePassword.VALID_NEEDS_REHASH) {
|
||||
// Upgrade hashed password with a more secure hash
|
||||
const improvedHash = await SecurePassword.hash(password)
|
||||
await prisma.user.update({where: {id: user.id}, data: {hashedPassword: improvedHash}})
|
||||
}
|
||||
|
||||
const {hashedPassword, ...rest} = user
|
||||
return rest
|
||||
}
|
||||
|
||||
export default api(async (req, res, ctx) => {
|
||||
const blitzContext = ctx
|
||||
|
||||
const user = await authenticateUser(req.query.email as string, req.query.password as string)
|
||||
|
||||
await blitzContext.session.$create({
|
||||
userId: user.id,
|
||||
})
|
||||
|
||||
res.status(200).json({email: req.query.email, userId: blitzContext.session.userId})
|
||||
})
|
||||
22
apps/web/pages/api/signup.ts
Normal file
22
apps/web/pages/api/signup.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import {api} from "app/blitz-server"
|
||||
import {prisma} from "../../prisma/index"
|
||||
import {SecurePassword} from "@blitzjs/auth"
|
||||
|
||||
export default api(async (req, res, ctx) => {
|
||||
const blitzContext = ctx
|
||||
|
||||
const hashedPassword = await SecurePassword.hash(
|
||||
(req.query.password as string) || "test-password",
|
||||
)
|
||||
const email = (req.query.email as string) || "test" + Math.random() + "@test.com"
|
||||
const user = await prisma.user.create({
|
||||
data: {email, hashedPassword, role: "user"},
|
||||
select: {id: true, name: true, email: true, role: true},
|
||||
})
|
||||
|
||||
await blitzContext.session.$create({
|
||||
userId: user.id,
|
||||
})
|
||||
|
||||
res.status(200).json({userId: blitzContext.session.userId, ...user, email: req.query.email})
|
||||
})
|
||||
17
apps/web/pages/authenticated-page.tsx
Normal file
17
apps/web/pages/authenticated-page.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
const AuthPage = () => {
|
||||
return (
|
||||
<div>
|
||||
{JSON.stringify(
|
||||
{
|
||||
message: "This is a page that requires authentication",
|
||||
},
|
||||
null,
|
||||
2,
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
AuthPage.authenticate = true
|
||||
|
||||
export default AuthPage
|
||||
41
apps/web/pages/create-user.tsx
Normal file
41
apps/web/pages/create-user.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import {useState} from "react"
|
||||
import {SessionContext} from "@blitzjs/auth"
|
||||
import {useMutation} from "@blitzjs/rpc"
|
||||
import createUser from "app/mutations/createUser"
|
||||
import {User} from "prisma"
|
||||
|
||||
function Page() {
|
||||
const [name, setName] = useState("")
|
||||
const [email, setEmail] = useState("")
|
||||
const [createUserMutation, {error}] = useMutation(createUser)
|
||||
const [newUser, setNewUser] = useState<null | User>(null)
|
||||
|
||||
return (
|
||||
<div>
|
||||
New User Form:
|
||||
<form
|
||||
onSubmit={async (e) => {
|
||||
e.preventDefault()
|
||||
const user = await createUserMutation({name, email})
|
||||
setNewUser(user)
|
||||
}}
|
||||
>
|
||||
<label>
|
||||
Name:
|
||||
<input type="text" value={name} onChange={(e) => setName(e.target.value)} />
|
||||
</label>
|
||||
<label>
|
||||
Email:
|
||||
<input type="text" value={email} onChange={(e) => setEmail(e.target.value)} />
|
||||
</label>
|
||||
<button type="submit">Create User</button>
|
||||
</form>
|
||||
<div style={{paddingTop: 20}}>
|
||||
<div>Error: {JSON.stringify(error, null, 2)}</div>
|
||||
New user: {JSON.stringify(newUser, null, 2)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Page
|
||||
15
apps/web/pages/index.tsx
Normal file
15
apps/web/pages/index.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import {invoke} from "@blitzjs/rpc"
|
||||
import getBasic from "app/queries/getBasic"
|
||||
|
||||
export const getServerSideProps = () => {
|
||||
return {props: {}}
|
||||
}
|
||||
|
||||
export default function Web() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Web</h1>
|
||||
<button onClick={() => invoke(getBasic, "FROM BROWSER")}>GetBasic</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
18
apps/web/pages/page-with-auth-redirect.tsx
Normal file
18
apps/web/pages/page-with-auth-redirect.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
const PageWithAuthRedirect = () => {
|
||||
return (
|
||||
<div>
|
||||
{JSON.stringify(
|
||||
{
|
||||
message:
|
||||
"This is a page that will redirect you to the Home page if you are authenticated",
|
||||
},
|
||||
null,
|
||||
2,
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
PageWithAuthRedirect.redirectAuthenticatedTo = "/"
|
||||
|
||||
export default PageWithAuthRedirect
|
||||
21
apps/web/pages/page-with-gsp.tsx
Normal file
21
apps/web/pages/page-with-gsp.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import {gSP} from "app/blitz-server"
|
||||
|
||||
export const getStaticProps = gSP(async ({ctx}) => {
|
||||
return {
|
||||
props: {
|
||||
data: {
|
||||
// userId: ctx?.session.userId,
|
||||
// session: {
|
||||
// id: session.userId,
|
||||
// publicData: session.$publicData,
|
||||
// },
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
function PageWithGsp({data}) {
|
||||
return <div>{JSON.stringify(data, null, 2)}</div>
|
||||
}
|
||||
|
||||
export default PageWithGsp
|
||||
23
apps/web/pages/page-with-gssp.tsx
Normal file
23
apps/web/pages/page-with-gssp.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import {SessionContext} from "@blitzjs/auth"
|
||||
import {gSSP} from "app/blitz-server"
|
||||
|
||||
type Props = {
|
||||
userId: unknown
|
||||
publicData: SessionContext["$publicData"]
|
||||
}
|
||||
|
||||
export const getServerSideProps = gSSP<Props>(async ({ctx}) => {
|
||||
const {session} = ctx
|
||||
return {
|
||||
props: {
|
||||
userId: session.userId,
|
||||
publicData: session.$publicData,
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
function PageWithGssp(props: Props) {
|
||||
return <div>{JSON.stringify(props, null, 2)}</div>
|
||||
}
|
||||
|
||||
export default PageWithGssp
|
||||
18
apps/web/pages/page-with-redirect.tsx
Normal file
18
apps/web/pages/page-with-redirect.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
const PageWithRedirect = () => {
|
||||
return (
|
||||
<div>
|
||||
{JSON.stringify(
|
||||
{
|
||||
message:
|
||||
"This page requires authentication. It will redirect you to the Home page if you are NOT authenticated",
|
||||
},
|
||||
null,
|
||||
2,
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
PageWithRedirect.authenticate = {redirectTo: "/"}
|
||||
|
||||
export default PageWithRedirect
|
||||
6
apps/web/pages/page-with-use-auth-session.tsx
Normal file
6
apps/web/pages/page-with-use-auth-session.tsx
Normal file
@@ -0,0 +1,6 @@
|
||||
import {useAuthenticatedSession} from "@blitzjs/auth"
|
||||
|
||||
export default function PageWithUseAuthSession() {
|
||||
useAuthenticatedSession()
|
||||
return <div>This page is using useAuthenticatedSession</div>
|
||||
}
|
||||
6
apps/web/pages/page-with-use-authorize-if.tsx
Normal file
6
apps/web/pages/page-with-use-authorize-if.tsx
Normal file
@@ -0,0 +1,6 @@
|
||||
import {useAuthorizeIf} from "@blitzjs/auth"
|
||||
|
||||
export default function PageWithUseAuthorizeIf() {
|
||||
useAuthorizeIf(Math.random() > 0.5)
|
||||
return <div>This page is using useAuthorizeIf</div>
|
||||
}
|
||||
6
apps/web/pages/page-with-use-authorize.tsx
Normal file
6
apps/web/pages/page-with-use-authorize.tsx
Normal file
@@ -0,0 +1,6 @@
|
||||
import {useAuthorize} from "@blitzjs/auth"
|
||||
|
||||
export default function PgaeWithUseAuthorize() {
|
||||
useAuthorize()
|
||||
return <div>This page is using useAuthorize</div>
|
||||
}
|
||||
6
apps/web/pages/page-with-use-redirect-auth.tsx
Normal file
6
apps/web/pages/page-with-use-redirect-auth.tsx
Normal file
@@ -0,0 +1,6 @@
|
||||
import {useRedirectAuthenticated} from "@blitzjs/auth"
|
||||
|
||||
export default function PageWithUseRedirectAuth() {
|
||||
useRedirectAuthenticated("/")
|
||||
return <div>This page is using useRedirectAuthenticated</div>
|
||||
}
|
||||
6
apps/web/pages/page-with-use-session.tsx
Normal file
6
apps/web/pages/page-with-use-session.tsx
Normal file
@@ -0,0 +1,6 @@
|
||||
import {useSession} from "@blitzjs/auth"
|
||||
|
||||
export default function PgaeWithUseSession() {
|
||||
const data = useSession()
|
||||
return <div>{JSON.stringify({data}, null, 2)}</div>
|
||||
}
|
||||
7
apps/web/pages/page-without-flicker.tsx
Normal file
7
apps/web/pages/page-without-flicker.tsx
Normal file
@@ -0,0 +1,7 @@
|
||||
const PageWithoutFlicker = ({data}) => {
|
||||
return <div>{JSON.stringify(data)}</div>
|
||||
}
|
||||
|
||||
PageWithoutFlicker.suppressFirstRenderFlicker = true
|
||||
|
||||
export default PageWithoutFlicker
|
||||
20
apps/web/pages/users.tsx
Normal file
20
apps/web/pages/users.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import {useQuery} from "@blitzjs/rpc"
|
||||
import getUsers from "app/queries/getUsers"
|
||||
|
||||
function UsersPage() {
|
||||
const [users] = useQuery(getUsers, {})
|
||||
return (
|
||||
<div>
|
||||
Users:
|
||||
<ul>
|
||||
{users.map((user) => (
|
||||
<li key={user.id}>
|
||||
{user.name} - {user.email}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default UsersPage
|
||||
8
apps/web/prisma/index.ts
Normal file
8
apps/web/prisma/index.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import {enhancePrisma} from "blitz"
|
||||
import {PrismaClient} from "@prisma/client"
|
||||
|
||||
const EnhancedPrisma = enhancePrisma(PrismaClient)
|
||||
|
||||
export * from "@prisma/client"
|
||||
const prisma = new EnhancedPrisma()
|
||||
export {prisma}
|
||||
47
apps/web/prisma/migrations/20220209111643_init/migration.sql
Normal file
47
apps/web/prisma/migrations/20220209111643_init/migration.sql
Normal file
@@ -0,0 +1,47 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "User" (
|
||||
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" DATETIME NOT NULL,
|
||||
"name" TEXT,
|
||||
"email" TEXT NOT NULL,
|
||||
"hashedPassword" TEXT,
|
||||
"role" TEXT NOT NULL DEFAULT 'user'
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Session" (
|
||||
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" DATETIME NOT NULL,
|
||||
"expiresAt" DATETIME,
|
||||
"handle" TEXT NOT NULL,
|
||||
"userId" INTEGER,
|
||||
"hashedSessionToken" TEXT,
|
||||
"antiCSRFToken" TEXT,
|
||||
"publicData" TEXT,
|
||||
"privateData" TEXT,
|
||||
CONSTRAINT "Session_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE SET NULL ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
-- 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,
|
||||
CONSTRAINT "Token_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Session_handle_key" ON "Session"("handle");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Token_hashedToken_type_key" ON "Token"("hashedToken", "type");
|
||||
3
apps/web/prisma/migrations/migration_lock.toml
Normal file
3
apps/web/prisma/migrations/migration_lock.toml
Normal file
@@ -0,0 +1,3 @@
|
||||
# Please do not edit this file manually
|
||||
# It should be added in your version-control system (i.e. Git)
|
||||
provider = "sqlite"
|
||||
50
apps/web/prisma/schema.prisma
Normal file
50
apps/web/prisma/schema.prisma
Normal file
@@ -0,0 +1,50 @@
|
||||
datasource sqlite {
|
||||
provider = "sqlite"
|
||||
url = "file:./db.sqlite"
|
||||
}
|
||||
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
}
|
||||
|
||||
model User {
|
||||
id Int @id @default(autoincrement())
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
name String?
|
||||
email String @unique
|
||||
hashedPassword String?
|
||||
role String @default("user")
|
||||
|
||||
sessions Session[]
|
||||
tokens Token[]
|
||||
}
|
||||
|
||||
model Session {
|
||||
id Int @id @default(autoincrement())
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
expiresAt DateTime?
|
||||
handle String @unique
|
||||
user User? @relation(fields: [userId], references: [id])
|
||||
userId Int?
|
||||
hashedSessionToken String?
|
||||
antiCSRFToken String?
|
||||
publicData String?
|
||||
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])
|
||||
}
|
||||
7
apps/web/test/hello.unit.test.ts
Normal file
7
apps/web/test/hello.unit.test.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
function sum(a, b) {
|
||||
return a + b
|
||||
}
|
||||
|
||||
test("adds 1 + 2 to equal 3", () => {
|
||||
expect(sum(1, 2)).toBe(3)
|
||||
})
|
||||
8
apps/web/tsconfig.json
Normal file
8
apps/web/tsconfig.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "@blitzjs/config/tsconfig.nextjs.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": "."
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "jest.config.js"],
|
||||
"exclude": ["node_modules", "test"]
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 20 KiB |
1
assets/boostry.svg
Normal file
1
assets/boostry.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 143.5 42.09"><defs><style>.cls-1{fill:none;}.cls-2{fill:#d71526;}</style></defs><title>アセット 15</title><g id="レイヤー_2" data-name="レイヤー 2"><g id="レイヤー_1-2" data-name="レイヤー 1"><path d="M21.87,20A13.2,13.2,0,0,1,34.92,6.83c6.44,0,10.88,4.74,10.88,11a13.21,13.21,0,0,1-13,13.21C26.31,31.05,21.87,26.32,21.87,20Zm20.22-2.1c0-4.34-2.9-7.71-7.27-7.71-5.17,0-9.25,4.51-9.25,9.71,0,4.34,2.91,7.71,7.28,7.71A9.52,9.52,0,0,0,42.09,17.94Z"/><path d="M47.67,20a13.2,13.2,0,0,1,13-13.21c6.44,0,10.88,4.74,10.88,11a13.21,13.21,0,0,1-13,13.21C52.11,31.05,47.67,26.32,47.67,20Zm20.22-2.1c0-4.34-2.9-7.71-7.27-7.71-5.17,0-9.25,4.51-9.25,9.71,0,4.34,2.91,7.71,7.28,7.71C63.85,27.65,67.89,23.14,67.89,17.94Z"/><path d="M72.84,24.81a9.7,9.7,0,0,0,6.41,3c2.4,0,4.07-1.4,4.07-3.57,0-1.57-.94-2.64-3.3-3.87l-1.37-.71a6.56,6.56,0,0,1-4.07-6.14c0-3.9,3.2-6.7,7.47-6.7A12.23,12.23,0,0,1,88.57,8.9l-.68,3.51-.23-.18A10.16,10.16,0,0,0,82,10c-2.2,0-3.84,1.4-3.84,3.3,0,1.17.57,2.37,2.51,3.34l1.33.67C85.46,19.07,87,21,87,24c0,4.3-3.11,7.1-7.61,7.1a11.56,11.56,0,0,1-7.45-2.8l.71-3.65Z"/><path d="M106.27,10.63h-5.85l-3.91,20H92.94l3.91-20H91l.66-3.37h15.32Z"/><path d="M115,21.54l5.27,9.08h-4.14l-5.3-9.74-1.91,9.74h-3.57L109.9,7.26h4.77c4.67,0,7.68,2.54,7.68,6.47S119.44,20.74,115,21.54Zm-3.84-2.37h1.33c3.51,0,6.18-2.17,6.18-5.07,0-2.3-1.7-3.7-4.44-3.7h-1.37Z"/><line class="cls-1" x1="72.32" y1="24.47" x2="72.69" y2="24.81"/><path d="M15.4,16.76l-.76-.31.78-.27C19,15,21.06,12.35,21.06,9.23c0-3.92-3.19-6.65-7.75-6.65H6L.5,30.62H9c5.94,0,10.42-3.48,10.42-8.08A6,6,0,0,0,15.4,16.76ZM8.93,5.68H12.3c2.94,0,4.77,1.55,4.77,4,0,3-2.58,5.18-6,5.18H7.13ZM9.1,27.52H4.75L6.59,18h3.82c3.05,0,5,1.7,5,4.32C15.43,25.34,12.76,27.52,9.1,27.52Z"/><polygon class="cls-2" points="134.09 25.12 132.58 21.09 132.54 20.97 132.49 20.86 127.39 7.26 123.39 7.26 130.13 25.3 121.55 42.09 125.56 42.09 131.87 29.85 131.93 29.73 131.98 29.61 134.06 25.58 134.12 25.47 134.18 25.35 134.14 25.24 134.09 25.12"/><polygon points="139.5 7.26 132.65 20.76 132.65 20.76 132.54 20.98 132.58 21.09 134.09 25.12 134.14 25.24 134.18 25.35 143.5 7.26 139.5 7.26"/><rect class="cls-1" width="143" height="42"/></g></g></svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
BIN
assets/meetkai.png
Normal file
BIN
assets/meetkai.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 46 KiB |
@@ -1 +0,0 @@
|
||||
<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>
|
||||
|
Before Width: | Height: | Size: 650 B |
Binary file not shown.
|
Before Width: | Height: | Size: 1.8 MiB |
@@ -1,64 +0,0 @@
|
||||
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",
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
57
examples/auth/.gitignore
vendored
57
examples/auth/.gitignore
vendored
@@ -1,57 +0,0 @@
|
||||
# dependencies
|
||||
node_modules
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.pnp.*
|
||||
.npm
|
||||
web_modules/
|
||||
|
||||
# blitz
|
||||
/.blitz/
|
||||
/.next/
|
||||
*.sqlite
|
||||
.now
|
||||
.blitz-console-history
|
||||
blitz-log.log
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
|
||||
# local env files
|
||||
.env
|
||||
.envrc
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Testing
|
||||
coverage
|
||||
*.lcov
|
||||
.nyc_output
|
||||
lib-cov
|
||||
|
||||
# Caches
|
||||
*.tsbuildinfo
|
||||
.eslintcache
|
||||
.node_repl_history
|
||||
.yarn-integrity
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
.vercel
|
||||
@@ -1 +0,0 @@
|
||||
save-exact=true
|
||||
@@ -1,5 +0,0 @@
|
||||
.gitkeep
|
||||
.env*
|
||||
*.ico
|
||||
*.lock
|
||||
db/migrations
|
||||
@@ -1,2 +0,0 @@
|
||||
.blitz
|
||||
*.sqlite
|
||||
@@ -1,26 +0,0 @@
|
||||
# auth
|
||||
|
||||
## Getting Started
|
||||
|
||||
1. Add this code to db/schema.prisma:
|
||||
|
||||
```
|
||||
model Project {
|
||||
id Int @default(autoincrement()) @id
|
||||
name String
|
||||
}
|
||||
```
|
||||
|
||||
2. DB migrate
|
||||
|
||||
```
|
||||
blitz prisma migrate dev
|
||||
```
|
||||
|
||||
3. Start the dev server
|
||||
|
||||
```
|
||||
blitz dev
|
||||
```
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
@@ -1,145 +0,0 @@
|
||||
import {passportAuth, PublicData} from "blitz"
|
||||
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)
|
||||
}
|
||||
|
||||
// These aren't required, but this is a good practice to ensure you have the env vars you need
|
||||
assert(process.env.TWITTER_CONSUMER_KEY, "You must provide the TWITTER_CONSUMER_KEY env variable")
|
||||
assert(
|
||||
process.env.TWITTER_CONSUMER_SECRET,
|
||||
"You must provide the TWITTER_CONSUMER_SECRET env variable",
|
||||
)
|
||||
assert(process.env.GITHUB_CLIENT_ID, "You must provide the GITHUB_CLIENT_ID env variable")
|
||||
assert(process.env.GITHUB_CLIENT_SECRET, "You must provide the GITHUB_CLIENT_SECRET env variable")
|
||||
|
||||
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((ctx) => ({
|
||||
successRedirectUrl: "/",
|
||||
strategies: [
|
||||
{
|
||||
strategy: new TwitterStrategy(
|
||||
{
|
||||
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"
|
||||
: "http://localhost:3000/api/auth/twitter/callback",
|
||||
includeEmail: true,
|
||||
},
|
||||
async function (_token, _tokenSecret, profile, done) {
|
||||
const email = profile.emails && profile.emails[0]?.value
|
||||
|
||||
if (!email) {
|
||||
// This can happen if you haven't enabled email access in your twitter app permissions
|
||||
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: {
|
||||
email,
|
||||
name: profile.displayName,
|
||||
},
|
||||
update: {email},
|
||||
})
|
||||
|
||||
const publicData = {userId: user.id, role: user.role, source: "twitter"}
|
||||
done(null, {publicData})
|
||||
},
|
||||
),
|
||||
},
|
||||
{
|
||||
strategy: new GitHubStrategy(
|
||||
{
|
||||
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"
|
||||
: "http://localhost:3000/api/auth/github/callback",
|
||||
},
|
||||
async function (
|
||||
_token: string,
|
||||
_tokenSecret: string,
|
||||
profile: any,
|
||||
done: (err: Error | null, data?: {publicData: PublicData}) => void,
|
||||
) {
|
||||
const email = profile.emails && profile.emails[0]?.value
|
||||
|
||||
if (!email) {
|
||||
// This can happen if you haven't enabled email access in your twitter app permissions
|
||||
return done(new Error("Twitter OAuth response doesn't have email."))
|
||||
}
|
||||
|
||||
const user = await db.user.upsert({
|
||||
where: {email},
|
||||
create: {
|
||||
email,
|
||||
name: profile.displayName,
|
||||
},
|
||||
update: {email},
|
||||
})
|
||||
|
||||
const publicData = {
|
||||
userId: user.id,
|
||||
role: user.role as Role,
|
||||
source: "github",
|
||||
githubUsername: profile.username,
|
||||
}
|
||||
done(null, {publicData})
|
||||
},
|
||||
),
|
||||
},
|
||||
{
|
||||
authenticateOptions: {scope: "openid email profile"},
|
||||
strategy: new Auth0Strategy(
|
||||
{
|
||||
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"
|
||||
: "http://localhost:3000/api/auth/auth0/callback",
|
||||
},
|
||||
async function (_token, _tokenSecret, _extraParams, profile, done) {
|
||||
const email = profile.emails && profile.emails[0]?.value
|
||||
|
||||
if (!email) {
|
||||
// This can happen if you haven't enabled email access in your twitter app permissions
|
||||
return done(new Error("Auth0 response doesn't have email."))
|
||||
}
|
||||
|
||||
const user = await db.user.upsert({
|
||||
where: {email},
|
||||
create: {
|
||||
email,
|
||||
name: profile.displayName,
|
||||
},
|
||||
update: {email},
|
||||
})
|
||||
|
||||
const publicData = {
|
||||
userId: user.id,
|
||||
role: user.role,
|
||||
source: "auth0",
|
||||
githubUsername: profile.username,
|
||||
}
|
||||
done(undefined, {publicData})
|
||||
},
|
||||
),
|
||||
},
|
||||
],
|
||||
}))
|
||||
@@ -1,56 +0,0 @@
|
||||
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
|
||||
}
|
||||
|
||||
export const LoginForm = (props: LoginFormProps) => {
|
||||
const [loginMutation] = useMutation(login)
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>Login</h1>
|
||||
|
||||
<Form
|
||||
submitText="Login"
|
||||
schema={Login}
|
||||
initialValues={{email: "", password: ""}}
|
||||
onSubmit={async (values) => {
|
||||
try {
|
||||
await loginMutation(values)
|
||||
props.onSuccess?.()
|
||||
} catch (error) {
|
||||
if (error instanceof AuthenticationError) {
|
||||
return {[FORM_ERROR]: "Sorry, those credentials are invalid"}
|
||||
} 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" />
|
||||
<div>
|
||||
<Link href={Routes.ForgotPasswordPage()}>
|
||||
<a>Forgot your password?</a>
|
||||
</Link>
|
||||
</div>
|
||||
</Form>
|
||||
|
||||
<div style={{marginTop: "1rem"}}>
|
||||
Or <Link href={Routes.SignupPage()}>Sign Up</Link>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default LoginForm
|
||||
@@ -1,44 +0,0 @@
|
||||
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
|
||||
@@ -1,23 +0,0 @@
|
||||
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
|
||||
},
|
||||
)
|
||||
@@ -1,56 +0,0 @@
|
||||
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()
|
||||
})
|
||||
})
|
||||
@@ -1,41 +0,0 @@
|
||||
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,37 +0,0 @@
|
||||
import {resolver, SecurePassword, AuthenticationError} from "blitz"
|
||||
import db from "db"
|
||||
import {Login} from "../validations"
|
||||
import {Role} from "types"
|
||||
|
||||
export const authenticateUser = async (email: string, password: string) => {
|
||||
const user = await db.user.findFirst({where: {email}})
|
||||
if (!user) throw new AuthenticationError()
|
||||
|
||||
const result = await SecurePassword.verify(user.hashedPassword, password)
|
||||
|
||||
if (result === SecurePassword.VALID_NEEDS_REHASH) {
|
||||
// Upgrade hashed password with a more secure hash
|
||||
const improvedHash = await SecurePassword.hash(password)
|
||||
await db.user.update({where: {id: user.id}, data: {hashedPassword: improvedHash}})
|
||||
}
|
||||
|
||||
const {hashedPassword, ...rest} = user
|
||||
return rest
|
||||
}
|
||||
|
||||
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 ctx.session.$create({userId: user.id, role: user.role as Role})
|
||||
|
||||
return user
|
||||
})
|
||||
|
||||
export const config = {
|
||||
api: {
|
||||
bodyParser: {
|
||||
sizeLimit: "2mb",
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
import {Ctx} from "blitz"
|
||||
|
||||
export default async function logout(_: any, ctx: Ctx) {
|
||||
return await ctx.session.$revoke()
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
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,
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -1,47 +0,0 @@
|
||||
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,15 +0,0 @@
|
||||
import {resolver, SecurePassword} from "blitz"
|
||||
import db from "db"
|
||||
import {Signup} from "app/auth/validations"
|
||||
import {Role} from "types"
|
||||
|
||||
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: email.toLowerCase(), hashedPassword, role: "user"},
|
||||
select: {id: true, name: true, email: true, role: true},
|
||||
})
|
||||
|
||||
await ctx.session.$create({userId: user.id, role: user.role as Role})
|
||||
return user
|
||||
})
|
||||
@@ -1,47 +0,0 @@
|
||||
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,25 +0,0 @@
|
||||
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 LoginPage: BlitzPage = () => {
|
||||
const router = useRouter()
|
||||
|
||||
return (
|
||||
<div>
|
||||
<LoginForm
|
||||
onSuccess={() => {
|
||||
const next = router.query.next ? decodeURIComponent(router.query.next as string) : "/"
|
||||
router.push(next)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
LoginPage.redirectAuthenticatedTo = Routes.Home().pathname
|
||||
LoginPage.getLayout = (page) => <Layout title="Log In">{page}</Layout>
|
||||
|
||||
export default LoginPage
|
||||
@@ -1,60 +0,0 @@
|
||||
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,17 +0,0 @@
|
||||
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()
|
||||
|
||||
return (
|
||||
<div>
|
||||
<SignupForm onSuccess={() => router.push("/")} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
SignupPage.getLayout = (page) => <Layout title="Sign Up">{page}</Layout>
|
||||
|
||||
export default SignupPage
|
||||
@@ -1,33 +0,0 @@
|
||||
import {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,59 +0,0 @@
|
||||
import React, {ReactNode, PropsWithoutRef} from "react"
|
||||
import {Form as FinalForm, FormProps as FinalFormProps} from "react-final-form"
|
||||
import {z} from "zod"
|
||||
import {validateZodSchema} from "blitz"
|
||||
export {FORM_ERROR} from "final-form"
|
||||
|
||||
export interface FormProps<S extends z.ZodType<any, any>>
|
||||
extends Omit<PropsWithoutRef<JSX.IntrinsicElements["form"]>, "onSubmit"> {
|
||||
/** All your form fields */
|
||||
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"]
|
||||
}
|
||||
|
||||
export function Form<S extends z.ZodType<any, any>>({
|
||||
children,
|
||||
submitText,
|
||||
schema,
|
||||
initialValues,
|
||||
onSubmit,
|
||||
...props
|
||||
}: FormProps<S>) {
|
||||
return (
|
||||
<FinalForm
|
||||
initialValues={initialValues}
|
||||
validate={validateZodSchema(schema)}
|
||||
onSubmit={onSubmit}
|
||||
render={({handleSubmit, submitting, submitError}) => (
|
||||
<form onSubmit={handleSubmit} className="form" {...props}>
|
||||
{/* Form fields supplied as children are rendered here */}
|
||||
{children}
|
||||
|
||||
{submitError && (
|
||||
<div role="alert" style={{color: "red"}}>
|
||||
{submitError}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{submitText && (
|
||||
<button type="submit" disabled={submitting}>
|
||||
{submitText}
|
||||
</button>
|
||||
)}
|
||||
|
||||
<style global jsx>{`
|
||||
.form > * + * {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
`}</style>
|
||||
</form>
|
||||
)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export default Form
|
||||
@@ -1,59 +0,0 @@
|
||||
import React, {PropsWithoutRef} from "react"
|
||||
import {useField} from "react-final-form"
|
||||
|
||||
export interface LabeledTextFieldProps extends PropsWithoutRef<JSX.IntrinsicElements["input"]> {
|
||||
/** Field name. */
|
||||
name: string
|
||||
/** Field label. */
|
||||
label: string
|
||||
/** Field type. Doesn't include radio buttons and checkboxes */
|
||||
type?: "text" | "password" | "email" | "number"
|
||||
outerProps?: PropsWithoutRef<JSX.IntrinsicElements["div"]>
|
||||
}
|
||||
|
||||
export const LabeledTextField = React.forwardRef<HTMLInputElement, LabeledTextFieldProps>(
|
||||
({name, label, outerProps, ...props}, ref) => {
|
||||
const {
|
||||
input,
|
||||
meta: {touched, error, submitError, submitting},
|
||||
} = useField(name, {
|
||||
parse: props.type === "number" ? Number : undefined,
|
||||
})
|
||||
|
||||
const normalizedError = Array.isArray(error) ? error.join(", ") : error || submitError
|
||||
|
||||
return (
|
||||
<div {...outerProps}>
|
||||
<label>
|
||||
{label}
|
||||
<input {...input} disabled={submitting} {...props} ref={ref} />
|
||||
</label>
|
||||
|
||||
{touched && normalizedError && (
|
||||
<div role="alert" style={{color: "red"}}>
|
||||
{normalizedError}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<style jsx>{`
|
||||
label {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: start;
|
||||
font-size: 1rem;
|
||||
}
|
||||
input {
|
||||
font-size: 1rem;
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 3px;
|
||||
border: 1px solid purple;
|
||||
appearance: none;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
export default LabeledTextField
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user