diff --git a/app/javascript/wardrobe-2020-page.js b/app/javascript/wardrobe-2020-page.js
index e2688984..7f428d79 100644
--- a/app/javascript/wardrobe-2020-page.js
+++ b/app/javascript/wardrobe-2020-page.js
@@ -1,7 +1,19 @@
import React from "react";
import ReactDOM from "react-dom";
+import { loadErrorMessages, loadDevMessages } from "@apollo/client/dev";
-import { WardrobePage } from "./wardrobe-2020";
+import { AppProvider, WardrobePage } from "./wardrobe-2020";
+
+// Use Apollo's error messages in development.
+if (process.env["NODE_ENV"] === "development") {
+ loadErrorMessages();
+ loadDevMessages();
+}
const rootNode = document.querySelector("#wardrobe-2020-root");
-ReactDOM.render(, rootNode);
+ReactDOM.render(
+
+
+ ,
+ rootNode
+);
diff --git a/app/javascript/wardrobe-2020/AppProvider.js b/app/javascript/wardrobe-2020/AppProvider.js
new file mode 100644
index 00000000..a4512c85
--- /dev/null
+++ b/app/javascript/wardrobe-2020/AppProvider.js
@@ -0,0 +1,157 @@
+import React from "react";
+import * as Sentry from "@sentry/react";
+import { Integrations } from "@sentry/tracing";
+import { Auth0Provider } from "@auth0/auth0-react";
+import { CSSReset, ChakraProvider, extendTheme } from "@chakra-ui/react";
+import { ApolloProvider } from "@apollo/client";
+import { useAuth0 } from "@auth0/auth0-react";
+import { mode } from "@chakra-ui/theme-tools";
+import { BrowserRouter } from "react-router-dom";
+
+import buildApolloClient from "./apolloClient";
+
+const theme = extendTheme({
+ styles: {
+ global: (props) => ({
+ html: {
+ // HACK: Chakra sets body as the relative position element, which is
+ // fine, except its `min-height: 100%` doesn't actually work
+ // unless paired with height on the root element too!
+ height: "100%",
+ },
+ body: {
+ background: mode("gray.50", "gray.800")(props),
+ color: mode("green.800", "green.50")(props),
+ transition: "all 0.25s",
+ },
+ }),
+ },
+});
+
+export default function AppProvider({ children }) {
+ React.useEffect(() => setupLogging(), []);
+
+ return (
+
+
+
+
+
+ {children}
+
+
+
+
+ );
+}
+
+function DTIApolloProvider({ children, additionalCacheState = {} }) {
+ const auth0 = useAuth0();
+ const auth0Ref = React.useRef(auth0);
+
+ React.useEffect(() => {
+ auth0Ref.current = auth0;
+ }, [auth0]);
+
+ // Save the first `additionalCacheState` we get as our `initialCacheState`,
+ // which we'll use to initialize the client without having to wait a tick.
+ const [initialCacheState, unusedSetInitialCacheState] =
+ React.useState(additionalCacheState);
+
+ const client = React.useMemo(
+ () =>
+ buildApolloClient({
+ getAuth0: () => auth0Ref.current,
+ initialCacheState,
+ }),
+ [initialCacheState]
+ );
+
+ // When we get a new `additionalCacheState` object, merge it into the cache:
+ // copy the previous cache state, merge the new cache state's entries in,
+ // and "restore" the new merged cache state.
+ //
+ // HACK: Using `useMemo` for this is a dastardly trick!! What we want is the
+ // semantics of `useEffect` kinda, but we need to ensure it happens
+ // *before* all the children below get rendered, so they don't fire off
+ // unnecessary network requests. Using `useMemo` but throwing away the
+ // result kinda does that. It's evil! It's nasty! It's... perfect?
+ // (This operation is safe to run multiple times too, in case memo
+ // re-runs it. It's just, y'know, a performance loss. Maybe it's
+ // actually kinda perfect lol)
+ //
+ // I feel like there's probably a better way to do this... like, I want
+ // the semantic of replacing this client with an updated client - but I
+ // don't want to actually replace the client, because that'll break
+ // other kinds of state, like requests loading in the shared layout.
+ // Idk! I'll see how it goes!
+ React.useMemo(() => {
+ const previousCacheState = client.cache.extract();
+ const mergedCacheState = { ...previousCacheState };
+ for (const key of Object.keys(additionalCacheState)) {
+ mergedCacheState[key] = {
+ ...mergedCacheState[key],
+ ...additionalCacheState[key],
+ };
+ }
+ console.debug(
+ "Merging Apollo cache:",
+ additionalCacheState,
+ mergedCacheState
+ );
+ client.cache.restore(mergedCacheState);
+ }, [client, additionalCacheState]);
+
+ return {children};
+}
+
+function setupLogging() {
+ Sentry.init({
+ dsn: "https://c55875c3b0904264a1a99e5b741a221e@o506079.ingest.sentry.io/5595379",
+ autoSessionTracking: true,
+ integrations: [
+ new Integrations.BrowserTracing({
+ beforeNavigate: (context) => ({
+ ...context,
+ // Assume any path segment starting with a digit is an ID, and replace
+ // it with `:id`. This will help group related routes in Sentry stats.
+ // NOTE: I'm a bit uncertain about the timing on this for tracking
+ // client-side navs... but we now only track first-time
+ // pageloads, and it definitely works correctly for them!
+ name: window.location.pathname.replaceAll(/\/[0-9][^/]*/g, "/:id"),
+ }),
+
+ // We have a _lot_ of location changes that don't actually signify useful
+ // navigations, like in the wardrobe page. It could be useful to trace
+ // them with better filtering someday, but frankly we don't use the perf
+ // features besides Web Vitals right now, and those only get tracked on
+ // first-time pageloads, anyway. So, don't track client-side navs!
+ startTransactionOnLocationChange: false,
+ }),
+ ],
+ denyUrls: [
+ // Don't log errors that were probably triggered by extensions and not by
+ // our own app. (Apparently Sentry's setting to ignore browser extension
+ // errors doesn't do this anywhere near as consistently as I'd expect?)
+ //
+ // Adapted from https://gist.github.com/impressiver/5092952, as linked in
+ // https://docs.sentry.io/platforms/javascript/configuration/filtering/.
+ /^chrome-extension:\/\//,
+ /^moz-extension:\/\//,
+ ],
+
+ // Since we're only tracking first-page loads and not navigations, 100%
+ // sampling isn't actually so much! Tune down if it becomes a problem, tho.
+ tracesSampleRate: 1.0,
+ });
+}
diff --git a/app/javascript/wardrobe-2020/ItemPage.js b/app/javascript/wardrobe-2020/ItemPage.js
index 0d655421..d53211e9 100644
--- a/app/javascript/wardrobe-2020/ItemPage.js
+++ b/app/javascript/wardrobe-2020/ItemPage.js
@@ -854,7 +854,7 @@ function ItemPageOutfitPreview({ itemId }) {
name
restrictedZones {
id
- label @client
+ label
}
compatibleBodiesAndTheirZones {
body {
@@ -867,7 +867,7 @@ function ItemPageOutfitPreview({ itemId }) {
}
zones {
id
- label @client
+ label
}
}
canonicalAppearance(
diff --git a/app/javascript/wardrobe-2020/WardrobePage/index.js b/app/javascript/wardrobe-2020/WardrobePage/index.js
index 4ace189c..5c57ad45 100644
--- a/app/javascript/wardrobe-2020/WardrobePage/index.js
+++ b/app/javascript/wardrobe-2020/WardrobePage/index.js
@@ -80,40 +80,34 @@ function WardrobePage() {
// that need it, where it's more useful and more performant to access
// via context.
return (
- <>
-
-
-
-
-
-
- }
- itemsAndMaybeSearchPanel={
-
- }
- searchFooter={
-
- }
- />
-
- >
+
+
+ }
+ itemsAndMaybeSearchPanel={
+
+ }
+ searchFooter={
+
+ }
+ />
+
);
}
diff --git a/app/javascript/wardrobe-2020/WardrobePage/support/PosePickerSupport.js b/app/javascript/wardrobe-2020/WardrobePage/support/PosePickerSupport.js
index 8302057e..4791e0b0 100644
--- a/app/javascript/wardrobe-2020/WardrobePage/support/PosePickerSupport.js
+++ b/app/javascript/wardrobe-2020/WardrobePage/support/PosePickerSupport.js
@@ -46,7 +46,7 @@ function PosePickerSupport({
id
zone {
id
- label @client
+ label
}
# For AppearanceLayerSupportModal
@@ -59,7 +59,7 @@ function PosePickerSupport({
}
restrictedZones {
id
- label @client
+ label
}
# For AppearanceLayerSupportModal to know the name
diff --git a/app/javascript/wardrobe-2020/WardrobePage/useOutfitState.js b/app/javascript/wardrobe-2020/WardrobePage/useOutfitState.js
index 5d26c4f9..a27b394b 100644
--- a/app/javascript/wardrobe-2020/WardrobePage/useOutfitState.js
+++ b/app/javascript/wardrobe-2020/WardrobePage/useOutfitState.js
@@ -2,6 +2,7 @@ import React from "react";
import gql from "graphql-tag";
import produce, { enableMapSet } from "immer";
import { useQuery, useApolloClient } from "@apollo/client";
+import { useSearchParams } from "react-router-dom";
import { itemAppearanceFragment } from "../components/useOutfitAppearance";
@@ -156,13 +157,13 @@ function useOutfitState() {
layers {
zone {
id
- label @client
+ label
}
}
restrictedZones {
id
- label @client
- isCommonlyUsedByItems @client
+ label
+ isCommonlyUsedByItems
}
}
}
@@ -387,7 +388,7 @@ function useParseOutfitUrl() {
// stable object!
const memoizedOutfitState = React.useMemo(
() => readOutfitStateFromSearchParams(searchParams),
- [query]
+ [searchParams]
);
return memoizedOutfitState;
diff --git a/app/javascript/wardrobe-2020/WardrobePage/useSearchResults.js b/app/javascript/wardrobe-2020/WardrobePage/useSearchResults.js
index 1dea94fc..a4e39b92 100644
--- a/app/javascript/wardrobe-2020/WardrobePage/useSearchResults.js
+++ b/app/javascript/wardrobe-2020/WardrobePage/useSearchResults.js
@@ -87,13 +87,13 @@ export function useSearchResults(
layers {
zone {
id
- label @client
+ label
}
}
restrictedZones {
id
- label @client
- isCommonlyUsedByItems @client
+ label
+ isCommonlyUsedByItems
}
}
}
diff --git a/app/javascript/wardrobe-2020/apolloClient.js b/app/javascript/wardrobe-2020/apolloClient.js
new file mode 100644
index 00000000..ca3a2f0c
--- /dev/null
+++ b/app/javascript/wardrobe-2020/apolloClient.js
@@ -0,0 +1,230 @@
+import { ApolloClient, createHttpLink, InMemoryCache } from "@apollo/client";
+import { setContext } from "@apollo/client/link/context";
+import { createPersistedQueryLink } from "apollo-link-persisted-queries";
+
+import { getAuthModeFeatureFlag } from "./components/useCurrentUser";
+
+// Teach Apollo to load certain fields from the cache, to avoid extra network
+// requests. This happens a lot - e.g. reusing data from item search on the
+// outfit immediately!
+const typePolicies = {
+ Query: {
+ fields: {
+ closetList: (_, { args, toReference }) => {
+ return toReference({ __typename: "ClosetList", id: args.id }, true);
+ },
+ items: (_, { args, toReference }) => {
+ return args.ids.map((id) =>
+ toReference({ __typename: "Item", id }, true)
+ );
+ },
+ item: (_, { args, toReference }) => {
+ return toReference({ __typename: "Item", id: args.id }, true);
+ },
+ petAppearanceById: (_, { args, toReference }) => {
+ return toReference({ __typename: "PetAppearance", id: args.id }, true);
+ },
+ species: (_, { args, toReference }) => {
+ return toReference({ __typename: "Species", id: args.id }, true);
+ },
+ color: (_, { args, toReference }) => {
+ return toReference({ __typename: "Color", id: args.id }, true);
+ },
+ outfit: (_, { args, toReference }) => {
+ return toReference({ __typename: "Outfit", id: args.id }, true);
+ },
+ user: (_, { args, toReference }) => {
+ return toReference({ __typename: "User", id: args.id }, true);
+ },
+ },
+ },
+
+ Item: {
+ fields: {
+ appearanceOn: (appearance, { args, readField, toReference }) => {
+ // If we already have this exact appearance in the cache, serve it!
+ if (appearance) {
+ return appearance;
+ }
+
+ // Otherwise, we're going to see if this is a standard color, in which
+ // case we can reuse the standard color appearance if we already have
+ // it! This helps for fast loading when switching between standard
+ // colors.
+ const { speciesId, colorId } = args;
+ console.debug(
+ "[appearanceOn] seeking cached appearance",
+ speciesId,
+ colorId,
+ readField("id")
+ );
+ const speciesStandardBodyId = readField(
+ "standardBodyId",
+ toReference({ __typename: "Species", id: speciesId })
+ );
+ const colorIsStandard = readField(
+ "isStandard",
+ toReference({ __typename: "Color", id: colorId })
+ );
+ if (speciesStandardBodyId == null || colorIsStandard == null) {
+ // We haven't loaded all the species/colors into cache yet. We might
+ // be loading them, depending on the page? Either way, return
+ // `undefined`, meaning we don't know how to serve this from cache.
+ // This will cause us to start loading it from the server.
+ console.debug("[appearanceOn] species/colors not loaded yet");
+ return undefined;
+ }
+
+ if (colorIsStandard) {
+ const itemId = readField("id");
+ console.debug(
+ "[appearanceOn] standard color, will read:",
+ `item-${itemId}-body-${speciesStandardBodyId}`
+ );
+ return toReference({
+ __typename: "ItemAppearance",
+ id: `item-${itemId}-body-${speciesStandardBodyId}`,
+ });
+ } else {
+ console.debug("[appearanceOn] non-standard color, failure");
+ // This isn't a standard color, so we don't support special
+ // cross-color caching for it. Return `undefined`, meaning we don't
+ // know how to serve this from cache. This will cause us to start
+ // loading it from the server.
+ return undefined;
+ }
+ },
+
+ currentUserOwnsThis: (cachedValue, { readField }) => {
+ if (cachedValue != null) {
+ return cachedValue;
+ }
+
+ // Do we know what items this user owns? If so, scan for this item.
+ const currentUserRef = readField("currentUser", {
+ __ref: "ROOT_QUERY",
+ });
+ if (!currentUserRef) {
+ return undefined;
+ }
+ const thisItemId = readField("id");
+ const itemsTheyOwn = readField("itemsTheyOwn", currentUserRef);
+ if (!itemsTheyOwn) {
+ return undefined;
+ }
+
+ const theyOwnThisItem = itemsTheyOwn.some(
+ (itemRef) => readField("id", itemRef) === thisItemId
+ );
+ return theyOwnThisItem;
+ },
+ currentUserWantsThis: (cachedValue, { readField }) => {
+ if (cachedValue != null) {
+ return cachedValue;
+ }
+
+ // Do we know what items this user owns? If so, scan for this item.
+ const currentUserRef = readField("currentUser", {
+ __ref: "ROOT_QUERY",
+ });
+ if (!currentUserRef) {
+ return undefined;
+ }
+ const thisItemId = readField("id");
+ const itemsTheyWant = readField("itemsTheyWant", currentUserRef);
+ if (!itemsTheyWant) {
+ return undefined;
+ }
+
+ const theyWantThisItem = itemsTheyWant.some(
+ (itemRef) => readField("id", itemRef) === thisItemId
+ );
+ return theyWantThisItem;
+ },
+ },
+ },
+
+ ClosetList: {
+ fields: {
+ // When loading the updated contents of a list, replace it entirely.
+ items: { merge: false },
+ },
+ },
+};
+
+const httpLink = createHttpLink({
+ uri: "https://impress-2020.openneo.net/api/graphql",
+});
+const buildAuthLink = (getAuth0) =>
+ setContext(async (_, { headers = {}, sendAuth = false }) => {
+ if (!sendAuth) {
+ return;
+ }
+
+ const token = await getAccessToken(getAuth0);
+ if (token) {
+ return {
+ headers: {
+ ...headers,
+ authorization: token ? `Bearer ${token}` : "",
+ },
+ };
+ }
+ });
+
+// This is a temporary way to pass the DTIAuthMode feature flag back to the
+// server!
+const authModeLink = setContext((_, { headers = {} }) => {
+ const authMode = getAuthModeFeatureFlag();
+ return {
+ headers: {
+ ...headers,
+ "DTI-Auth-Mode": authMode,
+ },
+ };
+});
+
+async function getAccessToken(getAuth0) {
+ // Wait for auth0 to stop loading, so we can maybe get a token!
+ // We'll do this hackily by checking every 100ms until it's true.
+ await new Promise((resolve) => {
+ function check() {
+ if (getAuth0().isLoading) {
+ setTimeout(check, 100);
+ } else {
+ resolve();
+ }
+ }
+ check();
+ });
+
+ const { isAuthenticated, getAccessTokenSilently } = getAuth0();
+ if (isAuthenticated) {
+ const token = await getAccessTokenSilently();
+ return token;
+ }
+}
+
+const buildLink = (getAuth0) =>
+ buildAuthLink(getAuth0)
+ .concat(authModeLink)
+ .concat(
+ createPersistedQueryLink({
+ useGETForHashedQueries: true,
+ })
+ )
+ .concat(httpLink);
+
+/**
+ * apolloClient is the global Apollo Client instance we use for GraphQL
+ * queries. This is how we communicate with the server!
+ */
+const buildClient = ({ getAuth0, initialCacheState }) => {
+ return new ApolloClient({
+ link: buildLink(getAuth0),
+ cache: new InMemoryCache({ typePolicies }).restore(initialCacheState),
+ connectToDevTools: true,
+ });
+};
+
+export default buildClient;
diff --git a/app/javascript/wardrobe-2020/components/SpeciesColorPicker.js b/app/javascript/wardrobe-2020/components/SpeciesColorPicker.js
index 0dd915f1..f6477dd9 100644
--- a/app/javascript/wardrobe-2020/components/SpeciesColorPicker.js
+++ b/app/javascript/wardrobe-2020/components/SpeciesColorPicker.js
@@ -31,7 +31,11 @@ function SpeciesColorPicker({
colorTestId = null,
onChange,
}) {
- const { loading: loadingMeta, error: errorMeta, data: meta } = useQuery(gql`
+ const {
+ loading: loadingMeta,
+ error: errorMeta,
+ data: meta,
+ } = useQuery(gql`
query SpeciesColorPicker {
allSpecies {
id
@@ -340,11 +344,14 @@ let cachedResponseForAllValidPetPoses = null;
* data from GraphQL serves on the first render, without a loading state.
*/
export function useAllValidPetPoses() {
- const networkResponse = useFetch("/api/validPetPoses", {
- responseType: "arrayBuffer",
- // If we already have globally-cached valids, skip the request.
- skip: cachedResponseForAllValidPetPoses != null,
- });
+ const networkResponse = useFetch(
+ "https://impress-2020.openneo.net/api/validPetPoses",
+ {
+ responseType: "arrayBuffer",
+ // If we already have globally-cached valids, skip the request.
+ skip: cachedResponseForAllValidPetPoses != null,
+ }
+ );
// Use the globally-cached response if we have one, or await the network
// response if not.
diff --git a/app/javascript/wardrobe-2020/components/getVisibleLayers.js b/app/javascript/wardrobe-2020/components/getVisibleLayers.js
index 4a2aab99..13be0014 100644
--- a/app/javascript/wardrobe-2020/components/getVisibleLayers.js
+++ b/app/javascript/wardrobe-2020/components/getVisibleLayers.js
@@ -94,8 +94,6 @@ function getVisibleLayers(petAppearance, itemAppearances) {
return visibleLayers;
}
-// TODO: The web client could save bandwidth by applying @client to the `depth`
-// field, because it already has zone depths cached.
export const itemAppearanceFragmentForGetVisibleLayers = gql`
fragment ItemAppearanceForGetVisibleLayers on ItemAppearance {
id
@@ -113,8 +111,6 @@ export const itemAppearanceFragmentForGetVisibleLayers = gql`
}
`;
-// TODO: The web client could save bandwidth by applying @client to the `depth`
-// field, because it already has zone depths cached.
export const petAppearanceFragmentForGetVisibleLayers = gql`
fragment PetAppearanceForGetVisibleLayers on PetAppearance {
id
diff --git a/app/javascript/wardrobe-2020/components/useOutfitAppearance.js b/app/javascript/wardrobe-2020/components/useOutfitAppearance.js
index 69394282..8d94da34 100644
--- a/app/javascript/wardrobe-2020/components/useOutfitAppearance.js
+++ b/app/javascript/wardrobe-2020/components/useOutfitAppearance.js
@@ -136,8 +136,8 @@ export const appearanceLayerFragment = gql`
knownGlitches # For HTML5 & Known Glitches UI
zone {
id
- depth @client
- label @client
+ depth
+ label
}
}
`;
@@ -149,7 +149,7 @@ export const appearanceLayerFragmentForSupport = gql`
swfUrl # HACK: This is for Support tools, but other views don't need it
zone {
id
- label @client # HACK: This is for Support tools, but other views don't need it
+ label # HACK: This is for Support tools, but other views don't need it
}
}
`;
diff --git a/app/javascript/wardrobe-2020/index.js b/app/javascript/wardrobe-2020/index.js
index c5122a19..b95e0699 100644
--- a/app/javascript/wardrobe-2020/index.js
+++ b/app/javascript/wardrobe-2020/index.js
@@ -1,3 +1,4 @@
+import AppProvider from "./AppProvider";
import WardrobePage from "./WardrobePage";
-export { WardrobePage };
+export { AppProvider, WardrobePage };
diff --git a/package.json b/package.json
index ccd70847..73286ae2 100644
--- a/package.json
+++ b/package.json
@@ -9,6 +9,8 @@
"@emotion/styled": "^11.0.0",
"@loadable/component": "^5.12.0",
"@sentry/react": "^5.30.0",
+ "@sentry/tracing": "^5.30.0",
+ "apollo-link-persisted-queries": "^0.2.2",
"easeljs": "^1.0.2",
"esbuild": "^0.19.0",
"framer-motion": "^4.1.11",
@@ -25,6 +27,7 @@
"tweenjs": "^1.0.2"
},
"scripts": {
- "build": "esbuild app/javascript/*.* --bundle --sourcemap --outdir=app/assets/builds --public-path=/assets --loader:.js=jsx --loader:.png=file --loader:.svg=file --loader:.min.js=text"
+ "build": "esbuild app/javascript/*.* --bundle --sourcemap --outdir=app/assets/builds --public-path=/assets --loader:.js=jsx --loader:.png=file --loader:.svg=file --loader:.min.js=text",
+ "build:production": "yarn build --minify"
}
}
diff --git a/yarn.lock b/yarn.lock
index 52c93b31..19ac328f 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -985,6 +985,17 @@
hoist-non-react-statics "^3.3.2"
tslib "^1.9.3"
+"@sentry/tracing@^5.30.0":
+ version "5.30.0"
+ resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-5.30.0.tgz#501d21f00c3f3be7f7635d8710da70d9419d4e1f"
+ integrity sha512-dUFowCr0AIMwiLD7Fs314Mdzcug+gBVo/+NCMyDw8tFxJkwWAKl7Qa2OZxLQ0ZHjakcj1hNKfCQJ9rhyfOl4Aw==
+ dependencies:
+ "@sentry/hub" "5.30.0"
+ "@sentry/minimal" "5.30.0"
+ "@sentry/types" "5.30.0"
+ "@sentry/utils" "5.30.0"
+ tslib "^1.9.3"
+
"@sentry/types@5.30.0":
version "5.30.0"
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-5.30.0.tgz#19709bbe12a1a0115bc790b8942917da5636f402"
@@ -1046,6 +1057,13 @@
dependencies:
tslib "^2.3.0"
+"@wry/equality@^0.1.2":
+ version "0.1.11"
+ resolved "https://registry.yarnpkg.com/@wry/equality/-/equality-0.1.11.tgz#35cb156e4a96695aa81a9ecc4d03787bc17f1790"
+ integrity sha512-mwEVBDUVODlsQQ5dfuLUS5/Tf7jqUKyhKYHmVi4fPB6bDMOfWvUPJmKgS1Z7Za/sOI3vzWt4+O7yCiL/70MogA==
+ dependencies:
+ tslib "^1.9.3"
+
"@wry/equality@^0.5.6":
version "0.5.6"
resolved "https://registry.yarnpkg.com/@wry/equality/-/equality-0.5.6.tgz#cd4a533c72c3752993ab8cbf682d3d20e3cb601e"
@@ -1072,6 +1090,34 @@ ansi-styles@^3.2.1:
dependencies:
color-convert "^1.9.0"
+apollo-link-persisted-queries@^0.2.2:
+ version "0.2.5"
+ resolved "https://registry.yarnpkg.com/apollo-link-persisted-queries/-/apollo-link-persisted-queries-0.2.5.tgz#76deabf68dac218d83f2fa23eebc3b25772fd914"
+ integrity sha512-PYWsMFcRGT9NZ6e6EK5rlhNDtcK6FR76JDy1RIngEfR6RdM5a2Z0IhZdn9RTTNB3V/+s7iWviQmoCfQrTVXu0A==
+ dependencies:
+ apollo-link "^1.2.1"
+ hash.js "^1.1.7"
+
+apollo-link@^1.2.1:
+ version "1.2.14"
+ resolved "https://registry.yarnpkg.com/apollo-link/-/apollo-link-1.2.14.tgz#3feda4b47f9ebba7f4160bef8b977ba725b684d9"
+ integrity sha512-p67CMEFP7kOG1JZ0ZkYZwRDa369w5PIjtMjvrQd/HnIV8FRsHRqLqK+oAZQnFa1DDdZtOtHTi+aMIW6EatC2jg==
+ dependencies:
+ apollo-utilities "^1.3.0"
+ ts-invariant "^0.4.0"
+ tslib "^1.9.3"
+ zen-observable-ts "^0.8.21"
+
+apollo-utilities@^1.3.0:
+ version "1.3.4"
+ resolved "https://registry.yarnpkg.com/apollo-utilities/-/apollo-utilities-1.3.4.tgz#6129e438e8be201b6c55b0f13ce49d2c7175c9cf"
+ integrity sha512-pk2hiWrCXMAy2fRPwEyhvka+mqwzeP60Jr1tRYi5xru+3ko94HI9o6lK0CT33/w4RDlxWchmdhDCrvdr+pHCig==
+ dependencies:
+ "@wry/equality" "^0.1.2"
+ fast-json-stable-stringify "^2.0.0"
+ ts-invariant "^0.4.0"
+ tslib "^1.10.0"
+
aria-hidden@^1.1.1:
version "1.2.3"
resolved "https://registry.yarnpkg.com/aria-hidden/-/aria-hidden-1.2.3.tgz#14aeb7fb692bbb72d69bebfa47279c1fd725e954"
@@ -1244,6 +1290,11 @@ escape-string-regexp@^4.0.0:
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
+fast-json-stable-stringify@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
+ integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
+
fast-text-encoding@^1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/fast-text-encoding/-/fast-text-encoding-1.0.6.tgz#0aa25f7f638222e3396d72bf936afcf1d42d6867"
@@ -1315,6 +1366,14 @@ has@^1.0.3:
dependencies:
function-bind "^1.1.1"
+hash.js@^1.1.7:
+ version "1.1.7"
+ resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42"
+ integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==
+ dependencies:
+ inherits "^2.0.3"
+ minimalistic-assert "^1.0.1"
+
hey-listen@^1.0.8:
version "1.0.8"
resolved "https://registry.yarnpkg.com/hey-listen/-/hey-listen-1.0.8.tgz#8e59561ff724908de1aa924ed6ecc84a56a9aa68"
@@ -1340,6 +1399,11 @@ import-fresh@^3.2.1:
parent-module "^1.0.0"
resolve-from "^4.0.0"
+inherits@^2.0.3:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
+ integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
+
invariant@^2.2.4:
version "2.2.4"
resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"
@@ -1398,6 +1462,11 @@ lru-cache@^6.0.0:
dependencies:
yallist "^4.0.0"
+minimalistic-assert@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7"
+ integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==
+
object-assign@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-3.0.0.tgz#9bedd5ca0897949bca47e7ff408062d549f587f2"
@@ -1689,7 +1758,14 @@ ts-invariant@^0.10.3:
dependencies:
tslib "^2.1.0"
-tslib@^1.0.0, tslib@^1.9.3:
+ts-invariant@^0.4.0:
+ version "0.4.4"
+ resolved "https://registry.yarnpkg.com/ts-invariant/-/ts-invariant-0.4.4.tgz#97a523518688f93aafad01b0e80eb803eb2abd86"
+ integrity sha512-uEtWkFM/sdZvRNNDL3Ehu4WVpwaulhwQszV8mrtcdeE8nN00BV9mAmQ88RkrBhFgl9gMgvjJLAQcZbnPXI9mlA==
+ dependencies:
+ tslib "^1.9.3"
+
+tslib@^1.0.0, tslib@^1.10.0, tslib@^1.9.3:
version "1.14.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
@@ -1741,6 +1817,14 @@ yaml@^1.10.0:
resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b"
integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==
+zen-observable-ts@^0.8.21:
+ version "0.8.21"
+ resolved "https://registry.yarnpkg.com/zen-observable-ts/-/zen-observable-ts-0.8.21.tgz#85d0031fbbde1eba3cd07d3ba90da241215f421d"
+ integrity sha512-Yj3yXweRc8LdRMrCC8nIc4kkjWecPAUVh0TI0OUrWXx6aX790vLcDlWca6I4vsyCGH3LpWxq0dJRcMOFoVqmeg==
+ dependencies:
+ tslib "^1.9.3"
+ zen-observable "^0.8.0"
+
zen-observable-ts@^1.2.5:
version "1.2.5"
resolved "https://registry.yarnpkg.com/zen-observable-ts/-/zen-observable-ts-1.2.5.tgz#6c6d9ea3d3a842812c6e9519209365a122ba8b58"
@@ -1748,7 +1832,7 @@ zen-observable-ts@^1.2.5:
dependencies:
zen-observable "0.8.15"
-zen-observable@0.8.15:
+zen-observable@0.8.15, zen-observable@^0.8.0:
version "0.8.15"
resolved "https://registry.yarnpkg.com/zen-observable/-/zen-observable-0.8.15.tgz#96415c512d8e3ffd920afd3889604e30b9eaac15"
integrity sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==