1
0
mirror of synced 2026-02-03 18:01:02 -05:00

Compare commits

...

92 Commits

Author SHA1 Message Date
Brandon Bayer
11fc19fee2 v0.30.3 2021-02-23 16:43:30 -05:00
Brandon Bayer
fb4a63acba Improve error message for missing _document page (patch) (#2000) 2021-02-23 16:39:24 -05:00
Hiren Chauhan
c3f8c7d07d Fix blitz install when using npm (patch) (#1986)
Co-authored-by: Brandon Bayer <b@bayer.ws>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2021-02-23 16:20:58 -05:00
allcontributors[bot]
51519166ef docs: add hirenchauhan2 as a contributor (#1994)
Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2021-02-23 15:16:07 -05:00
depfu[bot]
113a3a04f3 Update superjson to version 1.7.2 (#1993)
Co-authored-by: depfu[bot] <23717796+depfu[bot]@users.noreply.github.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2021-02-23 19:55:18 +00:00
depfu[bot]
eba2a8081d Update babel-plugin-superjson-next to version 0.2.2 (#1992)
Co-authored-by: depfu[bot] <23717796+depfu[bot]@users.noreply.github.com>
2021-02-23 17:27:08 +00:00
JuanM04
16d2cae5b8 Added @juanM04 as a L2 maintainer for website/docs (#1989)
(meta)
2021-02-23 12:07:20 -05:00
Brandon Bayer
c2bd242d46 Add @aditsachde as a contributor 2021-02-23 12:05:15 -05:00
Brandon Bayer
99966ab192 Add @andreadellacorte as a contributor 2021-02-22 16:48:59 -05:00
Brandon Bayer
387e6e420b add rob malko as seedling sponsor 2021-02-20 17:39:10 -05:00
Brandon Bayer
d39b461ddd v0.30.2 2021-02-20 15:39:25 -05:00
Brandon Bayer
0b7d463e7f fix error.statusCode missing in frontend (patch) (#1981) 2021-02-20 15:30:50 -05:00
Brandon Bayer
87c4ee058c fix blitz generate Windows Error: spawn prisma ENOENT (patch) (#1979) 2021-02-20 15:09:06 -05:00
allcontributors[bot]
177c2b0519 docs: add deniseyu as a contributor (#1975)
Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2021-02-19 15:56:57 -05:00
Denise Yu
31411baff5 Update bug_report.yml (#1974)
Fixes bug report YAML template
2021-02-19 15:56:48 -05:00
Brandon Bayer
2c4b803e7b v0.30.1 2021-02-18 21:14:34 -05:00
Brandon Bayer
117f0fda74 fix UnhandledPromiseRejectionWarning when renaming queries/mutations (patch) (#1967)
* fix UnhandledPromiseRejectionWarning when renaming queries/mutations

* log any watcher errors

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2021-02-19 01:35:27 +00:00
Brandon Bayer
f613e08866 fix blitz routes command interfering with the dev build (#1966) 2021-02-19 01:07:34 +00:00
Brandon Bayer
b696831e9f Try to fix windows EPERM issues (patch) (#1965)
* try to fix windows EPERM issue

* comment

* fix test

* bump test wait time

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2021-02-19 00:28:36 +00:00
Brandon Bayer
bd1781b99e Update @Mzaien as a contributor 2021-02-18 18:43:28 -05:00
depfu[bot]
9077397abe Upgrade next: 10.0.6 → 10.0.7 AND add isLocaleDomain to newapp template test/utils.ts (patch) (#1949)
* Update next to version 10.0.7

* fix

Co-authored-by: depfu[bot] <23717796+depfu[bot]@users.noreply.github.com>
Co-authored-by: Brandon Bayer <b@bayer.ws>
2021-02-18 23:37:24 +00:00
Cristian Granda
8e8fcffa2a Fix error if package.json doesn't have a "name" field (#1963) 2021-02-18 18:02:02 -05:00
allcontributors[bot]
364fb5e618 docs: add cristianbgp as a contributor (#1964)
Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2021-02-18 18:01:33 -05:00
Brandon Bayer
7678181bcf update issue template
(ignore)
2021-02-18 17:54:33 -05:00
Brandon Bayer
180d4cc4a1 update bug report 2021-02-18 17:52:19 -05:00
Brandon Bayer
2484b3a692 Update bug_report.yml
(ignore)
2021-02-18 17:50:33 -05:00
Brandon Bayer
f1e223cd47 add new issue form template
closes: #1704
(meta)
2021-02-18 17:47:51 -05:00
allcontributors[bot]
1a4dd4a2fe docs: add Immortalin as a contributor (#1962)
Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2021-02-18 17:18:35 -05:00
Immortalin
7f3fcf1489 Update gitignore (#1954)
(meta)
2021-02-18 17:18:25 -05:00
Brandon Bayer
219f069dba Add @gusgard as a contributor 2021-02-18 17:05:43 -05:00
Brandon Bayer
09bd6751fa Add @davetorbeck as a contributor 2021-02-18 16:50:59 -05:00
Brandon Bayer
f03fe566e5 Add @DecadentIpsum as a contributor 2021-02-17 13:12:18 -05:00
Brandon Bayer
4ed750a8f7 Add @oliverloops as a contributor 2021-02-17 13:10:46 -05:00
Brandon Bayer
39eb1cef9b Add @garytube as a contributor 2021-02-17 13:09:34 -05:00
Brandon Bayer
0a3c975b98 v0.30.0 2021-02-17 08:49:48 -05:00
Brandon Bayer
1b4119bb50 Update new app readme with latest file structure
(ignore)
2021-02-16 22:34:06 -05:00
Brandon Bayer
f0d880a4f2 v0.30.0-canary.15 2021-02-16 22:25:15 -05:00
Brandon Bayer
11a11be20b Update README.md
(ignore)
2021-02-16 21:38:27 -05:00
Brandon Bayer
0273e58749 Update README.md
(ignore)
2021-02-16 21:37:39 -05:00
Brandon Bayer
75c3dc635e tweak sponsors
(ignore)
2021-02-16 19:48:21 -05:00
Brandon Bayer
00e54948f2 fix reactbricks icon
(ignore)
2021-02-16 19:47:27 -05:00
Brandon Bayer
ffcd0b4243 fix error on blitz start for new project (patch) (#1952) 2021-02-16 11:35:53 -05:00
Brandon Bayer
6bc26128ee Upgrade prisma to 2.17 (patch) (#1951) 2021-02-16 10:55:25 -05:00
Brandon Bayer
f8b599aff4 v0.30.0-canary.14 2021-02-15 14:33:16 -05:00
Joe Edelman
fa09904d3a fix getQueryKey() to work on the server (patch) (#1624)
Co-authored-by: Brandon Bayer <b@bayer.ws>
2021-02-15 14:24:33 -05:00
allcontributors[bot]
7c6c7dfdb5 docs: add jxe as a contributor (#1946)
Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2021-02-15 14:24:18 -05:00
Brandon Bayer
ca850480ae fix blitz routes (#1945) 2021-02-15 18:56:28 +00:00
Brandon Bayer
f9006974bf fix blitz dev ENOENT error and mistaken console.log (patch) (#1942)
* fix `blitz dev` ENOENT error (patch)

* remove log
2021-02-15 13:01:37 -05:00
Brandon Bayer
5f6d296e37 Add @fmilani as a contributor 2021-02-15 12:54:38 -05:00
Brandon Bayer
5697173d5a Flavio retires from L1 maintainer (meta) 2021-02-15 12:52:23 -05:00
Brandon Bayer
69e5da6794 Fix type issue in blitz generate code (patch) (#1941) 2021-02-15 12:44:00 -05:00
Brandon Bayer
66e521e14b (newapp) add types.d.ts with type declarations for CSS/SCSS modules (#1929) 2021-02-15 12:41:15 -05:00
Brandon Bayer
2a25bab41c v0.30.0-canary.13 2021-02-13 21:25:08 -05:00
Brandon Bayer
c2fa1486df Fix file paths in blitz server logs to be the original path instead of compiled path (patch) (#1933) 2021-02-13 21:23:28 -05:00
Brandon Bayer
19cc7a1510 Hopefully fix Windows EPERM , EBUSY, ENOTEMPTY, ENOENT errors (patch) (#1931) 2021-02-13 18:39:28 -05:00
Brandon Bayer
b1ed61f96d Unify blitz build folder. Now .blitz/build is always used instead of .blitz/caches/dev or .blitz/caches/build (#1930)
(patch)
2021-02-13 16:48:18 -05:00
Brandon Bayer
a101964603 (newapp) remove browserslist from package.json because it only affects CSS (#1928)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2021-02-13 15:16:40 -05:00
Brandon Bayer
eeeab00f1b Add node 15 to CI test matrix (meta) (#1927) 2021-02-13 15:16:32 -05:00
Brandon Bayer
42bf665f44 Add @arenddeboer as a contributor 2021-02-13 15:07:53 -05:00
Brandon Bayer
abf4795395 remove all unneeded imports of React from templates (patch) (#1925)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2021-02-13 14:44:29 -05:00
Brandon Bayer
d48891a144 change blitz generate delete mutation to use db.deleteMany instead of db.delete (#1924) 2021-02-13 19:26:21 +00:00
Brandon Bayer
57f1e7d703 v0.30.0-canary.12 2021-02-12 14:10:31 -05:00
Brandon Bayer
2d3e46fd79 Fix query cache being deleted on login instead of being invalidated (#1917)
* fix querycache being deleted on login instead of being invalidated

* fix ci (patch)
2021-02-12 14:08:50 -05:00
Brandon Bayer
984de2cab1 v0.30.0-canary.11 2021-02-11 19:54:41 -05:00
Brandon Bayer
fca7791c47 Fix error name fields not being sent to the client (#1916)
(patch)
2021-02-11 19:51:51 -05:00
Brandon Bayer
c389a7ad6b fix useQuery hooks enabled flag not working (#1915)
(patch)
2021-02-11 18:49:05 -05:00
Brandon Bayer
a4320fd974 v0.30.0-canary.10 2021-02-11 17:17:22 -05:00
Brandon Bayer
157c397959 Fix to accept the previous, misspelled anti-csrf cookie for seamless transition for existing apps (#1914)
(patch)
2021-02-11 17:14:08 -05:00
Brandon Bayer
d7804089e1 Fix type of options for useQuery hooks (#1913)
(patch)
2021-02-11 17:07:48 -05:00
Brandon Bayer
04b0cd356c v0.30.0-canary.9 2021-02-11 15:56:49 -05:00
JuanM04
cd615f58bd Automatically clear console on blitz dev (can disable with cli.clearConsoleOnBlitzDev = false in blitz.config.js (#1909)
* Clear console

* Removed clearConsole on blitz start

* Moved no-clear-console to blitz.config.js

* Changed imports

* increase default jest timeout to 10000 for server env

Co-authored-by: Brandon Bayer <b@bayer.ws> (minor)
2021-02-11 15:54:03 -05:00
Brandon Bayer
c49da040b5 add useAuthenticatedSession() hook and fix publicData types for authenticated sessions (#1910)
(minor)
2021-02-11 15:50:52 -05:00
allcontributors[bot]
da6685cd2d docs: add JuanM04 as a contributor (#1912)
Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2021-02-11 15:49:04 -05:00
allcontributors[bot]
0f4de7761e docs: add JuanM04 as a contributor (#1911)
Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2021-02-11 15:41:32 -05:00
Brandon Bayer
3114e8bc4a fix error message grammar
(patch)
2021-02-11 14:32:21 -05:00
Brandon Bayer
e2383fbb5f v0.30.0-canary.8 2021-02-10 19:22:34 -05:00
Brandon Bayer
a4ea513a76 Change blitz generate default syntax from default[value] to default=value (#1906)
* change type:default[true] to type:default=true (major)

* update test

* update cli help
2021-02-10 19:20:11 -05:00
Brandon Bayer
9e1860d8b3 remove some old docs
(ignore)
2021-02-10 19:03:42 -05:00
Brandon Bayer
05673fbdb5 Create SECURITY.md
(ignore)
2021-02-10 19:02:29 -05:00
Brandon Bayer
a2cdcf4a74 Create codeql-analysis.yml
(ignore)
2021-02-10 19:01:06 -05:00
Kevin Østerkilde
9ff56beebf Remove requirement of publicData.roles, change new app to use publicData.role, and fix public data types (#1788)
Co-authored-by: Brandon Bayer <b@bayer.ws> (minor)
2021-02-10 18:25:59 -05:00
Brandon Bayer
b238bae52c Add @beerose as a contributor 2021-02-10 16:29:26 -05:00
Brandon Bayer
caf3d260c8 Fix page props not being copied to the Blitz wrapper from last PR (#1905)
(ignore)
2021-02-10 16:19:03 -05:00
Brandon Bayer
b108c5720f Add page.authenticate, page.redirectAuthenticatedTo, and page.suppressFirstRenderFlicker (#1901)
(minor)
2021-02-10 14:06:04 -05:00
depfu[bot]
8ccc744c5f Upgrade superjson: 1.6.0 → 1.6.2 (patch) (#1904)
Co-authored-by: depfu[bot] <23717796+depfu[bot]@users.noreply.github.com>
2021-02-10 14:03:11 -05:00
Brandon Bayer
b0ed47e1b8 add useAuthorize and useRedirectAuthenticated hooks (#1900)
(minor)
2021-02-10 12:42:19 -05:00
Brian Liu
ae43a07b4d Fix useQuery hooks return type when enabled or suspense could be false (patch) (#1893)
Co-authored-by: Brandon Bayer <b@bayer.ws>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2021-02-10 12:30:01 -05:00
allcontributors[bot]
0ba81b6474 docs: add LBrian as a contributor (#1903)
Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2021-02-10 12:21:42 -05:00
Brandon Bayer
190bc65c53 fix newapp type issue in last PR
(ignore)
2021-02-10 12:20:12 -05:00
Brandon Bayer
85bd4ec286 (newapp) fix react-hook-form reset password page (#1902) 2021-02-10 16:37:16 +00:00
Brandon Bayer
1792cc6788 v0.30.0-canary.7 2021-02-09 19:36:42 -05:00
Michael Ford
dc88bc68d2 Change useSession to use suspense by default (#1888)
Co-authored-by: Brandon Bayer <b@bayer.ws> (major)
2021-02-09 19:21:09 -05:00
158 changed files with 1711 additions and 973 deletions

View File

@@ -1587,7 +1587,8 @@
"avatar_url": "https://avatars3.githubusercontent.com/u/43112535?v=4",
"profile": "http://Dal.Design",
"contributions": [
"code"
"code",
"doc"
]
},
{
@@ -1975,6 +1976,160 @@
"contributions": [
"code"
]
},
{
"login": "LBrian",
"name": "Brian Liu",
"avatar_url": "https://avatars.githubusercontent.com/u/3888780?v=4",
"profile": "https://brianypliu.com",
"contributions": [
"code"
]
},
{
"login": "beerose",
"name": "Aleksandra Sikora",
"avatar_url": "https://avatars.githubusercontent.com/u/9019397?v=4",
"profile": "http://aleksandra.codes",
"contributions": [
"code"
]
},
{
"login": "JuanM04",
"name": "JuanM04",
"avatar_url": "https://avatars.githubusercontent.com/u/16712703?v=4",
"profile": "https://juanm04.com",
"contributions": [
"code",
"doc"
]
},
{
"login": "arenddeboer",
"name": "Arend de Boer",
"avatar_url": "https://avatars.githubusercontent.com/u/7022204?v=4",
"profile": "https://github.com/arenddeboer",
"contributions": [
"doc"
]
},
{
"login": "fmilani",
"name": "Felipe Milani",
"avatar_url": "https://avatars.githubusercontent.com/u/1580375?v=4",
"profile": "https://github.com/fmilani",
"contributions": [
"doc"
]
},
{
"login": "jxe",
"name": "Joe Edelman",
"avatar_url": "https://avatars.githubusercontent.com/u/13018?v=4",
"profile": "http://nxhx.org",
"contributions": [
"code"
]
},
{
"login": "garytube",
"name": "Gary",
"avatar_url": "https://avatars.githubusercontent.com/u/3823504?v=4",
"profile": "https://github.com/garytube",
"contributions": [
"doc"
]
},
{
"login": "oliverloops",
"name": "Oliver Lopez ",
"avatar_url": "https://avatars.githubusercontent.com/u/33361399?v=4",
"profile": "http://oliverloops.com",
"contributions": [
"doc"
]
},
{
"login": "DecadentIpsum",
"name": "Andreas Zaralis",
"avatar_url": "https://avatars.githubusercontent.com/u/32861532?v=4",
"profile": "https://decadentIpsum.me",
"contributions": [
"doc"
]
},
{
"login": "davetorbeck",
"name": "David Torbeck",
"avatar_url": "https://avatars.githubusercontent.com/u/5829885?v=4",
"profile": "https://github.com/davetorbeck",
"contributions": [
"doc"
]
},
{
"login": "gusgard",
"name": "Gustavo Gard",
"avatar_url": "https://avatars.githubusercontent.com/u/2577356?v=4",
"profile": "https://github.com/gusgard",
"contributions": [
"doc"
]
},
{
"login": "Immortalin",
"name": "Immortalin",
"avatar_url": "https://avatars.githubusercontent.com/u/7126128?v=4",
"profile": "https://narrationbox.com",
"contributions": [
"code"
]
},
{
"login": "cristianbgp",
"name": "Cristian Granda",
"avatar_url": "https://avatars.githubusercontent.com/u/8507974?v=4",
"profile": "https://cristianbgp.com",
"contributions": [
"code"
]
},
{
"login": "deniseyu",
"name": "Denise Yu",
"avatar_url": "https://avatars.githubusercontent.com/u/8420094?v=4",
"profile": "https://deniseyu.io",
"contributions": [
"code"
]
},
{
"login": "andreadellacorte",
"name": "Andrea Della Corte",
"avatar_url": "https://avatars.githubusercontent.com/u/295683?v=4",
"profile": "http://dellacorte.me",
"contributions": [
"doc"
]
},
{
"login": "aditsachde",
"name": "Adit Sachde",
"avatar_url": "https://avatars.githubusercontent.com/u/23707194?v=4",
"profile": "http://aditsachde.com",
"contributions": [
"doc"
]
},
{
"login": "hirenchauhan2",
"name": "Hiren Chauhan",
"avatar_url": "https://avatars.githubusercontent.com/u/8999668?v=4",
"profile": "https://github.com/hirenchauhan2",
"contributions": [
"code"
]
}
],
"contributorsPerLine": 7,

View File

@@ -1,23 +0,0 @@
---
name: Bug report
about: Something is not working right. Or error messages are unclear.
title: ''
labels: ''
assignees: ''
---
### What is the problem?
### Steps to Reproduce
1.
### Versions
```
output of `blitz --version --verbose`
```
### Other
Please include applicable logs and screenshots that show your problem.

31
.github/ISSUE_TEMPLATE/bug_report.yml vendored Normal file
View File

@@ -0,0 +1,31 @@
name: Bug Report
about: Something is not working right. Or error messages are unclear.
labels: "kind/bug, status/triage"
issue_body: true
body:
- type: markdown
attributes:
value: Thanks for taking the time to file a bug report! Please fill out this form as completely as possible.
- type: textarea
attributes:
label: What is the problem?
validations:
required: true
- type: textarea
attributes:
label: What are detailed steps to reproduce this?
value: "1."
validations:
required: true
- type: textarea
attributes:
label: "Run `blitz -v` and paste the output here:"
value: |
```
PASTE_HERE
```
validations:
required: true
- type: markdown
attributes:
value: "Please include below any other applicable logs and screenshots that show your problem:"

View File

@@ -2,7 +2,7 @@
name: Feature/change request
about: Something new or better!
title: ""
labels: ""
labels: "status/triage"
assignees: ""
---

67
.github/workflows/codeql-analysis.yml vendored Normal file
View File

@@ -0,0 +1,67 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ canary, master ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ canary ]
schedule:
- cron: '31 19 * * 5'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
language: [ 'javascript' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
# Learn more:
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
steps:
- name: Checkout repository
uses: actions/checkout@v2
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

View File

@@ -48,7 +48,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
node_version: [12, 14]
node_version: [12, 15]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
@@ -91,7 +91,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
node_version: [12, 14]
node_version: [12, 15]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2

2
.gitignore vendored
View File

@@ -25,3 +25,5 @@ dist
.tsbuildinfo
.nvmrc
**/.test*
examples/auth2
.idea

View File

@@ -1,11 +0,0 @@
# Blitz.js Governance
_From Brandon Bayer (@flybayer), the creator:_
Currently at this very early stage it's basically a [BDFL situation](https://opensource.guide/leadership-and-governance/#what-are-some-of-the-common-governance-structures-for-open-source-projects), with me having the final say in decisions.
However we will move away from BDFL to something that looks more like Ember.js. It's extremely important to me (Brandon) that Blitz.js is a long-term, sustainable, and community-run project.
I would love some mentorship from people with experience in large open-source projects on making this transition.
Also, it's possible I will create one or more business around Blitz, perhaps similar to how Taylor Otwell has around Laravel, but Blitz itself will always remain a separate community-run project.

View File

@@ -1 +0,0 @@
This document has moved here: https://blitzjs.com/docs/maintainers

View File

@@ -1,3 +0,0 @@
# Manifesto
[The Manifesto has been moved to Blitzjs.com](https://blitzjs.com/docs/manifesto)

View File

@@ -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-209-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-226-17BB8A.svg?style=for-the-badge&labelColor=000000"></a>
<!-- ALL-CONTRIBUTORS-BADGE:END -->
<a aria-label="License" href="https://github.com/blitz-js/blitz/blob/canary/LICENSE">
<img alt="" src="https://img.shields.io/npm/l/blitz.svg?style=for-the-badge&labelColor=000000&color=blue">
@@ -22,15 +22,15 @@
<h1 align="center">The Fullstack React Framework</h1>
<h5 align="center">"Zero-API" Data Layer — Built on Next.js — Inspired by Ruby on Rails</h3>
<h3 align="center">Makes you far more productive than you ever dreamed was possible 😉</h3>
<h3 align="center"><a href="https://blitzjs.com" target="_blank">Read the Documentation</a></h3>
<br>
“Zero-API” data layer **lets you import server code directly into your React components** instead of having to manually add API endpoints and do client-side fetching and caching.
Includes everything you need for production apps. **Everything end-to-end from the database to the frontend.**
New Blitz apps come with **all the boring stuff already set up for you!** Like ESLint, Prettier, Jest, user sign up, log in, and password reset.
Provides **helpful defaults and conventions** for things like routing, file structure, and authentication while also being extremely flexible.
Blitz brings back the **simplicity and conventions** of server-rendered frameworks like Ruby on Rails while preserving everything we love about React and client-side rendering!
<br>
@@ -49,7 +49,7 @@ _You can alternatively use [`npx`](https://www.npmjs.com/package/npx)_
1. `blitz new myAppName`
2. `cd myAppName`
3. `blitz dev`
4. View your baby app at http://localhost:3000
4. View your brand new app at http://localhost:3000
<br><br>
@@ -57,32 +57,10 @@ _You can alternatively use [`npx`](https://www.npmjs.com/package/npx)_
<img alt="Bytes Newsletter" src="https://files-8wtskjofb.vercel.app/smarter-16x1.jpg">
</a>
<br><br>
![Architecture diagram](https://blitzjs.now.sh/img/architecture-diagram.png)
<br><br>
**Features:**<br>
⚡️ Built on Next.js<br>
⚡️ Don't have to build an API for client-side rendering<br>
⚡️ Client-side rendering, Server-side rendering, and fully static pages all in the same app<br>
⚡️ Full TypeScript support with static, end-to-end typing (no code generation step needed like with GraphQL)<br>
⚡️ React Concurrent Mode enabled<br>
⚡️ Database/ORM agnostic, but Prisma 2 is default<br>
⚡️ CLI with code scaffolding, Rails-style console REPL, etc<br>
⚡️ GraphQL Ready<br>
⚡️ Deploy serverless or serverful<br>
⚡️ Highly secure authentication <br>
⚡️ Authorization you can use on both server and client<br>
⚡️ Recipes for easily adding libraries like Tailwind, CSS-in-JS, etc.<br>
**Other key features coming:**<br>
⚡️ Model validation you can use on both server and client<br>
⚡️ React native support<br>
⚡️ GUI so you don't have to use the CLI<br>
<br>
### The Foundational Principles
@@ -128,35 +106,38 @@ Your financial contributions help ensure Blitz continues to be developed and mai
### 🌱 Seedling Sponsors
<a aria-label="React Bricks" href="https://reactbricks.com/?utm_source=blitzjs&utm_medium=sponsorship&utm_campaign=blitzjs_sponsorship">
<img alt="" src="https://reactbricks.com/reactbricks_icon.svg" width="30px"/>
<img alt="" src="https://raw.githubusercontent.com/blitz-js/blitz/canary/assets/reactbricks_icon.svg" width="40px"/>
</a>
<a aria-label="Andreas Asprou" href="https://andreas.fyi">
<img alt="" src="https://raw.githubusercontent.com/blitz-js/blitz/canary/assets/andreas.jpg" width="30px"/>
<img alt="" src="https://raw.githubusercontent.com/blitz-js/blitz/canary/assets/andreas.jpg" width="40px"/>
</a>
<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>
### 🥉 Bronze Sponsors
<a aria-label="Render.com" href="https://render.com?utm_source=BlitzJS&utm_medium=sponsorship&utm_campaign=BlitzJS_Sponsorship_2020">
<img alt="" src="https://raw.githubusercontent.com/blitz-js/blitz/canary/assets/render-logo-color2.png" width="110px">
<img alt="" src="https://raw.githubusercontent.com/blitz-js/blitz/canary/assets/render-logo-color2.png" width="200px">
</a>
### 🥈 Silver Sponsors
<a aria-label="Fauna" href="https://dashboard.fauna.com/accounts/register?utm_source=BlitzJS&utm_medium=sponsorship&utm_campaign=BlitzJS_Sponsorship_2020">
<img alt="" src="https://raw.githubusercontent.com/blitz-js/blitz/canary/assets/Fauna_Logo_Blue.png" width="200px">
<img alt="" src="https://raw.githubusercontent.com/blitz-js/blitz/canary/assets/Fauna_Logo_Blue.png" width="300px">
</a>
### 🏆 Gold Sponsors
<a aria-label="Your Company" href="#">
<img alt="" src="https://dummyimage.com/1000x330/efe8ff/000000.png&text=Your+Logo+Here" width="300px">
<img alt="" src="https://dummyimage.com/1000x330/efe8ff/000000.png&text=Your+Logo+Here" width="400px">
</a>
### 💎 Diamond Sponsors
<a aria-label="Your Company" href="#">
<img alt="" src="https://dummyimage.com/1000x330/efe8ff/000000.png&text=Your+Logo+Here" width="400px">
<img alt="" src="https://dummyimage.com/1000x330/efe8ff/000000.png&text=Your+Logo+Here" width="500px">
</a>
<br>
@@ -177,7 +158,7 @@ Your financial contributions help ensure Blitz continues to be developed and mai
## Maintainers (Level 2) ✨
_Code ownership, pull request approvals and merging, etc_ (see [MAINTAINERS.md](./MAINTAINERS.md))
_Code ownership, pull request approvals and merging, etc_ (see [Maintainers L2](https://blitzjs.com/docs/maintainers#level-2-maintainers))
<!-- prettier-ignore-start -->
<!-- markdownlint-disable -->
@@ -186,6 +167,7 @@ _Code ownership, pull request approvals and merging, etc_ (see [MAINTAINERS.md](
<td align="center"><a href="https://github.com/aem"><img src="https://avatars0.githubusercontent.com/u/1909883?v=4" width="100px;" alt=""/><br /><sub><b>Adam Markon</b></sub></a><br />CLI</td>
<td align="center"><a href="http://robdrosenberg.com"><img src="https://avatars0.githubusercontent.com/u/20813991?v=4" width="100px;" alt=""/><br /><sub><b>Robert Rosenberg</b></sub></a><br />Website/Docs</td>
<td align="center"><a href="http://simonknott.de"><img src="https://avatars1.githubusercontent.com/u/14912729?v=4" width="100px;" alt=""/><br /><sub><b>Simon Knott</b></sub></a><br />SuperJSON</td>
<td align="center"><a href="https://juanm04.com"><img src="https://avatars0.githubusercontent.com/u/16712703?v=4" width="100px;" alt=""/><br /><sub><b>Juan Martín Seery</b></sub></a><br />Website/Docs</td>
</tr>
</table>
<!-- markdownlint-enable -->
@@ -195,7 +177,7 @@ _Code ownership, pull request approvals and merging, etc_ (see [MAINTAINERS.md](
## Maintainers (Level 1) ✨
_Issue triage, pull request triage, community encouragement and moderation, etc_ (see [MAINTAINERS.md](./MAINTAINERS.md))
_Issue triage, pull request triage, community encouragement and moderation, etc_ (see [Maintainers L1](https://blitzjs.com/docs/maintainers#level-1-maintainers))
<!-- prettier-ignore-start -->
@@ -212,7 +194,6 @@ _Issue triage, pull request triage, community encouragement and moderation, etc_
<tr>
<td align="center"><a href="https://twitter.com/jdavenport97"><img src="https://avatars2.githubusercontent.com/u/1329874?v=4" width="100px;" alt=""/><br /><sub><b>Jamie Davenport</b></sub></a></td>
<td align="center"><a href="https://twitter.com/myrondavis"><img src="https://avatars2.githubusercontent.com/u/1430136?v=4" width="100px;" alt=""/><br /><sub><b>Myron Davis</b></sub></a></td>
<td align="center"><a href="https://flavioander.com/"><img src="https://avatars2.githubusercontent.com/u/14948074?s=460&u=31d7ea58b5c5cd9f724d684ed578f68896c4af71&v=4" width="100px;" alt=""/><br /><sub><b>Flavio Andrade</b></sub></a></td>
<td align="center"><a href="https://twitter.com/NaReto1125_"><img src="https://avatars.githubusercontent.com/reo777" width="100px;" alt=""/><br /><sub><b>Reo Ishiyama</b></sub></a></td>
<td align="center"><a href="https://github.com/malkomalko"><img src="https://avatars.githubusercontent.com/malkomalko" width="100px;" alt=""/><br /><sub><b>Robert Malko</b></sub></a></td>
</tr>
@@ -444,7 +425,7 @@ Thanks to these wonderful people ([emoji key](https://allcontributors.org/docs/e
<td align="center"><a href="http://kattcorp.com"><img src="https://avatars1.githubusercontent.com/u/459267?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Alex Johansson</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=KATT" title="Code">💻</a></td>
<td align="center"><a href="http://davidmazza.com"><img src="https://avatars0.githubusercontent.com/u/120893?v=4?s=100" width="100px;" alt=""/><br /><sub><b>David Mazza</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=dmzza" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/rayandrews"><img src="https://avatars1.githubusercontent.com/u/4437323?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ray Andrew</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=rayandrews" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=rayandrews" title="Documentation">📖</a></td>
<td align="center"><a href="http://Dal.Design"><img src="https://avatars3.githubusercontent.com/u/43112535?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Abdullah Mzaien</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=Mzaien" title="Code">💻</a></td>
<td align="center"><a href="http://Dal.Design"><img src="https://avatars3.githubusercontent.com/u/43112535?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Abdullah Mzaien</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=Mzaien" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=Mzaien" title="Documentation">📖</a></td>
<td align="center"><a href="http://kwao.io"><img src="https://avatars2.githubusercontent.com/u/8839514?v=4?s=100" width="100px;" alt=""/><br /><sub><b>William Kwao</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=williamkwao" title="Documentation">📖</a></td>
</tr>
<tr>
@@ -499,6 +480,29 @@ Thanks to these wonderful people ([emoji key](https://allcontributors.org/docs/e
<td align="center"><a href="https://alistair.cloud"><img src="https://avatars.githubusercontent.com/u/25351731?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Alistair Smith</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=alii" title="Code">💻</a></td>
<td align="center"><a href="https://rodrigoehlers.com"><img src="https://avatars.githubusercontent.com/u/19683042?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Rodrigo Ehlers</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=rodrigoehlers" title="Code">💻</a></td>
<td align="center"><a href="https://www.builtopen.com/"><img src="https://avatars.githubusercontent.com/u/1734057?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Michael Ford</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=mtford90" title="Code">💻</a></td>
<td align="center"><a href="https://brianypliu.com"><img src="https://avatars.githubusercontent.com/u/3888780?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Brian Liu</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=LBrian" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="http://aleksandra.codes"><img src="https://avatars.githubusercontent.com/u/9019397?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Aleksandra Sikora</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=beerose" title="Code">💻</a></td>
<td align="center"><a href="https://juanm04.com"><img src="https://avatars.githubusercontent.com/u/16712703?v=4?s=100" width="100px;" alt=""/><br /><sub><b>JuanM04</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=JuanM04" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=JuanM04" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/arenddeboer"><img src="https://avatars.githubusercontent.com/u/7022204?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Arend de Boer</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=arenddeboer" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/fmilani"><img src="https://avatars.githubusercontent.com/u/1580375?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Felipe Milani</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=fmilani" title="Documentation">📖</a></td>
<td align="center"><a href="http://nxhx.org"><img src="https://avatars.githubusercontent.com/u/13018?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Joe Edelman</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=jxe" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/garytube"><img src="https://avatars.githubusercontent.com/u/3823504?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Gary</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=garytube" title="Documentation">📖</a></td>
<td align="center"><a href="http://oliverloops.com"><img src="https://avatars.githubusercontent.com/u/33361399?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Oliver Lopez </b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=oliverloops" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="https://decadentIpsum.me"><img src="https://avatars.githubusercontent.com/u/32861532?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Andreas Zaralis</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=DecadentIpsum" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/davetorbeck"><img src="https://avatars.githubusercontent.com/u/5829885?v=4?s=100" width="100px;" alt=""/><br /><sub><b>David Torbeck</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=davetorbeck" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/gusgard"><img src="https://avatars.githubusercontent.com/u/2577356?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Gustavo Gard</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=gusgard" title="Documentation">📖</a></td>
<td align="center"><a href="https://narrationbox.com"><img src="https://avatars.githubusercontent.com/u/7126128?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Immortalin</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=Immortalin" title="Code">💻</a></td>
<td align="center"><a href="https://cristianbgp.com"><img src="https://avatars.githubusercontent.com/u/8507974?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Cristian Granda</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=cristianbgp" title="Code">💻</a></td>
<td align="center"><a href="https://deniseyu.io"><img src="https://avatars.githubusercontent.com/u/8420094?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Denise Yu</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=deniseyu" title="Code">💻</a></td>
<td align="center"><a href="http://dellacorte.me"><img src="https://avatars.githubusercontent.com/u/295683?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Andrea Della Corte</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=andreadellacorte" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="http://aditsachde.com"><img src="https://avatars.githubusercontent.com/u/23707194?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Adit Sachde</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=aditsachde" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/hirenchauhan2"><img src="https://avatars.githubusercontent.com/u/8999668?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Hiren Chauhan</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=hirenchauhan2" title="Code">💻</a></td>
</tr>
</table>

10
SECURITY.md Normal file
View File

@@ -0,0 +1,10 @@
# Security Policy
## Supported Versions
TODO
## Reporting a Vulnerability
Email Brandon Bayer at b@bayer.ws to report a vulnerablity, and he will follow up with you asap.

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 980 979.97"><path d="M139.38,0c-185.82-3.54-185.82,277,0,273.46H251.44C437.25,277,437.25-3.5,251.44,0ZM729.25,353.24c-184.92-2.62-184.92,276.1,0,273.48H841.31c184.92,2.62,184.92-276.1,0-273.48ZM138.69,706.46c-184.92-2.61-184.92,276.1,0,273.49H250.75c184.91,2.61,184.91-276.1,0-273.49Z" fill="#b43278"/><path d="M583.47,0c-185.82-3.54-185.82,277,0,273.46H840.61c185.81,3.54,185.81-277,0-273.46ZM138.7,353.24c-184.92-2.62-184.92,276.1,0,273.48H395.85c184.92,2.62,184.92-276.1,0-273.48ZM584.13,706.46c-184.91-2.61-184.91,276.1,0,273.49H841.3c184.92,2.61,184.92-276.1,0-273.49Z" fill="#f65a8e"/></svg>

After

Width:  |  Height:  |  Size: 650 B

BIN
assets/rob_blitz.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

View File

@@ -53,7 +53,7 @@ export default passportAuth({
update: {email},
})
const publicData = {userId: user.id, roles: [user.role], source: "twitter"}
const publicData = {userId: user.id, role: user.role, source: "twitter"}
done(null, {publicData})
},
),
@@ -92,7 +92,7 @@ export default passportAuth({
const publicData = {
userId: user.id,
roles: [user.role as Role],
role: user.role as Role,
source: "github",
githubUsername: profile.username,
}
@@ -131,7 +131,7 @@ export default passportAuth({
const publicData = {
userId: user.id,
roles: [user.role],
role: user.role,
source: "auth0",
githubUsername: profile.username,
}

View File

@@ -23,7 +23,7 @@ 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, roles: [user.role as Role]})
await ctx.session.$create({userId: user.id, role: user.role as Role})
return user
})

View File

@@ -10,6 +10,6 @@ export default resolver.pipe(resolver.zod(Signup), async ({email, password}, ctx
select: {id: true, name: true, email: true, role: true},
})
await ctx.session.$create({userId: user.id, roles: [user.role as Role]})
await ctx.session.$create({userId: user.id, role: user.role as Role})
return user
})

View File

@@ -8,11 +8,17 @@ const LoginPage: BlitzPage = () => {
return (
<div>
<LoginForm onSuccess={() => router.push("/")} />
<LoginForm
onSuccess={() => {
const next = (router.query.next as string) ?? "/"
router.push(next)
}}
/>
</div>
)
}
LoginPage.redirectAuthenticatedTo = "/"
LoginPage.getLayout = (page) => <Layout title="Log In">{page}</Layout>
export default LoginPage

View File

@@ -1,7 +1,5 @@
import {Head, useRouter, BlitzPage, useMutation} from "blitz"
import {useRouter, BlitzPage} from "blitz"
import Layout from "app/core/layouts/Layout"
import {Form, FORM_ERROR} from "app/core/components/Form"
import {LabeledTextField} from "app/core/components/LabeledTextField"
import {SignupForm} from "app/auth/components/SignupForm"
const SignupPage: BlitzPage = () => {

View File

@@ -2,7 +2,7 @@ import {useSession, useRouter, useMutation, Head} from "blitz"
import logout from "app/auth/mutations/logout"
export default function Layout({title, children}: {title?: string; children: React.ReactNode}) {
const session = useSession()
const session = useSession({suspense: false})
const router = useRouter()
const [logoutMutation] = useMutation(logout)
return (

View File

@@ -1,5 +1,4 @@
import {
withBlitzAppRoot,
AppProps,
ErrorComponent,
useRouter,

View File

@@ -1,10 +1,17 @@
import {render} from "test/utils"
import Home from "./index"
import {useCurrentUser} from "app/core/hooks/useCurrentUser"
jest.mock("app/core/hooks/useCurrentUser")
const mockUseCurrentUser = useCurrentUser as jest.MockedFunction<typeof useCurrentUser>
jest.mock("blitz", () => ({
...jest.requireActual("blitz")!,
useQuery: () => [
{
id: 1,
name: "User",
email: "user@email.com",
role: "user",
},
],
}))
test("renders blitz documentation link", () => {
// This is an example of how to ensure a specific item is in the document
@@ -12,12 +19,6 @@ test("renders blitz documentation link", () => {
// when you remove the the default content from the page
// This is an example on how to mock api hooks when testing
mockUseCurrentUser.mockReturnValue({
id: 1,
name: "User",
email: "user@email.com",
role: "user",
})
const {getByText} = render(<Home />)
const element = getByText(/powered by blitz/i)

View File

@@ -1,30 +1,30 @@
import {Suspense} from "react"
import {Head, Link, useSession, useRouterQuery, useMutation, invoke} from "blitz"
import {
Head,
Link,
useSession,
useRouterQuery,
useMutation,
invoke,
useQuery,
BlitzPage,
} from "blitz"
import getUser from "app/users/queries/getUser"
import trackView from "app/users/mutations/trackView"
import Layout from "app/core/layouts/Layout"
import {useCurrentUser} from "app/core/hooks/useCurrentUser"
// import getUsers from "app/users/queries/getUsers"
const CurrentUserInfo = () => {
const currentUser = useCurrentUser()
const session = useSession()
const [currentUser] = useQuery(getUser, {where: {id: session.userId!}})
return <pre>{JSON.stringify(currentUser, null, 2)}</pre>
}
// const Users = () => {
// const [users] = useQuery(getUsers, {})
//
// return <pre style={{maxWidth: "30rem"}}>{JSON.stringify(users, null, 2)}</pre>
// }
const UserStuff = () => {
const session = useSession()
const query = useRouterQuery()
const [trackViewMutation] = useMutation(trackView)
if (session.isLoading) return <div>Loading...</div>
return (
<div>
{!session.userId && (
@@ -48,11 +48,6 @@ const UserStuff = () => {
<Suspense fallback="Loading...">
<CurrentUserInfo />
</Suspense>
{/*
<Suspense fallback="Loading...">
<Users />
</Suspense>
*/}
<button
onClick={async () => {
try {
@@ -81,7 +76,7 @@ const UserStuff = () => {
)
}
const Home = () => (
const Home: BlitzPage = () => (
<Layout>
<div className="container">
<Head>
@@ -94,7 +89,9 @@ const Home = () => (
<img src="/logo.png" alt="blitz.js" />
</div>
<UserStuff />
<Suspense fallback={"Loading..."}>
<UserStuff />
</Suspense>
</main>
<footer>
@@ -238,4 +235,6 @@ const Home = () => (
</Layout>
)
Home.suppressFirstRenderFlicker = true
export default Home

View File

@@ -10,6 +10,8 @@ export const Project = () => {
const [deleteProjectMutation, {isSuccess}] = useMutation(deleteProject)
const [project] = useQuery(getProject, {id: projectId}, {enabled: !isSuccess})
if (!project) return null
return (
<>
<Head>
@@ -57,6 +59,7 @@ const ShowProjectPage: BlitzPage = () => {
)
}
ShowProjectPage.authenticate = true
ShowProjectPage.getLayout = (page) => <Layout>{page}</Layout>
export default ShowProjectPage

View File

@@ -65,6 +65,7 @@ const EditProjectPage: BlitzPage = () => {
)
}
EditProjectPage.authenticate = true
EditProjectPage.getLayout = (page) => <Layout>{page}</Layout>
export default EditProjectPage

View File

@@ -61,6 +61,7 @@ const ProjectsPage: BlitzPage = () => {
)
}
ProjectsPage.authenticate = {redirectTo: "/login"}
ProjectsPage.getLayout = (page) => <Layout>{page}</Layout>
export default ProjectsPage

View File

@@ -40,6 +40,7 @@ const NewProjectPage: BlitzPage = () => {
)
}
NewProjectPage.authenticate = true
NewProjectPage.getLayout = (page) => <Layout title={"Create New Project"}>{page}</Layout>
export default NewProjectPage

View File

@@ -6,7 +6,7 @@ type GetUserInput = {
}
export default async function getUser({where}: GetUserInput, ctx: Ctx) {
ctx.session.$authorize()
if (!ctx.session.userId) return null
const user = await db.user.findFirst({where})

View File

@@ -19,6 +19,7 @@ describe("index page", () => {
const user = createRandomUser()
cy.signup(user)
cy.wait(1000)
cy.location("pathname").should("equal", "/")
cy.contains("button", "Logout")
@@ -28,9 +29,10 @@ describe("index page", () => {
const user = createRandomUser()
cy.signup(user)
cy.wait(1000)
cy.wait(500)
cy.contains("button", "Logout").click()
cy.wait(1000)
cy.contains("a", /login/i).click()
cy.contains("Email").find("input").type(user.email)
@@ -38,7 +40,7 @@ describe("index page", () => {
cy.contains("button", /login/i).click()
cy.location("pathname").should("equal", "/")
cy.wait(500)
cy.wait(1000)
cy.contains("button", "Logout")
})
@@ -46,6 +48,7 @@ describe("index page", () => {
const user = createRandomUser()
cy.signup(user)
cy.wait(1000)
cy.contains("button", "Logout").click()
@@ -60,9 +63,11 @@ describe("index page", () => {
cy.contains("button", "Track view").click()
cy.contains("button", "Track view").click()
cy.wait(1000)
cy.contains('"views": 2')
cy.signup(user)
cy.wait(1000)
cy.contains('"views": 2')
})

View File

@@ -1,6 +1,6 @@
{
"name": "@examples/auth",
"version": "0.30.0-canary.6",
"version": "0.30.3",
"scripts": {
"dev": "blitz dev",
"build": "blitz build",
@@ -12,7 +12,7 @@
"cy:run": "cypress run || cypress run",
"test": "prisma generate && yarn test:jest && yarn test:e2e",
"test:jest": "jest",
"test:server": "blitz prisma migrate deploy --preview-feature && blitz build && blitz start -p 3099",
"test:server": "cross-env NODE_ENV=test blitz prisma migrate deploy --preview-feature && blitz build && cross-env NODE_ENV=test blitz start -p 3099",
"test:e2e": "cross-env NODE_ENV=test start-server-and-test test:server http://localhost:3099 cy:run"
},
"browserslist": [
@@ -39,13 +39,13 @@
]
},
"dependencies": {
"@prisma/client": "2.16.0",
"blitz": "0.30.0-canary.6",
"@prisma/client": "2.17.0",
"blitz": "0.30.3",
"final-form": "4.20.1",
"passport-auth0": "1.4.0",
"passport-github2": "0.1.12",
"passport-twitter": "1.0.4",
"prisma": "2.16.0",
"prisma": "2.17.0",
"react": "0.0.0-experimental-3310209d0",
"react-dom": "0.0.0-experimental-3310209d0",
"react-error-boundary": "3.1.0",

View File

@@ -1,4 +1,3 @@
import React from "react"
import {RouterContext, BlitzRouter} from "blitz"
import {render as defaultRender} from "@testing-library/react"
import {renderHook as defaultRenderHook} from "@testing-library/react-hooks"
@@ -67,6 +66,7 @@ export const mockRouter: BlitzRouter = {
params: {},
query: {},
isReady: true,
isLocaleDomain: false,
push: jest.fn(),
replace: jest.fn(),
reload: jest.fn(),

View File

@@ -12,7 +12,7 @@ declare module "blitz" {
isAuthorized: SimpleRolesIsAuthorized<Role>
PublicData: {
userId: User["id"]
roles: Role[]
role: Role
views?: number
}
}

View File

@@ -9,7 +9,7 @@ export default async function login(input: LoginInputType, {session}: Ctx) {
// This throws an error if credentials are invalid
const user = await authenticateUser(email, password)
await session.$create({userId: user.id, roles: [user.role]})
await session.$create({userId: user.id})
return user
}

View File

@@ -13,7 +13,7 @@ export default async function signup(input: SignupInputType, {session}: Ctx) {
select: {id: true, name: true, email: true, role: true},
})
await session.$create({userId: user.id, roles: [user.role]})
await session.$create({userId: user.id})
return user
}

View File

@@ -1,6 +1,6 @@
{
"name": "@examples/custom-server",
"version": "0.30.0-canary.6",
"version": "0.30.3",
"scripts": {
"dev": "nodemon --watch server.js --exec 'blitz dev'",
"build": "blitz build",
@@ -40,10 +40,10 @@
]
},
"dependencies": {
"@prisma/client": "2.16.0",
"blitz": "0.30.0-canary.6",
"@prisma/client": "2.17.0",
"blitz": "0.30.3",
"final-form": "4.20.1",
"prisma": "2.16.0",
"prisma": "2.17.0",
"react": "0.0.0-experimental-3310209d0",
"react-dom": "0.0.0-experimental-3310209d0",
"react-error-boundary": "2.3.2",

View File

@@ -67,6 +67,7 @@ export const mockRouter: BlitzRouter = {
params: {},
query: {},
isReady: true,
isLocaleDomain: false,
push: jest.fn(),
replace: jest.fn(),
reload: jest.fn(),

View File

@@ -10,7 +10,6 @@ declare module "blitz" {
isAuthorized: typeof simpleRolesIsAuthorized
PublicData: {
userId: number
roles: string[]
}
}
}

View File

@@ -9,7 +9,7 @@ export default async function login(input: LoginInputType, { session }: Ctx) {
// This throws an error if credentials are invalid
const user = await authenticateUser(email, password)
await session.$create({ userId: user.id, roles: [user.role] })
await session.$create({ userId: user.id })
return user
}

View File

@@ -23,7 +23,7 @@ export default async function signup(input: SignupInputType, { session }: Ctx) {
)
console.log("Create user result:", user)
await session.$create({ userId: user.id, roles: [user.role] })
await session.$create({ userId: user.id })
return user
}

View File

@@ -1,6 +1,6 @@
{
"name": "@examples/fauna",
"version": "0.30.0-canary.6",
"version": "0.30.3",
"scripts": {
"dev": "blitz dev",
"build": "blitz build",
@@ -28,7 +28,7 @@
]
},
"dependencies": {
"blitz": "0.30.0-canary.6",
"blitz": "0.30.3",
"final-form": "4.20.1",
"graphql": "15.5.0",
"graphql-request": "3.4.0",

View File

@@ -78,6 +78,7 @@ export const mockRouter: BlitzRouter = {
params: {},
query: {},
isReady: true,
isLocaleDomain: false,
push: jest.fn(),
replace: jest.fn(),
reload: jest.fn(),

View File

@@ -9,7 +9,6 @@ declare module "blitz" {
isAuthorized: typeof simpleRolesIsAuthorized
PublicData: {
userId: string
roles: string[]
}
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "no-prisma",
"version": "0.30.0-canary.6",
"version": "0.30.3",
"scripts": {
"dev": "blitz dev",
"build": "blitz build",
@@ -27,7 +27,7 @@
]
},
"dependencies": {
"blitz": "0.30.0-canary.6",
"blitz": "0.30.3",
"knex": "0.21.16",
"react": "0.0.0-experimental-3310209d0",
"react-dom": "0.0.0-experimental-3310209d0",

View File

@@ -1,6 +1,6 @@
{
"name": "@examples/plain-js",
"version": "0.30.0-canary.6",
"version": "0.30.3",
"scripts": {
"dev": "blitz dev",
"build": "blitz prisma migrate deploy --preview-feature && blitz build",
@@ -30,9 +30,9 @@
]
},
"dependencies": {
"@prisma/client": "2.16.0",
"blitz": "0.30.0-canary.6",
"prisma": "2.16.0",
"@prisma/client": "2.17.0",
"blitz": "0.30.3",
"prisma": "2.17.0",
"react": "0.0.0-experimental-3310209d0",
"react-dom": "0.0.0-experimental-3310209d0"
},

View File

@@ -17,33 +17,32 @@ generator client {
provider = "prisma-client-js"
}
// --------------------------------------
model User {
id Int @default(autoincrement()) @id
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
email String @unique
name String?
role String?
storeId Int?
email String @unique
name String?
role String?
storeId Int?
}
model Product {
id Int @default(autoincrement()) @id
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
handle String @unique
name String?
description String?
price Int?
variants Variant[]
variants Variant[]
}
model Variant {
id Int @default(autoincrement()) @id
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
name String

View File

@@ -1,6 +1,6 @@
{
"name": "@examples/store",
"version": "0.30.0-canary.6",
"version": "0.30.3",
"private": true,
"scripts": {
"build": "blitz prisma migrate deploy --preview-feature && blitz build",
@@ -20,10 +20,10 @@
"trailingComma": "all"
},
"dependencies": {
"@prisma/client": "2.16.0",
"blitz": "0.30.0-canary.6",
"@prisma/client": "2.17.0",
"blitz": "0.30.3",
"final-form": "4.20.1",
"prisma": "2.16.0",
"prisma": "2.17.0",
"react": "0.0.0-experimental-3310209d0",
"react-dom": "0.0.0-experimental-3310209d0",
"react-error-boundary": "3.1.0",

View File

@@ -1,5 +1,5 @@
{
"version": "0.30.0-canary.6",
"version": "0.30.3",
"packages": ["packages/*"],
"npmClient": "yarn",
"useWorkspaces": true,

View File

@@ -77,7 +77,6 @@
"@types/passport": "1.0.5",
"@types/pluralize": "0.0.29",
"@types/prettier": "2.1.6",
"@types/npm-run": "5.0.0",
"@types/pump": "1.1.0",
"@types/pumpify": "1.4.1",
"@types/react": "17.0.0",

View File

@@ -1,6 +1,6 @@
{
"name": "@blitzjs/babel-preset",
"version": "0.30.0-canary.6",
"version": "0.30.3",
"license": "MIT",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
@@ -36,6 +36,6 @@
}
],
"dependencies": {
"babel-plugin-superjson-next": "0.2.1"
"babel-plugin-superjson-next": "0.2.2"
}
}

View File

@@ -1,5 +1,7 @@
require("@testing-library/jest-dom")
jest.setTimeout(10000)
afterAll(async () => {
try {
await global._blitz_prismaClient.$disconnect()

View File

@@ -1,7 +1,7 @@
{
"name": "blitz",
"description": "Blitz is a Rails-like framework for monolithic, full-stack React apps — built on Next.js",
"version": "0.30.0-canary.6",
"version": "0.30.3",
"license": "MIT",
"scripts": {
"clean": "rimraf dist",
@@ -43,20 +43,20 @@
"url": "https://github.com/blitz-js/blitz"
},
"dependencies": {
"@blitzjs/babel-preset": "0.30.0-canary.6",
"@blitzjs/cli": "0.30.0-canary.6",
"@blitzjs/config": "0.30.0-canary.6",
"@blitzjs/core": "0.30.0-canary.6",
"@blitzjs/display": "0.30.0-canary.6",
"@blitzjs/generator": "0.30.0-canary.6",
"@blitzjs/installer": "0.30.0-canary.6",
"@blitzjs/server": "0.30.0-canary.6",
"@blitzjs/babel-preset": "0.30.3",
"@blitzjs/cli": "0.30.3",
"@blitzjs/config": "0.30.3",
"@blitzjs/core": "0.30.3",
"@blitzjs/display": "0.30.3",
"@blitzjs/generator": "0.30.3",
"@blitzjs/installer": "0.30.3",
"@blitzjs/server": "0.30.3",
"@testing-library/jest-dom": "5.11.9",
"@testing-library/react": "^11.2.3",
"@testing-library/react-hooks": "^4.0.1",
"@types/jest": "^26.0.20",
"envinfo": "^7.7.3",
"eslint-config-blitz": "0.30.0-canary.6",
"eslint-config-blitz": "0.30.3",
"jest": "^26.6.3",
"jest-environment-jsdom-fourteen": "^1.0.1",
"jest-watch-typeahead": "^0.6.1",

View File

@@ -1,7 +1,7 @@
{
"name": "@blitzjs/cli",
"description": "Blitz.js CLI",
"version": "0.30.0-canary.6",
"version": "0.30.3",
"license": "MIT",
"scripts": {
"b": "./bin/run",
@@ -32,14 +32,14 @@
"/lib"
],
"dependencies": {
"@blitzjs/display": "0.30.0-canary.6",
"@blitzjs/repl": "0.30.0-canary.6",
"@blitzjs/display": "0.30.3",
"@blitzjs/repl": "0.30.3",
"@oclif/command": "1.8.0",
"@oclif/config": "1.17.0",
"@oclif/plugin-autocomplete": "0.3.0",
"@oclif/plugin-help": "3.2.1",
"@oclif/plugin-not-found": "1.2.4",
"@prisma/sdk": "2.16.0",
"@prisma/sdk": "2.17.0",
"@salesforce/lazy-require": "0.4.0",
"camelcase": "^6.2.0",
"chalk": "4.1.0",
@@ -64,13 +64,13 @@
"v8-compile-cache": "2.2.0"
},
"devDependencies": {
"@blitzjs/generator": "0.30.0-canary.6",
"@blitzjs/installer": "0.30.0-canary.6",
"@blitzjs/server": "0.30.0-canary.6",
"@blitzjs/generator": "0.30.3",
"@blitzjs/installer": "0.30.3",
"@blitzjs/server": "0.30.3",
"@oclif/dev-cli": "1.26.0",
"@oclif/test": "1.2.8",
"nock": "13.0.6",
"prisma": "2.16.0",
"prisma": "2.17.0",
"stdout-stderr": "0.1.13"
},
"jest": {

View File

@@ -37,6 +37,14 @@ export class Dev extends Command {
try {
const dev = (await import("@blitzjs/server")).dev
const {getConfig} = await import("@blitzjs/config")
const blitzConfig = getConfig()
if (blitzConfig.cli?.clearConsoleOnBlitzDev !== false) {
const {log} = await import("@blitzjs/display")
log.clearConsole()
}
await dev(config)
} catch (err) {
console.error(err)

View File

@@ -140,11 +140,11 @@ export class Generate extends Command {
# will generate the proper database model for a Task.`)}
> blitz generate model task \\
name:string \\
completed:boolean:default[false] \\
completed:boolean:default=false \\
belongsTo:project?
> blitz generate all tasks \\
name:string \\
completed:boolean:default[false] \\
completed:boolean:default=false \\
belongsTo:project?
`,
`${chalk.dim(`# Sometimes you want just a single query with no generated

View File

@@ -13,6 +13,6 @@
"noEmit": false,
"lib": ["dom", "dom.iterable", "ES2018"]
},
"include": ["src/**/*", "types"],
"include": ["src/**/*", "types", "../../types"],
"exclude": ["node_modules"]
}

View File

@@ -1,6 +1,6 @@
{
"name": "@blitzjs/config",
"version": "0.30.0-canary.6",
"version": "0.30.3",
"description": "Loads the blitz app config",
"license": "MIT",
"scripts": {

View File

@@ -27,6 +27,9 @@ export interface BlitzConfig extends Record<string, unknown> {
isomorphicResolverImports?: boolean
reactMode?: string
}
cli?: {
clearConsoleOnBlitzDev?: boolean
}
_meta: {
packageName: string
}

View File

@@ -1,7 +1,7 @@
{
"name": "@blitzjs/core",
"description": "Blitz.js core functionality",
"version": "0.30.0-canary.6",
"version": "0.30.3",
"license": "MIT",
"scripts": {
"clean": "rimraf dist",
@@ -40,20 +40,21 @@
"url": "https://github.com/blitz-js/blitz"
},
"dependencies": {
"@blitzjs/display": "0.30.0-canary.6",
"@blitzjs/display": "0.30.3",
"@types/htmlescape": "^1.1.1",
"@types/secure-password": "3.1.0",
"b64-lite": "^1.4.0",
"bad-behavior": "^1.0.1",
"cookie-session": "^1.4.0",
"cross-spawn": "7.0.3",
"htmlescape": "^1.1.1",
"lodash": "^4.17.20",
"lodash-es": "^4.17.20",
"npm-run": "^5.0.1",
"npm-which": "^3.0.1",
"passport": "0.4.1",
"react-query": "2.5.12",
"secure-password": "4.0.0",
"superjson": "1.6.0",
"superjson": "1.7.2",
"zod": ">=1.0.0"
},
"gitHead": "d3b9fce0bdd251c2b1890793b0aa1cd77c1c0922"

View File

@@ -1,11 +1,97 @@
import React from "react"
import {AppProps} from "."
import React, {ComponentPropsWithoutRef, useEffect} from "react"
import {Head} from "./nextjs"
import {publicDataStore} from "./public-data-store"
import {useAuthorizeIf} from "./supertokens"
import {AppProps, BlitzPage} from "./types"
export function withBlitzAppRoot(WrappedComponent: React.ComponentType<any>) {
const BlitzAppRoot = (props: AppProps) => {
// props comes afterwards so the can override the default ones.
return <WrappedComponent {...(props as any)} />
const customCSS = `
body::before {
content: "";
display: block;
position: fixed;
width: 100%;
height: 100%;
top: 0;
left: 0;
z-index: 99999;
background-color: white;
}
BlitzAppRoot.displayName = `BlitzAppRoot`
return BlitzAppRoot
.blitz-first-render-complete body::before {
display: none;
}
`
const noscriptCSS = `
body::before {
content: none
}
`
const NoPageFlicker = () => {
return (
<Head>
<style dangerouslySetInnerHTML={{__html: customCSS}} />
<noscript>
<style dangerouslySetInnerHTML={{__html: noscriptCSS}} />
</noscript>
</Head>
)
}
export function withBlitzInnerWrapper(Page: BlitzPage) {
const BlitzInnerRoot = (props: ComponentPropsWithoutRef<BlitzPage>) => {
useAuthorizeIf(Page.authenticate === true)
return <Page {...props} />
}
for (let [key, value] of Object.entries(Page)) {
;(BlitzInnerRoot as any)[key] = value
}
if (process.env.NODE_ENV !== "production") {
BlitzInnerRoot.displayName = `BlitzInnerRoot`
}
return BlitzInnerRoot
}
export function withBlitzAppRoot(UserAppRoot: React.ComponentType<any>) {
const BlitzOuterRoot = (props: AppProps) => {
if (typeof window !== "undefined") {
if (publicDataStore.getData().userId) {
if (props.Component.redirectAuthenticatedTo) {
window.location.replace(props.Component.redirectAuthenticatedTo)
}
} else {
const authenticate = props.Component.authenticate
if (
authenticate &&
typeof authenticate === "object" &&
typeof authenticate.redirectTo === "string"
) {
const url = new URL(authenticate.redirectTo, window.location.href)
url.searchParams.append("next", window.location.pathname)
window.location.replace(url.toString())
}
}
}
const noPageFlicker =
props.Component.suppressFirstRenderFlicker ||
props.Component.authenticate !== undefined ||
props.Component.redirectAuthenticatedTo
useEffect(() => {
document.documentElement.classList.add("blitz-first-render-complete")
}, [])
return (
<>
{noPageFlicker && <NoPageFlicker />}
<UserAppRoot {...props} Component={withBlitzInnerWrapper(props.Component)} />
</>
)
}
if (process.env.NODE_ENV !== "production") {
BlitzOuterRoot.displayName = `BlitzOuterRoot`
}
return BlitzOuterRoot
}

View File

@@ -11,13 +11,13 @@ export type BlitzRuntimeData = {
export function _getBlitzRuntimeData(): BlitzRuntimeData {
const config = getConfig()
return {
sessionCookiePrefix: config._meta.packageName.replace(/[^a-zA-Z0-9-_]/g, "_"),
sessionCookiePrefix: (config._meta.packageName || "blitz").replace(/[^a-zA-Z0-9-_]/g, "_"),
suspenseEnabled: config.experimental?.reactMode !== "legacy",
}
}
export function getBlitzRuntimeData() {
if (isClient && process.env.JEST_WORKER_ID === "undefined") {
if (isClient && !process.env.JEST_WORKER_ID) {
return window.__BLITZ_DATA__
} else {
if (!global.__BLITZ_DATA__) {
@@ -29,9 +29,7 @@ export function getBlitzRuntimeData() {
// Automatically deserialize __BLITZ_DATA__ in a browser environment
if (isClient) {
if (document.getElementById("__BLITZ_DATA__")) {
deserializeAndSetBlitzDataOnWindow()
}
deserializeAndSetBlitzDataOnWindow()
}
export function deserializeAndSetBlitzDataOnWindow() {
@@ -41,7 +39,10 @@ export function deserializeAndSetBlitzDataOnWindow() {
)
window.__BLITZ_DATA__ = data
} catch (e) {
console.error("Error deserializing __BLITZ__DATA__", e)
console.error(
"Error deserializing __BLITZ__DATA__. Make sure you have a custom _document.js/tsx page that uses <BlitzScript/>",
e,
)
}
}

View File

@@ -14,6 +14,10 @@ export const COOKIE_SESSION_TOKEN = () =>
export const COOKIE_REFRESH_TOKEN = () =>
`${getBlitzRuntimeData().sessionCookiePrefix}_sIdRefreshToken`
export const COOKIE_CSRF_TOKEN = () => `${getBlitzRuntimeData().sessionCookiePrefix}_sAntiCsrfToken`
// TODO remove before 1.0 -
// This is here for legacy compatability (misspelling)
export const COOKIE_LEGACY_CSRF_TOKEN = () =>
`${getBlitzRuntimeData().sessionCookiePrefix}_sAntiCrfToken`
export const COOKIE_PUBLIC_DATA_TOKEN = () =>
`${getBlitzRuntimeData().sessionCookiePrefix}_sPublicDataToken`

View File

@@ -1,6 +1,6 @@
import SuperJson from "superjson"
const errorProps = ["message", "code", "meta"]
const errorProps = ["name", "message", "code", "statusCode", "meta"]
if (process.env.JEST_WORKER_ID === undefined) {
SuperJson.allowErrorProps(...errorProps)
}

View File

@@ -1,6 +1,4 @@
import {NextComponentType, NextPage, NextPageContext} from "next"
import {AppProps as NextAppProps} from "next/app"
export * from "./nextjs"
export * from "./types"
export * from "./errors"
export * from "./constants"
@@ -30,49 +28,18 @@ export {
export {
getAntiCSRFToken,
useSession,
SessionConfig, // new
useAuthenticatedSession,
useAuthorize,
useRedirectAuthenticated,
SessionConfig,
SessionContext,
AuthenticatedSessionContext,
ClientSession,
AuthenticatedClientSession,
} from "./supertokens"
export {SecurePassword, hash256, generateToken} from "./auth-utils"
// --------------------
// Exports from Next.js
// --------------------
export {default as Head} from "next/head"
export {default as Link, LinkProps} from "next/link"
export {default as Router} from "next/router"
export {default as Image, ImageProps} from "next/image"
export {
default as Document,
Html,
Head as DocumentHead,
Main,
DocumentContext,
DocumentInitialProps,
} from "next/document"
export {default as dynamic} from "next/dynamic"
export {default as ErrorComponent, ErrorProps} from "next/error"
export {default as getConfig} from "next/config"
export type BlitzComponentType<C = NextPageContext, IP = {}, P = {}> = NextComponentType<C, IP, P>
export interface AppProps<P = {}> extends NextAppProps<P> {
Component: BlitzComponentType<NextPageContext, any, P> & {
getLayout?: (component: JSX.Element) => JSX.Element
}
}
export type BlitzPage<P = {}, IP = P> = NextPage<P, IP> & {
getLayout?: (component: JSX.Element) => JSX.Element
}
export {isLocalhost} from "./utils/index"
export {prettyMs} from "./utils/pretty-ms"

View File

@@ -0,0 +1,25 @@
// --------------------
// Exports from Next.js
// --------------------
export {default as Head} from "next/head"
export {default as Link, LinkProps} from "next/link"
export {default as Router} from "next/router"
export {default as Image, ImageProps} from "next/image"
export {
default as Document,
Html,
Head as DocumentHead,
Main,
DocumentContext,
DocumentInitialProps,
} from "next/document"
export {default as dynamic} from "next/dynamic"
export {default as ErrorComponent, ErrorProps} from "next/error"
export {default as getConfig} from "next/config"

View File

@@ -94,11 +94,11 @@ export function passportAuth(config: BlitzPassportConfig) {
}
assert(
typeof result === "object" && result !== null,
`Your '${strategyName}' passport verify callback returned empty data. Ensure you call 'done(null, {publicData: {userId: 1, roles: ['myRole']}})')`,
`Your '${strategyName}' passport verify callback returned empty data. Ensure you call 'done(null, {publicData: {userId: 1}})' along with any other publicData fields you need)`,
)
assert(
(result as any).publicData,
`'publicData' is missing from your '${strategyName}' passport verify callback. Ensure you call 'done(null, {publicData: {userId: 1, roles: ['myRole']}})')`,
`'publicData' is missing from your '${strategyName}' passport verify callback. Ensure you call 'done(null, {publicData: {userId: 1}})' along with any other publicData fields you need)`,
)
}

View File

@@ -1,4 +1,5 @@
import {exec} from "npm-run"
import {spawn} from "cross-spawn"
import which from "npm-which"
interface Constructor<T = unknown> {
new (...args: never[]): T
@@ -33,15 +34,15 @@ export const enhancePrisma = <TPrismaClientCtor extends Constructor>(
"You are calling db.$reset() in a production environment. We think you probably didn't mean to do that, so we are throwing this error instead of destroying your life's work.",
)
}
await new Promise<void>((res, rej) =>
exec("prisma migrate reset --force --skip-generate --preview-feature", function (err) {
if (err) {
rej(err)
} else {
res()
}
}),
)
const prismaBin = which(process.cwd()).sync("prisma")
await new Promise((res, rej) => {
const process = spawn(
prismaBin,
["migrate", "reset", "--force", "--skip-generate", "--preview-feature"],
{stdio: "inherit"},
)
process.on("exit", (code) => (code === 0 ? res(0) : rej(code)))
})
global._blitz_prismaClient.$disconnect()
}

View File

@@ -6,8 +6,7 @@ import {parsePublicDataToken} from "./utils/tokens"
class PublicDataStore {
private eventKey = `${LOCALSTORAGE_PREFIX}publicDataUpdated`
// TODO remove `as any` after https://github.com/blitz-js/blitz/pull/1788 merged
readonly emptyPublicData: PublicData = {userId: null, roles: []} as any
readonly emptyPublicData: PublicData = {userId: null}
readonly observable = BadBehavior<PublicData>()
constructor() {

View File

@@ -91,7 +91,7 @@ export const executeRpcCall = <TInput, TResult>(
}
if (response.headers.get(HEADER_SESSION_CREATED)) {
clientDebug("Session created")
queryCache.clear()
await queryCache.invalidateQueries("")
}
if (response.headers.get(HEADER_CSRF_ERROR)) {
const err = new CSRFTokenMismatchError()

View File

@@ -1,10 +1,12 @@
import {useState} from "react"
import {COOKIE_CSRF_TOKEN} from "./constants"
import {useEffect, useState} from "react"
import {getBlitzRuntimeData} from "./blitz-data"
import {COOKIE_CSRF_TOKEN, COOKIE_LEGACY_CSRF_TOKEN} from "./constants"
import {AuthenticationError} from "./errors"
import {Ctx} from "./middleware"
import {publicDataStore} from "./public-data-store"
import {IsAuthorizedArgs, PublicData} from "./types"
import {isServer} from "./utils"
import {readCookie} from "./utils/cookie"
import {useIsomorphicLayoutEffect} from "./utils/hooks"
export interface SessionModel extends Record<any, any> {
handle: string
@@ -21,6 +23,7 @@ export type SessionConfig = {
method?: "essential" | "advanced"
sameSite?: "none" | "lax" | "strict"
domain?: string
publicDataKeysToSyncAcrossSessions?: string[]
getSession: (handle: string) => Promise<SessionModel | null>
getSessions: (userId: PublicData["userId"]) => Promise<SessionModel[]>
createSession: (session: SessionModel) => Promise<SessionModel>
@@ -29,7 +32,7 @@ export type SessionConfig = {
isAuthorized: (data: {ctx: Ctx; args: any[]}) => boolean
}
export interface SessionContextBase extends PublicData {
export interface SessionContextBase {
$handle: string | null
$publicData: unknown
$authorize(...args: IsAuthorizedArgs): asserts this is AuthenticatedSessionContext
@@ -45,40 +48,86 @@ export interface SessionContextBase extends PublicData {
}
// Could be anonymous
export interface SessionContext extends SessionContextBase {
export interface SessionContext extends SessionContextBase, Partial<PublicData> {
userId: PublicData["userId"] | null
$publicData: Partial<PublicData>
}
export interface AuthenticatedSessionContext extends SessionContextBase {
export interface AuthenticatedSessionContext extends SessionContextBase, PublicData {
userId: PublicData["userId"]
$publicData: PublicData
}
export const getAntiCSRFToken = () => readCookie(COOKIE_CSRF_TOKEN())
export const getAntiCSRFToken = () =>
readCookie(COOKIE_CSRF_TOKEN()) || readCookie(COOKIE_LEGACY_CSRF_TOKEN())
export interface PublicDataWithLoading extends PublicData {
export interface ClientSession extends Partial<PublicData> {
userId: PublicData["userId"] | null
isLoading: boolean
}
export interface AuthenticatedClientSession extends PublicData {
isLoading: boolean
}
interface UseSessionOptions {
initialPublicData?: PublicData
suspense?: boolean | null
}
export const useSession = (options: UseSessionOptions = {}): PublicDataWithLoading => {
const [publicData, setPublicData] = useState(
options.initialPublicData ?? publicDataStore.emptyPublicData,
)
const [isLoading, setIsLoading] = useState(!options.initialPublicData)
export const useSession = (options: UseSessionOptions = {}): ClientSession => {
const suspense = options?.suspense ?? getBlitzRuntimeData().suspenseEnabled
useIsomorphicLayoutEffect(() => {
let initialState: ClientSession
if (options.initialPublicData) {
initialState = {...options.initialPublicData, isLoading: false}
} else if (suspense) {
if (isServer) {
throw new Promise((_) => {})
} else {
initialState = {...publicDataStore.getData(), isLoading: false}
}
} else {
initialState = {...publicDataStore.emptyPublicData, isLoading: true}
}
const [session, setSession] = useState(initialState)
useEffect(() => {
// Initialize on mount
setPublicData(publicDataStore.getData())
setIsLoading(false)
const subscription = publicDataStore.observable.subscribe(setPublicData)
setSession({...publicDataStore.getData(), isLoading: false})
const subscription = publicDataStore.observable.subscribe((data) =>
setSession({...data, isLoading: false}),
)
return subscription.unsubscribe
}, [])
return {...publicData, isLoading}
return session
}
export const useAuthenticatedSession = (
options: UseSessionOptions = {},
): AuthenticatedClientSession => {
useAuthorize()
return useSession(options)
}
export const useAuthorize = () => {
useAuthorizeIf(true)
}
export const useAuthorizeIf = (condition?: boolean) => {
useEffect(() => {
if (condition && !publicDataStore.getData().userId) {
const error = new AuthenticationError()
delete error.stack
throw error
}
})
}
export const useRedirectAuthenticated = (to: string) => {
if (typeof window !== "undefined" && publicDataStore.getData().userId) {
window.location.replace(to)
}
}

View File

@@ -0,0 +1,26 @@
export const suspend = <T>(promise: Promise<T>) => {
let result: any
let status = "pending"
const suspender = promise.then(
(response) => {
status = "success"
result = response
},
(error) => {
status = "error"
result = error
},
)
return (): T => {
switch (status) {
case "pending":
throw suspender
case "error":
throw result
default:
return result
}
}
}

View File

@@ -1,6 +1,13 @@
import {IncomingMessage, ServerResponse} from "http"
import {AppProps as NextAppProps} from "next/app"
import {NextRouter} from "next/router"
import {NextApiRequest, NextApiResponse, NextComponentType, NextPageContext} from "next/types"
import {
NextApiRequest,
NextApiResponse,
NextComponentType,
NextPage,
NextPageContext,
} from "next/types"
import {AuthenticateOptions, Strategy} from "passport"
import {MutateOptions, MutationResult} from "react-query"
import {BlitzRuntimeData} from "./blitz-data"
@@ -23,9 +30,20 @@ export {
} from "next"
export type BlitzApiRequest = NextApiRequest
export type BlitzApiResponse = NextApiResponse
export type BlitzComponentType = NextComponentType
export type BlitzPageContext = NextPageContext
export type BlitzComponentType<C = NextPageContext, IP = {}, P = {}> = NextComponentType<C, IP, P>
export interface AppProps<P = {}> extends NextAppProps<P> {
Component: BlitzComponentType<NextPageContext, any, P> & BlitzPage
}
export type BlitzPage<P = {}, IP = P> = NextPage<P, IP> & {
getLayout?: (component: JSX.Element) => JSX.Element
authenticate?: boolean | {redirectTo?: string}
suppressFirstRenderFlicker?: boolean
redirectAuthenticatedTo?: string
}
export interface BlitzRouter extends NextRouter {
query: ReturnType<typeof useRouterQuery>
params: ReturnType<typeof useParams>

View File

@@ -8,6 +8,7 @@ import {
usePaginatedQuery as usePaginatedReactQuery,
useQuery as useReactQuery,
} from "react-query"
import {isClient} from "./utils"
import {useSession} from "./supertokens"
import {FirstParam, PromiseReturnType, QueryFn} from "./types"
import {useRouter} from "./use-router"
@@ -20,6 +21,13 @@ import {
useDefaultQueryConfig,
} from "./utils/react-query-utils"
type QueryLazyOptions = {suspense: unknown} | {enabled: unknown}
type QueryNonLazyOptions =
| {suspense: true; enabled?: never}
| {suspense?: never; enabled: true}
| {suspense: true; enabled: true}
| {suspense?: never; enabled?: never}
// -------------------------
// useQuery
// -------------------------
@@ -28,17 +36,30 @@ type RestQueryResult<TResult> = Omit<QueryResult<TResult>, "data"> & QueryCacheF
export function useQuery<T extends QueryFn, TResult = PromiseReturnType<T>>(
queryFn: T,
params: FirstParam<T>,
options?: QueryOptions<TResult>,
): [TResult, RestQueryResult<TResult>] {
options?: QueryOptions<TResult> & QueryNonLazyOptions,
): [TResult, RestQueryResult<TResult>]
export function useQuery<T extends QueryFn, TResult = PromiseReturnType<T>>(
queryFn: T,
params: FirstParam<T>,
options: QueryOptions<TResult> & QueryLazyOptions,
): [TResult | undefined, RestQueryResult<TResult>]
export function useQuery<T extends QueryFn, TResult = PromiseReturnType<T>>(
queryFn: T,
params: FirstParam<T>,
options: QueryOptions<TResult> = {},
) {
if (typeof queryFn === "undefined") {
throw new Error("useQuery is missing the first argument - it must be a query function")
}
// TODO - useSession here is a tempory fix for logout query invalidation until RQ v3
// https://github.com/blitz-js/blitz/issues/1711
// NOTE: bug did not present in local dev build. Only via npm bundle
useSession()
const routerIsReady = useRouter().isReady
const suspense =
options?.enabled === false || options?.enabled === null ? false : options?.suspense
const session = useSession({suspense})
if (session.isLoading) {
options.enabled = false
}
const routerIsReady = useRouter().isReady && isClient
const enhancedResolverRpcClient = sanitize(queryFn)
const queryKey = getQueryKey(queryFn, params)
@@ -59,7 +80,7 @@ export function useQuery<T extends QueryFn, TResult = PromiseReturnType<T>>(
...getQueryCacheFunctions<FirstParam<T>, TResult, T>(queryFn, params),
}
return [data as TResult, rest as RestQueryResult<TResult>]
return [data, rest as RestQueryResult<TResult>]
}
// -------------------------
@@ -71,14 +92,29 @@ type RestPaginatedResult<TResult> = Omit<PaginatedQueryResult<TResult>, "resolve
export function usePaginatedQuery<T extends QueryFn, TResult = PromiseReturnType<T>>(
queryFn: T,
params: FirstParam<T>,
options?: QueryOptions<TResult>,
): [TResult, RestPaginatedResult<TResult>] {
options?: QueryOptions<TResult> & QueryNonLazyOptions,
): [TResult, RestPaginatedResult<TResult>]
export function usePaginatedQuery<T extends QueryFn, TResult = PromiseReturnType<T>>(
queryFn: T,
params: FirstParam<T>,
options: QueryOptions<TResult> & QueryLazyOptions,
): [TResult | undefined, RestPaginatedResult<TResult>]
export function usePaginatedQuery<T extends QueryFn, TResult = PromiseReturnType<T>>(
queryFn: T,
params: FirstParam<T>,
options: QueryOptions<TResult> = {},
) {
if (typeof queryFn === "undefined") {
throw new Error("usePaginatedQuery is missing the first argument - it must be a query function")
}
// TODO - useSession here is a tempory fix for logout query invalidation until RQ v3
useSession()
const suspense =
options?.enabled === false || options?.enabled === null ? false : options?.suspense
const session = useSession({suspense})
if (session.isLoading) {
options.enabled = false
}
const routerIsReady = useRouter().isReady
const enhancedResolverRpcClient = sanitize(queryFn)
const queryKey = getQueryKey(queryFn, params)
@@ -100,7 +136,7 @@ export function usePaginatedQuery<T extends QueryFn, TResult = PromiseReturnType
...getQueryCacheFunctions<FirstParam<T>, TResult, T>(queryFn, params),
}
return [resolvedData as TResult, rest as RestPaginatedResult<TResult>]
return [resolvedData, rest as RestPaginatedResult<TResult>]
}
// -------------------------
@@ -108,7 +144,7 @@ export function usePaginatedQuery<T extends QueryFn, TResult = PromiseReturnType
// -------------------------
type RestInfiniteResult<TResult, TMoreVariable> = Omit<
InfiniteQueryResult<TResult, TMoreVariable>,
"resolvedData"
"data"
> &
QueryCacheFunctions<TResult>
@@ -127,13 +163,36 @@ export function useInfiniteQuery<
queryFn: T,
params: (fetchMoreResult: TFetchMoreResult) => FirstParam<T>,
options: InfiniteQueryConfig<TResult, TFetchMoreResult>,
): [TResult[], RestInfiniteResult<TResult, TFetchMoreResult>] {
): [TResult[], RestInfiniteResult<TResult, TFetchMoreResult> & QueryNonLazyOptions]
export function useInfiniteQuery<
T extends QueryFn,
TFetchMoreResult = any,
TResult = PromiseReturnType<T>
>(
queryFn: T,
params: (fetchMoreResult: TFetchMoreResult) => FirstParam<T>,
options: InfiniteQueryConfig<TResult, TFetchMoreResult> & QueryLazyOptions,
): [TResult[] | undefined, RestInfiniteResult<TResult, TFetchMoreResult>]
export function useInfiniteQuery<
T extends QueryFn,
TFetchMoreResult = any,
TResult = PromiseReturnType<T>
>(
queryFn: T,
params: (fetchMoreResult: TFetchMoreResult) => FirstParam<T>,
options: InfiniteQueryConfig<TResult, TFetchMoreResult>,
) {
if (typeof queryFn === "undefined") {
throw new Error("useInfiniteQuery is missing the first argument - it must be a query function")
}
// TODO - useSession here is a tempory fix for logout query invalidation until RQ v3
useSession()
const suspense =
options?.enabled === false || options?.enabled === null ? false : options?.suspense
const session = useSession({suspense})
if (session.isLoading) {
options.enabled = false
}
const routerIsReady = useRouter().isReady
const enhancedResolverRpcClient = sanitize(queryFn)
const queryKey = getQueryKey(queryFn, params)
@@ -158,5 +217,5 @@ export function useInfiniteQuery<
...getQueryCacheFunctions<FirstParam<T>, TResult, T>(queryFn, params),
}
return [data as TResult[], rest as RestInfiniteResult<TResult, TFetchMoreResult>]
return [data, rest as RestInfiniteResult<TResult, TFetchMoreResult>]
}

View File

@@ -3,7 +3,7 @@ import {queryCache, QueryKey} from "react-query"
import {serialize} from "superjson"
import {getBlitzRuntimeData} from "../blitz-data"
import {EnhancedResolverRpcClient, QueryFn, Resolver} from "../types"
import {isClient, isServer} from "."
import {isClient} from "."
import {requestIdleCallback} from "./request-idle-callback"
type MutateOptions = {
@@ -60,13 +60,7 @@ export const validateQueryFn = <TInput, TResult>(
export const sanitize = <TInput, TResult>(
queryFn: Resolver<TInput, TResult> | EnhancedResolverRpcClient<TInput, TResult>,
) => {
if (isServer) {
// Prevents logging garbage during static pre-rendering
return emptyQueryFn as EnhancedResolverRpcClient<TInput, TResult>
}
validateQueryFn(queryFn)
return queryFn as EnhancedResolverRpcClient<TInput, TResult>
}

View File

@@ -54,8 +54,8 @@ describe("supertokens", () => {
expect(result.current).toEqual({
isLoading: false,
roles: ["foo"],
userId: "bar",
roles: ["foo"],
})
act(() => {
@@ -64,8 +64,8 @@ describe("supertokens", () => {
expect(result.current).toEqual({
isLoading: false,
roles: ["baz"],
userId: "boo",
roles: ["baz"],
})
})
@@ -86,8 +86,8 @@ describe("supertokens", () => {
expect(result.current).toEqual({
isLoading: false,
roles: ["foo"],
userId: "bar",
roles: ["foo"],
})
})
})

View File

@@ -27,6 +27,7 @@ const mockRouter: NextRouter = {
asPath: "/",
query: {},
isReady: true,
isLocaleDomain: false,
push: jest.fn(),
replace: jest.fn(),
reload: jest.fn(),

View File

@@ -22,14 +22,15 @@ describe("useQuery", () => {
const setupHook = (
params: any,
queryFn: (...args: any) => any,
options: Parameters<typeof useQuery>[2] = {} as any,
): [{data?: any; setQueryData?: any}, Function] => {
let res = {}
function TestHarness() {
const [data, {setQueryData}] = useQuery(queryFn, params)
const [data, {setQueryData}] = useQuery(queryFn, params, options as any)
Object.assign(res, {data, setQueryData})
return (
<div id="harness">
<span>{data ? "Ready" : "Missing Dependency"}</span>
<span>{data ? "Ready" : "No data"}</span>
<span>{data}</span>
</div>
)
@@ -73,7 +74,27 @@ describe("useQuery", () => {
console.error = jest.fn()
expect(() => setupHook("test", upcase)).toThrowErrorMatchingSnapshot()
})
it("suspense disabled if enabled is false", async () => {
setupHook("test", enhanceQueryFn(upcase), {enabled: false})
await screen.findByText("No data")
})
it("suspense disabled if enabled is null", async () => {
setupHook("test", enhanceQueryFn(upcase), {enabled: null})
await screen.findByText("No data")
})
it("suspense disabled if enabled is false and suspense set", async () => {
setupHook("test", enhanceQueryFn(upcase), {enabled: false, suspense: true})
await screen.findByText("No data")
})
})
// it("works with options other than enabled & suspense without type error", () => {
// const queryFn = ((() => true) as unknown) as () => Promise<boolean>
// useQuery(queryFn, undefined, {refetchInterval: 10000})
// })
})
describe("useInfiniteQuery", () => {
@@ -89,7 +110,7 @@ describe("useInfiniteQuery", () => {
Object.assign(res, {groupedData})
return (
<div id="harness">
<span>{groupedData ? "Ready" : "Missing Dependency"}</span>
<span>{groupedData ? "Ready" : "No data"}</span>
<div>
{groupedData.map((data: any) => (
<div>{data}</div>

View File

@@ -1,6 +1,6 @@
{
"extends": "../../tsconfig.json",
"include": ["src", "types", "test"],
"include": ["src", "types", "test", "../../types"],
"exclude": ["node_modules"],
"compilerOptions": {
"baseUrl": "./",

View File

@@ -1,6 +1,6 @@
{
"name": "@blitzjs/display",
"version": "0.30.0-canary.6",
"version": "0.30.3",
"description": "Display package for the Blitz CLI",
"homepage": "https://github.com/blitz-js/blitz#readme",
"license": "MIT",
@@ -35,7 +35,7 @@
"url": "git+https://github.com/blitz-js/blitz.git"
},
"dependencies": {
"@blitzjs/display": "0.30.0-canary.6",
"@blitzjs/display": "0.30.3",
"chalk": "^4.1.0",
"console-table-printer": "^2.7.5",
"ora": "^5.3.0",

View File

@@ -56,6 +56,9 @@ const withX = (str: string) => {
const withProgress = (str: string) => {
return withCaret(str)
}
const withError = (str: string) => {
return withX(c.red.bold(str))
}
/**
* Logs a branded purple message to stdout.
@@ -77,6 +80,14 @@ const clearLine = (msg?: string) => {
msg && process.stdout.write(msg)
}
const clearConsole = () => {
if (process.platform === "win32") {
process.stdout.write("\x1B[2J\x1B[0f")
} else {
process.stdout.write("\x1B[2J\x1B[3J\x1B[H")
}
}
/**
* Logs a red error message to stderr.
*
@@ -166,9 +177,7 @@ const variable = (val: any) => {
* If the DEBUG env var is set this will write to the console
* @param str msg
*/
const debug = (str: string) => {
process.env.DEBUG && console.log(str)
}
const debug = require("debug")("blitz")
declare module globalThis {
let _blitz_baseLogger: Logger
@@ -210,8 +219,10 @@ export const log = {
withCheck,
withX,
withProgress,
withError,
branded,
clearLine,
clearConsole,
error,
warning,
meta,

View File

@@ -1,6 +1,6 @@
{
"name": "eslint-config-blitz",
"version": "0.30.0-canary.6",
"version": "0.30.3",
"description": "Blitz.js eslint config",
"license": "MIT",
"scripts": {

View File

@@ -1,6 +1,6 @@
{
"name": "@blitzjs/file-pipeline",
"version": "0.30.0-canary.6",
"version": "0.30.3",
"description": "Display package for the Blitz CLI",
"homepage": "https://github.com/blitz-js/blitz#readme",
"license": "MIT",
@@ -32,7 +32,7 @@
"url": "git+https://github.com/blitz-js/blitz.git"
},
"dependencies": {
"@blitzjs/display": "0.30.0-canary.6",
"@blitzjs/display": "0.30.3",
"chalk": "^4.1.0",
"chokidar": "3.5.1",
"flush-write-stream": "2.0.0",
@@ -43,6 +43,7 @@
"parallel-transform": "1.2.0",
"pump": "3.0.0",
"pumpify": "2.0.1",
"rimraf": "3.0.2",
"through2": "4.0.2",
"vinyl": "2.2.1",
"vinyl-file": "3.0.0",

View File

@@ -38,6 +38,7 @@ export const watch = (includePaths: string[] | string, options: chokidar.WatchOp
fswatcher.on("add", processEvent("add"))
fswatcher.on("change", processEvent("change"))
fswatcher.on("unlink", processEvent("unlink"))
fswatcher.on("error", (error) => console.log(`Watcher error: ${error}`))
return {stream, fswatcher}
}
@@ -53,8 +54,8 @@ function getWatcher(watching: boolean, cwd: string, include: string[], ignore: s
ignoreInitial: true,
alwaysStat: true,
awaitWriteFinish: {
stabilityThreshold: 2000,
pollInterval: 100,
stabilityThreshold: 200,
pollInterval: 50,
},
})
}

View File

@@ -0,0 +1,13 @@
import rimrafCallback, {Options} from "rimraf"
export function rimraf(f: string, opts: Options = {}) {
return new Promise<void>((res, rej) => {
rimrafCallback(f, opts, (error) => {
if (error) {
rej(error)
} else {
res()
}
})
})
}

View File

@@ -1,7 +1,7 @@
import * as fs from "fs-extra"
import {relative, resolve} from "path"
import {transform} from "../../transform"
import {EventedFile} from "../../types"
import {rimraf} from "../rimraf-promise"
function getDestPath(folder: string, file: EventedFile) {
return resolve(folder, relative(file.cwd, file.path))
@@ -11,11 +11,11 @@ function getDestPath(folder: string, file: EventedFile) {
* Deletes a file in the stream from the filesystem
* @param folder The destination folder
*/
export function unlink(folder: string, unlinkFile = fs.unlink, pathExists = fs.pathExists) {
export function unlink(folder: string, unlinkFile = rimraf) {
return transform.file(async (file) => {
if (file.event === "unlink" || file.event === "unlinkDir") {
const destPath = getDestPath(folder, file)
if (await pathExists(destPath)) await unlinkFile(destPath)
await unlinkFile(destPath, {glob: false})
}
return file

View File

@@ -6,9 +6,8 @@ import {unlink} from "."
describe("unlink", () => {
it("should unlink the correct path", async () => {
const unlinkFile = jest.fn(() => Promise.resolve())
const pathExists = jest.fn(() => Promise.resolve(true))
const unlinkStream = unlink(normalize("/dest"), unlinkFile, pathExists)
const unlinkStream = unlink(normalize("/dest"), unlinkFile)
unlinkStream.write(
new File({
@@ -21,10 +20,7 @@ describe("unlink", () => {
await take(unlinkStream, 1)
// Test the file exists before attempting to unlink it
expect(pathExists).toHaveBeenCalledWith(resolve(normalize("/dest/bar/baz.tz")))
// Remove the correct file
expect(unlinkFile).toHaveBeenCalledWith(resolve(normalize("/dest/bar/baz.tz")))
expect(unlinkFile).toHaveBeenCalledWith(resolve(normalize("/dest/bar/baz.tz")), {glob: false})
})
})

View File

@@ -4,3 +4,4 @@ export * from "./events"
export {transform} from "./transform"
export {FileCache} from "./helpers/file-cache"
export {RouteCache} from "./helpers/route-cache"
export {rimraf} from "./helpers/rimraf-promise"

View File

@@ -1,7 +1,8 @@
import {ensureDir, pathExists, remove} from "fs-extra"
import {join} from "path"
import {Transform} from "stream"
import {createDisplay} from "../display"
import {ERROR_THROWN, READY} from "../events"
import {rimraf} from "../helpers/rimraf-promise"
import {createPipeline} from "../pipeline"
import {pipe, through} from "../streams"
import {Stage} from "../types"
@@ -42,7 +43,9 @@ export async function transformFiles(
clean: requestClean,
} = options
if (requestClean) await clean(dest)
if (requestClean) await rimraf(dest, {glob: false})
// Fix windows EPERM issues
if (process.platform === "win32") await rimraf(join(dest, ".next"), {glob: false})
const display = createDisplay()
return await new Promise((resolve, reject) => {
@@ -74,10 +77,3 @@ export async function transformFiles(
})
})
}
async function clean(path: string) {
if (await pathExists(path)) {
await remove(path)
}
return await ensureDir(path)
}

View File

@@ -1,6 +1,6 @@
{
"name": "@blitzjs/generator",
"version": "0.30.0-canary.6",
"version": "0.30.3",
"description": "File generation for the Blitz CLI",
"homepage": "https://github.com/blitz-js/blitz#readme",
"license": "MIT",
@@ -36,7 +36,7 @@
"dependencies": {
"@babel/core": "7.12.10",
"@babel/plugin-transform-typescript": "7.12.1",
"@blitzjs/display": "0.30.0-canary.6",
"@blitzjs/display": "0.30.3",
"@types/jscodeshift": "0.7.2",
"chalk": "^4.1.0",
"cross-spawn": "7.0.3",
@@ -44,11 +44,11 @@
"enquirer": "2.3.6",
"fs-extra": "^9.1.0",
"fs-readdir-recursive": "1.1.0",
"npm-run": "^5.0.1",
"got": "^11.8.1",
"jscodeshift": "0.11.0",
"mem-fs": "1.2.0",
"mem-fs-editor": "8.0.0",
"npm-which": "^3.0.1",
"pluralize": "^8.0.0",
"recast": "0.20.4",
"username": "^5.1.0",

View File

@@ -1,5 +1,6 @@
import {log} from "@blitzjs/display"
import {spawn} from "npm-run"
import {spawn} from "cross-spawn"
import which from "npm-which"
import path from "path"
import {Generator, GeneratorOptions} from "../generator"
import {Field} from "../prisma/field"
@@ -91,7 +92,8 @@ export class ModelGenerator extends Generator<ModelGeneratorOptions> {
const shouldMigrate = await this.prismaMigratePrompt()
if (shouldMigrate) {
await new Promise<void>((res, rej) => {
const child = spawn("prisma", ["migrate", "dev", "--preview-feature"], {stdio: "inherit"})
const prismaBin = which(process.cwd()).sync("prisma")
const child = spawn(prismaBin, ["migrate", "dev", "--preview-feature"], {stdio: "inherit"})
child.on("exit", (code) => (code === 0 ? res() : rej()))
})
}

View File

@@ -35,7 +35,7 @@ const fallbackIfUndef = <T extends any>(defaultValue: T, input?: T) => {
return input
}
const defaultValueTest = /\[([\w]+)\]/
const defaultValueTest = /=([\w]+)$/
const builtInGenerators = ["autoincrement", "now", "uuid", "cuid"]
class MissingFieldNameError extends Error {}

View File

@@ -66,6 +66,26 @@ Here is the starting structure of your app.
```
__name__
├── app/
│   ├── api/
│   ├── auth/
│   │   ├── components/
│   │   │   ├── LoginForm.tsx
│   │   │   └── SignupForm.tsx
│   │   ├── mutations/
│   │   │   ├── changePassword.ts
│   │   │   ├── forgotPassword.test.ts
│   │   │   ├── forgotPassword.ts
│   │   │   ├── login.ts
│   │   │   ├── logout.ts
│   │   │   ├── resetPassword.test.ts
│   │   │   ├── resetPassword.ts
│   │   │   └── signup.ts
│   │   ├── pages/
│   │   │   ├── forgot-password.tsx
│   │   │   ├── login.tsx
│   │   │   ├── reset-password.tsx
│   │   │   └── signup.tsx
│   │   └── validations.ts
│   ├── core/
│   │   ├── components/
│   │   │   ├── Form.tsx
@@ -80,20 +100,6 @@ __name__
│   │   ├── _document.tsx
│   │   ├── index.test.tsx
│   │   └── index.tsx
│   ├── api/
│   ├── auth/
│   │   ├── components/
│   │   │   ├── LoginForm.tsx
│   │   │   └── SignupForm.tsx
│   │   ├── mutations/
│   │   │   ├── changePassword.ts
│   │   │   ├── login.ts
│   │   │   ├── logout.ts
│   │   │   └── signup.ts
│   │   ├── pages/
│   │   │   ├── login.tsx
│   │   │   └── signup.tsx
│   │   └── validations.ts
│   └── users/
│   └── queries/
│   └── getCurrentUser.ts
@@ -102,6 +108,8 @@ __name__
│   ├── schema.prisma
│   └── seeds.ts
├── integrations/
├── mailers/
│   └── forgotPasswordMailer.ts
├── public/
│   ├── favicon.ico*
│   └── logo.png
@@ -114,6 +122,7 @@ __name__
├── jest.config.js
├── package.json
├── tsconfig.json
├── types.d.ts
├── types.ts
└── yarn.lock
```

View File

@@ -1,4 +1,4 @@
import React, { ReactNode, PropsWithoutRef } from "react"
import { ReactNode, PropsWithoutRef } from "react"
import { Form as FinalForm, FormProps as FinalFormProps } from "react-final-form"
import * as z from "zod"
export { FORM_ERROR } from "final-form"

View File

@@ -1,5 +1,5 @@
import React, {PropsWithoutRef} from "react"
import {useField} from "react-final-form"
import { forwardRef, PropsWithoutRef } from "react"
import { useField } from "react-final-form"
export interface LabeledTextFieldProps extends PropsWithoutRef<JSX.IntrinsicElements["input"]> {
/** Field name. */
@@ -11,13 +11,13 @@ export interface LabeledTextFieldProps extends PropsWithoutRef<JSX.IntrinsicElem
outerProps?: PropsWithoutRef<JSX.IntrinsicElements["div"]>
}
export const LabeledTextField = React.forwardRef<HTMLInputElement, LabeledTextFieldProps>(
({name, label, outerProps, ...props}, ref) => {
export const LabeledTextField = forwardRef<HTMLInputElement, LabeledTextFieldProps>(
({ name, label, outerProps, ...props }, ref) => {
const {
input,
meta: {touched, error, submitError, submitting},
meta: { touched, error, submitError, submitting },
} = useField(name, {
parse: props.type === "number" ? Number : undefined
parse: props.type === "number" ? Number : undefined,
})
const normalizedError = Array.isArray(error) ? error.join(", ") : error || submitError
@@ -30,7 +30,7 @@ export const LabeledTextField = React.forwardRef<HTMLInputElement, LabeledTextFi
</label>
{touched && normalizedError && (
<div role="alert" style={{color: "red"}}>
<div role="alert" style={{ color: "red" }}>
{normalizedError}
</div>
)}
@@ -53,7 +53,7 @@ export const LabeledTextField = React.forwardRef<HTMLInputElement, LabeledTextFi
`}</style>
</div>
)
},
}
)
export default LabeledTextField

View File

@@ -1,4 +1,4 @@
import React, { useState, ReactNode, PropsWithoutRef } from "react"
import { useState, ReactNode, PropsWithoutRef } from "react"
import { Formik, FormikProps } from "formik"
import * as z from "zod"

View File

@@ -1,57 +1,55 @@
import React, { PropsWithoutRef } from "react";
import { useField, useFormikContext, ErrorMessage } from "formik";
import { forwardRef, PropsWithoutRef } from "react"
import { useField, useFormikContext, ErrorMessage } from "formik"
export interface LabeledTextFieldProps
extends PropsWithoutRef<JSX.IntrinsicElements["input"]> {
export interface LabeledTextFieldProps extends PropsWithoutRef<JSX.IntrinsicElements["input"]> {
/** Field name. */
name: string;
name: string
/** Field label. */
label: string;
label: string
/** Field type. Doesn't include radio buttons and checkboxes */
type?: "text" | "password" | "email" | "number";
outerProps?: PropsWithoutRef<JSX.IntrinsicElements["div"]>;
type?: "text" | "password" | "email" | "number"
outerProps?: PropsWithoutRef<JSX.IntrinsicElements["div"]>
}
export const LabeledTextField = React.forwardRef<
HTMLInputElement,
LabeledTextFieldProps
>(({ name, label, outerProps, ...props }, ref) => {
const [input] = useField(name);
const { isSubmitting } = useFormikContext();
export const LabeledTextField = forwardRef<HTMLInputElement, LabeledTextFieldProps>(
({ name, label, outerProps, ...props }, ref) => {
const [input] = useField(name)
const { isSubmitting } = useFormikContext()
return (
<div {...outerProps}>
<label>
{label}
<input {...input} disabled={isSubmitting} {...props} ref={ref} />
</label>
return (
<div {...outerProps}>
<label>
{label}
<input {...input} disabled={isSubmitting} {...props} ref={ref} />
</label>
<ErrorMessage name={name}>
{(msg) => (
<div role="alert" style={{ color: "red" }}>
{msg}
</div>
)}
</ErrorMessage>
<ErrorMessage name={name}>
{(msg) => (
<div role="alert" style={{ color: "red" }}>
{msg}
</div>
)}
</ErrorMessage>
<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>
);
});
<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;
export default LabeledTextField

View File

@@ -1,4 +1,4 @@
import React, { useState, ReactNode, PropsWithoutRef } from "react"
import { useState, ReactNode, PropsWithoutRef } from "react"
import { FormProvider, useForm, UseFormOptions } from "react-hook-form"
import * as z from "zod"

View File

@@ -1,4 +1,4 @@
import React, { PropsWithoutRef } from "react"
import { forwardRef, PropsWithoutRef } from "react"
import { useFormContext } from "react-hook-form"
export interface LabeledTextFieldProps extends PropsWithoutRef<JSX.IntrinsicElements["input"]> {
@@ -11,7 +11,7 @@ export interface LabeledTextFieldProps extends PropsWithoutRef<JSX.IntrinsicElem
outerProps?: PropsWithoutRef<JSX.IntrinsicElements["div"]>
}
export const LabeledTextField = React.forwardRef<HTMLInputElement, LabeledTextFieldProps>(
export const LabeledTextField = forwardRef<HTMLInputElement, LabeledTextFieldProps>(
({ label, outerProps, ...props }, ref) => {
const {
register,

View File

@@ -1,4 +1,3 @@
import React from "react"
import { AuthenticationError, Link, useMutation } from "blitz"
import { LabeledTextField } from "app/core/components/LabeledTextField"
import { Form, FORM_ERROR } from "app/core/components/Form"

View File

@@ -1,4 +1,3 @@
import React from "react"
import { useMutation } from "blitz"
import { LabeledTextField } from "app/core/components/LabeledTextField"
import { Form, FORM_ERROR } from "app/core/components/Form"

View File

@@ -23,7 +23,7 @@ export default resolver.pipe(resolver.zod(Login), async ({ email, password }, ct
// This throws an error if credentials are invalid
const user = await authenticateUser(email, password)
await ctx.session.$create({ userId: user.id, roles: [user.role as Role] })
await ctx.session.$create({ userId: user.id, role: user.role as Role })
return user
})

Some files were not shown because too many files have changed in this diff Show More