Compare commits

..

171 Commits

Author SHA1 Message Date
Nuno Pato
c22b2621ba Merge pull request #1661 from nhost/changeset-release/main
chore: update versions
2023-02-23 20:33:34 -01:00
github-actions[bot]
726746c4d3 chore: update versions 2023-02-23 19:19:03 +00:00
Nuno Pato
c431570783 Merge pull request #1662 from nhost/fix/file-upload
fix(hasura-storage-js): fix forbidden error
2023-02-23 18:17:47 -01:00
Szilárd Dóró
445d8ef449 fix(hasura-storage-js): fix forbidden error 2023-02-23 18:16:22 +01:00
Szilárd Dóró
0f4ea18e42 Merge pull request #1655 from nhost/feat/auth-storage-permissions
fix(dashboard): allow permission editing for auth and storage schemas
2023-02-23 15:22:05 +01:00
Szilárd Dóró
dae7c5d517 Merge pull request #1660 from nhost/fix/user-creation-content-type
fix(dashboard): set correct Content-Type for user creation
2023-02-23 14:38:16 +01:00
Szilárd Dóró
f673adea00 fix(dashboard): set correct Content-Type for user creation 2023-02-23 12:50:00 +01:00
Szilárd Dóró
1c6f1e3b33 Merge pull request #1656 from nhost/changeset-release/main
chore: update versions
2023-02-23 11:56:07 +01:00
github-actions[bot]
d1365ea516 chore: update versions 2023-02-23 10:20:53 +00:00
Szilárd Dóró
72dbba7881 Merge pull request #1659 from nhost/chore/revert-graphql-client
chore(graphql-js): revert GraphQL Client
2023-02-23 11:19:26 +01:00
Szilárd Dóró
a3f3991d5a Merge pull request #1658 from nhost/fix/user-creation
fix(dashboard): false positive message on user creation
2023-02-23 11:01:44 +01:00
Szilárd Dóró
c71fe2cf72 Revert "chore(graphql-js): revert GraphQL client for now"
This reverts commit 9a0ab5b887.
2023-02-23 10:15:02 +01:00
Szilárd Dóró
24c5ed3ea4 chore(graphql-js): cleanup 2023-02-23 10:14:43 +01:00
Szilárd Dóró
2d9145f918 chore(graphql-js): revert GraphQL client for now 2023-02-23 10:10:14 +01:00
Szilárd Dóró
9a0ab5b887 chore(graphql-js): revert GraphQL client for now 2023-02-23 10:06:58 +01:00
Szilárd Dóró
1ddf704c5b fix(dashboard): false positive message on user creation 2023-02-23 09:39:32 +01:00
Szilárd Dóró
6f4ee845c6 Merge pull request #1643 from nhost/fix/auth-last-seen
fix(dashboard): use correct date for last seen
2023-02-22 20:01:18 +01:00
Szilárd Dóró
0368663dea fix(dashboard): allow permission editing for auth and storage schemas
fixes #1555
2023-02-22 19:59:20 +01:00
Pilou
76ce7d7b6e Merge pull request #1653 from nhost/changeset-release/main
chore: update versions
2023-02-22 15:43:41 +01:00
github-actions[bot]
538bfbcb3e chore: update versions 2023-02-22 14:23:55 +00:00
Pilou
07b35d1e5f Merge pull request #1650 from nhost/docs/graphql-client-readme
GraphQL client: fix snake_case bug and improve readme
2023-02-22 15:22:34 +01:00
Pierre-Louis Mercereau
2200a0ed07 fix: correct types on snake case operations 2023-02-22 15:13:43 +01:00
Szilárd Dóró
df23d97126 Merge pull request #1652 from nhost/changeset-release/main
chore: update versions
2023-02-22 14:44:46 +01:00
github-actions[bot]
104f149369 chore: update versions 2023-02-22 13:26:08 +00:00
Szilárd Dóró
01228583a0 Merge pull request #1651 from nhost/fix/permission-editor-dropdown
fix(dashboard): prevent permission editor dropdown from being always open
2023-02-22 14:24:51 +01:00
Pierre-Louis Mercereau
93309dd851 docs: strictNullChecks 2023-02-22 14:06:10 +01:00
Szilárd Dóró
2cc18dcb51 fix(dashboard): prevent permission editor dropdown from being always open 2023-02-22 13:54:31 +01:00
Pierre-Louis Mercereau
3b48a62790 docs: ✏️ improve readme instructions 2023-02-22 13:44:15 +01:00
Pierre-Louis Mercereau
8897dec056 docs: add package.json script instructions 2023-02-22 12:23:54 +01:00
Pierre-Louis Mercereau
324dda8309 docs: streamline readme instructions 2023-02-22 11:58:51 +01:00
Pilou
95f62bed07 Merge pull request #1644 from nhost/changeset-release/main
chore: update versions
2023-02-22 08:50:38 +01:00
Pierre-Louis Mercereau
0e4d8ff118 chore: manually bump @nhost/react-apollo 2023-02-21 18:20:41 +01:00
github-actions[bot]
baec5bada7 chore: update versions 2023-02-21 17:03:03 +00:00
Pilou
4e56cfc628 Merge pull request #1348 from nhost/sdk-next-major
Sdk next major version
2023-02-21 18:01:13 +01:00
Pierre-Louis Mercereau
54bc91923f docs: update changeset 2023-02-21 12:40:52 +01:00
Szilárd Dóró
77b12feb95 Merge pull request #1639 from nhost/renovate/testing-library-react-14.x
chore(deps): update dependency @testing-library/react to v14
2023-02-20 16:30:11 +01:00
Pierre-Louis Mercereau
32d4670bbb Merge branch 'main' into sdk-next-major 2023-02-20 16:05:23 +01:00
Pilou
1dc09942d2 Merge pull request #1587 from nhost/next-client
Next GraphQL client
2023-02-20 16:01:49 +01:00
Szilárd Dóró
3343a36358 chore(deps): bump @testing-library 2023-02-20 15:52:18 +01:00
Szilárd Dóró
b755e9086c fix(dashboard): use correct date for last seen 2023-02-20 14:19:56 +01:00
Pierre-Louis Mercereau
48866d0ee1 chore: fail on runtime when passing an invalid field 2023-02-20 12:45:52 +01:00
renovate[bot]
5b3b76bd41 chore(deps): update dependency @testing-library/react to v14 2023-02-20 10:08:36 +00:00
Pierre-Louis Mercereau
284ef7e7f2 test: multiple where 2023-02-17 08:57:02 +01:00
Pierre-Louis Mercereau
6d5c202da9 changes 2023-02-17 08:44:48 +01:00
Pierre-Louis Mercereau
9342937440 readme 2023-02-15 13:39:24 +01:00
Pierre-Louis Mercereau
e89cd4e262 simple mutation example 2023-02-15 13:30:16 +01:00
Pierre-Louis Mercereau
a05438352b inline documentation 2023-02-15 13:28:04 +01:00
Pierre-Louis Mercereau
78437959bb dew 2023-02-15 13:08:20 +01:00
Pierre-Louis Mercereau
e1a7433adb docs: static doc for nhost.functions 2023-02-15 12:41:54 +01:00
Pierre-Louis Mercereau
e23cf74975 fix tests 2023-02-10 22:11:53 +01:00
Pierre-Louis Mercereau
a3d01c4fad fix lint config 2023-02-10 21:33:26 +01:00
Pierre-Louis Mercereau
4cdcef9ef5 fix broken links 2023-02-10 19:12:29 +01:00
Pierre-Louis Mercereau
df894ef7e2 upgrade typescript version 2023-02-10 15:51:59 +01:00
Pierre-Louis Mercereau
f7dd6a9fc6 deactivate functions doc 2023-02-10 14:42:56 +01:00
Pierre-Louis Mercereau
2949ff0f62 fix docgen 2023-02-10 14:33:56 +01:00
Pierre-Louis Mercereau
1527b0a455 remove type-fest dependency 2023-02-06 14:18:13 +01:00
Pierre-Louis Mercereau
375e53a3f0 update ts 2023-02-06 13:35:33 +01:00
Pierre-Louis Mercereau
96e3ca5a32 basic readme 2023-02-06 12:53:11 +01:00
Pierre-Louis Mercereau
0e570df9c5 missing dependency 2023-02-06 12:11:02 +01:00
Pierre-Louis Mercereau
1f4c67283e lockfile 2023-02-06 10:49:47 +01:00
Pierre-Louis Mercereau
fc1c4861a3 cleanup 2023-02-06 10:49:02 +01:00
Pierre-Louis Mercereau
74feaf6add unions 2023-02-02 11:47:29 +01:00
Pierre-Louis Mercereau
8cd97206cc Merge branch 'next-client' of https://github.com/nhost/nhost into next-client 2023-01-31 21:06:17 +01:00
Pierre-Louis Mercereau
02197639f2 dew 2023-01-31 20:59:14 +01:00
Pilou
38b594aef9 Update README.md 2023-01-31 19:40:31 +01:00
Pierre-Louis Mercereau
f3a8886cd0 dew 2023-01-31 19:08:19 +01:00
Pierre-Louis Mercereau
8d76cf8d40 simplify 2023-01-31 17:37:44 +01:00
Pierre-Louis Mercereau
3e1fb974e4 dew 2023-01-31 16:37:40 +01:00
Pierre-Louis Mercereau
f74871d872 dew 2023-01-31 15:01:54 +01:00
Pierre-Louis Mercereau
3f26056688 Merge branch 'main' into sdk-next-major 2023-01-30 11:11:51 +01:00
Pierre-Louis Mercereau
6a7801be93 chore: update changeset 2023-01-10 09:10:33 +01:00
Pierre-Louis Mercereau
7bc5bb857c chore: add the deprecated useAxios option 2023-01-10 09:02:45 +01:00
Pierre-Louis Mercereau
c957039d75 chore: update lock file 2023-01-09 20:17:12 +01:00
Pierre-Louis Mercereau
96c4032424 Merge branch 'main' into sdk-next-major 2023-01-09 20:13:13 +01:00
Pierre-Louis Mercereau
ec70126b56 fix: iso 8859-1 2023-01-09 19:25:02 +01:00
Pierre-Louis Mercereau
86b9f9040c Merge branch 'main' into sdk-next-major 2023-01-09 16:31:04 +01:00
Pierre-Louis Mercereau
222f03725b fix(dashboard): use fetch instead of axios 2023-01-06 16:42:40 +01:00
Pierre-Louis Mercereau
10b786e5c6 chore: fix lockfile 2023-01-06 16:30:22 +01:00
Pierre-Louis Mercereau
aa8ae88d12 Merge branch 'main' into sdk-next-major 2023-01-06 16:25:12 +01:00
Pilou
0f2c86b41a Merge pull request #1351 from nhost/refactor/cross-fetch
refactor: replace axios by cross-fetch
2023-01-06 16:17:03 +01:00
Pilou
a4c76892dd Update packages/hasura-storage-js/src/hasura-storage-api.ts
Co-authored-by: Szilárd Dóró <doroszilard@icloud.com>
2023-01-03 10:36:33 +01:00
Pierre-Louis Mercereau
00d278b2cc refactor: adjustments from review 2023-01-02 17:32:34 +01:00
Pilou
cb6b5faeb9 Update packages/hasura-storage-js/src/hasura-storage-client.ts
Co-authored-by: Szilárd Dóró <doroszilard@icloud.com>
2023-01-02 17:29:21 +01:00
Pierre-Louis Mercereau
7c4c847b91 Merge branch 'sdk-next-major' into refactor/cross-fetch 2022-12-26 12:54:40 +01:00
Pierre-Louis Mercereau
908887d8c5 Merge branch 'main' into sdk-next-major 2022-12-26 12:54:19 +01:00
Pierre-Louis Mercereau
a2d67bc2db fix(dashboard): js error from error payload 2022-12-26 12:45:33 +01:00
Pierre-Louis Mercereau
1a6cd78254 Merge branch 'sdk-next-major' into refactor/cross-fetch 2022-12-21 16:42:58 +01:00
Pierre-Louis Mercereau
6500629c4b Merge branch 'main' into sdk-next-major 2022-12-21 16:22:28 +01:00
Pierre-Louis Mercereau
add3c2c10e Merge branch 'sdk-next-major' into refactor/cross-fetch 2022-12-21 14:29:00 +01:00
Pierre-Louis Mercereau
dd29b06260 Merge branch 'main' into sdk-next-major 2022-12-21 14:27:48 +01:00
Pierre-Louis Mercereau
490cb25a0f Merge branch 'sdk-next-major' into refactor/cross-fetch 2022-12-20 10:13:18 +01:00
Pierre-Louis Mercereau
0df0dd741e Merge branch 'main' into sdk-next-major 2022-12-20 10:12:56 +01:00
Pierre-Louis Mercereau
2172946879 Merge branch 'sdk-next-major' into refactor/cross-fetch 2022-12-20 09:17:50 +01:00
Pierre-Louis Mercereau
40e50f0e75 Merge branch 'main' into sdk-next-major 2022-12-20 09:15:30 +01:00
Pierre-Louis Mercereau
65cf0888b5 chore: extract patch changes 2022-12-16 12:14:38 +01:00
Pierre-Louis Mercereau
21833019ca chore: correct error from several mergers 2022-12-14 21:14:48 +01:00
Pierre-Louis Mercereau
b3171ba3e9 chore: remove lockfile 2022-12-14 20:40:16 +01:00
Pierre-Louis Mercereau
6f01f19d02 refactor: remove docgen 2022-12-14 20:33:03 +01:00
Pierre-Louis Mercereau
ce92b01eac Merge branch 'main' into sdk-next-major 2022-12-14 19:25:15 +01:00
Pierre-Louis Mercereau
e24a177434 Merge branch 'main' into sdk-next-major 2022-12-14 19:06:50 +01:00
Pierre-Louis Mercereau
56a52b6d48 chore: remove duplicate changesets 2022-12-12 16:45:42 +01:00
Pierre-Louis Mercereau
92bfa8c723 chore: phrasing 2022-12-12 16:03:07 +01:00
Pierre-Louis Mercereau
2a52aaa4a6 Merge branch 'sdk-next-major' into refactor/cross-fetch 2022-12-12 14:46:39 +01:00
Pierre-Louis Mercereau
8280a3e9d8 chore: use explicit changeset links 2022-12-12 14:30:38 +01:00
Pierre-Louis Mercereau
523f60bf68 Merge branch 'sdk-next-major' into refactor/cross-fetch 2022-12-12 14:20:28 +01:00
Pierre-Louis Mercereau
19b11d4084 chore: details on removed deprecations 2022-12-12 14:15:08 +01:00
Pierre-Louis Mercereau
805bae1507 chore: bump nextjs as major as React removed deprecated methods are reexported 2022-12-12 13:36:25 +01:00
Pierre-Louis Mercereau
f6c014c06f chore: correct changeset 2022-12-12 13:34:19 +01:00
Pierre-Louis Mercereau
c5794f4596 chore: improve changesets 2022-12-12 13:32:09 +01:00
Pierre-Louis Mercereau
fc28817380 chore: explain xhr in changeset 2022-12-12 10:06:56 +01:00
Pierre-Louis Mercereau
80bbd3a165 chore: changesets 2022-12-12 10:02:21 +01:00
Pierre-Louis Mercereau
7a10617a72 test: simplify email verification test 2022-12-12 09:26:02 +01:00
Pierre-Louis Mercereau
f0b6dca1a5 Merge branch 'sdk-next-major' into refactor/cross-fetch 2022-12-08 14:28:03 +01:00
Szilárd Dóró
5db20adc34 fix(react): eliminate error when testing the project 2022-12-08 12:01:05 +01:00
Pierre-Louis Mercereau
12dc41a517 test: TypedDocumentNode 2022-12-08 09:07:46 +01:00
Pierre-Louis Mercereau
768fd56891 feat: graphql-typed-document-node 2022-12-07 21:53:21 +01:00
Pierre-Louis Mercereau
8a508cb1cc chore: run CI on evey pull request, not only the ones to main 2022-12-07 20:47:26 +01:00
Pierre-Louis Mercereau
34f6a8eef4 refactor: upload progress with XHR 2022-12-07 19:48:34 +01:00
Pierre-Louis Mercereau
c9d2d31a9b feat: nhost.graphql.httpUrl and nhost.graphql.wsUrl 2022-12-07 18:53:58 +01:00
Pierre-Louis Mercereau
68fb23a361 refactor: remove last pieces of axios 2022-12-07 17:16:42 +01:00
Pierre-Louis Mercereau
476139e528 refactor: remove axios from hasura-storage-js, with breaking change 2022-12-07 13:27:34 +01:00
Pierre-Louis Mercereau
6a850818a0 Merge branch 'sdk-next-major' into refactor/cross-fetch 2022-12-07 11:41:55 +01:00
Pilou
3970dbba0d Merge pull request #1342 from nhost/chore/improve-internal-dependencies
chore: improve internal dependencies
2022-12-07 11:40:02 +01:00
Pierre-Louis Mercereau
8ee2166f0d fix: correct nhost clients 2022-12-07 11:08:54 +01:00
Szilárd Dóró
e13500a185 fix(docgen): correct class references when used as interface members 2022-12-07 10:47:04 +01:00
Pierre-Louis Mercereau
411f574a51 refactor: re-export @nhost/react in @nhost/nextjs 2022-12-07 09:47:45 +01:00
Szilárd Dóró
7fc91b992e chore: cleanup dependencies, fix references 2022-12-07 08:59:06 +01:00
Pierre-Louis Mercereau
b840012be0 fix: correct fetch errors 2022-12-07 08:56:11 +01:00
Pierre-Louis Mercereau
645c51a9dc refactor: 💡 replace axios by cross-fetch, step 1
Changes not done yet in `nhost.auth.graphql` (will soon use
graphql-request instead) and the upload machines (depends on prrogress)
2022-12-06 22:08:46 +01:00
Pierre-Louis Mercereau
0ce6f05539 Merge branch 'sdk-next-major' into chore/improve-internal-dependencies 2022-12-06 14:10:19 +01:00
Pierre-Louis Mercereau
8b1188af53 Merge branch 'main' into sdk-next-major 2022-12-06 14:08:55 +01:00
Pilou
12b01f8dee Merge pull request #1298 from nhost/fix/react-strict-mode
fix: react strict mode
2022-12-06 14:06:07 +01:00
Pierre-Louis Mercereau
60f4faf409 chore: remove @nhost/core from changeset 2022-12-06 13:05:45 +01:00
Pierre-Louis Mercereau
528dff3f1b Merge branch 'sdk-next-major' into fix/react-strict-mode 2022-12-06 12:52:33 +01:00
Pierre-Louis Mercereau
d429fb4a3e Merge branch 'sdk-next-major' into chore/improve-internal-dependencies 2022-12-06 12:41:25 +01:00
Pilou
816c916709 Merge pull request #1233 from nhost/docs/hide-old-deprecations
chore: remove deprecated methods
2022-12-06 12:40:42 +01:00
Pierre-Louis Mercereau
b7a2b8b537 docs: update installation instructions 2022-12-06 10:50:40 +01:00
Pierre-Louis Mercereau
261d8cf434 merge 2022-12-06 10:10:13 +01:00
Pierre-Louis Mercereau
41f49bde76 Merge branch 'sdk-next-major' into chore/improve-internal-dependencies 2022-12-06 10:06:25 +01:00
Pilou
65f685bdb2 Update packages/core/src/client.ts
Co-authored-by: Szilárd Dóró <doroszilard@icloud.com>
2022-12-06 09:57:29 +01:00
Pilou
f52a7f4aac Update packages/core/src/machines/index.ts
Co-authored-by: Szilárd Dóró <doroszilard@icloud.com>
2022-12-06 09:57:01 +01:00
Pierre-Louis Mercereau
e71b9903d9 Merge branch 'sdk-next-major' into docs/hide-old-deprecations 2022-12-06 09:50:40 +01:00
Pilou
325fd08aef Merge pull request #1246 from nhost/refactor/merge-core
refactor: decommission `@nhost/core`
2022-12-06 09:46:50 +01:00
Pierre-Louis Mercereau
3888704464 chore: remove comment 2022-12-06 09:23:31 +01:00
Pierre-Louis Mercereau
38e8a10a29 chore: 🤖 remove lockfile 2022-12-06 09:19:02 +01:00
Pierre-Louis Mercereau
d8545eae12 chore: 🤖 add changeset 2022-12-06 09:18:16 +01:00
Pierre-Louis Mercereau
3d5bfd87d2 refactor: simplify 2022-12-06 08:59:11 +01:00
Pierre-Louis Mercereau
e66c5626bd fix: don't fetch session when present in initial state 2022-12-05 23:14:17 +01:00
Pierre-Louis Mercereau
a227c6561e refactor: work with nextjs 2022-12-05 23:09:26 +01:00
Pierre-Louis Mercereau
e885c159df refactor: simplify onStart 2022-12-05 14:53:10 +01:00
Pierre-Louis Mercereau
09fcb74bef refactor: use Johan's function name 2022-12-05 14:31:03 +01:00
Pierre-Louis Mercereau
a089197197 Merge branch 'main' into fix/react-strict-mode 2022-12-05 14:29:27 +01:00
Pierre-Louis Mercereau
34f843875b Merge branch 'main' into fix/react-strict-mode 2022-12-05 12:15:01 +01:00
Pierre-Louis Mercereau
ca278a8c39 Merge branch 'main' into fix/react-strict-mode 2022-12-05 11:54:01 +01:00
Pierre-Louis Mercereau
75603786e0 refactor: 💡 Remove unused immer dependency 2022-12-05 11:43:47 +01:00
Pierre-Louis Mercereau
4e4e699b94 refactor: adapt to nextjs 2022-12-05 11:39:36 +01:00
Pierre-Louis Mercereau
da31fa9fba ci: explicit pnpm run 2022-12-02 20:46:25 +01:00
Pierre-Louis Mercereau
95e2afaf47 Merge branch 'main' into fix/react-strict-mode 2022-12-02 15:46:13 +01:00
Pierre-Louis Mercereau
958a56dde9 fix: adapt to nextjs 2022-12-02 15:11:08 +01:00
Pierre-Louis Mercereau
74cb15930e Merge branch 'main' into fix/react-strict-mode 2022-12-02 13:35:53 +01:00
Pierre-Louis Mercereau
aa37a98424 Merge branch 'main' into fix/react-strict-mode 2022-12-02 13:27:37 +01:00
Pierre-Louis Mercereau
11cbdda3a5 Merge branch 'main' into fix/react-strict-mode 2022-12-02 13:20:55 +01:00
Pierre-Louis Mercereau
6d1f4adf10 chore: update changeset 2022-12-02 13:16:31 +01:00
Pierre-Louis Mercereau
ddbc50c15e refactor: avoid ambiguous interpreter getter/setter 2022-12-02 13:15:04 +01:00
Pierre-Louis Mercereau
b2cbf570a3 fix: 🐛 don't throw error when re-setting the interpreter 2022-12-02 12:08:52 +01:00
Pierre-Louis Mercereau
22b8e65031 fix: 🐛 onstart only when interpreter is initialised 2022-12-02 11:26:14 +01:00
Pierre-Louis Mercereau
63c94d2036 chore: fine-tune (peer) dependencies 2022-11-30 12:56:46 +01:00
Pierre-Louis Mercereau
010df48c1e chore: remove deprecations 2022-11-28 20:44:24 +01:00
Pierre-Louis Mercereau
fdc11db93d Merge branch 'main' into docs/hide-old-deprecations 2022-11-28 20:18:26 +01:00
Pierre-Louis Mercereau
cb4749f168 chore: fix lint 2022-11-28 15:53:54 +01:00
Pierre-Louis Mercereau
46a8fcf471 docs: hide > 6m.o. deprecations in the generated documentation 2022-11-28 15:34:09 +01:00
164 changed files with 2010 additions and 1574 deletions

