Compare commits

..

14 Commits

Author SHA1 Message Date
github-actions[bot]
7b25c37c26 chore: update versions (#2569)
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.


# Releases
## @nhost/dashboard@1.8.2

### Patch Changes

- 6df4f02: fix: use custom error toast and show correct message when
sending an invite

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-02-29 13:10:09 +01:00
Hassan Ben Jobrane
6df4f02e95 fix(dashboard): show correct message when sending invite fails (#2567) 2024-02-29 12:52:25 +01:00
github-actions[bot]
aaae98f019 chore: update versions (#2559)
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.


# Releases
## @nhost/docs@2.6.0

### Minor Changes

-   dc23dc0: fix: docs run references

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-02-25 23:02:36 -01:00
Nuno Pato
dc23dc0f49 fix: docs run references (#2558) 2024-02-25 22:47:38 -01:00
github-actions[bot]
82728da100 chore: update versions (#2556)
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.


# Releases
## @nhost/apollo@6.0.7

### Patch Changes

-   @nhost/nhost-js@3.0.7

## @nhost/react-apollo@9.0.2

### Patch Changes

-   @nhost/apollo@6.0.7
-   @nhost/react@3.2.2

## @nhost/react-urql@6.0.2

### Patch Changes

-   @nhost/react@3.2.2

## @nhost/graphql-js@0.1.7

### Patch Changes

- 2d68fee: fix: resolve an issue where unauthenticated graphql requests
are not sent

## @nhost/nextjs@2.1.4

### Patch Changes

-   @nhost/react@3.2.2

## @nhost/nhost-js@3.0.7

### Patch Changes

-   Updated dependencies [2d68fee]
    -   @nhost/graphql-js@0.1.7

## @nhost/react@3.2.2

### Patch Changes

-   @nhost/nhost-js@3.0.7

## @nhost/vue@2.2.2

### Patch Changes

-   @nhost/nhost-js@3.0.7

## @nhost/dashboard@1.8.1

### Patch Changes

-   @nhost/react-apollo@9.0.2
-   @nhost/nextjs@2.1.4

## @nhost-examples/cli@0.1.8

### Patch Changes

-   @nhost/nhost-js@3.0.7

## @nhost-examples/codegen-react-apollo@0.1.16

### Patch Changes

-   @nhost/react@3.2.2
-   @nhost/react-apollo@9.0.2

## @nhost-examples/codegen-react-query@0.1.17

### Patch Changes

-   @nhost/react@3.2.2

## @nhost-examples/codegen-react-urql@0.0.13

### Patch Changes

-   @nhost/react@3.2.2
-   @nhost/react-urql@6.0.2

## @nhost-examples/multi-tenant-one-to-many@2.0.6

### Patch Changes

-   @nhost/nhost-js@3.0.7

## @nhost-examples/nextjs@0.1.18

### Patch Changes

-   @nhost/react@3.2.2
-   @nhost/react-apollo@9.0.2
-   @nhost/nextjs@2.1.4

## @nhost-examples/node-storage@0.0.10

### Patch Changes

-   @nhost/nhost-js@3.0.7

## @nhost-examples/nextjs-server-components@0.2.4

### Patch Changes

-   @nhost/nhost-js@3.0.7

## @nhost-examples/react-apollo@0.3.2

### Patch Changes

-   @nhost/react@3.2.2
-   @nhost/react-apollo@9.0.2

## @nhost-examples/react-gqty@1.0.6

### Patch Changes

-   @nhost/react@3.2.2

## @nhost-examples/vue-apollo@0.2.3

### Patch Changes

-   @nhost/nhost-js@3.0.7
-   @nhost/apollo@6.0.7
-   @nhost/vue@2.2.2

## @nhost-examples/vue-quickstart@0.0.15

### Patch Changes

-   @nhost/apollo@6.0.7
-   @nhost/vue@2.2.2

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-02-23 21:14:11 +01:00
Hassan Ben Jobrane
2d68fee54c fix(graphql-js): allow graphql requests with no access token (#2555) 2024-02-23 21:09:45 +01:00
github-actions[bot]
35010353c7 chore: update versions (#2547)
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.


# Releases
## @nhost/apollo@6.0.6

### Patch Changes

-   e0ab6d9: fix: add extra logic to check and wait for a valid JWT
    -   @nhost/nhost-js@3.0.6

## @nhost/react-apollo@9.0.1

### Patch Changes

-   Updated dependencies [e0ab6d9]
    -   @nhost/apollo@6.0.6
    -   @nhost/react@3.2.1

## @nhost/react-urql@6.0.1

### Patch Changes

-   @nhost/react@3.2.1

## @nhost/graphql-js@0.1.6

### Patch Changes

-   e0ab6d9: fix: add extra logic to check and wait for a valid JWT

## @nhost/hasura-auth-js@2.3.1

### Patch Changes

- 7baee8a: fix(hasura-auth-js): replace `jwt-decode` with `jose` for
decoding access tokens that works on both the browser and Node.js
-   e0ab6d9: fix: add extra logic to check and wait for a valid JWT

## @nhost/nextjs@2.1.3

### Patch Changes

-   @nhost/react@3.2.1

## @nhost/nhost-js@3.0.6

### Patch Changes

-   Updated dependencies [7baee8a]
-   Updated dependencies [e0ab6d9]
    -   @nhost/hasura-auth-js@2.3.1
    -   @nhost/graphql-js@0.1.6

## @nhost/react@3.2.1

### Patch Changes

-   @nhost/nhost-js@3.0.6

## @nhost/vue@2.2.1

### Patch Changes

-   @nhost/nhost-js@3.0.6

## @nhost/dashboard@1.8.0

### Minor Changes

- 713d53c: feat: add catch-all route for workspace/project - useful for
documentation

### Patch Changes

-   3db2999: fix: refresh table list after running SQL using the editor
- 3c4dd55: fix: handle `Error` objects properly in the `ErrorToast`
component
- 92b434e: fix: resolve an issue where the checkbox in the data-grid
header did not select all rows
    -   @nhost/react-apollo@9.0.1
    -   @nhost/nextjs@2.1.3

## @nhost-examples/cli@0.1.7

### Patch Changes

-   @nhost/nhost-js@3.0.6

## @nhost-examples/codegen-react-apollo@0.1.15

### Patch Changes

-   @nhost/react-apollo@9.0.1
-   @nhost/react@3.2.1

## @nhost-examples/codegen-react-query@0.1.16

### Patch Changes

-   @nhost/react@3.2.1

## @nhost-examples/codegen-react-urql@0.0.12

### Patch Changes

-   @nhost/react@3.2.1
-   @nhost/react-urql@6.0.1

## @nhost-examples/docker-compose@0.1.1

### Patch Changes

-   aff059e: fix: timers

## @nhost-examples/multi-tenant-one-to-many@2.0.5

### Patch Changes

-   @nhost/nhost-js@3.0.6

## @nhost-examples/nextjs@0.1.17

### Patch Changes

-   @nhost/react-apollo@9.0.1
-   @nhost/react@3.2.1
-   @nhost/nextjs@2.1.3

## @nhost-examples/node-storage@0.0.9

### Patch Changes

-   @nhost/nhost-js@3.0.6

## @nhost-examples/nextjs-server-components@0.2.3

### Patch Changes

-   @nhost/nhost-js@3.0.6

## @nhost-examples/react-apollo@0.3.1

### Patch Changes

-   @nhost/react-apollo@9.0.1
-   @nhost/react@3.2.1

## @nhost-examples/react-gqty@1.0.5

### Patch Changes

-   @nhost/react@3.2.1

## @nhost-examples/vue-apollo@0.2.2

### Patch Changes

-   Updated dependencies [e0ab6d9]
    -   @nhost/apollo@6.0.6
    -   @nhost/nhost-js@3.0.6
    -   @nhost/vue@2.2.1

## @nhost-examples/vue-quickstart@0.0.14

### Patch Changes

-   Updated dependencies [e0ab6d9]
    -   @nhost/apollo@6.0.6
    -   @nhost/vue@2.2.1

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-02-23 10:07:53 +01:00
David Barroso
aff059ec71 fix (examples/docker-compose): timers (#2553) 2024-02-23 10:04:20 +01:00
Nuno Pato
713d53cfc0 feat: dashboard: add catch-all route (#2545) 2024-02-22 17:59:24 -01:00
Hassan Ben Jobrane
e0ab6d9a37 fix: JWT expired bug (#2533)
fixes: https://github.com/nhost/nhost/issues/2348

---------

Co-authored-by: David Barroso <dbarrosop@dravetech.com>
2024-02-22 18:44:43 +01:00
Hassan Ben Jobrane
7baee8a9cc fix(hasura-auth-js): use jose instead of jwt-decode to decode the accessToken and get the Hasura claims (#2550)
fixes https://github.com/nhost/nhost/issues/2513
2024-02-21 11:17:03 +01:00
Hassan Ben Jobrane
3db2999f60 fix(dashboard): refresh table list after running a SQL stmt using the editor (#2549)
fixes https://github.com/nhost/projects/issues/52
2024-02-20 13:40:46 +01:00
Hassan Ben Jobrane
3c4dd55045 fix(dashboard): handle Error alongside ApolloError properly in the ErrorToast component (#2548)
fixes https://github.com/nhost/nhost/issues/2525
2024-02-20 12:16:26 +01:00
Hassan Ben Jobrane
92b434e840 fix: refactor DataGridHeader component to allow for selecting all rows (#2546)
fixes https://github.com/nhost/nhost/issues/2526
2024-02-19 17:30:59 +01:00
66 changed files with 744 additions and 133 deletions

View File

@@ -1,5 +1,32 @@
# @nhost/dashboard
## 1.8.2
### Patch Changes
- 6df4f02: fix: use custom error toast and show correct message when sending an invite
## 1.8.1
### Patch Changes
- @nhost/react-apollo@9.0.2
- @nhost/nextjs@2.1.4
## 1.8.0
### Minor Changes
- 713d53c: feat: add catch-all route for workspace/project - useful for documentation
### Patch Changes
- 3db2999: fix: refresh table list after running SQL using the editor
- 3c4dd55: fix: handle `Error` objects properly in the `ErrorToast` component
- 92b434e: fix: resolve an issue where the checkbox in the data-grid header did not select all rows
- @nhost/react-apollo@9.0.1
- @nhost/nextjs@2.1.3
## 1.7.0
### Minor Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost/dashboard",
"version": "1.7.0",
"version": "1.8.2",
"private": true,
"scripts": {
"preinstall": "npx only-allow pnpm",

View File

@@ -96,45 +96,52 @@ export default function DataGridHeader<T extends object>({
}}
key={column.id}
>
<Dropdown.Trigger
className={twMerge(
'focus:outline-none motion-safe:transition-colors',
)}
disabled={
column.isDisabled ||
column.id === 'selection' ||
(column.disableSortBy && !onRemoveColumn)
}
hideChevron
>
{column.id === 'selection' ? (
<span
{...headerProps}
className="relative grid w-full grid-flow-col items-center justify-between p-2"
>
{column.render('Header')}
{allowSort && (
<Box component="span" sx={{ color: 'text.primary' }}>
{column.isSorted && !column.isSortedDesc && (
<ArrowUpIcon className="h-3 w-3" />
)}
{column.isSorted && column.isSortedDesc && (
<ArrowDownIcon className="h-3 w-3" />
)}
</Box>
)}
</span>
{allowResize && !column.disableResizing && (
) : (
<Dropdown.Trigger
className={twMerge(
'focus:outline-none motion-safe:transition-colors',
)}
disabled={
column.isDisabled || (column.disableSortBy && !onRemoveColumn)
}
hideChevron
>
<span
{...column.getResizerProps({
onClick: (event: Event) => event.stopPropagation(),
})}
className="absolute top-0 bottom-0 -right-0.5 z-10 h-full w-1.5 group-hover:bg-slate-900 group-hover:bg-opacity-20 group-active:bg-slate-900 group-active:bg-opacity-20 motion-safe:transition-colors"
/>
)}
</Dropdown.Trigger>
{...headerProps}
className="relative grid w-full grid-flow-col items-center justify-between p-2"
>
{column.render('Header')}
{allowSort && (
<Box component="span" sx={{ color: 'text.primary' }}>
{column.isSorted && !column.isSortedDesc && (
<ArrowUpIcon className="h-3 w-3" />
)}
{column.isSorted && column.isSortedDesc && (
<ArrowDownIcon className="h-3 w-3" />
)}
</Box>
)}
</span>
{allowResize && !column.disableResizing && (
<span
{...column.getResizerProps({
onClick: (event: Event) => event.stopPropagation(),
})}
className="absolute -right-0.5 bottom-0 top-0 z-10 h-full w-1.5 group-hover:bg-slate-900 group-hover:bg-opacity-20 group-active:bg-slate-900 group-active:bg-opacity-20 motion-safe:transition-colors"
/>
)}
</Dropdown.Trigger>
)}
<Dropdown.Content
menu

View File

@@ -5,7 +5,7 @@ import { XIcon } from '@/components/ui/v2/icons/XIcon';
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
import { getToastBackgroundColor } from '@/utils/constants/settings';
import { copy } from '@/utils/copy';
import { type ApolloError } from '@apollo/client';
import type { ApolloError } from '@apollo/client';
import { useUserData } from '@nhost/nextjs';
import { AnimatePresence, motion } from 'framer-motion';
import { useRouter } from 'next/router';
@@ -20,6 +20,44 @@ interface ErrorDetails {
error: any;
}
const getInternalErrorMessage = (
error: Error | ApolloError | undefined,
): string | null => {
if (!error) {
return null;
}
if (error.name === 'ApolloError') {
// @ts-ignore
const internalError = error.graphQLErrors?.[0]?.extensions?.internal as {
error: { message: string };
};
return internalError?.error?.message || null;
}
if (error instanceof Error) {
return error.message;
}
return null;
};
const errorToObject = (error: ApolloError | Error) => {
if (error.name === 'ApolloError') {
return error;
}
if (error instanceof Error) {
return {
name: error.name,
message: error.message,
stack: error.stack,
};
}
return {};
};
export default function ErrorToast({
isVisible,
errorMessage,
@@ -28,7 +66,7 @@ export default function ErrorToast({
}: {
isVisible: boolean;
errorMessage: string;
error: ApolloError;
error: ApolloError | Error;
close: () => void;
}) {
const userData = useUserData();
@@ -43,19 +81,10 @@ export default function ErrorToast({
userId: userData?.id || 'local',
url: asPath,
},
error,
error: errorToObject(error),
};
const internalError = error?.graphQLErrors?.at(0)?.extensions?.internal as {
error: {
message: string;
};
};
const msg =
internalError?.error?.message ||
error?.graphQLErrors?.at(0).message ||
errorMessage;
const msg = getInternalErrorMessage(error) || errorMessage;
return (
<AnimatePresence>

View File

@@ -1,10 +1,12 @@
import { useDatabaseQuery } from '@/features/database/dataGrid/hooks/useDatabaseQuery';
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
import { generateAppServiceUrl } from '@/features/projects/common/utils/generateAppServiceUrl';
import { getToastStyleProps } from '@/utils/constants/settings';
import { getHasuraAdminSecret } from '@/utils/env';
import { parseIdentifiersFromSQL } from '@/utils/sql';
import toast from 'react-hot-toast';
import { useRouter } from 'next/router';
import { useState } from 'react';
import toast from 'react-hot-toast';
export default function useRunSQL(
sqlCode: string,
@@ -22,6 +24,14 @@ export default function useRunSQL(
const [columns, setColumns] = useState<string[]>([]);
const [rows, setRows] = useState<string[][]>([[]]);
const router = useRouter();
const {
query: { dataSourceSlug },
} = router;
const { refetch } = useDatabaseQuery([dataSourceSlug as string]);
const appUrl = generateAppServiceUrl(
currentProject?.subdomain,
currentProject?.region,
@@ -269,6 +279,9 @@ export default function useRunSQL(
}
}
// refresh the table list after running the sql
await refetch();
setLoading(false);
};

View File

@@ -7,7 +7,7 @@ import { useIsCurrentUserOwner } from '@/features/projects/common/hooks/useIsCur
import { PendingWorkspaceMemberInvitation } from '@/features/projects/workspaces/components/PendingWorkspaceMemberInvitation';
import { WorkspaceMember } from '@/features/projects/workspaces/components/WorkspaceMember';
import { discordAnnounce } from '@/utils/discordAnnounce';
import { getErrorMessage } from '@/utils/getErrorMessage';
import { execPromiseWithErrorToast } from '@/utils/execPromiseWithErrorToast';
import { triggerToast } from '@/utils/toast';
import {
refetchGetWorkspaceMembersQuery,
@@ -52,38 +52,42 @@ function WorkspaceMemberInviteForm({
return;
}
try {
await insertWorkspaceMemberInvite({
variables: {
workspaceMemberInvite: {
workspaceId: currentWorkspace.id,
email,
memberType: 'member',
await execPromiseWithErrorToast(
async () => {
await insertWorkspaceMemberInvite({
variables: {
workspaceMemberInvite: {
workspaceId: currentWorkspace.id,
email,
memberType: 'member',
},
},
},
});
triggerToast(
`Invite to join workspace ${currentWorkspace.name} sent to ${email}.`,
);
} catch (error) {
await discordAnnounce(
`Error trying to invite to ${email} to ${currentWorkspace.name} ${error.message}`,
);
if (
error.message ===
'Foreign key violation. insert or update on table "workspace_member_invites" violates foreign key constraint "workspace_member_invites_email_fkey"'
) {
setWorkspaceInviteError(
'You can only invite users that are already registered at Nhost. Ask the person to register an account, then invite them again.',
});
triggerToast(
`Invite to join workspace ${currentWorkspace.name} sent to ${email}.`,
);
},
{
loadingMessage: 'Sending invite...',
successMessage: 'The invite has been sent successfully.',
errorMessage: `Error trying to invite to ${email} to ${currentWorkspace.name}`,
onError: async (error) => {
await discordAnnounce(
`Error trying to invite to ${email} to ${currentWorkspace.name} ${error.message}`,
);
return;
}
setWorkspaceInviteError(getErrorMessage(error, 'invite'));
return;
}
if (
error.message ===
'Foreign key violation. insert or update on table "workspace_member_invites" violates foreign key constraint "workspace_member_invites_email_fkey"'
) {
setWorkspaceInviteError(
'You can only invite users that are already registered at Nhost. Ask the person to register an account, then invite them again.',
);
}
},
},
);
setEmail('');
};
@@ -130,8 +134,8 @@ export default function WorkspaceMembers() {
});
return (
<div className="mx-auto mt-18 max-w-3xl font-display">
<div className="mb-2 grid grid-flow-row gap-1">
<div className="max-w-3xl mx-auto mt-18 font-display">
<div className="grid grid-flow-row gap-1 mb-2">
<Text variant="h3">Members</Text>
<Text color="secondary" className="text-sm">
People in this workspace can manage all projects listed above.

View File

@@ -0,0 +1,158 @@
import { AuthenticatedLayout } from '@/components/layout/AuthenticatedLayout';
import { Container } from '@/components/layout/Container';
import { RetryableErrorBoundary } from '@/components/presentational/RetryableErrorBoundary';
import { ActivityIndicator } from '@/components/ui/v2/ActivityIndicator';
import { Box } from '@/components/ui/v2/Box';
import { Button } from '@/components/ui/v2/Button';
import { Input } from '@/components/ui/v2/Input';
import { List } from '@/components/ui/v2/List';
import { ListItem } from '@/components/ui/v2/ListItem';
import { Text } from '@/components/ui/v2/Text';
import {
useGetAllWorkspacesAndProjectsQuery,
type GetAllWorkspacesAndProjectsQuery,
} from '@/utils/__generated__/graphql';
import { Divider } from '@mui/material';
import { useUserData } from '@nhost/nextjs';
import debounce from 'lodash.debounce';
import Image from 'next/image';
import { useRouter } from 'next/router';
import type { ChangeEvent, ReactElement } from 'react';
import { Fragment, useEffect, useMemo, useState } from 'react';
type Workspace = Omit<
GetAllWorkspacesAndProjectsQuery['workspaces'][0],
'__typename'
>;
export default function SelectWorkspaceAndProject() {
const user = useUserData();
const router = useRouter();
const { data, loading } = useGetAllWorkspacesAndProjectsQuery({
skip: !user,
});
const workspaces: Workspace[] = data?.workspaces || [];
const projects = workspaces.flatMap((workspace) =>
workspace.projects.map((project) => ({
workspaceName: workspace.name,
projectName: project.name,
value: `${workspace.slug}/${project.slug}`,
})),
);
const [filter, setFilter] = useState('');
const handleFilterChange = useMemo(
() =>
debounce((event: ChangeEvent<HTMLInputElement>) => {
setFilter(event.target.value);
}, 200),
[],
);
useEffect(() => () => handleFilterChange.cancel(), [handleFilterChange]);
const goToProjectPage = async (project: {
workspaceName: string;
projectName: string;
value: string;
}) => {
const { slug } = router.query;
await router.push({
pathname: `/${project.value}/${
Array.isArray(slug) ? slug.join('/') : slug
}`,
});
};
const projectsToDisplay = filter
? projects.filter((project) =>
project.projectName.toLowerCase().includes(filter.toLowerCase()),
)
: projects;
if (loading) {
return (
<div className="flex w-full justify-center">
<ActivityIndicator
delay={500}
label="Loading workspaces and projects..."
/>
</div>
);
}
return (
<Container>
<div className="mx-auto grid max-w-[760px] grid-flow-row gap-4 py-6 sm:py-14">
<Text variant="h2" component="h1" className="">
Select a Project
</Text>
<div>
<div className="mb-2 flex w-full">
<Input
placeholder="Search..."
onChange={handleFilterChange}
fullWidth
autoFocus
/>
</div>
<RetryableErrorBoundary>
{projectsToDisplay.length === 0 ? (
<Box className="h-import py-2">
<Text variant="subtitle2">No results found.</Text>
</Box>
) : (
<List className="h-import overflow-y-auto">
{projectsToDisplay.map((project, index) => (
<Fragment key={project.value}>
<ListItem.Root
className="grid grid-flow-col justify-start gap-2 py-2.5"
secondaryAction={
<Button
variant="borderless"
color="primary"
onClick={() => goToProjectPage(project)}
>
Select
</Button>
}
>
<ListItem.Avatar>
<span className="inline-block h-6 w-6 overflow-hidden rounded-md">
<Image
src="/logos/new.svg"
alt="Nhost Logo"
width={24}
height={24}
/>
</span>
</ListItem.Avatar>
<ListItem.Text
primary={project.projectName}
secondary={`${project.workspaceName} / ${project.projectName}`}
/>
</ListItem.Root>
{index < projects.length - 1 && <Divider component="li" />}
</Fragment>
))}
</List>
)}
</RetryableErrorBoundary>
</div>
</div>
</Container>
);
}
SelectWorkspaceAndProject.getLayout = function getLayout(page: ReactElement) {
return (
<AuthenticatedLayout title="Select a Project">{page}</AuthenticatedLayout>
);
};

View File

@@ -29,6 +29,7 @@ import { Toaster } from 'react-hot-toast';
const emotionCache = createEmotionCache();
process.env = {
TEST_MODE: 'true',
NODE_ENV: 'development',
NEXT_PUBLIC_NHOST_PLATFORM: 'false',
NEXT_PUBLIC_ENV: 'dev',

View File

@@ -29,6 +29,7 @@ export default async function execPromiseWithErrorToast(
const result = await call();
toast.dismiss(loadingToastId);
toast.success(successMessage, {
style: toastStyle.style,
...toastStyle.success,

View File

@@ -1,5 +1,11 @@
# @nhost/docs
## 2.6.0
### Minor Changes
- dc23dc0: fix: docs run references
## 2.5.0
### Minor Changes

View File

@@ -9,7 +9,7 @@ If you are using the Nhost CLI for local development, as of [v0.12.0](https://gi
<Steps>
<Step title="Configuring the Service">
Follow the steps highlighed in the ["Enabling Service"](enabling-service) guide and don't forget to add the relevant secrets to your `.secrets` file.
Follow the steps highlighed in the [Enabling Service](enabling-service) guide and don't forget to add the relevant secrets to your `.secrets` file.
</Step>
<Step title="Start nhost">
Run `nhost up`:

View File

@@ -46,11 +46,11 @@ capacity=1
</Tab>
</Tabs>
<Info>Head to [CLI & CI deployments](/run/ci) for more details on how to deploy using a configuration file.</Info>
<Info>Head to [CLI & CI deployments](/guides/run/cli-deployments) for more details on how to deploy using a configuration file.</Info>
The `name` of the service is used as an identifier and to generate URLs when exposing the service to the Internet. You can use any container image publicly available or you can push your own to the [Nhost registry](/run/registry).
The `name` of the service is used as an identifier and to generate URLs when exposing the service to the Internet. You can use any container image publicly available or you can push your own to the [Nhost registry](/guides/run/registry).
All environment variables set here are exclusive to this service and will not be shared with other services or with the Nhost stack. If you are using a configuration file secrets are supported.
For more details about the `Ports` section head to [networking](/run/networking). You can also head to [resources](/run/resources) for more information about replicas, compute, and storage.
For more details about the `Ports` section head to [networking](/guides/run/networking). You can also head to [resources](/guides/run/resources) for more information about replicas, compute, and storage.

View File

@@ -12,11 +12,11 @@ Then on `New Service`:
![click on New Service](/images/guides/run/getting_started_2.png)
Now you can fill your [service configuration](/run/configuration):
Now you can fill your [service configuration](/guides/run/configuration):
![click on New Service](/images/guides/run/getting_started_3.png)
As you configure the `Ports` section you can take note of the generated URL. You can find more information about this section under [Networking](/run/networking).
As you configure the `Ports` section you can take note of the generated URL. You can find more information about this section under [Networking](/guides/run/networking).
![copy the URL](/images/guides/run/getting_started_4.png)

View File

@@ -76,7 +76,7 @@ To pause a service, simply set its number of replicas to `0`:
<Tab title="dashboard">
![pausing a service](/img/run/resources_3.png)
![pausing a service](/images/guides/run/resources_3.png)
</Tab>
<Tab title="toml">

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost/docs",
"version": "2.5.0",
"version": "2.6.0",
"private": true,
"scripts": {
"start": "mintlify dev"

View File

@@ -1,5 +1,17 @@
# @nhost-examples/cli
## 0.1.8
### Patch Changes
- @nhost/nhost-js@3.0.7
## 0.1.7
### Patch Changes
- @nhost/nhost-js@3.0.6
## 0.1.6
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost-examples/cli",
"version": "0.1.6",
"version": "0.1.8",
"main": "src/index.mjs",
"private": true,
"scripts": {

View File

@@ -1,5 +1,19 @@
# @nhost-examples/codegen-react-apollo
## 0.1.16
### Patch Changes
- @nhost/react@3.2.2
- @nhost/react-apollo@9.0.2
## 0.1.15
### Patch Changes
- @nhost/react-apollo@9.0.1
- @nhost/react@3.2.1
## 0.1.14
### Patch Changes

View File

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

View File

@@ -1,5 +1,17 @@
# @nhost-examples/codegen-react-query
## 0.1.17
### Patch Changes
- @nhost/react@3.2.2
## 0.1.16
### Patch Changes
- @nhost/react@3.2.1
## 0.1.15
### Patch Changes

View File

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

View File

@@ -1,5 +1,19 @@
# @nhost-examples/react-urql
## 0.0.13
### Patch Changes
- @nhost/react@3.2.2
- @nhost/react-urql@6.0.2
## 0.0.12
### Patch Changes
- @nhost/react@3.2.1
- @nhost/react-urql@6.0.1
## 0.0.11
### Patch Changes

View File

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

View File

@@ -1,5 +1,11 @@
# @nhost-examples/docker-compose
## 0.1.1
### Patch Changes
- aff059e: fix: timers
## 0.1.0
### Minor Changes

View File

@@ -42,6 +42,13 @@ services:
HASURA_GRAPHQL_UNAUTHORIZED_ROLE: public
HASURA_GRAPHQL_LOG_LEVEL: debug
HASURA_GRAPHQL_ENABLE_CONSOLE: 'true'
healthcheck:
test:
- CMD-SHELL
- curl http://localhost:8080/healthz > /dev/null 2>&1
timeout: 60s
interval: 30s
start_period: 90s
labels:
- "traefik.enable=true"
- "traefik.http.routers.hasura.rule=Host(`${PROXY_HOST}`, `localhost`) && PathPrefix(`/`)"
@@ -66,7 +73,7 @@ services:
AUTH_SMTP_USER: user
AUTH_SMTP_PASS: password
AUTH_SMTP_SENDER: mail@example.com
expose:
expose:
- 4000
labels:
- "traefik.enable=true"
@@ -112,7 +119,7 @@ services:
- "traefik.http.routers.functions.middlewares=strip-functions@docker"
- "traefik.http.routers.functions.entrypoints=web"
restart: always
expose:
expose:
- 3000
volumes:
- .:/opt/project
@@ -140,7 +147,7 @@ services:
SMTP_SECURE: "${AUTH_SMTP_SECURE:-false}"
SMTP_SENDER: ${AUTH_SMTP_SENDER:-hbp@hbp.com}
ports:
- ${AUTH_SMTP_PORT:-1025}:1025
- ${AUTH_SMTP_PORT:-1025}:1025
- 8025:8025
volumes:
- ./data/mailhog:/maildir

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost-examples/docker-compose",
"version": "0.1.0",
"version": "0.1.1",
"private": true,
"scripts": {
"e2e": "vitest run"

View File

@@ -11,8 +11,12 @@ describe(
beforeAll(async () => {
// * Start docker compose
await promisifiedExec(
'docker compose -f docker-compose.yaml --env-file .env.example up --wait --quiet-pull'
'docker compose -f docker-compose.yaml --env-file .env.example up --wait --wait-timeout 300 --quiet-pull'
)
// we wait a bit extra because sometimes traefik takes a bit to configure the services
setTimeout(() => {}, 30000);
}, 5 * 60 * 1000)
afterAll(async () => {

View File

@@ -1,5 +1,17 @@
# @nhost-examples/multi-tenant-one-to-many
## 2.0.6
### Patch Changes
- @nhost/nhost-js@3.0.7
## 2.0.5
### Patch Changes
- @nhost/nhost-js@3.0.6
## 2.0.4
### Patch Changes

View File

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

View File

@@ -1,5 +1,21 @@
# @nhost-examples/nextjs
## 0.1.18
### Patch Changes
- @nhost/react@3.2.2
- @nhost/react-apollo@9.0.2
- @nhost/nextjs@2.1.4
## 0.1.17
### Patch Changes
- @nhost/react-apollo@9.0.1
- @nhost/react@3.2.1
- @nhost/nextjs@2.1.3
## 0.1.16
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost-examples/nextjs",
"version": "0.1.16",
"version": "0.1.18",
"private": true,
"scripts": {
"dev": "next dev",

View File

@@ -1,5 +1,17 @@
# @nhost-examples/node-storage
## 0.0.10
### Patch Changes
- @nhost/nhost-js@3.0.7
## 0.0.9
### Patch Changes
- @nhost/nhost-js@3.0.6
## 0.0.8
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost-examples/node-storage",
"version": "0.0.8",
"version": "0.0.10",
"private": true,
"description": "This is an example of how to use the Storage with Node.js",
"main": "src/index.mjs",

View File

@@ -1,5 +1,17 @@
# @nhost-examples/nextjs-server-components
## 0.2.4
### Patch Changes
- @nhost/nhost-js@3.0.7
## 0.2.3
### Patch Changes
- @nhost/nhost-js@3.0.6
## 0.2.2
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost-examples/nextjs-server-components",
"version": "0.2.2",
"version": "0.2.4",
"private": true,
"scripts": {
"dev": "next dev",

View File

@@ -1,5 +1,19 @@
# @nhost-examples/react-apollo
## 0.3.2
### Patch Changes
- @nhost/react@3.2.2
- @nhost/react-apollo@9.0.2
## 0.3.1
### Patch Changes
- @nhost/react-apollo@9.0.1
- @nhost/react@3.2.1
## 0.3.0
### Minor Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost-examples/react-apollo",
"version": "0.3.0",
"version": "0.3.2",
"private": true,
"dependencies": {
"@apollo/client": "^3.9.4",

View File

@@ -1,5 +1,17 @@
# @nhost-examples/react-gqty
## 1.0.6
### Patch Changes
- @nhost/react@3.2.2
## 1.0.5
### Patch Changes
- @nhost/react@3.2.1
## 1.0.4
### Patch Changes

View File

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

View File

@@ -1,5 +1,22 @@
# @nhost-examples/vue-apollo
## 0.2.3
### Patch Changes
- @nhost/nhost-js@3.0.7
- @nhost/apollo@6.0.7
- @nhost/vue@2.2.2
## 0.2.2
### Patch Changes
- Updated dependencies [e0ab6d9]
- @nhost/apollo@6.0.6
- @nhost/nhost-js@3.0.6
- @nhost/vue@2.2.1
## 0.2.1
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "@nhost-examples/vue-apollo",
"private": true,
"version": "0.2.1",
"version": "0.2.3",
"scripts": {
"dev": "vite",
"build": "vite build",

View File

@@ -1,5 +1,20 @@
# @nhost-examples/vue-quickstart
## 0.0.15
### Patch Changes
- @nhost/apollo@6.0.7
- @nhost/vue@2.2.2
## 0.0.14
### Patch Changes
- Updated dependencies [e0ab6d9]
- @nhost/apollo@6.0.6
- @nhost/vue@2.2.1
## 0.0.13
### Patch Changes

View File

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

View File

@@ -1,5 +1,18 @@
# @nhost/apollo
## 6.0.7
### Patch Changes
- @nhost/nhost-js@3.0.7
## 6.0.6
### Patch Changes
- e0ab6d9: fix: add extra logic to check and wait for a valid JWT
- @nhost/nhost-js@3.0.6
## 6.0.5
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost/apollo",
"version": "6.0.5",
"version": "6.0.7",
"description": "Nhost Apollo Client library",
"license": "MIT",
"keywords": [
@@ -65,7 +65,8 @@
},
"dependencies": {
"graphql": "16.8.1",
"graphql-ws": "^5.14.3"
"graphql-ws": "^5.14.3",
"jwt-decode": "^3.1.2"
},
"devDependencies": {
"@apollo/client": "^3.9.4",

View File

@@ -12,6 +12,7 @@ import { setContext } from '@apollo/client/link/context'
import { GraphQLWsLink } from '@apollo/client/link/subscriptions'
import { getMainDefinition } from '@apollo/client/utilities'
import { AuthContext, NhostClient } from '@nhost/nhost-js'
import jwtDecode, { JwtPayload } from 'jwt-decode'
import { createRestartableClient } from './ws'
const isBrowser = typeof window !== 'undefined'
@@ -58,25 +59,41 @@ export const createApolloClient = ({
let accessToken: AuthContext['accessToken'] | null = null
const isJwtValid = () => {
if (!accessToken?.value) {
return false
}
const marginInSeconds = 3
const marginInMilliseconds = marginInSeconds * 1000
let decodedToken: JwtPayload = jwtDecode(accessToken.value)
return decodedToken.exp! * 1000 > Date.now() - marginInMilliseconds
}
const isTokenValid = () =>
!!accessToken?.value && !!accessToken?.expiresAt && accessToken?.expiresAt > new Date()
!!accessToken?.value &&
!!accessToken?.expiresAt &&
accessToken?.expiresAt > new Date() &&
isJwtValid()
const isTokenValidOrNull = () => !accessToken || isTokenValid()
const awaitValidTokenOrNull = () => {
if (isTokenValidOrNull()) {
return
return Promise.resolve()
}
return new Promise((resolve) => {
// doing this as an interval to avoid race conditions.
const interval = setInterval(() => {
if (isTokenValidOrNull()) {
clearInterval(interval)
resolve(true)
}
}, 100)
})
const waitForValidToken = () => {
if (isTokenValidOrNull()) {
return Promise.resolve(true)
}
return new Promise((resolve) => {
setTimeout(() => waitForValidToken().then(resolve), 100)
})
}
return waitForValidToken()
}
const getAuthHeaders = async () => {

View File

@@ -1,5 +1,20 @@
# @nhost/react-apollo
## 9.0.2
### Patch Changes
- @nhost/apollo@6.0.7
- @nhost/react@3.2.2
## 9.0.1
### Patch Changes
- Updated dependencies [e0ab6d9]
- @nhost/apollo@6.0.6
- @nhost/react@3.2.1
## 9.0.0
### Patch Changes

View File

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

View File

@@ -1,5 +1,17 @@
# @nhost/react-urql
## 6.0.2
### Patch Changes
- @nhost/react@3.2.2
## 6.0.1
### Patch Changes
- @nhost/react@3.2.1
## 6.0.0
### Patch Changes

View File

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

View File

@@ -1,5 +1,17 @@
# @nhost/graphql-js
## 0.1.7
### Patch Changes
- 2d68fee: fix: resolve an issue where unauthenticated graphql requests are not sent
## 0.1.6
### Patch Changes
- e0ab6d9: fix: add extra logic to check and wait for a valid JWT
## 0.1.5
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost/graphql-js",
"version": "0.1.5",
"version": "0.1.7",
"description": "Nhost GraphQL client",
"license": "MIT",
"keywords": [
@@ -54,7 +54,8 @@
},
"dependencies": {
"@graphql-typed-document-node/core": "^3.2.0",
"isomorphic-unfetch": "^3.1.0"
"isomorphic-unfetch": "^3.1.0",
"jwt-decode": "^3.1.2"
},
"peerDependencies": {
"graphql": "^14.0.0 || ^15.0.0 || ^16.0.0"

View File

@@ -12,6 +12,8 @@ import {
Variables
} from './types'
import jwtDecode, { JwtPayload } from 'jwt-decode'
/**
* @alias GraphQL
*/
@@ -28,6 +30,37 @@ export class NhostGraphqlClient {
this.adminSecret = adminSecret
}
private isAccessTokenValidOrNull = () => {
if (!this.accessToken) {
return true
}
try {
const decodedToken: JwtPayload = jwtDecode(this.accessToken)
return decodedToken.exp != null && decodedToken.exp * 1000 > Date.now()
} catch (error) {
console.error('Error decoding token:', error)
return false
}
}
private awaitForValidAccessTokenOrNull = async () => {
if (this.isAccessTokenValidOrNull()) {
return true
}
const waitForValidTokenOrNull = () => {
if (this.isAccessTokenValidOrNull()) {
return Promise.resolve(true)
}
return new Promise((resolve) => {
setTimeout(() => waitForValidTokenOrNull().then(resolve), 100)
})
}
return waitForValidTokenOrNull()
}
/**
* 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).
*
@@ -70,6 +103,12 @@ export class NhostGraphqlClient {
const { headers, ...otherOptions } = config || {}
const { query, operationName } = resolveRequestDocument(requestOptions.document)
if (!process.env.TEST_MODE) {
// We skip this while running unit tests because the accessToken is generated using faker
await this.awaitForValidAccessTokenOrNull()
}
try {
const response = await fetch(this.httpUrl, {
method: 'POST',

View File

@@ -1,5 +1,12 @@
# @nhost/hasura-auth-js
## 2.3.1
### Patch Changes
- 7baee8a: fix(hasura-auth-js): replace `jwt-decode` with `jose` for decoding access tokens that works on both the browser and Node.js
- e0ab6d9: fix: add extra logic to check and wait for a valid JWT
## 2.3.0
### Minor Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost/hasura-auth-js",
"version": "2.3.0",
"version": "2.3.1",
"description": "Hasura-auth client",
"license": "MIT",
"keywords": [
@@ -66,8 +66,8 @@
"dependencies": {
"@simplewebauthn/browser": "^6.2.2",
"fetch-ponyfill": "^7.1.0",
"jose": "^5.2.2",
"js-cookie": "^3.0.5",
"jwt-decode": "^3.1.2",
"xstate": "^4.38.3"
},
"devDependencies": {

View File

@@ -1,4 +1,4 @@
import jwt_decode from 'jwt-decode'
import * as jose from 'jose'
import { interpret } from 'xstate'
import {
EMAIL_NEEDS_VERIFICATION,
@@ -625,7 +625,7 @@ export class HasuraAuthClient {
public getDecodedAccessToken(): JWTClaims | null {
const jwt = this.getAccessToken()
if (!jwt) return null
return jwt_decode<JWTClaims>(jwt)
return jose.decodeJwt<JWTClaims>(jwt)
}
/**

View File

@@ -687,9 +687,16 @@ export const createAuthMachine = ({
isAutoRefreshDisabled: () => !autoRefreshToken,
refreshTimerShouldRefresh: (ctx) => {
const { expiresAt } = ctx.accessToken
if (!expiresAt) {
return false
}
// This happens when either the computer goes to sleep or when Chrome descides to suspend the tab
if (expiresAt.getTime() < Date.now()) {
return true
}
if (ctx.refreshTimer.lastAttempt) {
// * If the refresh timer reached the maximum number of attempts, we should not try again
if (ctx.refreshTimer.attempts > REFRESH_TOKEN_MAX_ATTEMPTS) {

View File

@@ -1,5 +1,17 @@
# @nhost/nextjs
## 2.1.4
### Patch Changes
- @nhost/react@3.2.2
## 2.1.3
### Patch Changes
- @nhost/react@3.2.1
## 2.1.2
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost/nextjs",
"version": "2.1.2",
"version": "2.1.4",
"description": "Nhost NextJS library",
"license": "MIT",
"keywords": [

View File

@@ -1,5 +1,21 @@
# @nhost/nhost-js
## 3.0.7
### Patch Changes
- Updated dependencies [2d68fee]
- @nhost/graphql-js@0.1.7
## 3.0.6
### Patch Changes
- Updated dependencies [7baee8a]
- Updated dependencies [e0ab6d9]
- @nhost/hasura-auth-js@2.3.1
- @nhost/graphql-js@0.1.6
## 3.0.5
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost/nhost-js",
"version": "3.0.5",
"version": "3.0.7",
"description": "Nhost JavaScript SDK",
"license": "MIT",
"keywords": [

View File

@@ -1,5 +1,17 @@
# @nhost/react
## 3.2.2
### Patch Changes
- @nhost/nhost-js@3.0.7
## 3.2.1
### Patch Changes
- @nhost/nhost-js@3.0.6
## 3.2.0
### Minor Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost/react",
"version": "3.2.0",
"version": "3.2.2",
"description": "Nhost React library",
"license": "MIT",
"keywords": [

View File

@@ -1,5 +1,17 @@
# @nhost/vue
## 2.2.2
### Patch Changes
- @nhost/nhost-js@3.0.7
## 2.2.1
### Patch Changes
- @nhost/nhost-js@3.0.6
## 2.2.0
### Minor Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost/vue",
"version": "2.2.0",
"version": "2.2.2",
"description": "Nhost Vue library",
"license": "MIT",
"keywords": [

18
pnpm-lock.yaml generated
View File

@@ -1407,6 +1407,9 @@ importers:
graphql-ws:
specifier: ^5.14.3
version: 5.14.3(graphql@16.8.1)
jwt-decode:
specifier: ^3.1.2
version: 3.1.2
devDependencies:
'@apollo/client':
specifier: ^3.9.4
@@ -1575,6 +1578,9 @@ importers:
isomorphic-unfetch:
specifier: ^3.1.0
version: 3.1.0
jwt-decode:
specifier: ^3.1.2
version: 3.1.2
devDependencies:
'@nhost/docgen':
specifier: workspace:*
@@ -1591,12 +1597,12 @@ importers:
fetch-ponyfill:
specifier: ^7.1.0
version: 7.1.0
jose:
specifier: ^5.2.2
version: 5.2.2
js-cookie:
specifier: ^3.0.5
version: 3.0.5
jwt-decode:
specifier: ^3.1.2
version: 3.1.2
xstate:
specifier: ^4.38.3
version: 4.38.3
@@ -5938,7 +5944,7 @@ packages:
/@graphql-tools/utils@8.13.1(graphql@16.8.1):
resolution: {integrity: sha512-qIh9yYpdUFmctVqovwMdheVNJqFh+DQNWIhX87FJStfXYnmweBUDATok9fWPleKeFwxnW8IapKmY8m8toJEkAw==}
peerDependencies:
graphql: '>=16.8.1'
graphql: 16.8.1
dependencies:
graphql: 16.8.1
tslib: 2.6.2
@@ -19441,6 +19447,10 @@ packages:
resolution: {integrity: sha512-W+oqK4H+r5sITxfxpSU+MMdr/YSWGvgZMQDIsNoBDGGy4i7GBPTtvFKibQzW06n3U3TqHjhvBJsirShsEJ6eeQ==}
dev: true
/jose@5.2.2:
resolution: {integrity: sha512-/WByRr4jDcsKlvMd1dRJnPfS1GVO3WuKyaurJ/vvXcOaUQO8rnNObCQMlv/5uCceVQIq5Q4WLF44ohsdiTohdg==}
dev: false
/jpeg-js@0.4.4:
resolution: {integrity: sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==}
dev: true