apollo-link-scalars
    Preparing search index...

    Function reviveScalarsInCache

    • Re-applies the custom scalar parseValue functions to a cache snapshot that has been round-tripped through JSON (localStorage / AsyncStorage / any store that uses apollo3-cache-persist or equivalent).

      Fix for https://github.com/eturino/apollo-link-scalars/issues/760 — the scalar link only sees operations flowing through ApolloLink, so values restored via cache.restore() come back as the JSON shape they had in storage (Date -> ISO string, custom Money -> the serialized form, etc.) and the consumer never gets the parsed types.

      Pure and schema-driven: no dependency on ApolloCache. Call it on the payload before cache.restore(...) or after you read it from the persisted store yourself.

      Type Parameters

      • T extends Record<string, unknown>

      Parameters

      Returns T

      const raw = JSON.parse(await AsyncStorage.getItem("apollo-cache"));
      cache.restore(reviveScalarsInCache(raw, { schema, typesMap }));
      await persistCache({ cache, storage });
      cache.restore(reviveScalarsInCache(cache.extract(), { schema, typesMap }));

      Mutates extracted in place and returns the same reference. The generic signature is there for type flow, not copy semantics. Pass a fresh snapshot such as cache.extract() (already a clone of the live cache) or a JSON.parse(...) result; don't pass a live in-memory structure shared with the rest of the app.

      Merges typesMap with the schema's own leaf types and honors nullFunctions the same way withScalars does (see src/lib/link.ts). A scalar defined programmatically via new GraphQLScalarType({ parseValue }) is therefore applied on rehydration even when the caller's typesMap omits it, and any null-monad transform passed to withScalars can be repeated here to keep both paths producing the same shape.

      Requires __typename on embedded non-normalized objects, which is Apollo's default behavior. A cache built with new InMemoryCache({ addTypename: false }) stores embedded objects without a __typename key, so reviveScalarsInCache cannot look them up in the schema and will leave their scalars unparsed. Top-level normalized entities still work because their cache key (Foo:1) and their __typename field are written by Apollo independent of the addTypename setting.

      Interfaces, unions, and enum-scalar validation are out of scope in this first pass. Scalar fields nested under an interface- or union-typed field are not revived because the helper does not resolve the runtime __typename on the value itself the way the network parser does.

      Idempotence is caller-contingent. reviveScalarsInCache calls parseValue once per field per pass, so invoking it twice on the same snapshot parses every scalar twice. Safe only when the supplied parseValue is itself idempotent — e.g. a DateTime parser that guards with typeof v === "string" before constructing a Date will leave Date instances alone on the second pass. A naive Money parser like (v) => Number(v) * 100 is NOT idempotent: first pass turns "1.50" into 150, second pass turns 150 into 15000 — silent corruption. When in doubt, make parseValue detect its own output and short-circuit.