Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@swan-io/graphql-client

Package Overview
Dependencies
Maintainers
0
Versions
45
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@swan-io/graphql-client - npm Package Compare versions

Comparing version 0.3.1 to 0.4.0

dist/cache/entry.d.ts

41

dist/cache/cache.d.ts

@@ -1,16 +0,25 @@

import { DocumentNode, OperationDefinitionNode } from "@0no-co/graphql.web";
import { DocumentNode } from "@0no-co/graphql.web";
import { Option, Result } from "@swan-io/boxed";
import { Connection, Edge } from "../types";
export declare const getCacheKeyFromJson: (json: unknown) => Option<symbol>;
import type { CacheEntry } from "./entry";
export type SchemaConfig = {
interfaceToTypes: Record<string, string[]>;
};
export declare const getCacheKeyFromOperationNode: (operationNode: OperationDefinitionNode) => Option<symbol>;
type ConnectionInfo = {
cacheEntry: CacheEntry;
document: DocumentNode;
variables: Record<string, unknown>;
pathInQuery: PropertyKey[];
fieldVariables: Record<string, unknown>;
};
export declare class ClientCache {
cache: Map<symbol, unknown>;
cache: Map<symbol, CacheEntry>;
operationCache: Map<import("graphql").DocumentNode, Map<string, Option<Result<unknown, unknown>>>>;
schemaConfig: Record<string, Set<string>>;
interfaceToType: Record<string, Set<string>>;
connectionCache: Map<number, ConnectionInfo>;
connectionRefCount: number;
constructor(schemaConfig: SchemaConfig);
registerConnectionInfo(info: ConnectionInfo): number;
isTypeCompatible(typename: string, typeCondition: string): boolean;
dump(): Map<symbol, unknown>;
dump(): Map<symbol, CacheEntry>;
getOperationFromCache(documentNode: DocumentNode, variables: Record<string, unknown>): Option<Result<unknown, unknown>>;

@@ -21,19 +30,4 @@ setOperationInCache(documentNode: DocumentNode, variables: Record<string, unknown>, data: Result<unknown, unknown>): void;

get(cacheKey: symbol): Option<unknown>;
getOrDefault(cacheKey: symbol): unknown;
set(cacheKey: symbol, entry: unknown): void;
cacheIfEligible<T>(value: T, requestedKeys: Set<symbol>): symbol | T;
updateFieldInClosestCachedAncestor({ originalFieldName, fieldNameWithArguments, value, path, jsonPath, ancestors, variables, queryVariables, rootTypename, selectedKeys, documentNode, }: {
originalFieldName: string;
fieldNameWithArguments: symbol | string;
value: unknown;
path: PropertyKey[];
jsonPath: PropertyKey[];
ancestors: unknown[];
variables: Record<string, unknown>;
queryVariables: Record<string, unknown>;
rootTypename: string;
selectedKeys: Set<symbol>;
documentNode: DocumentNode;
}): void;
unsafe__update<A>(cacheKey: symbol, path: (symbol | string)[], updater: (value: A) => A): void;
getOrCreateEntry(cacheKey: symbol, defaultValue: CacheEntry): unknown;
set(cacheKey: symbol, entry: CacheEntry): void;
updateConnection<A>(connection: Connection<A>, config: {

@@ -47,1 +41,2 @@ prepend: Edge<A>[];

}
export {};

@@ -1,3 +0,3 @@

import { DocumentNode } from "@0no-co/graphql.web";
import { ClientCache } from "./cache";
export declare const writeOperationToCache: (cache: ClientCache, document: DocumentNode, response: unknown, variables: Record<string, unknown>) => ClientCache;
import type { DocumentNode } from "graphql";
import { type ClientCache } from "./cache";
export declare const writeOperationToCache: (cache: ClientCache, document: DocumentNode, response: unknown, variables: Record<string, unknown>) => void;

@@ -63,1 +63,2 @@ import { DocumentNode, FieldNode, OperationDefinitionNode, SelectionSetNode } from "@0no-co/graphql.web";

export declare const isExcluded: (fieldNode: FieldNode, variables: Record<string, unknown>) => boolean;
export declare const getCacheKeyFromOperationNode: (operationNode: OperationDefinitionNode) => Option<symbol>;

@@ -5,3 +5,2 @@ 'use strict';

var request = require('@swan-io/request');
var tsPattern = require('ts-pattern');
var graphql_web = require('@0no-co/graphql.web');

@@ -11,41 +10,24 @@ var react = require('react');

// src/client.ts
// src/utils.ts
var DEEP_MERGE_DELETE = Symbol.for("DEEP_MERGE_DELETE");
var REQUESTED_KEYS = Symbol.for("__requestedKeys");
var deepMerge = (target, source) => {
if (target instanceof Set && source instanceof Set) {
return /* @__PURE__ */ new Set([...target, ...source]);
}
const next = Array.isArray(target) ? Array(target.length) : Array.isArray(source) ? Array(source.length) : {};
Object.getOwnPropertyNames(target).forEach((name) => {
if (source[name] !== DEEP_MERGE_DELETE) {
next[name] = target[name];
}
});
Object.getOwnPropertySymbols(target).forEach((name) => {
if (source[name] !== DEEP_MERGE_DELETE) {
next[name] = target[name];
}
});
Object.getOwnPropertyNames(source).forEach((name) => {
if (source[name] !== DEEP_MERGE_DELETE) {
if (isRecord(next[name]) && isRecord(source[name])) {
next[name] = deepMerge(next[name], source[name]);
} else {
next[name] = source[name];
var OPERATION_TYPES = /* @__PURE__ */ new Set(["Query", "Mutation", "Subscription"]);
var getCacheEntryKey = (json) => {
if (typeof json === "object" && json != null) {
if ("__typename" in json && typeof json.__typename === "string") {
const typename = json.__typename;
if (OPERATION_TYPES.has(typename)) {
return boxed.Option.Some(Symbol.for(typename));
}
}
});
Object.getOwnPropertySymbols(source).forEach((name) => {
if (source[name] !== DEEP_MERGE_DELETE) {
if (isRecord(next[name]) && isRecord(source[name])) {
next[name] = deepMerge(next[name], source[name]);
} else {
next[name] = source[name];
if ("id" in json && typeof json.id === "string") {
return boxed.Option.Some(Symbol.for(`${typename}<${json.id}>`));
}
}
});
return next;
}
return boxed.Option.None();
};
// src/utils.ts
var REQUESTED_KEYS = Symbol.for("__requestedKeys");
var CONNECTION_REF = "__connectionRef";
var TYPENAME_KEY = Symbol.for("__typename");
var EDGES_KEY = Symbol.for("edges");
var NODE_KEY = Symbol.for("node");
var containsAll = (a, b) => {

@@ -83,17 +65,2 @@ const keys = [...b.values()];

// src/cache/cache.ts
var getCacheKeyFromJson = (json) => {
return tsPattern.match(json).with(
{ __typename: tsPattern.P.select(tsPattern.P.union("Query", "Mutation", "Subscription")) },
(name) => boxed.Option.Some(Symbol.for(name))
).with(
{ __typename: tsPattern.P.select("name", tsPattern.P.string), id: tsPattern.P.select("id", tsPattern.P.string) },
({ name, id }) => boxed.Option.Some(Symbol.for(`${name}<${id}>`))
).otherwise(() => boxed.Option.None());
};
var getCacheKeyFromOperationNode = (operationNode) => {
return tsPattern.match(operationNode.operation).with(graphql_web.OperationTypeNode.QUERY, () => boxed.Option.Some(Symbol.for("Query"))).with(
graphql_web.OperationTypeNode.SUBSCRIPTION,
() => boxed.Option.Some(Symbol.for("Subscription"))
).otherwise(() => boxed.Option.None());
};
var ClientCache = class {

@@ -103,3 +70,4 @@ constructor(schemaConfig) {

this.operationCache = /* @__PURE__ */ new Map();
this.schemaConfig = Object.fromEntries(
this.connectionRefCount = -1;
this.interfaceToType = Object.fromEntries(
Object.entries(schemaConfig.interfaceToTypes).map(([key, value]) => [

@@ -110,3 +78,9 @@ key,

);
this.connectionCache = /* @__PURE__ */ new Map();
}
registerConnectionInfo(info) {
const id = ++this.connectionRefCount;
this.connectionCache.set(id, info);
return id;
}
isTypeCompatible(typename, typeCondition) {

@@ -116,3 +90,3 @@ if (typename === typeCondition) {

}
const compatibleTypes = this.schemaConfig[typeCondition];
const compatibleTypes = this.interfaceToType[typeCondition];
if (compatibleTypes == void 0) {

@@ -163,7 +137,9 @@ return false;

}
getOrDefault(cacheKey) {
getOrCreateEntry(cacheKey, defaultValue) {
if (this.cache.has(cacheKey)) {
return this.cache.get(cacheKey);
} else {
return {};
const entry = defaultValue;
this.cache.set(cacheKey, entry);
return entry;
}

@@ -174,188 +150,60 @@ }

}
cacheIfEligible(value, requestedKeys) {
const cacheKeyOption = getCacheKeyFromJson(value);
if (cacheKeyOption.isSome()) {
const cacheKey = cacheKeyOption.get();
const existingEntry = this.getOrDefault(cacheKey);
this.set(
cacheKey,
deepMerge(
existingEntry,
isRecord(value) ? { ...value, [REQUESTED_KEYS]: requestedKeys } : value
)
);
return cacheKey;
} else {
return value;
updateConnection(connection, config) {
if (connection == null) {
return;
}
}
updateFieldInClosestCachedAncestor({
originalFieldName,
fieldNameWithArguments,
value,
path,
jsonPath,
ancestors,
variables,
queryVariables,
rootTypename,
selectedKeys,
documentNode
}) {
const ancestorsCopy = ancestors.concat();
const pathCopy = path.concat();
const writePath = [];
let ancestor;
while (ancestor = ancestorsCopy.pop()) {
const maybeCacheKey = getCacheKeyFromJson(
ancestorsCopy.length === 0 ? { ...ancestor, __typename: rootTypename } : ancestor
if (CONNECTION_REF in connection && typeof connection[CONNECTION_REF] === "number") {
const connectionConfig = this.connectionCache.get(
connection[CONNECTION_REF]
);
if (maybeCacheKey.isSome()) {
const cacheKey = maybeCacheKey.get();
const existingEntry = this.getOrDefault(cacheKey);
if (isRecord(value) && !boxed.Array.isArray(value)) {
if (typeof value.__typename === "string" && value.__typename.endsWith("Connection")) {
value.__connectionCacheKey = cacheKey.description;
value.__connectionCachePath = [
[...writePath, fieldNameWithArguments].map(
(item) => typeof item === "symbol" ? { symbol: item.description } : item
if (connectionConfig == null) {
return;
}
if ("prepend" in config) {
const edges = config.prepend;
connectionConfig.cacheEntry[EDGES_KEY] = [
...boxed.Array.filterMap(
edges,
({ node, __typename }) => getCacheEntryKey(node).flatMap(
(key) => (
// we can omit the requested fields here because the Connection<A> contrains the fields
this.getFromCacheWithoutKey(key).map(() => ({
[TYPENAME_KEY]: __typename,
[NODE_KEY]: key
}))
)
];
value.__connectionArguments = variables;
value.__connectionQueryArguments = queryVariables;
value.__connectionDocumentNode = documentNode;
value.__connectionJsonPath = [...jsonPath, originalFieldName];
}
value[REQUESTED_KEYS] = selectedKeys;
}
const deepUpdate = writePath.reduce(
(acc, key) => {
return {
[key]: acc
};
},
// remote original field
{
[originalFieldName]: DEEP_MERGE_DELETE,
[fieldNameWithArguments]: value
}
);
this.set(cacheKey, deepMerge(existingEntry, deepUpdate));
break;
)
),
...connectionConfig.cacheEntry[EDGES_KEY]
];
return;
}
writePath.push(pathCopy.pop());
}
}
unsafe__update(cacheKey, path, updater) {
this.get(cacheKey).map((cachedAncestor) => {
const value = path.reduce(
(acc, key) => acc.flatMap(
(acc2) => boxed.Option.fromNullable(isRecord(acc2) ? acc2[key] : null)
),
boxed.Option.fromNullable(cachedAncestor)
);
value.map((item) => {
const deepUpdate = path.reduce(
(acc, key) => {
return {
[key]: acc
};
},
updater(item)
);
this.set(cacheKey, deepMerge(cachedAncestor, deepUpdate));
});
});
}
updateConnection(connection, config) {
tsPattern.match(connection).with(
{
__connectionCacheKey: tsPattern.P.string,
__connectionCachePath: tsPattern.P.array(
tsPattern.P.array(tsPattern.P.union({ symbol: tsPattern.P.string }, tsPattern.P.string))
)
},
({ __connectionCacheKey, __connectionCachePath }) => {
const cacheKey = Symbol.for(__connectionCacheKey);
const cachePath = __connectionCachePath.map(
(path) => path.map(
(item) => typeof item === "string" ? item : Symbol.for(item.symbol)
if ("append" in config) {
const edges = config.append;
connectionConfig.cacheEntry[EDGES_KEY] = [
...connectionConfig.cacheEntry[EDGES_KEY],
...boxed.Array.filterMap(
edges,
({ node, __typename }) => getCacheEntryKey(node).flatMap(
(key) => (
// we can omit the requested fields here because the Connection<A> contrains the fields
this.getFromCacheWithoutKey(key).map(() => ({
[TYPENAME_KEY]: __typename,
[NODE_KEY]: key
}))
)
)
)
);
const typenameSymbol = Symbol.for("__typename");
const edgesSymbol = Symbol.for("edges");
const nodeSymbol = Symbol.for("node");
tsPattern.match(config).with({ prepend: tsPattern.P.select(tsPattern.P.nonNullable) }, (edges) => {
const firstPath = cachePath[0];
if (firstPath != null) {
this.unsafe__update(cacheKey, firstPath, (value) => {
if (!isRecord(value) || !boxed.Array.isArray(value[edgesSymbol])) {
return value;
}
return {
...value,
[edgesSymbol]: [
...boxed.Array.filterMap(
edges,
({ node, __typename }) => getCacheKeyFromJson(node).flatMap(
(key) => (
// we can omit the requested fields here because the Connection<A> contrains the fields
this.getFromCacheWithoutKey(key).map(() => ({
[typenameSymbol]: __typename,
[nodeSymbol]: key
}))
)
)
),
...value[edgesSymbol]
]
};
});
}
}).with({ append: tsPattern.P.select(tsPattern.P.nonNullable) }, (edges) => {
const lastPath = cachePath[cachePath.length - 1];
if (lastPath != null) {
this.unsafe__update(cacheKey, lastPath, (value) => {
if (!isRecord(value) || !boxed.Array.isArray(value[edgesSymbol])) {
return value;
}
return {
...value,
[edgesSymbol]: [
...value[edgesSymbol],
...boxed.Array.filterMap(
edges,
({ node, __typename }) => getCacheKeyFromJson(node).flatMap(
(key) => (
// we can omit the requested fields here because the Connection<A> contrains the fields
this.getFromCacheWithoutKey(key).map(() => ({
[typenameSymbol]: __typename,
[nodeSymbol]: key
}))
)
)
)
]
};
});
}
}).with({ remove: tsPattern.P.select(tsPattern.P.array()) }, (nodeIds) => {
cachePath.forEach((path) => {
this.unsafe__update(cacheKey, path, (value) => {
return isRecord(value) && boxed.Array.isArray(value[edgesSymbol]) ? {
...value,
[edgesSymbol]: value[edgesSymbol].filter((edge) => {
const node = edge[nodeSymbol];
return !nodeIds.some((nodeId) => {
var _a;
return (_a = node.description) == null ? void 0 : _a.includes(`<${nodeId}>`);
});
})
} : value;
});
});
}).exhaustive();
];
return;
}
).otherwise(() => {
});
const nodeIds = config.remove;
connectionConfig.cacheEntry[EDGES_KEY] = connectionConfig.cacheEntry[EDGES_KEY].filter((edge) => {
const node = edge[NODE_KEY];
return !nodeIds.some((nodeId) => {
var _a;
return (_a = node.description) == null ? void 0 : _a.includes(`<${nodeId}>`);
});
});
}
}

@@ -402,28 +250,25 @@ };

var extractValue = (valueNode, variables) => {
return tsPattern.match(valueNode).with({ kind: graphql_web.Kind.NULL }, () => null).with(
{
kind: tsPattern.P.union(
graphql_web.Kind.INT,
graphql_web.Kind.FLOAT,
graphql_web.Kind.STRING,
graphql_web.Kind.BOOLEAN,
graphql_web.Kind.ENUM
)
},
({ value }) => value
).with(
{ kind: graphql_web.Kind.LIST },
({ values }) => values.map((value) => extractValue(value, variables))
).with(
{ kind: graphql_web.Kind.OBJECT },
({ fields }) => Object.fromEntries(
fields.map(({ name: { value: name }, value }) => [
name,
extractValue(value, variables)
])
)
).with(
{ kind: graphql_web.Kind.VARIABLE },
({ name: { value: name } }) => variables[name]
).exhaustive();
switch (valueNode.kind) {
case graphql_web.Kind.NULL:
return null;
case graphql_web.Kind.INT:
case graphql_web.Kind.FLOAT:
case graphql_web.Kind.STRING:
case graphql_web.Kind.BOOLEAN:
case graphql_web.Kind.ENUM:
return valueNode.value;
case graphql_web.Kind.LIST:
return valueNode.values.map((value) => extractValue(value, variables));
case graphql_web.Kind.OBJECT:
return Object.fromEntries(
valueNode.fields.map(({ name: { value: name }, value }) => [
name,
extractValue(value, variables)
])
);
case graphql_web.Kind.VARIABLE:
return variables[valueNode.name.value];
default:
return null;
}
};

@@ -508,11 +353,10 @@ var getFieldName = (fieldNode) => {

var getIdFieldNode = (selection) => {
return tsPattern.match(selection).with(
{ kind: graphql_web.Kind.FIELD },
(fieldNode) => fieldNode.name.value === "id" ? boxed.Option.Some(fieldNode) : boxed.Option.None()
).with({ kind: graphql_web.Kind.INLINE_FRAGMENT }, (inlineFragmentNode) => {
return boxed.Array.findMap(
inlineFragmentNode.selectionSet.selections,
getIdFieldNode
);
}).otherwise(() => boxed.Option.None());
switch (selection.kind) {
case graphql_web.Kind.FIELD:
return selection.name.value === "id" ? boxed.Option.Some(selection) : boxed.Option.None();
case graphql_web.Kind.INLINE_FRAGMENT:
return boxed.Array.findMap(selection.selectionSet.selections, getIdFieldNode);
default:
return boxed.Option.None();
}
};

@@ -546,3 +390,25 @@ var addIdIfPreviousSelected = (oldSelectionSet, newSelectionSet) => {

};
var getCacheKeyFromOperationNode = (operationNode) => {
switch (operationNode.operation) {
case graphql_web.OperationTypeNode.QUERY:
return boxed.Option.Some(Symbol.for("Query"));
case graphql_web.OperationTypeNode.SUBSCRIPTION:
return boxed.Option.Some(Symbol.for("Subscription"));
default:
return boxed.Option.None();
}
};
// src/json/getTypename.ts
var getTypename = (json) => {
if (typeof json === "object" && json != null) {
if (Array.isArray(json)) {
return getTypename(json[0]);
}
if ("__typename" in json && typeof json.__typename === "string") {
return json.__typename;
}
}
};
// src/cache/read.ts

@@ -655,6 +521,3 @@ var getFromCacheOrReturnValue = (cache, valueOrKey, selectedKeys) => {

const typeCondition = (_a = inlineFragmentNode.typeCondition) == null ? void 0 : _a.name.value;
const dataTypename = tsPattern.match(data3).with({ __typename: tsPattern.P.select(tsPattern.P.string) }, (name) => name).with(
{ __typename: tsPattern.P.array({ __typename: tsPattern.P.select(tsPattern.P.string) }) },
(name) => name[0]
).otherwise(() => void 0);
const dataTypename = getTypename(data3);
if (typeCondition != null && dataTypename != null) {

@@ -745,28 +608,55 @@ if (cache.isTypeCompatible(dataTypename, typeCondition)) {

(selection) => {
return tsPattern.match(selection).with({ kind: graphql_web.Kind.FIELD }, (fieldNode) => {
const fieldNameWithArguments = getFieldNameWithArguments(
fieldNode,
variables
);
if (data == void 0) {
return boxed.Option.Some(fieldNode);
}
const cacheHasKey = hasOwnProperty.call(
data,
fieldNameWithArguments
);
if (!cacheHasKey) {
return boxed.Option.Some(fieldNode);
}
if (parentSelectedKeys.has(fieldNameWithArguments)) {
const valueOrKeyFromCache = data[fieldNameWithArguments];
const subFieldSelectedKeys = getSelectedKeys(
switch (selection.kind) {
case graphql_web.Kind.FIELD: {
const fieldNode = selection;
const fieldNameWithArguments = getFieldNameWithArguments(
fieldNode,
variables
);
if (boxed.Array.isArray(valueOrKeyFromCache)) {
return valueOrKeyFromCache.reduce((acc, valueOrKey) => {
if (data == void 0) {
return boxed.Option.Some(fieldNode);
}
const cacheHasKey = hasOwnProperty.call(
data,
fieldNameWithArguments
);
if (!cacheHasKey) {
return boxed.Option.Some(fieldNode);
}
if (parentSelectedKeys.has(fieldNameWithArguments)) {
const valueOrKeyFromCache = data[fieldNameWithArguments];
const subFieldSelectedKeys = getSelectedKeys(
fieldNode,
variables
);
if (boxed.Array.isArray(valueOrKeyFromCache)) {
return valueOrKeyFromCache.reduce((acc, valueOrKey) => {
const value = getFromCacheOrReturnValueWithoutKeyFilter(
cache,
valueOrKey
);
if (value.isNone()) {
return boxed.Option.Some(fieldNode);
}
const originalSelectionSet = fieldNode.selectionSet;
if (originalSelectionSet != null) {
return traverse(
originalSelectionSet,
value.get(),
subFieldSelectedKeys
).map((selectionSet) => ({
...fieldNode,
selectionSet: addIdIfPreviousSelected(
originalSelectionSet,
selectionSet
)
}));
} else {
return acc;
}
}, boxed.Option.None());
} else {
const value = getFromCacheOrReturnValueWithoutKeyFilter(
cache,
valueOrKey
valueOrKeyFromCache
);

@@ -790,44 +680,22 @@ if (value.isNone()) {

} else {
return acc;
return boxed.Option.None();
}
}, boxed.Option.None());
}
} else {
const value = getFromCacheOrReturnValueWithoutKeyFilter(
cache,
valueOrKeyFromCache
);
if (value.isNone()) {
return boxed.Option.Some(fieldNode);
}
const originalSelectionSet = fieldNode.selectionSet;
if (originalSelectionSet != null) {
return traverse(
originalSelectionSet,
value.get(),
subFieldSelectedKeys
).map((selectionSet) => ({
...fieldNode,
selectionSet: addIdIfPreviousSelected(
originalSelectionSet,
selectionSet
)
}));
} else {
return boxed.Option.None();
}
return boxed.Option.Some(fieldNode);
}
} else {
return boxed.Option.Some(fieldNode);
}
}).with({ kind: graphql_web.Kind.INLINE_FRAGMENT }, (inlineFragmentNode) => {
return traverse(
inlineFragmentNode.selectionSet,
data,
parentSelectedKeys
).map(
(selectionSet) => ({ ...inlineFragmentNode, selectionSet })
);
}).with({ kind: graphql_web.Kind.FRAGMENT_SPREAD }, () => {
return boxed.Option.None();
}).exhaustive();
case graphql_web.Kind.INLINE_FRAGMENT: {
const inlineFragmentNode = selection;
return traverse(
inlineFragmentNode.selectionSet,
data,
parentSelectedKeys
).map(
(selectionSet) => ({ ...inlineFragmentNode, selectionSet })
);
}
default:
return boxed.Option.None();
}
}

@@ -868,137 +736,119 @@ );

};
// src/cache/entry.ts
var createEmptyCacheEntry = () => ({
[REQUESTED_KEYS]: /* @__PURE__ */ new Set()
});
// src/cache/write.ts
var writeOperationToCache = (cache, document, response, variables) => {
const traverse = (selections, data, path = [], jsonPath = [], rootTypename) => {
selections.selections.forEach((selection) => {
tsPattern.match(selection).with({ kind: graphql_web.Kind.FIELD }, (fieldNode) => {
const originalFieldName = getFieldName(fieldNode);
const fieldNameWithArguments = getFieldNameWithArguments(
fieldNode,
variables
);
const fieldArguments = extractArguments(fieldNode, variables);
const parent = data[data.length - 1];
const fieldValue = parent[originalFieldName];
const selectedKeys = getSelectedKeys(fieldNode, variables);
if (fieldValue != void 0) {
if (Array.isArray(fieldValue)) {
cache.updateFieldInClosestCachedAncestor({
originalFieldName,
fieldNameWithArguments,
value: fieldValue,
path,
jsonPath,
ancestors: data,
variables: fieldArguments,
queryVariables: variables,
rootTypename,
selectedKeys,
documentNode: document
});
const nextValue = Array(fieldValue.length);
cache.updateFieldInClosestCachedAncestor({
originalFieldName,
fieldNameWithArguments,
value: nextValue,
path: [...path, fieldNameWithArguments],
jsonPath: [...jsonPath, originalFieldName],
ancestors: [...data, fieldValue],
variables: fieldArguments,
queryVariables: variables,
rootTypename,
selectedKeys,
documentNode: document
});
fieldValue.forEach((item, index) => {
const value = cache.cacheIfEligible(item, selectedKeys);
cache.updateFieldInClosestCachedAncestor({
originalFieldName: index.toString(),
fieldNameWithArguments: index.toString(),
value,
path: [...path, fieldNameWithArguments],
jsonPath: [...jsonPath, originalFieldName],
ancestors: [...data, fieldValue],
variables: fieldArguments,
queryVariables: variables,
rootTypename,
selectedKeys,
documentNode: document
});
if (isRecord(item)) {
traverse(
fieldNode.selectionSet,
[...data, fieldValue, item],
[...path, fieldNameWithArguments, index.toString()],
[...jsonPath, originalFieldName, index.toString()],
rootTypename
);
}
});
} else {
const value = cache.cacheIfEligible(fieldValue, selectedKeys);
cache.updateFieldInClosestCachedAncestor({
originalFieldName,
fieldNameWithArguments,
value,
path,
jsonPath,
ancestors: data,
variables: fieldArguments,
queryVariables: variables,
rootTypename,
selectedKeys,
documentNode: document
});
if (isRecord(fieldValue) && fieldNode.selectionSet != void 0) {
traverse(
fieldNode.selectionSet,
[...data, fieldValue],
[...path, fieldNameWithArguments],
[...jsonPath, originalFieldName],
rootTypename
);
}
}
} else {
if (originalFieldName in parent) {
cache.updateFieldInClosestCachedAncestor({
originalFieldName,
fieldNameWithArguments,
value: fieldValue,
path,
jsonPath,
ancestors: data,
variables: fieldArguments,
queryVariables: variables,
rootTypename,
selectedKeys,
documentNode: document
});
}
const registerConnection = (cacheEntry, pathInQuery, fieldVariables) => {
if (cacheEntry[CONNECTION_REF]) {
return;
}
const id = cache.registerConnectionInfo({
cacheEntry,
variables,
pathInQuery,
fieldVariables,
document
});
cacheEntry[CONNECTION_REF] = id;
};
const cacheField = (field, parentJson, parentCache, path) => {
var _a, _b;
const originalFieldName = getFieldName(field);
const fieldNameWithArguments = getFieldNameWithArguments(field, variables);
const fieldValue = parentJson[originalFieldName];
if (!Array.isArray(parentCache)) {
parentCache[REQUESTED_KEYS].add(fieldNameWithArguments);
}
const subSelectionSet = field.selectionSet;
if (subSelectionSet === void 0 || fieldValue == null) {
parentCache[fieldNameWithArguments] = fieldValue;
return;
}
if (Array.isArray(fieldValue)) {
const arrayCache = (_a = parentCache[fieldNameWithArguments]) != null ? _a : Array(fieldValue.length);
if (parentCache[fieldNameWithArguments] == void 0) {
parentCache[fieldNameWithArguments] = arrayCache;
}
fieldValue.forEach((item, index) => {
var _a2;
if (item == null) {
arrayCache[index] = item;
return;
}
}).with({ kind: graphql_web.Kind.INLINE_FRAGMENT }, (inlineFragmentNode) => {
traverse(
inlineFragmentNode.selectionSet,
data,
path,
jsonPath,
rootTypename
const cacheKey2 = getCacheEntryKey(item);
const cacheEntry2 = cacheKey2.map(
(key) => cache.getOrCreateEntry(key, createEmptyCacheEntry())
);
}).with({ kind: graphql_web.Kind.FRAGMENT_SPREAD }, () => {
}).exhaustive();
});
const cacheObject2 = cacheEntry2.getOr(
// @ts-expect-error It's fine
(_a2 = arrayCache[index]) != null ? _a2 : createEmptyCacheEntry()
);
const cacheValueInParent2 = cacheKey2.getOr(cacheObject2);
arrayCache[index] = cacheValueInParent2;
cacheSelectionSet(subSelectionSet, item, cacheObject2, [
...path,
originalFieldName,
index
]);
});
return;
}
const record = fieldValue;
const cacheKey = getCacheEntryKey(record);
const cacheEntry = cacheKey.map(
(key) => cache.getOrCreateEntry(key, createEmptyCacheEntry())
);
const cacheObject = cacheEntry.getOr(
(_b = parentCache[fieldNameWithArguments]) != null ? _b : createEmptyCacheEntry()
);
const cacheValueInParent = cacheKey.getOr(cacheObject);
parentCache[fieldNameWithArguments] = cacheValueInParent;
if (typeof record.__typename === "string" && record.__typename.endsWith("Connection")) {
registerConnection(
cacheObject,
[...path, originalFieldName],
extractArguments(field, variables)
);
}
return cacheSelectionSet(subSelectionSet, record, cacheObject, [
...path,
originalFieldName
]);
};
const cacheSelectionSet = (selectionSet, json, cached, path) => {
for (const selection of selectionSet.selections) {
switch (selection.kind) {
case graphql_web.Kind.INLINE_FRAGMENT:
cacheSelectionSet(selection.selectionSet, json, cached, path);
continue;
case graphql_web.Kind.FIELD:
cacheField(selection, json, cached, path);
continue;
default:
continue;
}
}
};
document.definitions.forEach((definition) => {
if (definition.kind === graphql_web.Kind.OPERATION_DEFINITION) {
const rootTypename = tsPattern.match(definition.operation).with(graphql_web.OperationTypeNode.QUERY, () => "Query").with(graphql_web.OperationTypeNode.SUBSCRIPTION, () => "Subscription").with(graphql_web.OperationTypeNode.MUTATION, () => "Mutation").exhaustive();
cache.cacheIfEligible(
isRecord(response) ? {
...response,
__typename: rootTypename
} : response,
getSelectedKeys(definition, variables)
const operationName = definition.operation === graphql_web.OperationTypeNode.QUERY ? "Query" : definition.operation === graphql_web.OperationTypeNode.SUBSCRIPTION ? "Subscription" : "Mutation";
if (!isRecord(response)) {
return;
}
const cacheEntry = cache.getOrCreateEntry(
Symbol.for(operationName),
createEmptyCacheEntry()
);
traverse(definition.selectionSet, [response], [], [], rootTypename);
return cacheSelectionSet(
definition.selectionSet,
response,
cacheEntry,
[]
);
}
});
return cache;
};

@@ -1014,25 +864,16 @@ var InvalidGraphQLResponseError = class _InvalidGraphQLResponseError extends Error {

var parseGraphQLError = (error) => {
return tsPattern.match(error).with(
{
message: tsPattern.P.string,
nodes: tsPattern.P.optional(tsPattern.P.any),
source: tsPattern.P.optional(tsPattern.P.any),
positions: tsPattern.P.optional(tsPattern.P.any),
path: tsPattern.P.optional(tsPattern.P.any),
error: tsPattern.P.optional(tsPattern.P.any),
extensions: tsPattern.P.optional(tsPattern.P.any)
},
({ message, nodes: nodes2, source, positions, path, error: error2, extensions }) => {
const originalError = tsPattern.match(error2).with({ message: tsPattern.P.string }, ({ message: message2 }) => new Error(message2)).otherwise(() => void 0);
return new graphql_web.GraphQLError(
message,
nodes2,
source,
positions,
path,
originalError,
extensions
);
}
).otherwise((error2) => new graphql_web.GraphQLError(JSON.stringify(error2)));
if (typeof error === "object" && error != null && "message" in error && typeof error.message === "string") {
const graphqlError = error;
const originalError = "error" in error && typeof error.error === "object" && error.error != null && "message" in error.error && typeof error.error.message === "string" ? new Error(error.error.message) : void 0;
return new graphql_web.GraphQLError(
graphqlError.message,
graphqlError.nodes,
graphqlError.source,
graphqlError.positions,
graphqlError.path,
originalError,
graphqlError.extensions
);
}
return new graphql_web.GraphQLError(JSON.stringify(error));
};

@@ -1196,10 +1037,13 @@ var ClientError = {

})
}).mapOkToResult(request.badStatusToError).mapOkToResult(request.emptyToError).mapOkToResult(
(payload) => tsPattern.match(payload).returnType().with(
{ errors: tsPattern.P.select(tsPattern.P.array()) },
(errors) => boxed.Result.Error(errors.map(parseGraphQLError))
).with({ data: tsPattern.P.select(tsPattern.P.nonNullable) }, (data) => boxed.Result.Ok(data)).otherwise(
(response) => boxed.Result.Error(new InvalidGraphQLResponseError(response))
)
);
}).mapOkToResult(request.badStatusToError).mapOkToResult(request.emptyToError).mapOkToResult((payload) => {
if (payload != null && typeof payload === "object") {
if ("errors" in payload && Array.isArray(payload.errors)) {
return boxed.Result.Error(payload.errors.map(parseGraphQLError));
}
if ("data" in payload && payload.data != null) {
return boxed.Result.Ok(payload.data);
}
}
return boxed.Result.Error(new InvalidGraphQLResponseError(payload));
});
};

@@ -1315,21 +1159,17 @@ var prepend = (connection, edges) => {

const transformedDocument = this.getTransformedDocument(document);
return tsPattern.match({
cached: this.cache.getOperationFromCache(
transformedDocument,
variablesAsRecord
),
normalize
}).with(
{ cached: boxed.Option.P.Some(boxed.Result.P.Error(tsPattern.P._)) },
(value) => value.cached
).with(
{ cached: boxed.Option.P.Some(boxed.Result.P.Ok(tsPattern.P._)), normalize: false },
(value) => value.cached
).otherwise(
() => readOperationFromCache(
this.cache,
transformedDocument,
variablesAsRecord
)
const cached = this.cache.getOperationFromCache(
transformedDocument,
variablesAsRecord
);
if (cached.isSome() && cached.get().isError()) {
return cached;
}
if (cached.isSome() && cached.get().isOk() && normalize === false) {
return cached;
}
return readOperationFromCache(
this.cache,
transformedDocument,
variablesAsRecord
);
}

@@ -1437,2 +1277,3 @@ query(document, variables, requestOptions) {

var mergeConnection = (previous, next, mode) => {
var _a, _b, _c, _d;
if (next == null) {

@@ -1444,7 +1285,2 @@ return next;

}
if ("__connectionArguments" in next && isRecord(next.__connectionArguments)) {
if (next.__connectionArguments[mode] == null) {
return next;
}
}
if (mode === "after" && next.pageInfo.endCursor === previous.pageInfo.endCursor) {

@@ -1458,17 +1294,4 @@ return previous;

...next,
__connectionCachePath: tsPattern.match(mode).with("before", () => [
..."__connectionCachePath" in next && Array.isArray(next.__connectionCachePath) ? next.__connectionCachePath : [],
..."__connectionCachePath" in previous && Array.isArray(previous.__connectionCachePath) ? previous.__connectionCachePath : []
]).with("after", () => [
..."__connectionCachePath" in previous && Array.isArray(previous.__connectionCachePath) ? previous.__connectionCachePath : [],
..."__connectionCachePath" in next && Array.isArray(next.__connectionCachePath) ? next.__connectionCachePath : []
]).exhaustive(),
edges: tsPattern.match(mode).with("before", () => {
var _a, _b;
return [...(_a = next.edges) != null ? _a : [], ...(_b = previous.edges) != null ? _b : []];
}).with("after", () => {
var _a, _b;
return [...(_a = previous.edges) != null ? _a : [], ...(_b = next.edges) != null ? _b : []];
}).exhaustive(),
pageInfo: tsPattern.match(mode).with("before", () => ({
edges: mode === "before" ? [...(_a = next.edges) != null ? _a : [], ...(_b = previous.edges) != null ? _b : []] : [...(_c = previous.edges) != null ? _c : [], ...(_d = next.edges) != null ? _d : []],
pageInfo: mode === "before" ? {
hasPreviousPage: next.pageInfo.hasPreviousPage,

@@ -1478,3 +1301,3 @@ startCursor: next.pageInfo.startCursor,

endCursor: previous.pageInfo.endCursor
})).with("after", () => ({
} : {
hasPreviousPage: previous.pageInfo.hasPreviousPage,

@@ -1484,3 +1307,3 @@ startCursor: previous.pageInfo.startCursor,

endCursor: next.pageInfo.endCursor
})).exhaustive()
}
};

@@ -1491,37 +1314,24 @@ };

const client = react.useContext(ClientContext);
const connectionArgumentsRef = react.useRef(
[]
);
if (connection != null && "__connectionQueryArguments" in connection) {
const arg = connection.__connectionQueryArguments;
const serializedArg = serializeVariables(arg);
if (!connectionArgumentsRef.current.find(
([serialized]) => serializedArg === serialized
)) {
if ("__connectionArguments" in connection && isRecord(connection.__connectionArguments) && connection.__connectionArguments[direction] == null) {
connectionArgumentsRef.current = [[serializedArg, arg]];
} else {
connectionArgumentsRef.current = [
...connectionArgumentsRef.current,
[serializedArg, arg]
];
}
const connectionRefs = react.useRef([]);
const lastReturnedValueRef = react.useRef(boxed.Option.None());
if (connection == null) {
connectionRefs.current = [];
} else {
if (CONNECTION_REF in connection && typeof connection[CONNECTION_REF] === "number" && !connectionRefs.current.includes(connection[CONNECTION_REF])) {
connectionRefs.current.push(connection[CONNECTION_REF]);
}
}
const jsonPath = react.useRef(
connection != null && "__connectionJsonPath" in connection ? connection.__connectionJsonPath : []
);
const documentNode = connection != null && "__connectionDocumentNode" in connection ? connection.__connectionDocumentNode : void 0;
const lastReturnedValueRef = react.useRef(boxed.Option.None());
const getSnapshot = react.useCallback(() => {
if (documentNode == null) {
return boxed.Option.None();
}
const value = boxed.Option.all(
connectionArgumentsRef.current.map(
([, args]) => client.readFromCache(documentNode, args, {})
boxed.Array.filterMap(
connectionRefs.current,
(id) => boxed.Option.fromNullable(client.cache.connectionCache.get(id))
).flatMap(
(info) => client.readFromCache(info.document, info.variables, {}).map(
(query) => query.map((query2) => ({ query: query2, pathInQuery: info.pathInQuery }))
)
)
).map(boxed.Result.all).flatMap((x) => x.toOption()).map(
(queries) => queries.map(
(query) => jsonPath.current.reduce(
(queries) => queries.map(({ query, pathInQuery }) => {
return pathInQuery.reduce(
(acc, key) => acc != null && typeof acc === "object" && key in acc ? (

@@ -1532,4 +1342,4 @@ // @ts-expect-error indexable

query
)
)
);
})
);

@@ -1542,3 +1352,3 @@ if (!deepEqual(value, lastReturnedValueRef.current)) {

}
}, [client, documentNode]);
}, [client]);
const data = react.useSyncExternalStore(

@@ -1545,0 +1355,0 @@ (func) => client.subscribe(func),

@@ -1,4 +0,6 @@

export declare const DEEP_MERGE_DELETE: unique symbol;
export declare const REQUESTED_KEYS: unique symbol;
export declare const deepMerge: (target: any, source: any) => any;
export declare const CONNECTION_REF = "__connectionRef";
export declare const TYPENAME_KEY: unique symbol;
export declare const EDGES_KEY: unique symbol;
export declare const NODE_KEY: unique symbol;
export declare const containsAll: <T>(a: Set<T>, b: Set<T>) => boolean;

@@ -5,0 +7,0 @@ export declare const isRecord: (value: unknown) => value is Record<PropertyKey, unknown>;

{
"name": "@swan-io/graphql-client",
"version": "0.3.1",
"version": "0.4.0",
"license": "MIT",

@@ -54,4 +54,3 @@ "description": "A simple, typesafe GraphQL client for React",

"@swan-io/boxed": "^3.0.0",
"@swan-io/request": "^1.0.6",
"ts-pattern": "^5.1.0"
"@swan-io/request": "^1.0.6"
},

@@ -80,2 +79,3 @@ "peerDependencies": {

"react-dom": "^18.2.0",
"ts-pattern": "^5.1.0",
"tsup": "^8.0.2",

@@ -82,0 +82,0 @@ "tsx": "^4.7.1",

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc