diff --git a/functions/all-projects.js b/functions/all-projects.js new file mode 100644 index 0000000..019370e --- /dev/null +++ b/functions/all-projects.js @@ -0,0 +1,36 @@ +import { + getFauna, + getProjectPreviewData, + withErrorWrapper, +} from './utils/fauna'; + +exports.handler = withErrorWrapper(async (event, context) => { + const { before, after, size = 24 } = event.queryStringParameters; + const { q, client } = getFauna(); + const result = await client.query( + q.Paginate(q.Match(q.Index('all_projects_sorted_by_date_created')), { + size, + after: after ? parseInt(after) : undefined, + before: before ? parseInt(before) : undefined, + }) + ); + const getAllprojectDataQuery = result.data.map(ref => q.Get(ref[1])); + const allProjects = await client.query(getAllprojectDataQuery); + + return { + statusCode: 200, + body: JSON.stringify({ + after: result.after, + before: result.before, + data: allProjects.map(({ ref, data, ts }, i) => { + return { + ref, + ts, + _id: ref.id, + index: i, + ...getProjectPreviewData(data), + }; + }), + }), + }; +}); diff --git a/functions/my-projects.js b/functions/my-projects.js new file mode 100644 index 0000000..c594de5 --- /dev/null +++ b/functions/my-projects.js @@ -0,0 +1,46 @@ +import { + getFauna, + getProjectPreviewData, + withErrorWrapper, +} from './utils/fauna'; + +exports.handler = withErrorWrapper(async (event, context) => { + const { user } = context.clientContext; + if (!user) { + return { statusCode: 401, body: 'Not logged in' }; + } + + // const { before, after, size = 24 } = event.queryStringParameters; + const { q, client } = getFauna(); + console.log('USER', user); + const result = await client.query( + q.Paginate( + q.Match(q.Index('projectsByUserId'), user.sub) + // { + // size, + // after: after ? [parseInt(after)] : undefined, + // before: before ? [parseInt(before)] : undefined, + // } + ) + ); + + const getAllprojectDataQuery = result.data.map(ref => q.Get(ref)); + const allProjects = await client.query(getAllprojectDataQuery); + + return { + statusCode: 200, + body: JSON.stringify({ + after: result.after, + before: result.before, + data: allProjects.map(({ ref, data, ts }, i) => { + return { + ref, + ts, + _id: ref.id, + index: i, + ...getProjectPreviewData(data), + }; + }), + }), + }; +}); diff --git a/functions/project.js b/functions/project.js new file mode 100644 index 0000000..55ee73c --- /dev/null +++ b/functions/project.js @@ -0,0 +1,80 @@ +import { getFauna, getId, withErrorWrapper } from './utils/fauna'; + +exports.handler = withErrorWrapper(async (event, context) => { + const method = event.httpMethod; + const { q, client } = getFauna(); + const id = getId(event.path); + const { user } = context.clientContext; + if (!id) return { statusCode: 400, body: 'invalid request' }; + + console.log(`Function 'project' invoked. Method: ${method}, Read id: ${id}`); + const projectIdPath = `classes/Project/${id}`; + + const verifyProjectOwner = async (id, user) => { + if (!user) { + throw new Error('Not logged in'); + } + const project = await client.query(q.Get(q.Ref(projectIdPath))); + if (!project) { + throw new Error('Project not found'); + } + const { userId } = project.data; + if (userId !== user.sub) { + throw new Error('Unauthorized'); + } + }; + + switch (method) { + case 'GET': { + const response = await client.query(q.Get(q.Ref(projectIdPath))); + return { statusCode: 200, body: JSON.stringify(response) }; + } + + case 'POST': { + if (!user) { + return { statusCode: 401, body: 'Not logged in' }; + } + const userId = user.sub; + const userName = user.user_metadata.full_name; + const dateCreated = Date.now() * 1000; + const data = JSON.parse(event.body); + const response = await client.query( + q.Create(q.Ref('classes/Project'), { + data: { ...data, userId, userName, dateCreated }, + }) + ); + return { + statusCode: 200, + body: JSON.stringify({ + data: { ...response.data, _id: response.ref.id }, + }), + }; + } + + case 'PATCH': { + try { + await verifyProjectOwner(id, user); + } catch (err) { + return { statusCode: 401, body: JSON.stringify(err) }; + } + const data = JSON.parse(event.body); + const response = await client.query( + q.Update(q.Ref(projectIdPath), { data }) + ); + return { statusCode: 200, body: JSON.stringify(response) }; + } + + case 'DELETE': { + try { + await verifyProjectOwner(id, user); + } catch (err) { + return { statusCode: 401, body: JSON.stringify(err) }; + } + const response = await client.query(q.Delete(q.Ref(projectIdPath))); + return { statusCode: 200, body: JSON.stringify(response) }; + } + + default: + return { statusCode: 400, body: 'invalid method' }; + } +}); diff --git a/functions/utils/errors.js b/functions/utils/errors.js index 5925f5c..580aca3 100644 --- a/functions/utils/errors.js +++ b/functions/utils/errors.js @@ -9,6 +9,13 @@ export const initSentry = () => { }); }; +export const captureError = (error, context) => { + Sentry.withScope(scope => { + scope.setExtra('context', context); + Sentry.captureException(error); + }); +}; + export const objectMap = (object, mapper) => Object.entries(object).reduce( (acc, [key, value]) => ({ diff --git a/functions/utils/fauna.js b/functions/utils/fauna.js new file mode 100644 index 0000000..2446810 --- /dev/null +++ b/functions/utils/fauna.js @@ -0,0 +1,51 @@ +import faunadb from 'faunadb'; +import * as Sentry from '@sentry/node'; +import { initSentry, captureError } from './errors'; + +initSentry(); + +export const getFauna = () => ({ + q: faunadb.query, + client: new faunadb.Client({ + secret: process.env.FAUNADB_SERVER_SECRET, + }), +}); + +export const getId = urlPath => urlPath.match(/([^\/]*)\/*$/)[0]; +export const getUserName = user => user && user.user_metadata.full_name; + +export const getProjectPreviewData = ({ + name, + userName, + shapesList, + dateCreated, +}) => { + return { + name, + userName, + dateCreated, + shapesList, + }; +}; + +export const withErrorWrapper = callback => async (event, context) => { + try { + return await callback(event, context); + } catch (err) { + console.log('GOT ERROR', err.name); + console.log('ERROR MESSAGE:', err.message); + + captureError(err, context); + try { + return { + statusCode: err.requestResult.statusCode, + body: JSON.stringify(err), + }; + } catch (err) { + return { + statusCode: 500, + body: JSON.stringify(err), + }; + } + } +}; diff --git a/package.json b/package.json index b477d9a..d49ef4e 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "color": "^2.0.1", "craco-antd": "^1.19.0", "date-fns": "^2.0.1", + "faunadb": "2.6.1", "file-saver": "2.0.0", "graphql": "^14.5.8", "graphql-request": "^1.8.2", @@ -64,7 +65,7 @@ "upload-schema": "yarn checkForFaunaKey && curl -u $FAUNADB_SERVER_SECRET: https://graphql.fauna.com/import --data-binary \"@schema.gql\"", "start:server": "yarn upload-schema && netlify-lambda serve functions -c ./webpack.config.js", "start:devserver": "NODE_ENV=development yarn start:server", - "build": "npm-run-all --parallel build:**", + "build": "CI=false npm-run-all --parallel build:**", "build:app": "craco build", "build:functions": "netlify-lambda build functions -c ./webpack.config.js", "test": "craco test --env=jsdom", diff --git a/src/utils/middleware.js b/src/utils/middleware.js new file mode 100644 index 0000000..5c3d94b --- /dev/null +++ b/src/utils/middleware.js @@ -0,0 +1,58 @@ +import netlifyIdentity from 'netlify-identity-widget'; +const API_URL = `/.netlify/functions`; + +export const apiDeleteProject = async id => { + const url = `${API_URL}/project/${id}`; + return fetcher(url, { method: 'DELETE' }); +}; + +export const apiPatchProject = async (id, data) => { + const url = `${API_URL}/project/${id}`; + return fetcher(url, { + method: 'PATCH', + body: JSON.stringify(data), + }); +}; + +export const apiPostProject = async data => { + const url = `${API_URL}/project`; + return fetcher(url, { + method: 'POST', + body: JSON.stringify(data), + }); +}; + +export const fetchProject = async id => { + const url = `${API_URL}/project/${id}`; + return fetcher(url); +}; + +export const fetchAllProjects = async pagination => { + const queryParams = new URLSearchParams(pagination); + const url = `${API_URL}/all-projects?${queryParams}`; + return fetcher(url); +}; + +export const fetchMyProjects = async pagination => { + const queryParams = new URLSearchParams(pagination); + const url = `${API_URL}/my-projects?${queryParams}`; + return fetcher(url); +}; + +export const fetcher = async (url, opts) => { + const currentUser = netlifyIdentity.currentUser(); + let token; + if (currentUser) { + token = await currentUser.jwt(); + } + const res = await fetch(url, { + headers: { + authorization: token ? `Bearer ${token}` : '', + }, + ...opts, + }); + if (!res.ok) { + throw new Error(res.statusText); + } + return res.json(); +}; diff --git a/src/views/DiscoverGQL/index.js b/src/views/DiscoverGQL/index.js index e87e5dc..5af3af0 100644 --- a/src/views/DiscoverGQL/index.js +++ b/src/views/DiscoverGQL/index.js @@ -1,34 +1,37 @@ -import React, { useState } from 'react'; -import { useQuery } from '@apollo/react-hooks'; - +import React, { useEffect, useState } from 'react'; import ProjectList from 'components/ProjectList'; import Loading from 'components/Loading'; import PageContainer from 'components/PageContainer'; -import { GET_ALL_PROJECTS_SORTED_BY_DATE_CREATED } from 'graphql/queries'; import ErrorMessage from 'components/ErrorMessage'; +import { fetchAllProjects } from 'utils/middleware'; function DiscoverGQLContainer() { - const pageSize = 24; - const [cursor, setCursor] = useState(null); - const { loading, error, data } = useQuery( - GET_ALL_PROJECTS_SORTED_BY_DATE_CREATED, - { variables: { _size: pageSize, _cursor: cursor } } - ); + const [pagination, setPagination] = useState({}); + const [{ loading, error, data }, setResult] = useState({ loading: true }); + + useEffect(() => { + setResult({ loading: true }); + const fetchData = async () => { + try { + const result = await fetchAllProjects(pagination); + setResult({ loading: false, data: result }); + } catch (error) { + setResult({ loading: false, error }); + } + }; + fetchData(); + }, [pagination]); - if (loading) return ; if (error) return ; + if (!data || loading) return ; - const { - before, - after, - data: projectsData, - } = data.allProjectsSortedByDateCreated; + const { before, after, data: projectsData } = data; const onNextPageClick = () => { - setCursor(after); + setPagination({ after }); }; const onPrevPageClick = () => { - setCursor(before); + setPagination({ before }); }; return ( diff --git a/src/views/ProjectCreate/index.jsx b/src/views/ProjectCreate/index.jsx index 52aea29..7c396cb 100644 --- a/src/views/ProjectCreate/index.jsx +++ b/src/views/ProjectCreate/index.jsx @@ -1,6 +1,6 @@ import React, { useContext } from 'react'; import { withRouter } from 'react-router'; -import { useMutation } from '@apollo/react-hooks'; +// import { useMutation } from '@apollo/react-hooks'; import { DEFAULT_PROJECT, getProjectSaveData } from 'utils/project'; import { CurrentUserContext } from 'context/CurrentUserContext/CurrentUserContextProvider'; @@ -11,31 +11,14 @@ import { showSuccessMessage, } from 'utils/message'; import ProjectContainer from 'components/Project'; -import { CREATE_PROJECT } from 'graphql/mutations'; -import { GET_ALL_PROJECTS, GET_MY_PROJECTS } from 'graphql/queries'; import { ROUTES } from 'Routes'; +import { apiPostProject } from 'utils/middleware'; function ProjectCreate(props) { const { user: currentUser, authenticate } = useContext(CurrentUserContext); const { history } = props; - const [createProjectMutation] = useMutation(CREATE_PROJECT, { - refetchQueries: () => [ - { query: GET_ALL_PROJECTS }, - { query: GET_MY_PROJECTS }, - ], - onError: showErrorMessage, - onCompleted: ({ createProject }) => { - const { _id, name } = createProject; - showSuccessMessage(`Saved "${name}"`); - history.push({ - pathname: `${ROUTES.PROJECT}/${_id}`, - state: { projectData: createProject }, - }); - }, - }); - - const saveProject = project => { + const saveProject = async project => { /* TODO: Automatically save on user login success */ if (!currentUser) { authenticate(); @@ -44,11 +27,18 @@ function ProjectCreate(props) { showLoadingMessage('Saving...'); const projectSaveData = getProjectSaveData(project); - createProjectMutation({ - variables: { - data: projectSaveData, - }, - }); + try { + const { data: savedProject } = await apiPostProject(projectSaveData); + console.log({ savedProject }); + showSuccessMessage(`Saved "${savedProject.name}"`); + history.push({ + pathname: `${ROUTES.PROJECT}/${savedProject._id}`, + state: { projectData: savedProject }, + }); + } catch (error) { + showErrorMessage(error); + return; + } }; const projectProps = { diff --git a/src/views/ProjectEdit/index.jsx b/src/views/ProjectEdit/index.jsx index 771eb13..f1faabb 100644 --- a/src/views/ProjectEdit/index.jsx +++ b/src/views/ProjectEdit/index.jsx @@ -1,6 +1,6 @@ -import React, { useContext } from 'react'; +import React from 'react'; +import { useContext, useEffect, useState } from 'react'; import { CurrentUserContext } from 'context/CurrentUserContext/CurrentUserContextProvider'; -import { useQuery, useMutation } from '@apollo/react-hooks'; import { withRouter } from 'react-router'; import Loading from 'components/Loading'; @@ -14,13 +14,12 @@ import { import { DEFAULT_SYNTHS } from 'utils/synths'; import { ROUTES } from 'Routes'; -import { - GET_PROJECT, - GET_ALL_PROJECTS, - GET_MY_PROJECTS, -} from 'graphql/queries'; -import { UPDATE_PROJECT, DELETE_PROJECT } from 'graphql/mutations'; import ErrorMessage from 'components/ErrorMessage'; +import { + apiDeleteProject, + apiPatchProject, + fetchProject, +} from 'utils/middleware'; function ProjectEdit(props) { const { @@ -33,40 +32,27 @@ function ProjectEdit(props) { console.log('ProjectEdit render. projectId:', projectId); - const { loading, data, error } = useQuery(GET_PROJECT, { - skip: !!projectData, - variables: { id: projectId }, - }); - - const [saveProjectMutation] = useMutation(UPDATE_PROJECT, { - onError: showErrorMessage, - onCompleted: ({ updateProject }) => { - const { name } = updateProject; - showSuccessMessage(`Saved "${name}"`); - // Update local state that may contain old data - history.push({ - state: { projectData: updateProject }, - }); - }, - }); - - const [deleteProjectMutation] = useMutation(DELETE_PROJECT, { - refetchQueries: () => [ - { query: GET_ALL_PROJECTS }, - { query: GET_MY_PROJECTS }, - ], - onError: showErrorMessage, - onCompleted: ({ deleteProject }) => { - const { name } = deleteProject; - showSuccessMessage(`Deleted "${name}"`); - history.push(ROUTES.INDEX); - }, - }); + const [{ loading, data, error }, setResult] = useState({ loading: true }); + + useEffect(() => { + setResult({ loading: true }); + const fetchData = async () => { + try { + const result = await fetchProject(projectId); + console.log({ result }); + setResult({ loading: false, data: result }); + } catch (error) { + setResult({ loading: false, error }); + } + }; + fetchData(); + }, [projectId]); if (loading) return ; if (error) return ; - const project = projectData || data.findProjectByID; + const project = projectData || data.data; + console.log({ project }); const { shapesList, selectedSynths } = project; const newSelectedSynths = @@ -82,21 +68,30 @@ function ProjectEdit(props) { })), }; - const saveProject = project => { + const saveProject = async project => { showLoadingMessage('Saving...'); const projectSaveData = getProjectSaveData(project); - console.log('project save data', projectSaveData); - saveProjectMutation({ - variables: { - id: projectId, - data: projectSaveData, - }, - }); + try { + const updatedProject = await apiPatchProject(projectId, projectSaveData); + showSuccessMessage(`Saved "${updatedProject.data.name}"`); + history.push({ + state: { projectData: updatedProject.data }, + }); + } catch (error) { + showErrorMessage(error.message); + } }; - const deleteProject = () => { + const deleteProject = async () => { showLoadingMessage('Deleting...'); - deleteProjectMutation({ variables: { id: newProjectData._id } }); + try { + const { data: deletedProject } = await apiDeleteProject(projectId); + showSuccessMessage(`Deleted "${deletedProject.name}"`); + history.push(ROUTES.INDEX); + } catch (error) { + showErrorMessage(error.message); + } + // deleteProjectMutation({ variables: { id: newProjectData._id } }); }; const userIsProjectAuthor = diff --git a/src/views/UserProjects/index.js b/src/views/UserProjects/index.js index 3ae0f03..8a2a30d 100644 --- a/src/views/UserProjects/index.js +++ b/src/views/UserProjects/index.js @@ -1,5 +1,4 @@ -import React, { useContext } from 'react'; -import { useQuery } from '@apollo/react-hooks'; +import React, { useContext, useEffect, useState } from 'react'; import { Alert } from 'antd'; import PageContainer from 'components/PageContainer'; @@ -7,12 +6,41 @@ import ProjectList from 'components/ProjectList'; import Loading from 'components/Loading'; import { CurrentUserContext } from 'context/CurrentUserContext/CurrentUserContextProvider'; import { getUserName } from 'utils/user'; -import { GET_MY_PROJECTS } from 'graphql/queries'; import ErrorMessage from 'components/ErrorMessage'; +import { fetchMyProjects } from 'utils/middleware'; function UserProjects() { const { user } = useContext(CurrentUserContext); - const { loading, error, data } = useQuery(GET_MY_PROJECTS, { skip: !user }); + const [pagination, setPagination] = useState({}); + const [{ loading, error, data }, setResult] = useState({ loading: true }); + + useEffect(() => { + setResult({ loading: true }); + const fetchData = async () => { + try { + const result = await fetchMyProjects(pagination); + setResult({ loading: false, data: result }); + } catch (error) { + setResult({ loading: false, error }); + } + }; + fetchData(); + }, [pagination]); + + if (error) return ; + if (!data || loading) return ; + + const { + // before, after, + data: projectsData, + } = data; + + // const onNextPageClick = () => { + // setPagination({ after }); + // }; + // const onPrevPageClick = () => { + // setPagination({ before }); + // }; if (!user) return ( @@ -31,7 +59,11 @@ function UserProjects() { ); diff --git a/yarn.lock b/yarn.lock index 71cf129..8132d37 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3949,6 +3949,11 @@ base64-js@^1.0.2: resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1" integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g== +base64-js@^1.2.0: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + base@^0.11.1: version "0.11.2" resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" @@ -4181,6 +4186,11 @@ bser@^2.0.0: dependencies: node-int64 "^0.4.0" +btoa-lite@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/btoa-lite/-/btoa-lite-1.0.0.tgz#337766da15801210fdd956c22e9c6891ab9d0337" + integrity sha512-gvW7InbIyF8AicrqWoptdW08pUxuhq8BEgowNajy9RhiE86fmGAGl+bLKo6oB8QP0CkqHLowfN0oJdKC/J6LbA== + buffer-from@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" @@ -4302,6 +4312,17 @@ call-bind@^1.0.0: function-bind "^1.1.1" get-intrinsic "^1.0.0" +call-bind@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" + integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + set-function-length "^1.2.1" + call-me-maybe@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.1.tgz#26d208ea89e37b5cbde60250a15f031c16a4d66b" @@ -4665,6 +4686,11 @@ component-classes@^1.2.5: dependencies: component-indexof "0.0.3" +component-emitter@^1.2.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.1.tgz#ef1d5796f7d93f135ee6fb684340b26403c97d17" + integrity sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ== + component-emitter@^1.2.1: version "1.3.0" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" @@ -4798,6 +4824,11 @@ cookie@^0.3.1: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" integrity sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s= +cookiejar@^2.1.0: + version "2.1.4" + resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.4.tgz#ee669c1fea2cf42dc31585469d193fef0d65771b" + integrity sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw== + copy-concurrently@^1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz#92297398cae34937fcafd6ec8139c18051f0b5e0" @@ -5288,6 +5319,13 @@ debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: dependencies: ms "^2.1.1" +debug@^3.1.0: + version "3.2.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== + dependencies: + ms "^2.1.1" + debug@^3.1.1, debug@^3.2.5, debug@^3.2.6: version "3.2.6" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" @@ -5335,6 +5373,15 @@ default-gateway@^4.2.0: execa "^1.0.0" ip-regex "^2.1.0" +define-data-property@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" + integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + gopd "^1.0.1" + define-properties@^1.1.2, define-properties@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" @@ -5782,6 +5829,18 @@ es-abstract@^1.18.0-next.0, es-abstract@^1.18.0-next.1: string.prototype.trimend "^1.0.1" string.prototype.trimstart "^1.0.1" +es-define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" + integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== + dependencies: + get-intrinsic "^1.2.4" + +es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + es-to-primitive@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.0.tgz#edf72478033456e8dda8ef09e00ad9650707f377" @@ -5818,6 +5877,11 @@ es6-iterator@2.0.3, es6-iterator@~2.0.3: es5-ext "^0.10.35" es6-symbol "^3.1.1" +es6-promise@^4.1.1: + version "4.2.8" + resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" + integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== + es6-symbol@^3.1.1, es6-symbol@~3.1.2: version "3.1.3" resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" @@ -6242,7 +6306,7 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2: assign-symbols "^1.0.0" is-extendable "^1.0.1" -extend@~3.0.2: +extend@^3.0.0, extend@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== @@ -6335,6 +6399,19 @@ fastq@^1.6.0: dependencies: reusify "^1.0.0" +faunadb@2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/faunadb/-/faunadb-2.6.1.tgz#90783b32da13dd5500a873aca2629543eaa8d51f" + integrity sha512-n/aRPDqrE7Sor7qzNDvZHmM7nEw0wERZZPjXRx1sBS3+9mJAscFa4zOQMgbh+pTz8M3r6Djd3vxl11JLn3eSMA== + dependencies: + base64-js "^1.2.0" + btoa-lite "^1.0.0" + es6-promise "^4.1.1" + fn-annotate "^1.1.3" + object-assign "^4.1.0" + superagent "^3.8.1" + util-deprecate "^1.0.2" + faye-websocket@^0.10.0: version "0.10.0" resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4" @@ -6525,6 +6602,11 @@ flush-write-stream@^1.0.0: inherits "^2.0.3" readable-stream "^2.3.6" +fn-annotate@^1.1.3: + version "1.2.0" + resolved "https://registry.yarnpkg.com/fn-annotate/-/fn-annotate-1.2.0.tgz#28da000117dea61842fe61f353f41cf4c93a7a7e" + integrity sha512-j2gv2wkRhQgkJNf1ygdca8ynP3tK+a87bowc+RG81iWTye3yKIOeAkrKYv0Kqyh8yCeSyljOk3ZFelfXUFpirA== + follow-redirects@^1.0.0: version "1.14.8" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.8.tgz#016996fb9a11a100566398b1c6839337d7bfa8fc" @@ -6566,6 +6648,15 @@ fork-ts-checker-webpack-plugin@3.1.1: tapable "^1.0.0" worker-rpc "^0.1.0" +form-data@^2.3.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.1.tgz#f2cbec57b5e59e23716e128fe44d4e5dd23895f4" + integrity sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" + form-data@~2.3.2: version "2.3.3" resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" @@ -6575,6 +6666,11 @@ form-data@~2.3.2: combined-stream "^1.0.6" mime-types "^2.1.12" +formidable@^1.2.0: + version "1.2.6" + resolved "https://registry.yarnpkg.com/formidable/-/formidable-1.2.6.tgz#d2a51d60162bbc9b4a055d8457a7c75315d1a168" + integrity sha512-KcpbcpuLNOwrEjnbpMC0gS+X8ciDoZE1kkqzat4a8vrprf+s9pKNQ/QIwWfbfs4ltgmFl3MD177SNTkve3BwGQ== + forwarded@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" @@ -6696,6 +6792,11 @@ function-bind@^1.0.2, function-bind@^1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + functional-red-black-tree@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" @@ -6734,6 +6835,17 @@ get-intrinsic@^1.0.0: has "^1.0.3" has-symbols "^1.0.1" +get-intrinsic@^1.1.3, get-intrinsic@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" + integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + has-proto "^1.0.1" + has-symbols "^1.0.3" + hasown "^2.0.0" + get-own-enumerable-property-symbols@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.1.tgz#6f7764f88ea11e0b514bd9bd860a132259992ca4" @@ -6875,6 +6987,13 @@ globby@^6.1.0: pify "^2.0.0" pinkie-promise "^2.0.0" +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" + graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0: version "4.2.3" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423" @@ -6992,6 +7111,18 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== +has-property-descriptors@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" + integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== + dependencies: + es-define-property "^1.0.0" + +has-proto@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd" + integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q== + has-symbols@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.0.tgz#ba1a8f1af2a0fc39650f5c850367704122063b44" @@ -7002,6 +7133,11 @@ has-symbols@^1.0.1: resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg== +has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + has-unicode@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" @@ -7061,6 +7197,13 @@ hash.js@^1.0.0, hash.js@^1.0.3: inherits "^2.0.3" minimalistic-assert "^1.0.1" +hasown@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + he@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" @@ -9011,7 +9154,7 @@ merge2@^1.2.3, merge2@^1.3.0: resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.3.0.tgz#5b366ee83b2f1582c48f87e47cf1a9352103ca81" integrity sha512-2j4DAdlBOkiSZIsaXk4mTE3sRS02yBHAtfy127xRV3bQUFqXkjHCHLW6Scv7DwNRbIWNHH8zpnz9zMaKXIdvYw== -methods@~1.1.2: +methods@^1.1.1, methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= @@ -9628,6 +9771,11 @@ object-hash@^2.0.1: resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-2.0.3.tgz#d12db044e03cd2ca3d77c0570d87225b02e1e6ea" integrity sha512-JPKn0GMu+Fa3zt3Bmr66JhokJU5BaNBIh4ZeTlaCBzrBsOeXzwcKKAK1tbLiPKgvwmPXsDvvLHoWh5Bm7ofIYg== +object-inspect@^1.13.1: + version "1.13.1" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" + integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== + object-inspect@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.6.0.tgz#c70b6cbf72f274aab4c34c0c82f5167bf82cf15b" @@ -11111,6 +11259,13 @@ qs@6.7.0: resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== +qs@^6.5.1: + version "6.12.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.12.0.tgz#edd40c3b823995946a8a0b1f208669c7a200db77" + integrity sha512-trVZiI6RMOkO476zLGaBIzszOdFPnCCXHPG9kn0yuS1uz6xdVxPfZdB3vUig9pxPFDM9BRAgz/YUIVQ1/vuiUg== + dependencies: + side-channel "^1.0.6" + qs@~6.5.2: version "6.5.3" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.3.tgz#3aeeffc91967ef6e35c0e488ef46fb296ab76aad" @@ -11975,6 +12130,19 @@ read-pkg@^3.0.0: string_decoder "~1.1.1" util-deprecate "~1.0.1" +readable-stream@^2.3.5: + version "2.3.8" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" + integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + readable-stream@^3.0.6, readable-stream@^3.1.1: version "3.4.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.4.0.tgz#a51c26754658e0a3c21dbf59163bd45ba6f447fc" @@ -12605,6 +12773,18 @@ set-blocking@^2.0.0, set-blocking@~2.0.0: resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= +set-function-length@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" + integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + gopd "^1.0.1" + has-property-descriptors "^1.0.2" + set-immediate-shim@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" @@ -12714,6 +12894,16 @@ side-channel@^1.0.2: es-abstract "^1.18.0-next.0" object-inspect "^1.8.0" +side-channel@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2" + integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA== + dependencies: + call-bind "^1.0.7" + es-errors "^1.3.0" + get-intrinsic "^1.2.4" + object-inspect "^1.13.1" + signal-exit@^3.0.0, signal-exit@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" @@ -13231,6 +13421,22 @@ subscriptions-transport-ws@^0.9.11: symbol-observable "^1.0.4" ws "^5.2.0" +superagent@^3.8.1: + version "3.8.3" + resolved "https://registry.yarnpkg.com/superagent/-/superagent-3.8.3.tgz#460ea0dbdb7d5b11bc4f78deba565f86a178e128" + integrity sha512-GLQtLMCoEIK4eDv6OGtkOoSMt3D+oq0y3dsxMuYuDvaNUvuT8eFBuLmfR0iYYzHC1e8hpzC6ZsxbuP6DIalMFA== + dependencies: + component-emitter "^1.2.0" + cookiejar "^2.1.0" + debug "^3.1.0" + extend "^3.0.0" + form-data "^2.3.1" + formidable "^1.2.0" + methods "^1.1.1" + mime "^1.4.1" + qs "^6.5.1" + readable-stream "^2.3.5" + supports-color@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"