View File

@@ -40,14 +40,14 @@ runs:
- shell: bash
name: Build packages
if: ${{ inputs.BUILD == 'all' }}
run: pnpm build:all
run: pnpm run build:all
env:
TURBO_TOKEN: ${{ inputs.TURBO_TOKEN }}
TURBO_TEAM: ${{ inputs.TURBO_TEAM }}
- shell: bash
name: Build everything in the monorepo
if: ${{ inputs.BUILD == 'default' }}
run: pnpm build
run: pnpm run build
env:
TURBO_TOKEN: ${{ inputs.TURBO_TOKEN }}
TURBO_TEAM: ${{ inputs.TURBO_TEAM }}

View File

@@ -8,7 +8,6 @@ on:
- '**.md'
- 'LICENSE'
pull_request:
branches: [main]
types: [opened, synchronize]
paths-ignore:
- 'assets/**'
@@ -56,7 +55,7 @@ jobs:
| xargs -I@ realpath --relative-to=$PWD @ \
| xargs -I@ jq "if (.scripts.e2e | length) != 0 then {name: .name, path: \"@\"} else null end" @/package.json \
| awk "!/null/" \
| jq -c --slurp)
| jq -c --slurp 'map(select(length > 0))')
echo "matrix=$PACKAGES" >> $GITHUB_OUTPUT
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}

View File

@@ -45,6 +45,9 @@
"@nhost/docgen": [
"../packages/docgen/src/index.ts"
],
"@nhost/graphql-js": [
"../packages/graphql-js/src/index.ts"
],
"@nhost/hasura-auth-js": [
"../packages/hasura-auth-js/src/index.ts"
],

View File

@@ -1,7 +1,6 @@
import replace from '@rollup/plugin-replace'
import fs from 'fs'
import path from 'path'
import { defineConfig } from 'vite'
import dts from 'vite-plugin-dts'
import tsconfigPaths from 'vite-tsconfig-paths'
@@ -61,7 +60,6 @@ export default defineConfig({
'@apollo/client/utilities': '@apollo/client/utilities',
'graphql-ws': 'graphql-ws',
xstate: 'xstate',
axios: 'axios',
'js-cookie': 'Cookies',
react: 'React',
'react-dom': 'ReactDOM',

View File

@@ -1,5 +1,49 @@
# @nhost/dashboard
## 0.11.17
### Patch Changes
- f673adea: fix(dashboard): set correct Content-Type for user creation
- 445d8ef4: chore(deps): bump `@nhost/react-apollo` to 5.0.4
- 445d8ef4: chore(deps): bump `@nhost/nextjs` to 1.13.9
- 0368663d: fix(dashboard): allow permission editing for auth and storage schemas
- Updated dependencies [445d8ef4]
- Updated dependencies [445d8ef4]
- @nhost/react-apollo@5.0.4
- @nhost/nextjs@1.13.9
## 0.11.16
### Patch Changes
- b755e908: fix(dashboard): use correct date for last seen
- 2d9145f9: chore(deps): revert GraphQL client
- 1ddf704c: fix(dashboard): don't show false positive message for failed user creation
- @nhost/react-apollo@5.0.3
- @nhost/nextjs@1.13.8
## 0.11.15
### Patch Changes
- @nhost/react-apollo@5.0.2
- @nhost/nextjs@1.13.7
## 0.11.14
### Patch Changes
- 2cc18dcb: fix(dashboard): prevent permission editor dropdown from being always open
## 0.11.13
### Patch Changes
- 3343a363: chore(dashboard): bump `@testing-library/react` to v14 and `@testing-library/dom` to v9
- @nhost/react-apollo@5.0.1
- @nhost/nextjs@1.13.6
## 0.11.12
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost/dashboard",
"version": "0.11.12",
"version": "0.11.17",
"private": true,
"scripts": {
"preinstall": "npx only-allow pnpm",
@@ -44,7 +44,6 @@
"@tanstack/react-table": "^8.5.30",
"@tanstack/react-virtual": "^3.0.0-beta.23",
"analytics-node": "^6.2.0",
"axios": "^0.27.2",
"bcryptjs": "^2.4.3",
"clsx": "^1.2.1",
"cross-fetch": "^3.1.5",
@@ -98,9 +97,9 @@
"@storybook/manager-webpack5": "^6.5.14",
"@storybook/react": "^6.5.14",
"@storybook/testing-library": "^0.0.13",
"@testing-library/dom": "^8.19.0",
"@testing-library/dom": "^9.0.0",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@testing-library/react": "^14.0.0",
"@testing-library/user-event": "^14.4.3",
"@types/lodash.debounce": "^4.0.7",
"@types/node": "^16.11.7",

View File

@@ -52,7 +52,9 @@ function ControlledAutocomplete(
return (
<Autocomplete
inputValue={typeof field.value === 'string' ? field.value : undefined}
inputValue={
typeof field.value !== 'object' ? field.value.toString() : undefined
}
{...props}
{...field}
ref={mergeRefs([field.ref, ref])}

View File

@@ -328,53 +328,50 @@ function DataBrowserSidebarContent({
className="group"
key={tablePath}
secondaryAction={
!isSelectedSchemaLocked && (
<Dropdown.Root
id="table-management-menu"
onOpen={() => setSidebarMenuTable(tablePath)}
onClose={() => setSidebarMenuTable(undefined)}
<Dropdown.Root
id="table-management-menu"
onOpen={() => setSidebarMenuTable(tablePath)}
onClose={() => setSidebarMenuTable(undefined)}
>
<Dropdown.Trigger
asChild
hideChevron
disabled={tablePath === removableTable}
>
<Dropdown.Trigger
asChild
hideChevron
disabled={tablePath === removableTable}
<IconButton
variant="borderless"
color={isSelected ? 'primary' : 'secondary'}
className={twMerge(
!isSelected &&
'opacity-0 group-focus-within:opacity-100 group-hover:opacity-100 group-active:opacity-100',
)}
>
<IconButton
variant="borderless"
color={isSelected ? 'primary' : 'secondary'}
className={twMerge(
!isSelected &&
'opacity-0 group-focus-within:opacity-100 group-hover:opacity-100 group-active:opacity-100',
)}
<DotsHorizontalIcon />
</IconButton>
</Dropdown.Trigger>
<Dropdown.Content menu PaperProps={{ className: 'w-52' }}>
{isGitHubConnected ? (
<Dropdown.Item
className="grid grid-flow-col items-center gap-2 p-2 text-sm+ font-medium"
onClick={() =>
handleEditPermissionClick(
table.table_schema,
table.table_name,
true,
)
}
>
<DotsHorizontalIcon />
</IconButton>
</Dropdown.Trigger>
<UsersIcon
className="h-4 w-4"
sx={{ color: 'text.secondary' }}
/>
<Dropdown.Content
menu
PaperProps={{ className: 'w-52' }}
>
{isGitHubConnected ? (
<Dropdown.Item
className="grid grid-flow-col items-center gap-2 p-2 text-sm+ font-medium"
onClick={() =>
handleEditPermissionClick(
table.table_schema,
table.table_name,
true,
)
}
>
<UsersIcon
className="h-4 w-4"
sx={{ color: 'text.secondary' }}
/>
<span>View Permissions</span>
</Dropdown.Item>
) : (
[
<span>View Permissions</span>
</Dropdown.Item>
) : (
[
!isSelectedSchemaLocked && (
<Dropdown.Item
key="edit-table"
className="grid grid-flow-col items-center gap-2 p-2 text-sm+ font-medium"
@@ -400,32 +397,38 @@ function DataBrowserSidebarContent({
/>
<span>Edit Table</span>
</Dropdown.Item>,
</Dropdown.Item>
),
!isSelectedSchemaLocked && (
<Divider
key="edit-table-separator"
component="li"
/>,
<Dropdown.Item
key="edit-permissions"
className="grid grid-flow-col items-center gap-2 p-2 text-sm+ font-medium"
onClick={() =>
handleEditPermissionClick(
table.table_schema,
table.table_name,
)
}
>
<UsersIcon
className="h-4 w-4"
sx={{ color: 'text.secondary' }}
/>
/>
),
<Dropdown.Item
key="edit-permissions"
className="grid grid-flow-col items-center gap-2 p-2 text-sm+ font-medium"
onClick={() =>
handleEditPermissionClick(
table.table_schema,
table.table_name,
)
}
>
<UsersIcon
className="h-4 w-4"
sx={{ color: 'text.secondary' }}
/>
<span>Edit Permissions</span>
</Dropdown.Item>,
<span>Edit Permissions</span>
</Dropdown.Item>,
!isSelectedSchemaLocked && (
<Divider
key="edit-permissions-separator"
component="li"
/>,
/>
),
!isSelectedSchemaLocked && (
<Dropdown.Item
key="delete-table"
className="grid grid-flow-col items-center gap-2 p-2 text-sm+ font-medium"
@@ -443,12 +446,12 @@ function DataBrowserSidebarContent({
/>
<span>Delete Table</span>
</Dropdown.Item>,
]
)}
</Dropdown.Content>
</Dropdown.Root>
)
</Dropdown.Item>
),
]
)}
</Dropdown.Content>
</Dropdown.Root>
}
>
<ListItem.Button

View File

@@ -245,7 +245,7 @@ export default function RolePermissionEditorForm({
: permission?.check,
backend_only: values.backendOnly,
computed_fields:
permission?.computed_fields.length > 0
permission?.computed_fields?.length > 0
? permission?.computed_fields
: null,
},

View File

@@ -6,6 +6,7 @@ import Input from '@/ui/v2/Input';
import Radio from '@/ui/v2/Radio';
import RadioGroup from '@/ui/v2/RadioGroup';
import Text from '@/ui/v2/Text';
import type { FocusEvent } from 'react';
import { useState } from 'react';
import { useFormContext } from 'react-hook-form';
import PermissionSettingsSection from './PermissionSettingsSection';
@@ -130,7 +131,13 @@ export default function RowPermissionsSection({
{action === 'select' && (
<Input
{...register('limit')}
{...register('limit', {
onBlur: (event: FocusEvent<HTMLInputElement>) => {
if (!event.target.value) {
setValue('limit', null);
}
},
})}
disabled={disabled}
id="limit"
type="number"

View File

@@ -43,7 +43,10 @@ const baseValidationSchema = Yup.object().shape({
});
const selectValidationSchema = baseValidationSchema.shape({
limit: Yup.number().min(0, 'Limit must not be negative.').nullable(true),
limit: Yup.number()
.label('Limit')
.min(0, 'Limit must not be negative.')
.nullable(true),
allowAggregations: Yup.boolean().nullable(true),
queryRootFields: Yup.array().of(Yup.string()).nullable(true),
subscriptionRootFields: Yup.array().of(Yup.string()).nullable(true),

View File

@@ -6,6 +6,7 @@ import ColumnAutocomplete from '@/components/dataBrowser/ColumnAutocomplete';
import { useCurrentWorkspaceAndApplication } from '@/hooks/useCurrentWorkspaceAndApplication';
import type { HasuraOperator } from '@/types/dataBrowser';
import ActivityIndicator from '@/ui/v2/ActivityIndicator';
import type { AutocompleteOption } from '@/ui/v2/Autocomplete';
import type { InputProps } from '@/ui/v2/Input';
import { inputClasses } from '@/ui/v2/Input';
import Option from '@/ui/v2/Option';
@@ -211,12 +212,13 @@ export default function RuleValueInput({
<ControlledAutocomplete
disabled={disabled}
freeSolo={!isHasuraInput}
autoSelect={!isHasuraInput}
autoHighlight={isHasuraInput}
open
isOptionEqualToValue={(option, value) => {
if (typeof value === 'string') {
return option.value.toLowerCase() === (value as string).toLowerCase();
isOptionEqualToValue={(
option,
value: string | number | AutocompleteOption<string>,
) => {
if (typeof value !== 'object') {
return option.value.toLowerCase() === value?.toString().toLowerCase();
}
return option.value.toLowerCase() === value.value.toLowerCase();

View File

@@ -277,7 +277,7 @@ export default function FilesDataGrid(props: FilesDataGridProps) {
}
if (fileError) {
throw fileError;
throw new Error(fileError.message);
}
triggerToast(`File has been uploaded successfully (${fileMetadata?.id})`);

View File

@@ -67,7 +67,7 @@ export function InviteAnnounce() {
triggerToast('An error occurred when trying to accept the invitation.');
return setSubmitState({
error: res.error,
error: new Error(res.error.message),
loading: false,
});
}

View File

@@ -107,7 +107,7 @@ export default function SystemEnvironmentVariableSettings() {
),
},
{ key: 'NHOST_AUTH_URL', value: appClient.auth.url },
{ key: 'NHOST_GRAPHQL_URL', value: appClient.graphql.url },
{ key: 'NHOST_GRAPHQL_URL', value: appClient.graphql.httpUrl },
{ key: 'NHOST_STORAGE_URL', value: appClient.storage.url },
{ key: 'NHOST_FUNCTIONS_URL', value: appClient.functions.url },
];

View File

@@ -222,9 +222,9 @@ function Autocomplete(
inputValue: inputValue || '',
getOptionLabel: props.getOptionLabel
? props.getOptionLabel
: (option) => {
if (typeof option === 'string') {
return option;
: (option: string | number | AutocompleteOption<string>) => {
if (typeof option !== 'object') {
return option.toString();
}
return option.label ?? option.dropdownLabel;
@@ -284,33 +284,46 @@ function Autocomplete(
}}
PopperComponent={AutocompletePopper}
popupIcon={<ChevronDownIcon sx={{ width: 12, height: 12 }} />}
getOptionLabel={(option) => {
if (typeof option === 'string') {
return option;
getOptionLabel={(
option: string | number | AutocompleteOption<string>,
) => {
if (!option) {
return '';
}
if (typeof option !== 'object') {
return option.toString();
}
return option.label ?? option.dropdownLabel;
}}
isOptionEqualToValue={(option, value) => {
isOptionEqualToValue={(
option,
value: string | number | AutocompleteOption<string>,
) => {
if (!value) {
return false;
}
if (typeof value === 'string') {
return option.value === value;
if (typeof value !== 'object') {
return option.value.toString() === value.toString();
}
return option.value === value.value && option.custom === value.custom;
}}
renderTags={(value, getTagProps) =>
value.map((option, index) => (
<StyledTag
deleteIcon={<XIcon />}
size="small"
label={typeof option === 'string' ? option : option.value}
{...getTagProps({ index })}
/>
))
value.map(
(option: string | number | AutocompleteOption<string>, index) => (
<StyledTag
deleteIcon={<XIcon />}
size="small"
label={
typeof option !== 'object' ? option.toString() : option.value
}
{...getTagProps({ index })}
/>
),
)
}
renderGroup={({ group, key, children }) =>
group ? (
@@ -323,9 +336,12 @@ function Autocomplete(
<div key={key}>{children}</div>
)
}
renderOption={(optionProps, option) => {
if (typeof option === 'string') {
return <OptionBase {...optionProps}>{option}</OptionBase>;
renderOption={(
optionProps,
option: string | number | AutocompleteOption<string>,
) => {
if (typeof option !== 'object') {
return <OptionBase {...optionProps}>{option.toString()}</OptionBase>;
}
return (

View File

@@ -6,7 +6,7 @@ import Input from '@/ui/v2/Input';
import generateAppServiceUrl from '@/utils/common/generateAppServiceUrl';
import { getToastStyleProps } from '@/utils/settings/settingsConstants';
import { yupResolver } from '@hookform/resolvers/yup';
import axios from 'axios';
import fetch from 'cross-fetch';
import { useState } from 'react';
import { FormProvider, useForm } from 'react-hook-form';
import { toast } from 'react-hot-toast';
@@ -79,28 +79,37 @@ export default function CreateUserForm({
try {
await toast.promise(
axios.post(signUpUrl, {
email,
password,
fetch(signUpUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password }),
}).then(async (res) => {
const data = await res.json();
if (res.ok) {
return data;
}
if (res.status === 409) {
setError('email', { message: data?.message });
}
throw new Error(data?.message || 'Something went wrong.');
}),
{
loading: 'Creating user...',
success: 'User created successfully.',
error: 'An error occurred while trying to create the user.',
error: (arg) =>
arg?.message
? `Error: ${arg.message}`
: 'An error occurred while trying to create the user.',
},
getToastStyleProps(),
);
onSuccess?.();
} catch (error) {
if (error.response?.status === 409) {
setError('email', {
message: error.response.data.message,
});
return;
}
setCreateUserFormError(
new Error(error.response.data.message || 'Something went wrong.'),
);
} catch {
// Note: Error is already handled by toast.promise
}
}
@@ -137,7 +146,7 @@ export default function CreateUserForm({
{createUserFormError && (
<Alert
severity="error"
className="grid items-center justify-between grid-flow-col px-4 py-3"
className="grid grid-flow-col items-center justify-between px-4 py-3"
>
<span className="text-left">
<strong>Error:</strong> {createUserFormError.message}

View File

@@ -268,7 +268,7 @@ export default function EditUserForm({
Created At
</InputLabel>
<Text className="col-span-3 font-medium">
{format(new Date(user.createdAt), 'yyyy-MM-dd hh:mm:ss')}
{format(new Date(user.createdAt), 'yyyy-MM-dd HH:mm:ss')}
</Text>
<InputLabel as="h3" className="col-span-1 self-center ">
@@ -276,7 +276,7 @@ export default function EditUserForm({
</InputLabel>
<Text className="col-span-3 font-medium">
{user.lastSeen
? `${format(new Date(user.lastSeen), 'yyyy-mm-dd hh:mm:ss')}`
? `${format(new Date(user.lastSeen), 'yyyy-MM-dd HH:mm:ss')}`
: '-'}
</Text>
</Box>

View File

@@ -28,7 +28,7 @@ The GraphQL API is available at `https://[subdomain].graphql.[region].nhost.run/
## GraphQL Clients for JavaScript
The [Nhost JavaScript client](/reference/javascript) comes with a simple [GraphQL client](/reference/javascript/nhost-js/graphql) that works well for the backend or simple applications.
The [Nhost JavaScript client](/reference/javascript) comes with a simple [GraphQL client](/reference/javascript/graphql) that works well for the backend or simple applications.
When building more complex frontend applications, we recommend using a more advanced GraphQL client such as:

View File

@@ -10,8 +10,8 @@ In this section:
- [Overview](/reference/javascript)
- [Authentication](/reference/javascript/auth)
- [Storage](/reference/javascript/storage)
- [Functions](/reference/javascript/nhost-js/functions)
- [GraphQL](/reference/javascript/nhost-js/graphql)
- [Functions](/reference/javascript/functions)
- [GraphQL](/reference/javascript/graphql)
### React

View File

@@ -0,0 +1,60 @@
---
title: call()
sidebar_label: call()
slug: /reference/javascript/functions/call
description: Use `nhost.functions.call` to call (sending a POST request to) a serverless function.
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/nhost-js/src/clients/functions/index.ts#L55
---
# `call()`
## Overload 1 of 2
Use `nhost.functions.call` to call (sending a POST request to) a serverless function.
:::caution Deprecated
Axios will be replaced by cross-fetch in the near future. Only the headers configuration will be kept.
:::
### Parameters
---
**<span className="parameter-name">url</span>** <span className="optional-status">required</span> <code>string</code>
---
**<span className="parameter-name">data</span>** <span className="optional-status">optional</span> <code>D</code>
---
**<span className="parameter-name">config</span>** <span className="optional-status">optional</span> <code>AxiosRequestConfig&lt;any&gt; &amp; { useAxios: "true" } &amp; [`NhostFunctionCallConfig`](/reference/javascript/functions/types/nhost-function-call-config) &amp; { useAxios: "true" }</code>
---
## Overload 2 of 2
Use `nhost.functions.call` to call (sending a POST request to) a serverless function.
```ts
await nhost.functions.call('send-welcome-email', {
email: 'joe@example.com',
name: 'Joe Doe'
})
```
### Parameters
---
**<span className="parameter-name">url</span>** <span className="optional-status">required</span> <code>string</code>
---
**<span className="parameter-name">data</span>** <span className="optional-status">required</span> <code>D</code>
---
**<span className="parameter-name">config</span>** <span className="optional-status">optional</span> <code>[`NhostFunctionCallConfig`](/reference/javascript/functions/types/nhost-function-call-config) &amp; { useAxios: "false" }</code>
---

View File

@@ -0,0 +1,23 @@
---
title: setAccessToken()
sidebar_label: setAccessToken()
slug: /reference/javascript/functions/set-access-token
description: Use `nhost.functions.setAccessToken` to a set an access token to be used in subsequent functions requests. Note that if you're signin in users with `nhost.auth.signIn()` the access token will be set automatically.
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/nhost-js/src/clients/functions/index.ts#L155
---
# `setAccessToken()`
Use `nhost.functions.setAccessToken` to a set an access token to be used in subsequent functions requests. Note that if you're signin in users with `nhost.auth.signIn()` the access token will be set automatically.
```ts
nhost.functions.setAccessToken('some-access-token')
```
## Parameters
---
**<span className="parameter-name">accessToken</span>** <span className="optional-status">required</span> <code>undefined &#124; string</code>
---

View File

@@ -0,0 +1,22 @@
---
title: NhostFunctionsClient
sidebar_label: Functions
description: No description provided.
slug: /reference/javascript/functions
custom_edit_url: https://github.com/nhost/nhost/edit/main/docs/docs/reference/javascript/functions/index.mdx
---
# `NhostFunctionsClient`
## Parameters
---
**<span className="parameter-name">params</span>** <span className="optional-status">required</span> [`NhostFunctionsConstructorParams`](/reference/javascript/functions/types/nhost-functions-constructor-params)
| Property | Type | Required | Notes |
| :--------------------------------------------------------------------------------------------- | :------------------ | :------: | :---------------------------------------------------------------------------------------- |
| <span className="parameter-name"><span className="light-grey">params.</span>url</span> | <code>string</code> | ✔️ | Serverless Functions endpoint. |
| <span className="parameter-name"><span className="light-grey">params.</span>adminSecret</span> | <code>string</code> | | Admin secret. When set, it is sent as an `x-hasura-admin-secret` header for all requests. |
---

View File

@@ -0,0 +1,19 @@
---
title: NhostFunctionCallConfig
sidebar_label: NhostFunctionCallConfig
description: Subset of RequestInit parameters that are supported by the functions client
displayed_sidebar: referenceSidebar
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/nhost-js/src/clients/functions/types.ts#L41
---
# `NhostFunctionCallConfig`
Subset of RequestInit parameters that are supported by the functions client
## Parameters
---
**<span className="parameter-name">headers</span>** <span className="optional-status">optional</span> <code>Record&lt;string, string&gt;</code>
---

View File

@@ -0,0 +1,15 @@
---
title: NhostFunctionCallResponse
sidebar_label: NhostFunctionCallResponse
description: No description provided.
displayed_sidebar: referenceSidebar
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/nhost-js/src/clients/functions/types.ts#L15
---
# `NhostFunctionCallResponse`
```ts
type NhostFunctionCallResponse =
| { res: { data: T; status: number; statusText: string }; error: null }
| { res: null; error: ErrorPayload }
```

View File

@@ -0,0 +1,25 @@
---
title: NhostFunctionsConstructorParams
sidebar_label: NhostFunctionsConstructorParams
description: No description provided.
displayed_sidebar: referenceSidebar
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/nhost-js/src/clients/functions/types.ts#L4
---
# `NhostFunctionsConstructorParams`
## Parameters
---
**<span className="parameter-name">url</span>** <span className="optional-status">required</span> <code>string</code>
Serverless Functions endpoint.
---
**<span className="parameter-name">adminSecret</span>** <span className="optional-status">optional</span> <code>string</code>
Admin secret. When set, it is sent as an `x-hasura-admin-secret` header for all requests.
---

View File

@@ -10,12 +10,12 @@ The Nhost JavaScript client is the primary way of interacting with your Nhost pr
- [Authentication](/reference/javascript/auth)
- [Storage](/reference/javascript/storage)
- [Functions](/reference/javascript/nhost-js/functions)
- [GraphQL](/reference/javascript/nhost-js/graphql)
- [Functions](/reference/javascript/functions)
- [GraphQL](/reference/javascript/graphql)
## Installation
Install the the Nhost client together with GraphQL:
Install the Nhost client together with GraphQL:
<Tabs groupId="package-manager">
<TabItem value="npm" label="npm" default>

View File

@@ -111,12 +111,12 @@ const sidebars = {
label: 'Functions',
link: {
type: 'doc',
id: 'reference/docgen/javascript/nhost-js/content/nhost-functions-client/index'
id: 'reference/javascript/functions/index'
},
items: [
{
type: 'autogenerated',
dirName: 'reference/docgen/javascript/nhost-js/content/nhost-functions-client/content'
dirName: 'reference/javascript/functions/content'
}
]
},
@@ -125,12 +125,12 @@ const sidebars = {
label: 'GraphQL',
link: {
type: 'doc',
id: 'reference/docgen/javascript/nhost-js/content/nhost-graphql-client/index'
id: 'reference/docgen/javascript/graphql/content/nhost-graphql-client/index'
},
items: [
{
type: 'autogenerated',
dirName: 'reference/docgen/javascript/nhost-js/content/nhost-graphql-client/content'
dirName: 'reference/docgen/javascript/graphql/content/nhost-graphql-client/content'
}
]
}

View File

@@ -1,5 +1,17 @@
# @nhost-examples/codegen-react-apollo
## 0.1.6
### Patch Changes
- 445d8ef4: chore(deps): bump `@nhost/react` to 2.0.3
- 445d8ef4: chore(deps): bump `@nhost/react-apollo` to 5.0.4
- Updated dependencies [445d8ef4]
- Updated dependencies [445d8ef4]
- Updated dependencies [445d8ef4]
- @nhost/react-apollo@5.0.4
- @nhost/react@2.0.3
## 0.1.5
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost-examples/codegen-react-apollo",
"version": "0.1.5",
"version": "0.1.6",
"private": true,
"scripts": {
"codegen": "graphql-codegen",

View File

@@ -1,5 +1,13 @@
# @nhost-examples/codegen-react-query
## 0.1.6
### Patch Changes
- 445d8ef4: chore(deps): bump `@nhost/react` to 2.0.3
- Updated dependencies [445d8ef4]
- @nhost/react@2.0.3
## 0.1.5
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost-examples/codegen-react-query",
"version": "0.1.5",
"version": "0.1.6",
"private": true,
"scripts": {
"codegen": "graphql-codegen",

View File

@@ -1,5 +1,15 @@
# @nhost-examples/react-urql
## 0.0.3
### Patch Changes
- 445d8ef4: chore(deps): bump `@nhost/react` to 2.0.3
- Updated dependencies [445d8ef4]
- Updated dependencies [445d8ef4]
- @nhost/react-urql@2.0.3
- @nhost/react@2.0.3
## 0.0.2
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "@nhost-examples/codegen-react-urql",
"private": true,
"version": "0.0.2",
"version": "0.0.3",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",

View File

@@ -1,5 +1,13 @@
# @nhost-examples/multi-tenant-one-to-many
## 1.0.2
### Patch Changes
- 445d8ef4: chore(deps): bump `@nhost/nhost-js` version to 2.0.3
- Updated dependencies [445d8ef4]
- @nhost/nhost-js@2.0.3
## 1.0.1
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "@nhost-examples/multi-tenant-one-to-many",
"private": true,
"version": "1.0.1",
"version": "1.0.2",
"description": "",
"main": "index.js",
"scripts": {},

View File

@@ -1,5 +1,19 @@
# @nhost-examples/nextjs
## 0.1.6
### Patch Changes
- 445d8ef4: chore(deps): bump `@nhost/react` to 2.0.3
- 445d8ef4: chore(deps): bump `@nhost/react-apollo` to 5.0.4
- 445d8ef4: chore(deps): bump `@nhost/nextjs` to 1.13.9
- Updated dependencies [445d8ef4]
- Updated dependencies [445d8ef4]
- Updated dependencies [445d8ef4]
- @nhost/react-apollo@5.0.4
- @nhost/nextjs@1.13.9
- @nhost/react@2.0.3
## 0.1.5
### Patch Changes

View File

@@ -18,6 +18,7 @@ export function authProtected(Comp) {
if (isLoading) {
return <div>Loading...</div>
}
return <Comp {...props} />
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost-examples/nextjs",
"version": "0.1.5",
"version": "0.1.6",
"private": true,
"scripts": {
"dev": "next dev",
@@ -35,8 +35,8 @@
"@types/react": "18.0.25",
"@xstate/inspect": "^0.6.2",
"eslint-config-next": "12.0.10",
"typescript": "4.5.5",
"typescript": "^4.8.2",
"ws": "^8.8.1",
"xstate": "^4.33.5"
}
}
}

View File

@@ -1,5 +1,17 @@
# @nhost-examples/react-apollo
## 0.1.8
### Patch Changes
- 445d8ef4: chore(deps): bump `@nhost/react` to 2.0.3
- 445d8ef4: chore(deps): bump `@nhost/react-apollo` to 5.0.4
- Updated dependencies [445d8ef4]
- Updated dependencies [445d8ef4]
- Updated dependencies [445d8ef4]
- @nhost/react-apollo@5.0.4
- @nhost/react@2.0.3
## 0.1.7
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost-examples/react-apollo",
"version": "0.1.7",
"version": "0.1.8",
"private": true,
"dependencies": {
"@apollo/client": "^3.6.9",

View File

@@ -1,5 +1,13 @@
# @nhost-examples/react-gqty
## 0.0.5
### Patch Changes
- 445d8ef4: chore(deps): bump `@nhost/react` to 2.0.3
- Updated dependencies [445d8ef4]
- @nhost/react@2.0.3
## 0.0.4
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "@nhost-examples/react-gqty",
"private": true,
"version": "0.0.4",
"version": "0.0.5",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -1,5 +1,14 @@
# @nhost-examples/vue-apollo
## 0.0.6
### Patch Changes
- 445d8ef4: chore(deps): bump `@nhost/apollo` to 5.0.3
- Updated dependencies [445d8ef4]
- @nhost/apollo@5.0.3
- @nhost/vue@1.13.9
## 0.0.5
### Patch Changes

View File

@@ -1,10 +1,10 @@
{
"name": "@nhost-examples/vue-apollo",
"private": true,
"version": "0.0.5",
"version": "0.0.6",
"scripts": {
"dev": "vite",
"build": "vue-tsc --noEmit && vite build",
"build": "vite build",
"preview": "vite preview",
"prettier": "prettier --check src/",
"prettier:fix": "prettier --write src/",
@@ -34,7 +34,7 @@
"@vitejs/plugin-vue": "^4.0.0",
"@xstate/inspect": "^0.6.2",
"sass": "1.32.0",
"typescript": "^4.8.4",
"typescript": "4.9.4",
"vite": "^4.0.2",
"vue-tsc": "^0.38.9"
},

View File

@@ -1,5 +1,14 @@
# @nhost-examples/vue-quickstart
## 0.0.5
### Patch Changes
- 445d8ef4: chore(deps): bump `@nhost/apollo` to 5.0.3
- Updated dependencies [445d8ef4]
- @nhost/apollo@5.0.3
- @nhost/vue@1.13.9
## 0.0.4
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost-examples/vue-quickstart",
"version": "0.0.4",
"version": "0.0.5",
"private": true,
"scripts": {
"build": "vite build",

View File

@@ -1,5 +1,36 @@
# @nhost/apollo
## 5.0.3
### Patch Changes
- 445d8ef4: chore(deps): bump `@nhost/nhost-js` version to 2.0.3
- Updated dependencies [445d8ef4]
- @nhost/nhost-js@2.0.3
## 5.0.2
### Patch Changes
- Updated dependencies [2d9145f9]
- @nhost/nhost-js@2.0.2
## 5.0.1
### Patch Changes
- @nhost/nhost-js@2.0.1
## 5.0.0
### Patch Changes
- Updated dependencies [c9d2d31a]
- Updated dependencies [80bbd3a1]
- Updated dependencies [80bbd3a1]
- Updated dependencies [2949ff0f]
- @nhost/nhost-js@2.0.0
## 4.13.4
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost/apollo",
"version": "4.13.4",
"version": "5.0.3",
"description": "Nhost Apollo Client library",
"license": "MIT",
"keywords": [

View File

@@ -1,5 +1,38 @@
# @nhost/react-apollo
## 5.0.4
### Patch Changes
- 445d8ef4: chore(deps): bump `@nhost/react` to 2.0.3
- 445d8ef4: chore(deps): bump `@nhost/apollo` to 5.0.3
- Updated dependencies [445d8ef4]
- @nhost/apollo@5.0.3
- @nhost/react@2.0.3
## 5.0.3
### Patch Changes
- @nhost/apollo@5.0.2
- @nhost/react@2.0.2
## 5.0.2
### Patch Changes
- @nhost/apollo@5.0.1
- @nhost/react@2.0.1
## 5.0.1
### Patch Changes
- Updated dependencies [19b11d40]
- Updated dependencies [19b11d40]
- @nhost/react@2.0.0
- @nhost/apollo@5.0.0
## 4.13.5
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost/react-apollo",
"version": "4.13.5",
"version": "5.0.4",
"description": "Nhost React Apollo client",
"license": "MIT",
"keywords": [

View File

@@ -1,5 +1,33 @@
# @nhost/react-urql
## 2.0.3
### Patch Changes
- 445d8ef4: chore(deps): bump `@nhost/react` to 2.0.3
- Updated dependencies [445d8ef4]
- @nhost/react@2.0.3
## 2.0.2
### Patch Changes
- @nhost/react@2.0.2
## 2.0.1
### Patch Changes
- @nhost/react@2.0.1
## 2.0.0
### Patch Changes
- Updated dependencies [19b11d40]
- Updated dependencies [19b11d40]
- @nhost/react@2.0.0
## 1.0.5
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost/react-urql",
"version": "1.0.5",
"version": "2.0.3",
"description": "Nhost React URQL client",
"license": "MIT",
"keywords": [

View File

@@ -0,0 +1,9 @@
const base = require('../../config/.eslintrc.js')
module.exports = {
...base,
parserOptions: {
project: 'tsconfig.json',
tsconfigRootDir: __dirname
},
ignorePatterns: [...base.ignorePatterns, 'functions/**/*.ts']
}

View File

@@ -0,0 +1,14 @@
# @nhost/graphql-js
## 0.0.3
### Patch Changes
- 2d9145f9: chore(deps): revert GraphQL client
## 0.0.2
### Patch Changes
- 2200a0ed: Correct type inference on snake case operations
- 3b48a627: Improve readme instructions

View File

@@ -0,0 +1,16 @@
<h1 align="center">@nhost/graphql-js</h1>
<h2 align="center">Nhost GraphQL client</h2>
<p align="center">
<img alt="npm" src="https://img.shields.io/npm/v/@nhost/graphql-js">
<img alt="npm" src="https://img.shields.io/npm/dm/@nhost/graphql-js">
<a href="LICENSE">
<img src="https://img.shields.io/badge/license-MIT-yellow.svg" alt="license: MIT" />
</a>
</p>
Nhost GraphQL client.
## Documentation
[https://docs.nhost.io/reference/javascript/graphql](https://docs.nhost.io/reference/javascript/graphql)

View File

@@ -0,0 +1,10 @@
{
"title": "GraphQL",
"path": "./.docgen/graphql.json",
"output": "../../docs/docs/reference/docgen/javascript/graphql",
"root": "reference/docgen/javascript/graphql",
"slug": "/reference/javascript/graphql",
"sidebarConfig": "referenceSidebar",
"baseEditUrl": "https://github.com/nhost/nhost/edit/main/packages",
"cleanup": true
}

View File

@@ -7,8 +7,8 @@
"sort": [
"source-order"
],
"json": "./.docgen/nhost-js.json",
"name": "Nhost JS",
"json": "./.docgen/graphql.json",
"name": "GraphQL",
"readme": "none",
"githubPages": false,
"cleanOutputDir": false,
@@ -26,6 +26,9 @@
"@nhost/hasura-storage-js": [
"../hasura-storage-js/src/index.ts"
],
"@nhost/graphql-js": [
"../graphql-js/src/index.ts"
],
"@nhost/nextjs": [
"../nextjs/src/index.ts"
],

View File

@@ -0,0 +1,65 @@
{
"name": "@nhost/graphql-js",
"version": "0.0.3",
"description": "Nhost GraphQL client",
"license": "MIT",
"keywords": [
"nhost",
"hasura",
"graphql"
],
"author": "Nhost",
"homepage": "https://nhost.io",
"bugs": "https://github.com/nhost/nhost/issues",
"repository": {
"type": "git",
"url": "https://github.com/nhost/nhost.git"
},
"main": "dist/index.cjs.js",
"module": "dist/index.esm.js",
"types": "dist/index.d.ts",
"source": "src/index.ts",
"files": [
"dist",
"umd",
"README.md"
],
"exports": {
"./package.json": "./package.json",
".": {
"import": {
"node": "./dist/index.cjs.js",
"default": "./dist/index.esm.js"
},
"require": "./dist/index.cjs.js"
}
},
"publishConfig": {
"access": "public"
},
"scripts": {
"dev": "vite build",
"build": "run-p build:lib build:umd",
"build:lib": "vite build",
"build:umd": "vite build --config ../../config/vite.lib.umd.config.js",
"prettier": "prettier --check src/",
"prettier:fix": "prettier --write src/",
"lint": "eslint . --ext .ts,.tsx",
"lint:fix": "eslint . --ext .ts,.tsx --fix",
"verify": "run-p prettier lint",
"verify:fix": "run-p prettier:fix lint:fix",
"typedoc": "typedoc --options ./graphql.typedoc.json --tsconfig ./typedoc.tsconfig.json",
"docgen": "pnpm typedoc && docgen --config ./graphql.docgen.json"
},
"dependencies": {
"@graphql-typed-document-node/core": "^3.1.1",
"cross-fetch": "^3.1.5"
},
"peerDependencies": {
"graphql": "^14.0.0 || ^15.0.0 || ^16.0.0"
},
"devDependencies": {
"@nhost/docgen": "workspace:*",
"graphql": "16.6.0"
}
}

View File

@@ -0,0 +1,205 @@
import { TypedDocumentNode } from '@graphql-typed-document-node/core'
import fetch from 'cross-fetch'
import { parseRequestArgs } from './parse-args'
import { resolveRequestDocument } from './resolve-request-document'
import {
NhostGraphqlConstructorParams,
NhostGraphqlRequestConfig,
NhostGraphqlRequestResponse,
RemoveIndex,
RequestDocument,
RequestOptions,
Variables
} from './types'
/**
* @alias GraphQL
*/
export class NhostGraphqlClient {
readonly _url: string
private accessToken: string | null
private adminSecret?: string
constructor(params: NhostGraphqlConstructorParams) {
const { url, adminSecret } = params
this._url = url
this.accessToken = null
this.adminSecret = adminSecret
}
/**
* Use `nhost.graphql.request` to send a GraphQL request. For more serious GraphQL usage we recommend using a GraphQL client such as Apollo Client (https://www.apollographql.com/docs/react).
*
* @example
* ```ts
* const CUSTOMERS = gql`
* query {
* customers {
* id
* name
* }
* }
* `
* const { data, error } = await nhost.graphql.request(CUSTOMERS)
* ```
*
* @docs https://docs.nhost.io/reference/javascript/graphql/request
*/
request<T = any, V = Variables>(
document: RequestDocument | TypedDocumentNode<T, V>,
...variablesAndRequestHeaders: V extends Record<any, never>
? [variables?: V, config?: NhostGraphqlRequestConfig]
: keyof RemoveIndex<V> extends never
? [variables?: V, config?: NhostGraphqlRequestConfig]
: [variables: V, config?: NhostGraphqlRequestConfig]
): Promise<NhostGraphqlRequestResponse<T>>
async request<T = any, V extends Variables = Variables>(
options: RequestOptions<V, T>
): Promise<NhostGraphqlRequestResponse<T>>
async request<T = any, V extends Variables = Variables>(
documentOrOptions: RequestDocument | TypedDocumentNode<T, V> | RequestOptions<V>,
...variablesAndRequestHeaders: V extends Record<any, never>
? [variables?: V, config?: NhostGraphqlRequestConfig]
: keyof RemoveIndex<V> extends never
? [variables?: V, config?: NhostGraphqlRequestConfig]
: [variables: V, config?: NhostGraphqlRequestConfig]
): Promise<NhostGraphqlRequestResponse<T>> {
const [variables, config] = variablesAndRequestHeaders
const requestOptions = parseRequestArgs(documentOrOptions, variables, config)
const { headers, ...otherOptions } = config || {}
const { query, operationName } = resolveRequestDocument(requestOptions.document)
try {
const response = await fetch(this.httpUrl, {
method: 'POST',
body: JSON.stringify({
operationName,
query,
variables
}),
headers: {
'Content-Type': 'application/json',
...this.generateAccessTokenHeaders(),
...headers
},
...otherOptions
})
if (!response.ok) {
return {
data: null,
error: {
error: response.statusText,
message: response.statusText,
status: response.status
}
}
}
const { data, errors } = await response.json()
if (errors) {
return {
data: null,
error: errors
}
}
if (typeof data !== 'object' || Array.isArray(data) || data === null) {
return {
data: null,
error: {
error: 'invalid-response',
message: 'incorrect response data from GraphQL server',
status: 0
}
}
}
return { data, error: null }
} catch (e) {
const error = e as Error
return {
data: null,
error: {
message: error.message,
status: error.name === 'AbortError' ? 0 : 500,
error: error.name === 'AbortError' ? 'abort-error' : 'unknown'
}
}
}
}
/**
* Use `nhost.graphql.httpUrl` to get the GraphQL HTTP URL.
* @example
* ```ts
* const url = nhost.graphql.httpUrl;
* ```
*
* @docs https://docs.nhost.io/reference/javascript/graphql/get-http-url
*/
get httpUrl(): string {
return this._url
}
/**
* Use `nhost.graphql.wsUrl` to get the GraphQL WebSocket URL.
* @example
* ```ts
* const url = nhost.graphql.wsUrl;
* ```
*
* @docs https://docs.nhost.io/reference/javascript/graphql/get-ws-url
*/
get wsUrl(): string {
return this._url.replace(/^(http)(s?):\/\//, 'ws$2://')
}
/**
* Use `nhost.graphql.url` to get the GraphQL URL.
* @deprecated Use `nhost.graphql.httpUrl` and `nhost.graphql.wsUrl` instead.
*/
get url(): string {
return this._url
}
/**
* Use `nhost.graphql.getUrl()` to get the GraphQL URL.
* @deprecated Use `nhost.graphql.httpUrl` and `nhost.graphql.wsUrl` instead.
*/
getUrl(): string {
return this._url
}
/**
* Use `nhost.graphql.setAccessToken` to a set an access token to be used in subsequent graphql requests. Note that if you're signin in users with `nhost.auth.signIn()` the access token will be set automatically.
*
* @example
* ```ts
* nhost.graphql.setAccessToken('some-access-token')
* ```
*
* @docs https://docs.nhost.io/reference/javascript/graphql/set-access-token
*/
setAccessToken(accessToken: string | undefined) {
if (!accessToken) {
this.accessToken = null
return
}
this.accessToken = accessToken
}
private generateAccessTokenHeaders(): NhostGraphqlRequestConfig['headers'] {
if (this.adminSecret) {
return {
'x-hasura-admin-secret': this.adminSecret
}
}
if (this.accessToken) {
return {
Authorization: `Bearer ${this.accessToken}`
}
}
return {}
}
}

View File

@@ -0,0 +1 @@
export * from './client'

View File

@@ -0,0 +1,17 @@
import { RequestDocument, RequestOptions, Variables } from './types'
export function parseRequestArgs<V extends Variables = Variables>(
documentOrOptions: RequestDocument | RequestOptions<V>,
variables?: V,
config?: RequestInit
): RequestOptions<V> {
return (
(documentOrOptions as RequestOptions<V>).document
? documentOrOptions
: {
document: documentOrOptions,
variables,
config
}
) as RequestOptions<V>
}

View File

@@ -0,0 +1,42 @@
import { DocumentNode, OperationDefinitionNode, parse, print } from 'graphql'
import { RequestDocument } from './types'
/**
* helpers
*/
function extractOperationName(document: DocumentNode): string | undefined {
let operationName = undefined
const operationDefinitions = document.definitions.filter(
(definition) => definition.kind === 'OperationDefinition'
) as OperationDefinitionNode[]
if (operationDefinitions.length === 1) {
operationName = operationDefinitions[0].name?.value
}
return operationName
}
export function resolveRequestDocument(document: RequestDocument): {
query: string
operationName?: string
} {
if (typeof document === 'string') {
let operationName = undefined
try {
const parsedDocument = parse(document)
operationName = extractOperationName(parsedDocument)
} catch (err) {
// Failed parsing the document, the operationName will be undefined
}
return { query: document, operationName }
}
const operationName = extractOperationName(document)
return { query: print(document), operationName }
}

View File

@@ -0,0 +1,52 @@
import { TypedDocumentNode } from '@graphql-typed-document-node/core'
import { DocumentNode, GraphQLError } from 'graphql'
// TODO shared with other packages
export type ErrorPayload = {
error: string
status: number
message: string
}
export type RequestOptions<V extends Variables = Variables, T = any> = NhostGraphqlRequestConfig & {
document: RequestDocument | TypedDocumentNode<T, V>
} & (V extends Record<any, never>
? { variables?: V }
: keyof RemoveIndex<V> extends never
? { variables?: V }
: { variables: V })
export type Variables = { [key: string]: any }
export type RequestDocument = string | DocumentNode
export type RemoveIndex<T> = {
[K in keyof T as string extends K ? never : number extends K ? never : K]: T[K]
}
export interface NhostGraphqlConstructorParams {
/**
* GraphQL endpoint.
*/
url: string
/**
* Admin secret. When set, it is sent as an `x-hasura-admin-secret` header for all requests.
*/
adminSecret?: string
}
export type NhostGraphqlRequestResponse<T = unknown> =
| {
data: null
error: GraphQLError[] | ErrorPayload
}
| {
data: T
error: null
}
/** Subset of RequestInit parameters that are supported by the graphql client */
export interface NhostGraphqlRequestConfig {
headers?: Record<string, string>
/** @deprecated Axios has been replaced by cross-fetch. You should now remove this option. */
useAxios?: false
}

View File

@@ -0,0 +1,4 @@
{
"extends": "../../config/tsconfig.base.json",
"include": ["src"]
}

View File

@@ -0,0 +1,7 @@
{
"extends": "./tsconfig.json",
"$schema": "https://json.schemastore.org/tsconfig",
"include": [
"tests/types"
]
}

View File

@@ -0,0 +1,13 @@
import { defineConfig } from 'vite'
import baseConfig from '../../config/vite.lib.config'
export default defineConfig({
...baseConfig,
test: {
...(baseConfig.test || {}),
include: [`tests/e2e/**/*.{spec,test}.{ts,tsx}`],
testTimeout: 30000,
environment: 'node'
}
})

View File

@@ -0,0 +1,5 @@
import { defineConfig } from 'vite'
import baseConfig from '../../config/vite.lib.config'
export default defineConfig(baseConfig)

View File

@@ -0,0 +1,13 @@
import { defineConfig } from 'vite'
import baseConfig from '../../config/vite.lib.config'
export default defineConfig({
...baseConfig,
test: {
...(baseConfig.test || {}),
include: [`tests/unit/**/*.{spec,test}.{ts,tsx}`],
testTimeout: 30000,
environment: 'node'
}
})

View File

@@ -1,5 +1,41 @@
# @nhost/hasura-auth-js
## 2.0.0
### Major Changes
- 19b11d40: Remove the deprecated `AuthCookieClient` and `AuthClientSSR` constructors
Use the `clientStorageType` option instead:
```ts
const nhost = new NhostClient({ clientStorageType: 'cookie' })
```
- 19b11d40: Remove the deprecated `nhost.auth.getJWTToken` method
Use `nhost.auth.getAccessToken()` instead.
- 19b11d40: Remove the deprecated `autoLogin` option
Use `autoSignIn` instead:
```ts
const nhost = new NhostClient({ autoSignIn: true })
```
- 19b11d40: Remove the deprecated `clientStorageGetter` and `clientStorageSetter` options
Use `clientStorageType` and `clientStorage` instead:
```ts
const nhost = new NhostClient({ clientStorageType: 'custom', clientStorage: TODO })
```
### Minor Changes
- 80bbd3a1: Replace `axios` by `cross-fetch`
## 1.12.4
### Patch Changes

View File

@@ -1,7 +1,6 @@
import { faker } from '@faker-js/faker'
import axios from 'axios'
import fetch from 'cross-fetch'
import { afterEach, describe, expect, it } from 'vitest'
import { auth, getHtmlLink, mailhog } from './helpers'
describe('emails', () => {
@@ -22,10 +21,11 @@ describe('emails', () => {
const verifyEmailLink = await getHtmlLink(email, 'verifyEmail')
// verify email
await axios.get(verifyEmailLink, {
maxRedirects: 0,
validateStatus: (status) => status === 302
})
try {
await fetch(verifyEmailLink, { method: 'GET', redirect: 'follow' })
} catch {
// ignore
}
const signInA = await auth.signIn({
email,
@@ -46,10 +46,11 @@ describe('emails', () => {
const changeEmailLink = await getHtmlLink(email, 'emailConfirmChange')
// verify email
await axios.get(changeEmailLink, {
maxRedirects: 0,
validateStatus: (status) => status === 302
})
try {
await fetch(changeEmailLink, { method: 'GET', redirect: 'follow' })
} catch {
// ignore
}
})
it('reset email verification', async () => {
@@ -71,28 +72,21 @@ describe('emails', () => {
expect(signInA.error).toBeTruthy()
expect(signInA.session).toBeNull()
await mailhog.deleteAll()
await auth.sendVerificationEmail({ email })
// make sure onle a single message exists
const messages = await mailhog.messages()
if (!messages) {
throw new Error('no messages')
}
expect(messages.count).toBe(1)
const message = await mailhog.latestTo(email)
expect(message?.subject).toBe('Verify your email')
// test email link
// get verify email link
const verifyEmailLink = await getHtmlLink(email, 'verifyEmail')
// verify email
await axios.get(verifyEmailLink, {
maxRedirects: 0,
validateStatus: (status) => status === 302
})
try {
await fetch(verifyEmailLink, { method: 'GET', redirect: 'follow' })
} catch {
// ignore
}
// sign in should work
const signInB = await auth.signIn({

View File

@@ -1,8 +1,7 @@
import axios from 'axios'
import { load } from 'cheerio'
import fetch from 'cross-fetch'
import createMailhogClient from 'mailhog'
import { expect } from 'vitest'
import { HasuraAuthClient, SignUpParams } from '../src'
const AUTH_BACKEND_URL = 'http://localhost:1337/v1/auth'
@@ -43,11 +42,11 @@ export const signUpAndVerifyUser = async (params: SignUpParams) => {
// get verify email link
const verifyEmailLink = await getHtmlLink(email, 'verifyEmail')
// verify email
await axios.get(verifyEmailLink, {
maxRedirects: 0,
validateStatus: (status) => status === 302
})
try {
await fetch(verifyEmailLink, { method: 'GET', redirect: 'follow' })
} catch {
// ignore
}
}
export const signUpAndInUser = async (params: SignUpParams) => {
@@ -60,10 +59,11 @@ export const signUpAndInUser = async (params: SignUpParams) => {
const verifyEmailLink = await getHtmlLink(email, 'verifyEmail')
// verify email
await axios.get(verifyEmailLink, {
maxRedirects: 0,
validateStatus: (status) => status === 302
})
try {
await fetch(verifyEmailLink, { method: 'GET', redirect: 'follow' })
} catch {
// ignore
}
// sign in
const { session, error } = await auth.signIn({ email, password })

View File

@@ -1,7 +1,6 @@
import { faker } from '@faker-js/faker'
import axios from 'axios'
import fetch from 'cross-fetch'
import { afterEach, describe, expect, it } from 'vitest'
import { auth, getHtmlLink, signUpAndInUser, signUpAndVerifyUser } from './helpers'
describe('passwords', () => {
@@ -48,9 +47,10 @@ describe('passwords', () => {
const resetPasswordLink = await getHtmlLink(email, 'passwordReset')
// verify email
await axios.get(resetPasswordLink, {
maxRedirects: 0,
validateStatus: (status) => status === 302
})
try {
await fetch(resetPasswordLink, { method: 'GET', redirect: 'follow' })
} catch {
// ignore
}
})
})

View File

@@ -1,8 +1,7 @@
import { faker } from '@faker-js/faker'
import axios from 'axios'
import fetch from 'cross-fetch'
import { afterEach, describe, expect, it } from 'vitest'
import { USER_ALREADY_SIGNED_IN } from '../src'
import { auth, getHtmlLink, signUpAndInUser, signUpAndVerifyUser } from './helpers'
describe('sign-in', () => {
@@ -57,10 +56,11 @@ describe('sign-in', () => {
const emailLink = await getHtmlLink(email, 'signinPasswordless')
// verify email
await axios.get(emailLink, {
maxRedirects: 0,
validateStatus: (status) => status === 302
})
try {
await fetch(emailLink, { method: 'GET', redirect: 'follow' })
} catch {
// ignore
}
})
it('should not be possible to sign in with email+password in when already authenticated', async () => {

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost/hasura-auth-js",
"version": "1.12.4",
"version": "2.0.0",
"description": "Hasura-auth client",
"license": "MIT",
"keywords": [
@@ -63,7 +63,7 @@
},
"dependencies": {
"@simplewebauthn/browser": "^6.0.0",
"axios": "^1.2.0",
"cross-fetch": "^3.1.5",
"js-cookie": "^3.0.1",
"jwt-decode": "^3.1.2",
"xstate": "^4.33.5"

View File

@@ -75,11 +75,8 @@ export class HasuraAuthClient {
url,
autoRefreshToken = true,
autoSignIn = true,
autoLogin,
clientStorage,
clientStorageType,
clientStorageGetter,
clientStorageSetter,
refreshIntervalTime,
start = true
}: NhostAuthConstructorParams) {
@@ -88,12 +85,10 @@ export class HasuraAuthClient {
backendUrl: url,
clientUrl: (typeof window !== 'undefined' && window.location?.origin) || '',
autoRefreshToken,
autoSignIn: typeof autoLogin === 'boolean' ? autoLogin : autoSignIn,
autoSignIn,
start,
clientStorage,
clientStorageType,
clientStorageGetter,
clientStorageSetter,
refreshIntervalTime
})
}
@@ -552,16 +547,6 @@ export class HasuraAuthClient {
return { isAuthenticated: this.isAuthenticated(), isLoading: false, connectionAttempts }
}
/**
* @internal
* @deprecated Use `nhost.auth.getAccessToken()` instead.
* @docs https://docs.nhost.io/reference/javascript/auth/get-access-token
*/
getJWTToken(): string | undefined {
return this.getAccessToken()
}
/**
* Use `nhost.auth.getAccessToken` to get the access token of the user.
*

View File

@@ -5,7 +5,6 @@ import type {
PublicKeyCredentialRequestOptionsJSON,
RegistrationCredentialJSON
} from '@simplewebauthn/typescript-types'
import type { AxiosRequestConfig } from 'axios'
import { assign, createMachine, InterpreterFrom, send } from 'xstate'
import {
NHOST_JWT_EXPIRES_AT_KEY,
@@ -42,16 +41,14 @@ import {
} from '../../types'
import {
getParameterByName,
nhostApiClient,
removeParameterFromWindow,
rewriteRedirectTo
} from '../../utils'
import {
isValidEmail,
isValidPassword,
isValidPhoneNumber,
isValidTicket
} from '../../utils/validators'
isValidTicket,
postFetch,
removeParameterFromWindow,
rewriteRedirectTo
} from '../../utils'
import { AuthContext, INITIAL_MACHINE_CONTEXT } from './context'
import { AuthEvents } from './events'
@@ -81,23 +78,20 @@ type AuthServices = {
export const createAuthMachine = ({
backendUrl,
clientUrl,
clientStorageGetter,
clientStorageSetter,
clientStorageType = 'web',
clientStorage,
refreshIntervalTime,
autoRefreshToken = true,
autoSignIn = true
}: AuthMachineOptions) => {
const storageGetter = clientStorageGetter || localStorageGetter(clientStorageType, clientStorage)
const storageSetter = clientStorageSetter || localStorageSetter(clientStorageType, clientStorage)
const api = nhostApiClient(backendUrl)
const storageGetter = localStorageGetter(clientStorageType, clientStorage)
const storageSetter = localStorageSetter(clientStorageType, clientStorage)
const postRequest = async <T = any, D = any>(
url: string,
data?: D,
config?: AxiosRequestConfig<D>
token?: string | null
): Promise<T> => {
const result = await api.post(url, data, config)
const result = await postFetch<T>(`${backendUrl}${url}`, data, token)
return result.data
}
@@ -704,11 +698,7 @@ export const createAuthMachine = ({
phoneNumber,
options: rewriteRedirectTo(clientUrl, options)
},
{
headers: {
authorization: `Bearer ${context.accessToken.value}`
}
}
context.accessToken.value
)
} else {
return postRequest('/signin/passwordless/sms', {
@@ -739,11 +729,7 @@ export const createAuthMachine = ({
email,
options: rewriteRedirectTo(clientUrl, options)
},
{
headers: {
authorization: `Bearer ${context.accessToken.value}`
}
}
context.accessToken.value
)
} else {
return postRequest('/signin/passwordless/email', {
@@ -811,11 +797,7 @@ export const createAuthMachine = ({
password,
options: rewriteRedirectTo(clientUrl, options)
},
{
headers: {
authorization: `Bearer ${context.accessToken.value}`
}
}
context.accessToken.value
)
} else {
return postRequest<SignUpResponse>('/signup/email-password', {

View File

@@ -1,9 +1,8 @@
import { assign, createMachine, send } from 'xstate'
import { INVALID_EMAIL_ERROR } from '../errors'
import { AuthClient } from '../internal-client'
import { ChangeEmailOptions, ChangeEmailResponse, ErrorPayload } from '../types'
import { nhostApiClient, rewriteRedirectTo } from '../utils'
import { postFetch, rewriteRedirectTo } from '../utils'
import { isValidEmail } from '../utils/validators'
export type ChangeEmailContext = {
@@ -26,7 +25,6 @@ export type ChangeEmailServices = {
export type ChangeEmailMachine = ReturnType<typeof createChangeEmailMachine>
export const createChangeEmailMachine = ({ backendUrl, clientUrl, interpreter }: AuthClient) => {
const api = nhostApiClient(backendUrl)
return createMachine(
{
schema: {
@@ -86,17 +84,10 @@ export const createChangeEmailMachine = ({ backendUrl, clientUrl, interpreter }:
},
services: {
requestChange: async (_, { email, options }) => {
const res = await api.post(
'/user/email/change',
{
newEmail: email,
options: rewriteRedirectTo(clientUrl, options)
},
{
headers: {
authorization: `Bearer ${interpreter?.getSnapshot().context.accessToken.value}`
}
}
const res = await postFetch(
`${backendUrl}/user/email/change`,
{ newEmail: email, options: rewriteRedirectTo(clientUrl, options) },
interpreter?.getSnapshot().context.accessToken.value
)
return res.data
}

View File

@@ -1,9 +1,8 @@
import { assign, createMachine, send } from 'xstate'
import { INVALID_PASSWORD_ERROR } from '../errors'
import { AuthClient } from '../internal-client'
import { ChangePasswordResponse, ErrorPayload } from '../types'
import { nhostApiClient } from '../utils'
import { postFetch } from '../utils'
import { isValidPassword } from '../utils/validators'
export type ChangePasswordContext = {
@@ -25,7 +24,6 @@ export type ChangePasswordServices = {
export type ChangePasswordMachine = ReturnType<typeof createChangePasswordMachine>
export const createChangePasswordMachine = ({ backendUrl, interpreter }: AuthClient) => {
const api = nhostApiClient(backendUrl)
return createMachine(
{
schema: {
@@ -84,14 +82,10 @@ export const createChangePasswordMachine = ({ backendUrl, interpreter }: AuthCli
},
services: {
requestChange: (_, { password, ticket }) =>
api.post<string, ChangePasswordResponse>(
'/user/password',
postFetch<ChangePasswordResponse>(
`${backendUrl}/user/password`,
{ newPassword: password, ticket: ticket },
{
headers: {
authorization: `Bearer ${interpreter?.getSnapshot().context.accessToken.value}`
}
}
interpreter?.getSnapshot().context.accessToken.value
)
}
}

View File

@@ -1,9 +1,8 @@
import { assign, createMachine, send } from 'xstate'
import { INVALID_MFA_CODE_ERROR, INVALID_MFA_TYPE_ERROR } from '../errors'
import { AuthClient } from '../internal-client'
import { ErrorPayload } from '../types'
import { nhostApiClient } from '../utils'
import { getFetch, postFetch } from '../utils'
export type EnableMfaContext = {
error: ErrorPayload | null
@@ -28,7 +27,6 @@ export type EnableMfaEvents =
export type EnableMfadMachine = ReturnType<typeof createEnableMfaMachine>
export const createEnableMfaMachine = ({ backendUrl, interpreter }: AuthClient) => {
const api = nhostApiClient(backendUrl)
return createMachine(
{
schema: {
@@ -107,7 +105,10 @@ export const createEnableMfaMachine = ({ backendUrl, interpreter }: AuthClient)
imageUrl: (_, { data: { imageUrl } }: any) => imageUrl,
secret: (_, { data: { totpSecret } }: any) => totpSecret
}),
reportError: send((ctx) => ({ type: 'ERROR', error: ctx.error })),
reportError: send((ctx, event) => {
console.log('REPORT', ctx, event)
return { type: 'ERROR', error: ctx.error }
}),
reportSuccess: send('SUCCESS'),
reportGeneratedSuccess: send('GENERATED'),
reportGeneratedError: send((ctx) => ({ type: 'GENERATED_ERROR', error: ctx.error }))
@@ -118,25 +119,17 @@ export const createEnableMfaMachine = ({ backendUrl, interpreter }: AuthClient)
},
services: {
generate: async (_) => {
const { data } = await api.get('/mfa/totp/generate', {
headers: {
authorization: `Bearer ${interpreter?.getSnapshot().context.accessToken.value}`
}
})
const { data } = await getFetch(
`${backendUrl}/mfa/totp/generate`,
interpreter?.getSnapshot().context.accessToken.value
)
return data
},
activate: (_, { code, activeMfaType }) =>
api.post(
'/user/mfa',
{
code,
activeMfaType
},
{
headers: {
authorization: `Bearer ${interpreter?.getSnapshot().context.accessToken.value}`
}
}
postFetch(
`${backendUrl}/user/mfa`,
{ code, activeMfaType },
interpreter?.getSnapshot().context.accessToken.value
)
}
}

View File

@@ -1,9 +1,8 @@
import { assign, createMachine, send } from 'xstate'
import { INVALID_EMAIL_ERROR } from '../errors'
import { AuthClient } from '../internal-client'
import { ErrorPayload, ResetPasswordOptions, ResetPasswordResponse } from '../types'
import { nhostApiClient, rewriteRedirectTo } from '../utils'
import { postFetch, rewriteRedirectTo } from '../utils'
import { isValidEmail } from '../utils/validators'
export type ResetPasswordContext = {
@@ -25,7 +24,6 @@ export type ResetPasswordServices = {
export type ResetPasswordMachine = ReturnType<typeof createResetPasswordMachine>
export const createResetPasswordMachine = ({ backendUrl, clientUrl }: AuthClient) => {
const api = nhostApiClient(backendUrl)
return createMachine(
{
schema: {
@@ -84,7 +82,7 @@ export const createResetPasswordMachine = ({ backendUrl, clientUrl }: AuthClient
},
services: {
requestChange: (_, { email, options }) =>
api.post<string, ResetPasswordResponse>('/user/password/reset', {
postFetch<ResetPasswordResponse>(`${backendUrl}/user/password/reset`, {
email,
options: rewriteRedirectTo(clientUrl, options)
})

View File

@@ -1,9 +1,8 @@
import { assign, createMachine, send } from 'xstate'
import { INVALID_EMAIL_ERROR } from '../errors'
import { AuthClient } from '../internal-client'
import { ErrorPayload, SendVerificationEmailOptions, SendVerificationEmailResponse } from '../types'
import { nhostApiClient, rewriteRedirectTo } from '../utils'
import { postFetch, rewriteRedirectTo } from '../utils'
import { isValidEmail } from '../utils/validators'
export type SendVerificationEmailContext = {
@@ -25,7 +24,6 @@ export type SendVerificationEmailServices = {
export type SendVerificationEmailMachine = ReturnType<typeof createSendVerificationEmailMachine>
export const createSendVerificationEmailMachine = ({ backendUrl, clientUrl }: AuthClient) => {
const api = nhostApiClient(backendUrl)
return createMachine(
{
schema: {
@@ -84,12 +82,9 @@ export const createSendVerificationEmailMachine = ({ backendUrl, clientUrl }: Au
},
services: {
request: async (_, { email, options }) => {
const res = await api.post<SendVerificationEmailResponse>(
'/user/email/send-verification-email',
{
email,
options: rewriteRedirectTo(clientUrl, options)
}
const res = await postFetch<SendVerificationEmailResponse>(
`${backendUrl}/user/email/send-verification-email`,
{ email, options: rewriteRedirectTo(clientUrl, options) }
)
return res.data
}

View File

@@ -3,13 +3,12 @@ import {
PublicKeyCredentialCreationOptionsJSON,
RegistrationCredentialJSON
} from '@simplewebauthn/typescript-types'
import { postFetch } from '..'
import { CodifiedError } from '../errors'
import { AuthClient } from '../internal-client'
import { ErrorPayload, SecurityKey } from '../types'
import { nhostApiClient } from '../utils'
import { ActionErrorState, ActionLoadingState, ActionSuccessState } from './types'
export interface AddSecurityKeyHandlerResult extends ActionErrorState, ActionSuccessState {
key?: SecurityKey
}
@@ -20,16 +19,11 @@ export const addSecurityKeyPromise = async (
{ backendUrl, interpreter }: AuthClient,
nickname?: string
): Promise<AddSecurityKeyHandlerResult> => {
const api = nhostApiClient(backendUrl)
try {
const { data: options } = await api.post<PublicKeyCredentialCreationOptionsJSON>(
const { data: options } = await postFetch<PublicKeyCredentialCreationOptionsJSON>(
'/user/webauthn/add',
{},
{
headers: {
authorization: `Bearer ${interpreter?.getSnapshot().context.accessToken.value}`
}
}
interpreter?.getSnapshot().context.accessToken.value
)
let credential: RegistrationCredentialJSON
try {
@@ -37,14 +31,10 @@ export const addSecurityKeyPromise = async (
} catch (e) {
throw new CodifiedError(e as Error)
}
const { data: key } = await api.post<SecurityKey>(
'/user/webauthn/verify',
const { data: key } = await postFetch<SecurityKey>(
`${backendUrl}/user/webauthn/verify`,
{ credential, nickname },
{
headers: {
authorization: `Bearer ${interpreter?.getSnapshot().context.accessToken.value}`
}
}
interpreter?.getSnapshot().context.accessToken.value
)
return { key, isError: false, error: null, isSuccess: true }
} catch (e) {

View File

@@ -1,7 +1,7 @@
import { ErrorPayload, NhostSession } from './common'
// Hasura-auth API response types
interface NullableErrorResponse {
export interface NullableErrorResponse {
error: ErrorPayload | null
}

View File

@@ -1,25 +0,0 @@
import axios, { AxiosError } from 'axios'
import { NETWORK_ERROR_CODE } from '../errors'
import { ErrorPayload } from '../types'
export const nhostApiClient = (backendUrl: string) => {
const client = axios.create({ baseURL: backendUrl })
client.interceptors.response.use(
(response) => response,
(error: AxiosError<{ message: string; error?: string; statusCode?: number }>) =>
Promise.reject<{ error: ErrorPayload }>({
error: {
message:
error.response?.data?.message ??
error.message ??
error.request.responseText ??
JSON.stringify(error),
status: error.response?.status ?? error.response?.data?.statusCode ?? NETWORK_ERROR_CODE,
error: error.response?.data?.error || error.request.statusText || 'network'
}
})
)
return client
}

View File

@@ -0,0 +1,58 @@
import fetch from 'cross-fetch'
import { NETWORK_ERROR_CODE } from '../errors'
import { NullableErrorResponse } from '../types'
interface FetcResponse<T> extends NullableErrorResponse {
data: T
}
const fetchWrapper = async <T>(
url: string,
method: 'GET' | 'POST',
{ token, body }: { token?: string | null; body?: any } = {}
): Promise<FetcResponse<T>> => {
const headers: HeadersInit = {
'Content-Type': 'application/json',
Accept: '*/*'
}
if (token) {
headers['Authorization'] = `Bearer ${token}`
}
const options: RequestInit = {
method,
headers
}
if (body) {
options.body = JSON.stringify(body)
}
try {
const result = await fetch(url, options)
if (!result.ok) {
const error = await result.json()
return Promise.reject<FetcResponse<T>>({ error })
}
try {
const data = await result.json()
return { data, error: null }
} catch {
console.warn(`Unexpected response: can't parse the response of the server at ${url}`)
return { data: 'OK' as any, error: null }
}
} catch (e) {
const error = {
message: 'Network Error',
status: NETWORK_ERROR_CODE,
error: 'network'
}
return Promise.reject<FetcResponse<T>>({ error })
}
}
export const postFetch = async <T>(
url: string,
body: any,
token?: string | null
): Promise<FetcResponse<T>> => fetchWrapper<T>(url, 'POST', { token, body })
export const getFetch = <T>(url: string, token?: string | null): Promise<FetcResponse<T>> =>
fetchWrapper<T>(url, 'GET', { token })

View File

@@ -1,5 +1,5 @@
export * from './axios'
export * from './client-helpers'
export * from './environment'
export * from './fetch'
export * from './url'
export * from './validators'

View File

@@ -10,7 +10,6 @@ import {
invalidDeamonymisationEmailError
} from './helpers/handlers'
import { fakeAnonymousUser } from './helpers/mocks/user'
import server from './helpers/server'
import CustomClientStorage from './helpers/storage'
@@ -51,7 +50,7 @@ describe('Anonymous Sign-in', () => {
expect(state.context.errors).toMatchInlineSnapshot(`
{
"authentication": {
"error": "OK",
"error": "network",
"message": "Network Error",
"status": 0,
},

View File

@@ -61,7 +61,7 @@ test(`should fail if there is a network error`, async () => {
expect(state.context.error).toMatchInlineSnapshot(`
{
"error": "OK",
"error": "network",
"message": "Network Error",
"status": 0,
}

View File

@@ -64,7 +64,7 @@ test(`should fail if there is a network error`, async () => {
expect(state.context.error).toMatchInlineSnapshot(`
{
"error": "OK",
"error": "network",
"message": "Network Error",
"status": 0,
}

View File

@@ -54,7 +54,7 @@ test(`should fail if network is unavailable`, async () => {
expect(state.context.errors).toMatchInlineSnapshot(`
{
"registration": {
"error": "OK",
"error": "network",
"message": "Network Error",
"status": 0,
},

View File

@@ -68,7 +68,7 @@ describe(`Generation`, () => {
expect(state.context.error).toMatchInlineSnapshot(`
{
"error": "OK",
"error": "network",
"message": "Network Error",
"status": 0,
}

View File

@@ -53,7 +53,7 @@ export const correctAnonymousHandler = rest.post(
export const deamonymisationSuccessfulHandler = rest.post(
`${BASE_URL}/user/deanonymize`,
(_req, res, ctx) => {
return res(ctx.status(200))
return res(ctx.status(200), ctx.json('OK'))
}
)

View File

@@ -48,6 +48,6 @@ export const changeEmailUnauthorizedErrorHandler = rest.post(
export const changeEmailSuccessHandler = rest.post(
`${BASE_URL}/user/email/change`,
(_req, res, ctx) => {
return res(ctx.status(200))
return res(ctx.status(200), ctx.json('OK'))
}
)

View File

@@ -48,6 +48,6 @@ export const changePasswordUnauthorizedErrorHandler = rest.post(
export const changePasswordSuccessHandler = rest.post(
`${BASE_URL}/user/password`,
(_req, res, ctx) => {
return res(ctx.status(200))
return res(ctx.status(200), ctx.json('OK'))
}
)

View File

@@ -101,5 +101,5 @@ export const activateMfaTotpUnauthorizedErrorHandler = rest.post(
* Request handler for MSW to mock an successful network request when activating MFA.
*/
export const activateMfaTotpSuccessHandler = rest.post(`${BASE_URL}/user/mfa`, (_req, res, ctx) => {
return res(ctx.status(200))
return res(ctx.status(200), ctx.json('OK'))
})

View File

@@ -8,7 +8,7 @@ import { BASE_URL } from '../config'
export const correctPasswordlessEmailHandler = rest.post(
`${BASE_URL}/signin/passwordless/email`,
(_req, res, ctx) => {
return res(ctx.status(200))
return res(ctx.status(200), ctx.json('OK'))
}
)

View File

@@ -11,7 +11,7 @@ import fakeUser from '../mocks/user'
export const correctPasswordlessSmsHandler = rest.post(
`${BASE_URL}/signin/passwordless/sms`,
(_req, res, ctx) => {
return res(ctx.status(200))
return res(ctx.status(200), ctx.json('OK'))
}
)

View File

@@ -64,6 +64,6 @@ export const resetPasswordUserNotFoundHandler = rest.post(
export const resetPasswordSuccessHandler = rest.post(
`${BASE_URL}/user/password/reset`,
(_req, res, ctx) => {
return res(ctx.status(200))
return res(ctx.status(200), ctx.json('OK'))
}
)

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