@settlemint/sdk-thegraph 2.6.2 → 2.6.3-pr2085fb82
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -10
- package/dist/browser/thegraph.js +20 -4
- package/dist/browser/thegraph.js.map +1 -1
- package/dist/thegraph.cjs +38 -13
- package/dist/thegraph.cjs.map +1 -1
- package/dist/thegraph.js +20 -4
- package/dist/thegraph.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -52,7 +52,7 @@ The SDK offers a type-safe interface for all TheGraph operations, with comprehen
|
|
|
52
52
|
|
|
53
53
|
> **createTheGraphClient**\<`Setup`\>(`options`, `clientOptions?`): `object`
|
|
54
54
|
|
|
55
|
-
Defined in: [sdk/thegraph/src/thegraph.ts:92](https://github.com/settlemint/sdk/blob/v2.6.
|
|
55
|
+
Defined in: [sdk/thegraph/src/thegraph.ts:92](https://github.com/settlemint/sdk/blob/v2.6.3/sdk/thegraph/src/thegraph.ts#L92)
|
|
56
56
|
|
|
57
57
|
Creates a TheGraph GraphQL client with proper type safety using gql.tada
|
|
58
58
|
|
|
@@ -83,8 +83,8 @@ An object containing:
|
|
|
83
83
|
|
|
84
84
|
| Name | Type | Defined in |
|
|
85
85
|
| ------ | ------ | ------ |
|
|
86
|
-
| `client` | `GraphQLClient` | [sdk/thegraph/src/thegraph.ts:96](https://github.com/settlemint/sdk/blob/v2.6.
|
|
87
|
-
| `graphql` | `initGraphQLTada`\<`Setup`\> | [sdk/thegraph/src/thegraph.ts:97](https://github.com/settlemint/sdk/blob/v2.6.
|
|
86
|
+
| `client` | `GraphQLClient` | [sdk/thegraph/src/thegraph.ts:96](https://github.com/settlemint/sdk/blob/v2.6.3/sdk/thegraph/src/thegraph.ts#L96) |
|
|
87
|
+
| `graphql` | `initGraphQLTada`\<`Setup`\> | [sdk/thegraph/src/thegraph.ts:97](https://github.com/settlemint/sdk/blob/v2.6.3/sdk/thegraph/src/thegraph.ts#L97) |
|
|
88
88
|
|
|
89
89
|
##### Throws
|
|
90
90
|
|
|
@@ -137,7 +137,7 @@ const result = await client.request(query);
|
|
|
137
137
|
|
|
138
138
|
> **ClientOptions** = `object`
|
|
139
139
|
|
|
140
|
-
Defined in: [sdk/thegraph/src/thegraph.ts:27](https://github.com/settlemint/sdk/blob/v2.6.
|
|
140
|
+
Defined in: [sdk/thegraph/src/thegraph.ts:27](https://github.com/settlemint/sdk/blob/v2.6.3/sdk/thegraph/src/thegraph.ts#L27)
|
|
141
141
|
|
|
142
142
|
Type definition for client options derived from the ClientOptionsSchema
|
|
143
143
|
|
|
@@ -145,10 +145,10 @@ Type definition for client options derived from the ClientOptionsSchema
|
|
|
145
145
|
|
|
146
146
|
| Name | Type | Defined in |
|
|
147
147
|
| ------ | ------ | ------ |
|
|
148
|
-
| <a id="accesstoken"></a> `accessToken?` | `string` | [sdk/thegraph/src/thegraph.ts:19](https://github.com/settlemint/sdk/blob/v2.6.
|
|
149
|
-
| <a id="cache"></a> `cache?` | `"default"` \| `"force-cache"` \| `"no-cache"` \| `"no-store"` \| `"only-if-cached"` \| `"reload"` | [sdk/thegraph/src/thegraph.ts:21](https://github.com/settlemint/sdk/blob/v2.6.
|
|
150
|
-
| <a id="instances"></a> `instances` | `string`[] | [sdk/thegraph/src/thegraph.ts:18](https://github.com/settlemint/sdk/blob/v2.6.
|
|
151
|
-
| <a id="subgraphname"></a> `subgraphName` | `string` | [sdk/thegraph/src/thegraph.ts:20](https://github.com/settlemint/sdk/blob/v2.6.
|
|
148
|
+
| <a id="accesstoken"></a> `accessToken?` | `string` | [sdk/thegraph/src/thegraph.ts:19](https://github.com/settlemint/sdk/blob/v2.6.3/sdk/thegraph/src/thegraph.ts#L19) |
|
|
149
|
+
| <a id="cache"></a> `cache?` | `"default"` \| `"force-cache"` \| `"no-cache"` \| `"no-store"` \| `"only-if-cached"` \| `"reload"` | [sdk/thegraph/src/thegraph.ts:21](https://github.com/settlemint/sdk/blob/v2.6.3/sdk/thegraph/src/thegraph.ts#L21) |
|
|
150
|
+
| <a id="instances"></a> `instances` | `string`[] | [sdk/thegraph/src/thegraph.ts:18](https://github.com/settlemint/sdk/blob/v2.6.3/sdk/thegraph/src/thegraph.ts#L18) |
|
|
151
|
+
| <a id="subgraphname"></a> `subgraphName` | `string` | [sdk/thegraph/src/thegraph.ts:20](https://github.com/settlemint/sdk/blob/v2.6.3/sdk/thegraph/src/thegraph.ts#L20) |
|
|
152
152
|
|
|
153
153
|
***
|
|
154
154
|
|
|
@@ -156,7 +156,7 @@ Type definition for client options derived from the ClientOptionsSchema
|
|
|
156
156
|
|
|
157
157
|
> **RequestConfig** = `ConstructorParameters`\<*typeof* `GraphQLClient`\>\[`1`\]
|
|
158
158
|
|
|
159
|
-
Defined in: [sdk/thegraph/src/thegraph.ts:12](https://github.com/settlemint/sdk/blob/v2.6.
|
|
159
|
+
Defined in: [sdk/thegraph/src/thegraph.ts:12](https://github.com/settlemint/sdk/blob/v2.6.3/sdk/thegraph/src/thegraph.ts#L12)
|
|
160
160
|
|
|
161
161
|
Type definition for GraphQL client configuration options
|
|
162
162
|
|
|
@@ -166,7 +166,7 @@ Type definition for GraphQL client configuration options
|
|
|
166
166
|
|
|
167
167
|
> `const` **ClientOptionsSchema**: `ZodObject`\<[`ClientOptions`](#clientoptions)\>
|
|
168
168
|
|
|
169
|
-
Defined in: [sdk/thegraph/src/thegraph.ts:17](https://github.com/settlemint/sdk/blob/v2.6.
|
|
169
|
+
Defined in: [sdk/thegraph/src/thegraph.ts:17](https://github.com/settlemint/sdk/blob/v2.6.3/sdk/thegraph/src/thegraph.ts#L17)
|
|
170
170
|
|
|
171
171
|
Schema for validating client options for the TheGraph client.
|
|
172
172
|
|
package/dist/browser/thegraph.js
CHANGED
|
@@ -189,7 +189,6 @@ function createSingleFieldQuery(document, targetField, skip, first) {
|
|
|
189
189
|
} });
|
|
190
190
|
}
|
|
191
191
|
function createNonListQuery(document, listFields) {
|
|
192
|
-
let hasFields = false;
|
|
193
192
|
const pathStack = [];
|
|
194
193
|
const filtered = visit(document, { Field: {
|
|
195
194
|
enter: (node) => {
|
|
@@ -200,14 +199,28 @@ function createNonListQuery(document, listFields) {
|
|
|
200
199
|
pathStack.pop();
|
|
201
200
|
return null;
|
|
202
201
|
}
|
|
203
|
-
hasFields = true;
|
|
204
202
|
return undefined;
|
|
205
203
|
},
|
|
206
|
-
leave: () => {
|
|
204
|
+
leave: (node) => {
|
|
207
205
|
pathStack.pop();
|
|
206
|
+
if (node.selectionSet && node.selectionSet.selections.length === 0) {
|
|
207
|
+
return null;
|
|
208
|
+
}
|
|
209
|
+
return undefined;
|
|
208
210
|
}
|
|
209
211
|
} });
|
|
210
|
-
return
|
|
212
|
+
return filtered;
|
|
213
|
+
}
|
|
214
|
+
function countExecutableFields(document) {
|
|
215
|
+
let fieldCount = 0;
|
|
216
|
+
visit(document, { Field: (node) => {
|
|
217
|
+
if (!node.name.value.startsWith("__")) {
|
|
218
|
+
if (!node.selectionSet || node.selectionSet.selections.length > 0) {
|
|
219
|
+
fieldCount += 1;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
} });
|
|
223
|
+
return fieldCount;
|
|
211
224
|
}
|
|
212
225
|
function filterVariables(variables, document) {
|
|
213
226
|
if (!variables) return undefined;
|
|
@@ -291,6 +304,9 @@ function createTheGraphClientWithPagination(theGraphClient) {
|
|
|
291
304
|
}
|
|
292
305
|
const nonListQuery = createNonListQuery(processedDocument, listFields);
|
|
293
306
|
if (nonListQuery) {
|
|
307
|
+
if (countExecutableFields(nonListQuery) === 0) {
|
|
308
|
+
return result;
|
|
309
|
+
}
|
|
294
310
|
const nonListResult = await theGraphClient.request(nonListQuery, filterVariables(variables, nonListQuery) ?? {}, requestHeaders);
|
|
295
311
|
const merged = customMerge(nonListResult, result);
|
|
296
312
|
return merged;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"thegraph.js","names":["result: Record<string, unknown>","fields: ListFieldWithFetchAllDirective[]","pathStack: string[]","firstValue: number | undefined","skipValue: number | undefined","otherArgs: ArgumentNode[]","newArgs: ArgumentNode[]","filtered: Variables","results: unknown[]","document: TadaDocumentNode<TResult, TVariables> | RequestDocument","variables: Omit<TVariables, \"skip\" | \"first\">","requestHeaders: HeadersInit | undefined","instance"],"sources":["../../src/utils/pagination.ts","../../src/thegraph.ts"],"sourcesContent":["import { sortBy } from \"es-toolkit\";\nimport { get, isArray, isEmpty, set } from \"es-toolkit/compat\";\nimport type { TadaDocumentNode } from \"gql.tada\";\nimport { type ArgumentNode, type DocumentNode, Kind, parse, visit } from \"graphql\";\nimport type { GraphQLClient, RequestDocument, RequestOptions, Variables } from \"graphql-request\";\n\n// Constants for TheGraph limits\nconst THE_GRAPH_LIMIT = 500;\nconst FIRST_ARG = \"first\";\nconst SKIP_ARG = \"skip\";\nconst FETCH_ALL_DIRECTIVE = \"fetchAll\";\n\ninterface ListFieldWithFetchAllDirective {\n path: string[];\n fieldName: string;\n firstValue?: number;\n skipValue?: number;\n otherArgs: ArgumentNode[];\n}\n\n/**\n * Detects and strips @fetchAll directives from a GraphQL document\n *\n * @param {DocumentNode} document - The GraphQL document to process\n * @returns {Object} Processed document and list of fields with @fetchAll\n *\n * @remarks\n * This function:\n * - Identifies fields decorated with @fetchAll directive\n * - Removes the directive from the AST (The Graph doesn't recognize it)\n * - Returns both the cleaned document and a list of fields to auto-paginate\n */\nfunction stripFetchAllDirective(document: DocumentNode | RequestDocument): {\n document: DocumentNode;\n fetchAllFields: Set<string>;\n} {\n const fetchAllFields = new Set<string>();\n const documentNode = typeof document === \"string\" ? parse(document) : document;\n const strippedDocument = visit(documentNode, {\n Field(node) {\n // Check if this field has the @fetchAll directive\n if (node.directives && node.directives.length > 0) {\n const hasFetchAll = node.directives.some((dir) => dir.name.value === FETCH_ALL_DIRECTIVE);\n\n if (hasFetchAll) {\n const fieldIdentifier = node.alias?.value || node.name.value;\n fetchAllFields.add(fieldIdentifier);\n\n // Return a new node without the @fetchAll directive\n return {\n ...node,\n directives: node.directives.filter((dir) => dir.name.value !== FETCH_ALL_DIRECTIVE),\n };\n }\n }\n\n return node;\n },\n });\n\n return {\n document: strippedDocument,\n fetchAllFields,\n };\n}\n\n/**\n * Custom merge function for deep object merging with special handling for lists\n *\n * @param {unknown} target - The target object or value to merge into\n * @param {unknown} source - The source object or value to merge from\n * @returns {unknown} Merged result with preservation of arrays and specific merge logic\n *\n * @remarks\n * Key behaviors:\n * - Preserves existing arrays without merging\n * - Handles null and undefined values\n * - Performs deep merge for nested objects\n * - Prioritizes source values for primitives\n *\n */\nfunction customMerge(target: unknown, source: unknown): unknown {\n if (source == null) return target;\n if (target == null) return source;\n\n // If source is an array, return it, don't merge arrays\n if (isArray(source)) {\n return source;\n }\n\n if (typeof target !== \"object\" || typeof source !== \"object\") {\n return source;\n }\n\n // Manually merge objects to ensure arrays are preserved\n const targetObj = target as Record<string, unknown>;\n const sourceObj = source as Record<string, unknown>;\n const result: Record<string, unknown> = { ...targetObj };\n\n for (const key in sourceObj) {\n if (Object.hasOwn(sourceObj, key)) {\n result[key] = key in result ? customMerge(result[key], sourceObj[key]) : sourceObj[key];\n }\n }\n\n return result;\n}\n\n// Extract all fields that have @fetchAll directive\nfunction extractFetchAllFields(\n document: DocumentNode,\n variables?: Variables,\n fetchAllFields?: Set<string>,\n): ListFieldWithFetchAllDirective[] {\n const fields: ListFieldWithFetchAllDirective[] = [];\n const pathStack: string[] = [];\n\n visit(document, {\n Field: {\n enter: (node) => {\n const fieldIdentifier = node.alias?.value || node.name.value;\n pathStack.push(fieldIdentifier);\n\n // Skip meta fields\n if (node.name.value.startsWith(\"__\")) {\n return;\n }\n\n // Check if this field has pagination arguments (first or skip)\n let firstValue: number | undefined;\n let skipValue: number | undefined;\n const otherArgs: ArgumentNode[] = [];\n\n if (node.arguments) {\n for (const arg of node.arguments) {\n if (arg.name.value === FIRST_ARG) {\n if (arg.value.kind === Kind.INT) {\n firstValue = Number.parseInt(arg.value.value, 10);\n } else if (arg.value.kind === Kind.VARIABLE && variables) {\n const varName = arg.value.name.value;\n const varValue = (variables as Record<string, unknown>)[varName];\n firstValue = typeof varValue === \"number\" ? varValue : undefined;\n }\n } else if (arg.name.value === SKIP_ARG) {\n if (arg.value.kind === Kind.INT) {\n skipValue = Number.parseInt(arg.value.value, 10);\n } else if (arg.value.kind === Kind.VARIABLE && variables) {\n const varName = arg.value.name.value;\n const varValue = (variables as Record<string, unknown>)[varName];\n skipValue = typeof varValue === \"number\" ? varValue : undefined;\n }\n } else {\n otherArgs.push(arg);\n }\n }\n }\n\n // Check if this field has @fetchAll directive\n const fieldIdentifierForDirective = node.alias?.value || node.name.value;\n const hasFetchAllDirective = fetchAllFields?.has(fieldIdentifierForDirective);\n\n if (hasFetchAllDirective) {\n // Do not allow nesting @fetchAll fields\n const parentFetchAllField = fields.find((field) => pathStack.join(\",\").startsWith(field.path.join(\",\")));\n if (parentFetchAllField) {\n throw new Error(\n `Nesting of @fetchAll directive is not supported: ${pathStack.join(\".\")} is a child of ${parentFetchAllField.path.join(\".\")}`,\n );\n }\n fields.push({\n path: [...pathStack],\n fieldName: node.name.value,\n firstValue: firstValue ?? THE_GRAPH_LIMIT,\n skipValue: skipValue ?? 0,\n otherArgs,\n });\n }\n },\n leave: () => {\n pathStack.pop();\n },\n },\n });\n\n return fields;\n}\n\n// Create a query for a single field with specific pagination\nfunction createSingleFieldQuery(\n document: DocumentNode,\n targetField: ListFieldWithFetchAllDirective,\n skip: number,\n first: number,\n): DocumentNode {\n const targetPath = [...targetField.path];\n const pathStack: string[] = [];\n\n return visit(document, {\n Field: {\n enter: (node) => {\n const fieldIdentifier = node.alias?.value || node.name.value;\n pathStack.push(fieldIdentifier);\n\n // Check if we're on the path to target field\n const onPath = pathStack.every((segment, i) => i >= targetPath.length || segment === targetPath[i]);\n\n if (!onPath) {\n pathStack.pop();\n return null; // Remove fields not on path\n }\n\n // If this is our target field, update pagination\n const isTarget =\n pathStack.length === targetPath.length && pathStack.every((segment, i) => segment === targetPath[i]);\n\n if (isTarget) {\n const newArgs: ArgumentNode[] = [...targetField.otherArgs];\n\n // Add pagination arguments\n newArgs.push(\n {\n kind: Kind.ARGUMENT,\n name: { kind: Kind.NAME, value: FIRST_ARG },\n value: { kind: Kind.INT, value: first.toString() },\n },\n {\n kind: Kind.ARGUMENT,\n name: { kind: Kind.NAME, value: SKIP_ARG },\n value: { kind: Kind.INT, value: skip.toString() },\n },\n );\n\n return { ...node, arguments: newArgs };\n }\n\n return undefined;\n },\n leave: () => {\n pathStack.pop();\n },\n },\n });\n}\n\n// Create query without list fields\nfunction createNonListQuery(document: DocumentNode, listFields: ListFieldWithFetchAllDirective[]): DocumentNode | null {\n let hasFields = false;\n const pathStack: string[] = [];\n\n const filtered = visit(document, {\n Field: {\n enter: (node) => {\n const fieldIdentifier = node.alias?.value || node.name.value;\n pathStack.push(fieldIdentifier);\n\n // Check if this field is a list field\n const isList = listFields.some(\n (field) =>\n field.path.length === pathStack.length && field.path.every((segment, i) => segment === pathStack[i]),\n );\n\n if (isList) {\n pathStack.pop();\n return null;\n }\n\n hasFields = true;\n return undefined;\n },\n leave: () => {\n pathStack.pop();\n },\n },\n });\n\n return hasFields ? filtered : null;\n}\n\n// Filter variables to only include used ones\nfunction filterVariables(variables: Variables | undefined, document: DocumentNode): Variables | undefined {\n if (!variables) return undefined;\n\n const usedVariables = new Set<string>();\n\n visit(document, {\n Variable: (node) => {\n usedVariables.add(node.name.value);\n },\n });\n\n const filtered: Variables = {};\n const varsObj = variables as Record<string, unknown>;\n for (const key of usedVariables) {\n if (key in varsObj) {\n (filtered as Record<string, unknown>)[key] = varsObj[key];\n }\n }\n\n return isEmpty(filtered) ? undefined : filtered;\n}\n\n/**\n * Creates a TheGraph client that supports pagination for list fields\n *\n * @param theGraphClient - The GraphQL client to use for requests\n * @returns A TheGraph client that supports pagination for list fields\n * @internal Used internally by createTheGraphClient\n */\nexport function createTheGraphClientWithPagination(theGraphClient: Pick<GraphQLClient, \"request\">) {\n // Execute pagination for a list field\n async function executeListFieldPagination(\n document: DocumentNode,\n variables: Variables | undefined,\n field: ListFieldWithFetchAllDirective,\n requestHeaders?: HeadersInit,\n ): Promise<unknown[]> {\n const results: unknown[] = [];\n let currentSkip = field.skipValue || 0;\n let hasMore = true;\n\n // For fields with pagination arguments, always attempt to fetch data\n // and continue if we get a full page (indicating more data might exist)\n const batchSize = Math.min(field.firstValue || THE_GRAPH_LIMIT, THE_GRAPH_LIMIT);\n\n while (hasMore) {\n const query = createSingleFieldQuery(document, field, currentSkip, batchSize);\n const existingVariables = filterVariables(variables, query) ?? {};\n const response = await theGraphClient.request(\n query,\n {\n ...existingVariables,\n first: batchSize,\n skip: currentSkip,\n },\n requestHeaders,\n );\n\n // Use array path format for es-toolkit's get function\n const data = get(response, field.path) ?? get(response, field.fieldName);\n\n const parentPath = field.path.slice(0, -1);\n const parentData = get(response, parentPath);\n if (isArray(parentData) && parentData.length > 0) {\n throw new Error(\n `Response is an array, but expected a single object for field ${parentPath.join(\".\")}. The @fetchAll directive is not supported inside a query that returns a list of items.`,\n );\n }\n\n if (isArray(data) && data.length > 0) {\n results.push(...data);\n\n // With @fetchAll, continue if we got a full batch\n hasMore = data.length === batchSize;\n } else {\n hasMore = false;\n }\n\n currentSkip += batchSize;\n }\n\n return results;\n }\n\n return {\n async query<TResult, TVariables extends Variables>(\n documentOrOptions: TadaDocumentNode<TResult, TVariables> | RequestDocument | RequestOptions<TVariables, TResult>,\n variablesRaw?: Omit<TVariables, \"skip\" | \"first\">,\n requestHeadersRaw?: HeadersInit,\n ): Promise<TResult> {\n let document: TadaDocumentNode<TResult, TVariables> | RequestDocument;\n let variables: Omit<TVariables, \"skip\" | \"first\">;\n let requestHeaders: HeadersInit | undefined;\n\n if (isRequestOptions(documentOrOptions)) {\n document = documentOrOptions.document;\n variables = (documentOrOptions.variables ?? {}) as TVariables;\n requestHeaders = documentOrOptions.requestHeaders;\n } else {\n document = documentOrOptions;\n variables = variablesRaw ?? ({} as TVariables);\n requestHeaders = requestHeadersRaw;\n }\n\n // First, detect and strip @fetchAll directives\n const { document: processedDocument, fetchAllFields } = stripFetchAllDirective(document);\n\n // Extract all list fields (including those with @fetchAll)\n const listFields = extractFetchAllFields(processedDocument, variables, fetchAllFields);\n\n // If no list fields, execute normally\n if (listFields.length === 0) {\n return theGraphClient.request(processedDocument, variables as Variables, requestHeaders);\n }\n\n // Execute paginated queries for all list fields\n const result: Record<string, unknown> = {};\n\n // Sort fields by depth to handle nested fields correctly\n const sortedFields = sortBy(listFields, [(field) => field.path.length]);\n\n // Process list fields in parallel for better performance\n const fieldDataPromises = sortedFields.map(async (field) => ({\n field,\n data: await executeListFieldPagination(processedDocument, variables, field, requestHeaders),\n }));\n\n const fieldResults = await Promise.all(fieldDataPromises);\n\n // Set results in order\n for (const { field, data } of fieldResults) {\n // Use array path format for es-toolkit's set function\n set(result, field.path, data);\n }\n\n // Execute non-list fields (single entity queries)\n const nonListQuery = createNonListQuery(processedDocument, listFields);\n\n if (nonListQuery) {\n const nonListResult = await theGraphClient.request(\n nonListQuery,\n filterVariables(variables, nonListQuery) ?? {},\n requestHeaders,\n );\n\n // Merge results, preserving list data\n const merged = customMerge(nonListResult, result);\n return merged as TResult;\n }\n\n return result as TResult;\n },\n } as const;\n}\n\nfunction isRequestOptions(args: unknown): args is RequestOptions<Variables, unknown> {\n return typeof args === \"object\" && args !== null && \"document\" in args;\n}\n","import { appendHeaders } from \"@settlemint/sdk-utils/http\";\nimport { ensureServer } from \"@settlemint/sdk-utils/runtime\";\nimport { ApplicationAccessTokenSchema, UrlOrPathSchema, validate } from \"@settlemint/sdk-utils/validation\";\nimport { type AbstractSetupSchema, initGraphQLTada } from \"gql.tada\";\nimport { GraphQLClient } from \"graphql-request\";\nimport { z } from \"zod\";\nimport { createTheGraphClientWithPagination } from \"./utils/pagination.js\";\n\n/**\n * Type definition for GraphQL client configuration options\n */\nexport type RequestConfig = ConstructorParameters<typeof GraphQLClient>[1];\n\n/**\n * Schema for validating client options for the TheGraph client.\n */\nexport const ClientOptionsSchema = z.object({\n instances: z.array(UrlOrPathSchema),\n accessToken: ApplicationAccessTokenSchema.optional(),\n subgraphName: z.string(),\n cache: z.enum([\"default\", \"force-cache\", \"no-cache\", \"no-store\", \"only-if-cached\", \"reload\"]).optional(),\n});\n\n/**\n * Type definition for client options derived from the ClientOptionsSchema\n */\nexport type ClientOptions = z.infer<typeof ClientOptionsSchema>;\n\n/**\n * Constructs the full URL for TheGraph GraphQL API based on the provided options\n *\n * @param options - The client options for configuring TheGraph client\n * @returns The complete GraphQL API URL as a string\n * @throws Will throw an error if no matching instance is found for the specified subgraph\n */\nfunction getFullUrl(options: ClientOptions): string {\n const instance = options.instances.find((instance) => instance.endsWith(`/${options.subgraphName}`));\n if (!instance) {\n throw new Error(`Instance for subgraph ${options.subgraphName} not found`);\n }\n return new URL(instance).toString();\n}\n\n/**\n * Creates a TheGraph GraphQL client with proper type safety using gql.tada\n *\n * @param options - Configuration options for the client including instance URLs,\n * access token and subgraph name\n * @param clientOptions - Optional GraphQL client configuration options\n * @returns An object containing:\n * - client: The configured GraphQL client instance\n * - graphql: The initialized gql.tada function for type-safe queries\n * @throws Will throw an error if the options fail validation against ClientOptionsSchema\n * @example\n * import { createTheGraphClient } from '@settlemint/sdk-thegraph';\n * import type { introspection } from '@schemas/the-graph-env-kits';\n * import { createLogger, requestLogger } from '@settlemint/sdk-utils/logging';\n *\n * const logger = createLogger();\n *\n * const { client, graphql } = createTheGraphClient<{\n * introspection: introspection;\n * disableMasking: true;\n * scalars: {\n * Bytes: string;\n * Int8: string;\n * BigInt: string;\n * BigDecimal: string;\n * Timestamp: string;\n * };\n * }>({\n * instances: JSON.parse(process.env.SETTLEMINT_THEGRAPH_SUBGRAPHS_ENDPOINTS || '[]'),\n * accessToken: process.env.SETTLEMINT_ACCESS_TOKEN,\n * subgraphName: 'kits'\n * }, {\n * fetch: requestLogger(logger, \"the-graph-kits\", fetch) as typeof fetch,\n * });\n *\n * // Making GraphQL queries\n * const query = graphql(`\n * query SearchAssets {\n * assets @fetchAll {\n * id\n * name\n * symbol\n * }\n * }\n * `);\n *\n * const result = await client.request(query);\n */\nexport function createTheGraphClient<const Setup extends AbstractSetupSchema>(\n options: ClientOptions,\n clientOptions?: RequestConfig,\n): {\n client: GraphQLClient;\n graphql: initGraphQLTada<Setup>;\n} {\n ensureServer();\n const validatedOptions = validate(ClientOptionsSchema, options);\n const graphql = initGraphQLTada<Setup>();\n const fullUrl = getFullUrl(validatedOptions);\n\n const client = new GraphQLClient(fullUrl, {\n ...clientOptions,\n headers: appendHeaders(clientOptions?.headers, { \"x-auth-token\": validatedOptions.accessToken }),\n });\n const originalRequest = client.request.bind(client);\n const paginatedClient = createTheGraphClientWithPagination({\n request: originalRequest,\n });\n client.request = paginatedClient.query;\n return {\n client,\n graphql,\n };\n}\n\nexport type { FragmentOf, ResultOf, VariablesOf } from \"gql.tada\";\nexport { readFragment } from \"gql.tada\";\nexport { createTheGraphClientWithPagination } from \"./utils/pagination.js\";\n"],"mappings":";;;;;;;;;;;;AAOA,MAAM,kBAAkB;AACxB,MAAM,YAAY;AAClB,MAAM,WAAW;AACjB,MAAM,sBAAsB;;;;;;;;;;;;;AAsB5B,SAAS,uBAAuB,UAG9B;CACA,MAAM,iBAAiB,IAAI;CAC3B,MAAM,eAAe,OAAO,aAAa,WAAW,MAAM,YAAY;CACtE,MAAM,mBAAmB,MAAM,cAAc,EAC3C,MAAM,MAAM;AAEV,MAAI,KAAK,cAAc,KAAK,WAAW,SAAS,GAAG;GACjD,MAAM,cAAc,KAAK,WAAW,MAAM,QAAQ,IAAI,KAAK,UAAU;AAErE,OAAI,aAAa;IACf,MAAM,kBAAkB,KAAK,OAAO,SAAS,KAAK,KAAK;AACvD,mBAAe,IAAI;AAGnB,WAAO;KACL,GAAG;KACH,YAAY,KAAK,WAAW,QAAQ,QAAQ,IAAI,KAAK,UAAU;;;;AAKrE,SAAO;;AAIX,QAAO;EACL,UAAU;EACV;;;;;;;;;;;;;;;;;;AAmBJ,SAAS,YAAY,QAAiB,QAA0B;AAC9D,KAAI,UAAU,KAAM,QAAO;AAC3B,KAAI,UAAU,KAAM,QAAO;AAG3B,KAAI,QAAQ,SAAS;AACnB,SAAO;;AAGT,KAAI,OAAO,WAAW,YAAY,OAAO,WAAW,UAAU;AAC5D,SAAO;;CAIT,MAAM,YAAY;CAClB,MAAM,YAAY;CAClB,MAAMA,SAAkC,EAAE,GAAG;AAE7C,MAAK,MAAM,OAAO,WAAW;AAC3B,MAAI,OAAO,OAAO,WAAW,MAAM;AACjC,UAAO,OAAO,OAAO,SAAS,YAAY,OAAO,MAAM,UAAU,QAAQ,UAAU;;;AAIvF,QAAO;;AAIT,SAAS,sBACP,UACA,WACA,gBACkC;CAClC,MAAMC,SAA2C;CACjD,MAAMC,YAAsB;AAE5B,OAAM,UAAU,EACd,OAAO;EACL,QAAQ,SAAS;GACf,MAAM,kBAAkB,KAAK,OAAO,SAAS,KAAK,KAAK;AACvD,aAAU,KAAK;AAGf,OAAI,KAAK,KAAK,MAAM,WAAW,OAAO;AACpC;;GAIF,IAAIC;GACJ,IAAIC;GACJ,MAAMC,YAA4B;AAElC,OAAI,KAAK,WAAW;AAClB,SAAK,MAAM,OAAO,KAAK,WAAW;AAChC,SAAI,IAAI,KAAK,UAAU,WAAW;AAChC,UAAI,IAAI,MAAM,SAAS,KAAK,KAAK;AAC/B,oBAAa,OAAO,SAAS,IAAI,MAAM,OAAO;iBACrC,IAAI,MAAM,SAAS,KAAK,YAAY,WAAW;OACxD,MAAM,UAAU,IAAI,MAAM,KAAK;OAC/B,MAAM,WAAY,UAAsC;AACxD,oBAAa,OAAO,aAAa,WAAW,WAAW;;gBAEhD,IAAI,KAAK,UAAU,UAAU;AACtC,UAAI,IAAI,MAAM,SAAS,KAAK,KAAK;AAC/B,mBAAY,OAAO,SAAS,IAAI,MAAM,OAAO;iBACpC,IAAI,MAAM,SAAS,KAAK,YAAY,WAAW;OACxD,MAAM,UAAU,IAAI,MAAM,KAAK;OAC/B,MAAM,WAAY,UAAsC;AACxD,mBAAY,OAAO,aAAa,WAAW,WAAW;;YAEnD;AACL,gBAAU,KAAK;;;;GAMrB,MAAM,8BAA8B,KAAK,OAAO,SAAS,KAAK,KAAK;GACnE,MAAM,uBAAuB,gBAAgB,IAAI;AAEjD,OAAI,sBAAsB;IAExB,MAAM,sBAAsB,OAAO,MAAM,UAAU,UAAU,KAAK,KAAK,WAAW,MAAM,KAAK,KAAK;AAClG,QAAI,qBAAqB;AACvB,WAAM,IAAI,MACR,oDAAoD,UAAU,KAAK,KAAK,iBAAiB,oBAAoB,KAAK,KAAK;;AAG3H,WAAO,KAAK;KACV,MAAM,CAAC,GAAG;KACV,WAAW,KAAK,KAAK;KACrB,YAAY,cAAc;KAC1B,WAAW,aAAa;KACxB;;;;EAIN,aAAa;AACX,aAAU;;;AAKhB,QAAO;;AAIT,SAAS,uBACP,UACA,aACA,MACA,OACc;CACd,MAAM,aAAa,CAAC,GAAG,YAAY;CACnC,MAAMH,YAAsB;AAE5B,QAAO,MAAM,UAAU,EACrB,OAAO;EACL,QAAQ,SAAS;GACf,MAAM,kBAAkB,KAAK,OAAO,SAAS,KAAK,KAAK;AACvD,aAAU,KAAK;GAGf,MAAM,SAAS,UAAU,OAAO,SAAS,MAAM,KAAK,WAAW,UAAU,YAAY,WAAW;AAEhG,OAAI,CAAC,QAAQ;AACX,cAAU;AACV,WAAO;;GAIT,MAAM,WACJ,UAAU,WAAW,WAAW,UAAU,UAAU,OAAO,SAAS,MAAM,YAAY,WAAW;AAEnG,OAAI,UAAU;IACZ,MAAMI,UAA0B,CAAC,GAAG,YAAY;AAGhD,YAAQ,KACN;KACE,MAAM,KAAK;KACX,MAAM;MAAE,MAAM,KAAK;MAAM,OAAO;;KAChC,OAAO;MAAE,MAAM,KAAK;MAAK,OAAO,MAAM;;OAExC;KACE,MAAM,KAAK;KACX,MAAM;MAAE,MAAM,KAAK;MAAM,OAAO;;KAChC,OAAO;MAAE,MAAM,KAAK;MAAK,OAAO,KAAK;;;AAIzC,WAAO;KAAE,GAAG;KAAM,WAAW;;;AAG/B,UAAO;;EAET,aAAa;AACX,aAAU;;;;AAOlB,SAAS,mBAAmB,UAAwB,YAAmE;CACrH,IAAI,YAAY;CAChB,MAAMJ,YAAsB;CAE5B,MAAM,WAAW,MAAM,UAAU,EAC/B,OAAO;EACL,QAAQ,SAAS;GACf,MAAM,kBAAkB,KAAK,OAAO,SAAS,KAAK,KAAK;AACvD,aAAU,KAAK;GAGf,MAAM,SAAS,WAAW,MACvB,UACC,MAAM,KAAK,WAAW,UAAU,UAAU,MAAM,KAAK,OAAO,SAAS,MAAM,YAAY,UAAU;AAGrG,OAAI,QAAQ;AACV,cAAU;AACV,WAAO;;AAGT,eAAY;AACZ,UAAO;;EAET,aAAa;AACX,aAAU;;;AAKhB,QAAO,YAAY,WAAW;;AAIhC,SAAS,gBAAgB,WAAkC,UAA+C;AACxG,KAAI,CAAC,UAAW,QAAO;CAEvB,MAAM,gBAAgB,IAAI;AAE1B,OAAM,UAAU,EACd,WAAW,SAAS;AAClB,gBAAc,IAAI,KAAK,KAAK;;CAIhC,MAAMK,WAAsB;CAC5B,MAAM,UAAU;AAChB,MAAK,MAAM,OAAO,eAAe;AAC/B,MAAI,OAAO,SAAS;AAClB,GAAC,SAAqC,OAAO,QAAQ;;;AAIzD,QAAO,QAAQ,YAAY,YAAY;;;;;;;;;AAUzC,SAAgB,mCAAmC,gBAAgD;CAEjG,eAAe,2BACb,UACA,WACA,OACA,gBACoB;EACpB,MAAMC,UAAqB;EAC3B,IAAI,cAAc,MAAM,aAAa;EACrC,IAAI,UAAU;EAId,MAAM,YAAY,KAAK,IAAI,MAAM,cAAc,iBAAiB;AAEhE,SAAO,SAAS;GACd,MAAM,QAAQ,uBAAuB,UAAU,OAAO,aAAa;GACnE,MAAM,oBAAoB,gBAAgB,WAAW,UAAU;GAC/D,MAAM,WAAW,MAAM,eAAe,QACpC,OACA;IACE,GAAG;IACH,OAAO;IACP,MAAM;MAER;GAIF,MAAM,OAAO,IAAI,UAAU,MAAM,SAAS,IAAI,UAAU,MAAM;GAE9D,MAAM,aAAa,MAAM,KAAK,MAAM,GAAG,CAAC;GACxC,MAAM,aAAa,IAAI,UAAU;AACjC,OAAI,QAAQ,eAAe,WAAW,SAAS,GAAG;AAChD,UAAM,IAAI,MACR,gEAAgE,WAAW,KAAK,KAAK;;AAIzF,OAAI,QAAQ,SAAS,KAAK,SAAS,GAAG;AACpC,YAAQ,KAAK,GAAG;AAGhB,cAAU,KAAK,WAAW;UACrB;AACL,cAAU;;AAGZ,kBAAe;;AAGjB,SAAO;;AAGT,QAAO,EACL,MAAM,MACJ,mBACA,cACA,mBACkB;EAClB,IAAIC;EACJ,IAAIC;EACJ,IAAIC;AAEJ,MAAI,iBAAiB,oBAAoB;AACvC,cAAW,kBAAkB;AAC7B,eAAa,kBAAkB,aAAa;AAC5C,oBAAiB,kBAAkB;SAC9B;AACL,cAAW;AACX,eAAY,gBAAiB;AAC7B,oBAAiB;;EAInB,MAAM,EAAE,UAAU,mBAAmB,mBAAmB,uBAAuB;EAG/E,MAAM,aAAa,sBAAsB,mBAAmB,WAAW;AAGvE,MAAI,WAAW,WAAW,GAAG;AAC3B,UAAO,eAAe,QAAQ,mBAAmB,WAAwB;;EAI3E,MAAMX,SAAkC;EAGxC,MAAM,eAAe,OAAO,YAAY,EAAE,UAAU,MAAM,KAAK;EAG/D,MAAM,oBAAoB,aAAa,IAAI,OAAO,WAAW;GAC3D;GACA,MAAM,MAAM,2BAA2B,mBAAmB,WAAW,OAAO;;EAG9E,MAAM,eAAe,MAAM,QAAQ,IAAI;AAGvC,OAAK,MAAM,EAAE,OAAO,UAAU,cAAc;AAE1C,OAAI,QAAQ,MAAM,MAAM;;EAI1B,MAAM,eAAe,mBAAmB,mBAAmB;AAE3D,MAAI,cAAc;GAChB,MAAM,gBAAgB,MAAM,eAAe,QACzC,cACA,gBAAgB,WAAW,iBAAiB,IAC5C;GAIF,MAAM,SAAS,YAAY,eAAe;AAC1C,UAAO;;AAGT,SAAO;;;AAKb,SAAS,iBAAiB,MAA2D;AACnF,QAAO,OAAO,SAAS,YAAY,SAAS,QAAQ,cAAc;;;;;;;;ACnapE,MAAa,sBAAsB,EAAE,OAAO;CAC1C,WAAW,EAAE,MAAM;CACnB,aAAa,6BAA6B;CAC1C,cAAc,EAAE;CAChB,OAAO,EAAE,KAAK;EAAC;EAAW;EAAe;EAAY;EAAY;EAAkB;IAAW;;;;;;;;;AAehG,SAAS,WAAW,SAAgC;CAClD,MAAM,WAAW,QAAQ,UAAU,MAAM,eAAaY,WAAS,SAAS,IAAI,QAAQ;AACpF,KAAI,CAAC,UAAU;AACb,QAAM,IAAI,MAAM,yBAAyB,QAAQ,aAAa;;AAEhE,QAAO,IAAI,IAAI,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmD3B,SAAgB,qBACd,SACA,eAIA;AACA;CACA,MAAM,mBAAmB,SAAS,qBAAqB;CACvD,MAAM,UAAU;CAChB,MAAM,UAAU,WAAW;CAE3B,MAAM,SAAS,IAAI,cAAc,SAAS;EACxC,GAAG;EACH,SAAS,cAAc,eAAe,SAAS,EAAE,gBAAgB,iBAAiB;;CAEpF,MAAM,kBAAkB,OAAO,QAAQ,KAAK;CAC5C,MAAM,kBAAkB,mCAAmC,EACzD,SAAS;AAEX,QAAO,UAAU,gBAAgB;AACjC,QAAO;EACL;EACA"}
|
|
1
|
+
{"version":3,"file":"thegraph.js","names":["result: Record<string, unknown>","fields: ListFieldWithFetchAllDirective[]","pathStack: string[]","firstValue: number | undefined","skipValue: number | undefined","otherArgs: ArgumentNode[]","newArgs: ArgumentNode[]","filtered: Variables","results: unknown[]","document: TadaDocumentNode<TResult, TVariables> | RequestDocument","variables: Omit<TVariables, \"skip\" | \"first\">","requestHeaders: HeadersInit | undefined","instance"],"sources":["../../src/utils/pagination.ts","../../src/thegraph.ts"],"sourcesContent":["import { sortBy } from \"es-toolkit\";\nimport { get, isArray, isEmpty, set } from \"es-toolkit/compat\";\nimport type { TadaDocumentNode } from \"gql.tada\";\nimport { type ArgumentNode, type DocumentNode, type FieldNode, Kind, parse, visit } from \"graphql\";\nimport type { GraphQLClient, RequestDocument, RequestOptions, Variables } from \"graphql-request\";\n\n// Constants for TheGraph limits\nconst THE_GRAPH_LIMIT = 500;\nconst FIRST_ARG = \"first\";\nconst SKIP_ARG = \"skip\";\nconst FETCH_ALL_DIRECTIVE = \"fetchAll\";\n\ninterface ListFieldWithFetchAllDirective {\n path: string[];\n fieldName: string;\n firstValue?: number;\n skipValue?: number;\n otherArgs: ArgumentNode[];\n}\n\n/**\n * Detects and strips @fetchAll directives from a GraphQL document\n *\n * @param {DocumentNode} document - The GraphQL document to process\n * @returns {Object} Processed document and list of fields with @fetchAll\n *\n * @remarks\n * This function:\n * - Identifies fields decorated with @fetchAll directive\n * - Removes the directive from the AST (The Graph doesn't recognize it)\n * - Returns both the cleaned document and a list of fields to auto-paginate\n */\nfunction stripFetchAllDirective(document: DocumentNode | RequestDocument): {\n document: DocumentNode;\n fetchAllFields: Set<string>;\n} {\n const fetchAllFields = new Set<string>();\n const documentNode = typeof document === \"string\" ? parse(document) : document;\n const strippedDocument = visit(documentNode, {\n Field(node) {\n // Check if this field has the @fetchAll directive\n if (node.directives && node.directives.length > 0) {\n const hasFetchAll = node.directives.some((dir) => dir.name.value === FETCH_ALL_DIRECTIVE);\n\n if (hasFetchAll) {\n const fieldIdentifier = node.alias?.value || node.name.value;\n fetchAllFields.add(fieldIdentifier);\n\n // Return a new node without the @fetchAll directive\n return {\n ...node,\n directives: node.directives.filter((dir) => dir.name.value !== FETCH_ALL_DIRECTIVE),\n };\n }\n }\n\n return node;\n },\n });\n\n return {\n document: strippedDocument,\n fetchAllFields,\n };\n}\n\n/**\n * Custom merge function for deep object merging with special handling for lists\n *\n * @param {unknown} target - The target object or value to merge into\n * @param {unknown} source - The source object or value to merge from\n * @returns {unknown} Merged result with preservation of arrays and specific merge logic\n *\n * @remarks\n * Key behaviors:\n * - Preserves existing arrays without merging\n * - Handles null and undefined values\n * - Performs deep merge for nested objects\n * - Prioritizes source values for primitives\n *\n */\nfunction customMerge(target: unknown, source: unknown): unknown {\n if (source == null) return target;\n if (target == null) return source;\n\n // If source is an array, return it, don't merge arrays\n if (isArray(source)) {\n return source;\n }\n\n if (typeof target !== \"object\" || typeof source !== \"object\") {\n return source;\n }\n\n // Manually merge objects to ensure arrays are preserved\n const targetObj = target as Record<string, unknown>;\n const sourceObj = source as Record<string, unknown>;\n const result: Record<string, unknown> = { ...targetObj };\n\n for (const key in sourceObj) {\n if (Object.hasOwn(sourceObj, key)) {\n result[key] = key in result ? customMerge(result[key], sourceObj[key]) : sourceObj[key];\n }\n }\n\n return result;\n}\n\n// Extract all fields that have @fetchAll directive\nfunction extractFetchAllFields(\n document: DocumentNode,\n variables?: Variables,\n fetchAllFields?: Set<string>,\n): ListFieldWithFetchAllDirective[] {\n const fields: ListFieldWithFetchAllDirective[] = [];\n const pathStack: string[] = [];\n\n visit(document, {\n Field: {\n enter: (node) => {\n const fieldIdentifier = node.alias?.value || node.name.value;\n pathStack.push(fieldIdentifier);\n\n // Skip meta fields\n if (node.name.value.startsWith(\"__\")) {\n return;\n }\n\n // Check if this field has pagination arguments (first or skip)\n let firstValue: number | undefined;\n let skipValue: number | undefined;\n const otherArgs: ArgumentNode[] = [];\n\n if (node.arguments) {\n for (const arg of node.arguments) {\n if (arg.name.value === FIRST_ARG) {\n if (arg.value.kind === Kind.INT) {\n firstValue = Number.parseInt(arg.value.value, 10);\n } else if (arg.value.kind === Kind.VARIABLE && variables) {\n const varName = arg.value.name.value;\n const varValue = (variables as Record<string, unknown>)[varName];\n firstValue = typeof varValue === \"number\" ? varValue : undefined;\n }\n } else if (arg.name.value === SKIP_ARG) {\n if (arg.value.kind === Kind.INT) {\n skipValue = Number.parseInt(arg.value.value, 10);\n } else if (arg.value.kind === Kind.VARIABLE && variables) {\n const varName = arg.value.name.value;\n const varValue = (variables as Record<string, unknown>)[varName];\n skipValue = typeof varValue === \"number\" ? varValue : undefined;\n }\n } else {\n otherArgs.push(arg);\n }\n }\n }\n\n // Check if this field has @fetchAll directive\n const fieldIdentifierForDirective = node.alias?.value || node.name.value;\n const hasFetchAllDirective = fetchAllFields?.has(fieldIdentifierForDirective);\n\n if (hasFetchAllDirective) {\n // Do not allow nesting @fetchAll fields\n const parentFetchAllField = fields.find((field) => pathStack.join(\",\").startsWith(field.path.join(\",\")));\n if (parentFetchAllField) {\n throw new Error(\n `Nesting of @fetchAll directive is not supported: ${pathStack.join(\".\")} is a child of ${parentFetchAllField.path.join(\".\")}`,\n );\n }\n fields.push({\n path: [...pathStack],\n fieldName: node.name.value,\n firstValue: firstValue ?? THE_GRAPH_LIMIT,\n skipValue: skipValue ?? 0,\n otherArgs,\n });\n }\n },\n leave: () => {\n pathStack.pop();\n },\n },\n });\n\n return fields;\n}\n\n// Create a query for a single field with specific pagination\nfunction createSingleFieldQuery(\n document: DocumentNode,\n targetField: ListFieldWithFetchAllDirective,\n skip: number,\n first: number,\n): DocumentNode {\n const targetPath = [...targetField.path];\n const pathStack: string[] = [];\n\n return visit(document, {\n Field: {\n enter: (node) => {\n const fieldIdentifier = node.alias?.value || node.name.value;\n pathStack.push(fieldIdentifier);\n\n // Check if we're on the path to target field\n const onPath = pathStack.every((segment, i) => i >= targetPath.length || segment === targetPath[i]);\n\n if (!onPath) {\n pathStack.pop();\n return null; // Remove fields not on path\n }\n\n // If this is our target field, update pagination\n const isTarget =\n pathStack.length === targetPath.length && pathStack.every((segment, i) => segment === targetPath[i]);\n\n if (isTarget) {\n const newArgs: ArgumentNode[] = [...targetField.otherArgs];\n\n // Add pagination arguments\n newArgs.push(\n {\n kind: Kind.ARGUMENT,\n name: { kind: Kind.NAME, value: FIRST_ARG },\n value: { kind: Kind.INT, value: first.toString() },\n },\n {\n kind: Kind.ARGUMENT,\n name: { kind: Kind.NAME, value: SKIP_ARG },\n value: { kind: Kind.INT, value: skip.toString() },\n },\n );\n\n return { ...node, arguments: newArgs };\n }\n\n return undefined;\n },\n leave: () => {\n pathStack.pop();\n },\n },\n });\n}\n\n// Create query without list fields\nfunction createNonListQuery(document: DocumentNode, listFields: ListFieldWithFetchAllDirective[]): DocumentNode | null {\n const pathStack: string[] = [];\n\n const filtered = visit(document, {\n Field: {\n enter: (node) => {\n const fieldIdentifier = node.alias?.value || node.name.value;\n pathStack.push(fieldIdentifier);\n\n // Check if this field is a list field\n const isList = listFields.some(\n (field) =>\n field.path.length === pathStack.length && field.path.every((segment, i) => segment === pathStack[i]),\n );\n\n if (isList) {\n pathStack.pop();\n return null;\n }\n return undefined;\n },\n leave: (node: FieldNode) => {\n pathStack.pop();\n if (node.selectionSet && node.selectionSet.selections.length === 0) {\n return null;\n }\n return undefined;\n },\n },\n });\n\n return filtered;\n}\n\nfunction countExecutableFields(document: DocumentNode): number {\n let fieldCount = 0;\n\n visit(document, {\n Field: (node) => {\n if (!node.name.value.startsWith(\"__\")) {\n if (!node.selectionSet || node.selectionSet.selections.length > 0) {\n fieldCount += 1;\n }\n }\n },\n });\n\n return fieldCount;\n}\n\n// Filter variables to only include used ones\nfunction filterVariables(variables: Variables | undefined, document: DocumentNode): Variables | undefined {\n if (!variables) return undefined;\n\n const usedVariables = new Set<string>();\n\n visit(document, {\n Variable: (node) => {\n usedVariables.add(node.name.value);\n },\n });\n\n const filtered: Variables = {};\n const varsObj = variables as Record<string, unknown>;\n for (const key of usedVariables) {\n if (key in varsObj) {\n (filtered as Record<string, unknown>)[key] = varsObj[key];\n }\n }\n\n return isEmpty(filtered) ? undefined : filtered;\n}\n\n/**\n * Creates a TheGraph client that supports pagination for list fields\n *\n * @param theGraphClient - The GraphQL client to use for requests\n * @returns A TheGraph client that supports pagination for list fields\n * @internal Used internally by createTheGraphClient\n */\nexport function createTheGraphClientWithPagination(theGraphClient: Pick<GraphQLClient, \"request\">) {\n // Execute pagination for a list field\n async function executeListFieldPagination(\n document: DocumentNode,\n variables: Variables | undefined,\n field: ListFieldWithFetchAllDirective,\n requestHeaders?: HeadersInit,\n ): Promise<unknown[]> {\n const results: unknown[] = [];\n let currentSkip = field.skipValue || 0;\n let hasMore = true;\n\n // For fields with pagination arguments, always attempt to fetch data\n // and continue if we get a full page (indicating more data might exist)\n const batchSize = Math.min(field.firstValue || THE_GRAPH_LIMIT, THE_GRAPH_LIMIT);\n\n while (hasMore) {\n const query = createSingleFieldQuery(document, field, currentSkip, batchSize);\n const existingVariables = filterVariables(variables, query) ?? {};\n const response = await theGraphClient.request(\n query,\n {\n ...existingVariables,\n first: batchSize,\n skip: currentSkip,\n },\n requestHeaders,\n );\n\n // Use array path format for es-toolkit's get function\n const data = get(response, field.path) ?? get(response, field.fieldName);\n\n const parentPath = field.path.slice(0, -1);\n const parentData = get(response, parentPath);\n if (isArray(parentData) && parentData.length > 0) {\n throw new Error(\n `Response is an array, but expected a single object for field ${parentPath.join(\".\")}. The @fetchAll directive is not supported inside a query that returns a list of items.`,\n );\n }\n\n if (isArray(data) && data.length > 0) {\n results.push(...data);\n\n // With @fetchAll, continue if we got a full batch\n hasMore = data.length === batchSize;\n } else {\n hasMore = false;\n }\n\n currentSkip += batchSize;\n }\n\n return results;\n }\n\n return {\n async query<TResult, TVariables extends Variables>(\n documentOrOptions: TadaDocumentNode<TResult, TVariables> | RequestDocument | RequestOptions<TVariables, TResult>,\n variablesRaw?: Omit<TVariables, \"skip\" | \"first\">,\n requestHeadersRaw?: HeadersInit,\n ): Promise<TResult> {\n let document: TadaDocumentNode<TResult, TVariables> | RequestDocument;\n let variables: Omit<TVariables, \"skip\" | \"first\">;\n let requestHeaders: HeadersInit | undefined;\n\n if (isRequestOptions(documentOrOptions)) {\n document = documentOrOptions.document;\n variables = (documentOrOptions.variables ?? {}) as TVariables;\n requestHeaders = documentOrOptions.requestHeaders;\n } else {\n document = documentOrOptions;\n variables = variablesRaw ?? ({} as TVariables);\n requestHeaders = requestHeadersRaw;\n }\n\n // First, detect and strip @fetchAll directives\n const { document: processedDocument, fetchAllFields } = stripFetchAllDirective(document);\n\n // Extract all list fields (including those with @fetchAll)\n const listFields = extractFetchAllFields(processedDocument, variables, fetchAllFields);\n\n // If no list fields, execute normally\n if (listFields.length === 0) {\n return theGraphClient.request(processedDocument, variables as Variables, requestHeaders);\n }\n\n // Execute paginated queries for all list fields\n const result: Record<string, unknown> = {};\n\n // Sort fields by depth to handle nested fields correctly\n const sortedFields = sortBy(listFields, [(field) => field.path.length]);\n\n // Process list fields in parallel for better performance\n const fieldDataPromises = sortedFields.map(async (field) => ({\n field,\n data: await executeListFieldPagination(processedDocument, variables, field, requestHeaders),\n }));\n\n const fieldResults = await Promise.all(fieldDataPromises);\n\n // Set results in order\n for (const { field, data } of fieldResults) {\n // Use array path format for es-toolkit's set function\n set(result, field.path, data);\n }\n\n // Execute non-list fields (single entity queries)\n const nonListQuery = createNonListQuery(processedDocument, listFields);\n\n if (nonListQuery) {\n if (countExecutableFields(nonListQuery) === 0) {\n return result as TResult;\n }\n const nonListResult = await theGraphClient.request(\n nonListQuery,\n filterVariables(variables, nonListQuery) ?? {},\n requestHeaders,\n );\n\n // Merge results, preserving list data\n const merged = customMerge(nonListResult, result);\n return merged as TResult;\n }\n\n return result as TResult;\n },\n } as const;\n}\n\nfunction isRequestOptions(args: unknown): args is RequestOptions<Variables, unknown> {\n return typeof args === \"object\" && args !== null && \"document\" in args;\n}\n","import { appendHeaders } from \"@settlemint/sdk-utils/http\";\nimport { ensureServer } from \"@settlemint/sdk-utils/runtime\";\nimport { ApplicationAccessTokenSchema, UrlOrPathSchema, validate } from \"@settlemint/sdk-utils/validation\";\nimport { type AbstractSetupSchema, initGraphQLTada } from \"gql.tada\";\nimport { GraphQLClient } from \"graphql-request\";\nimport { z } from \"zod\";\nimport { createTheGraphClientWithPagination } from \"./utils/pagination.js\";\n\n/**\n * Type definition for GraphQL client configuration options\n */\nexport type RequestConfig = ConstructorParameters<typeof GraphQLClient>[1];\n\n/**\n * Schema for validating client options for the TheGraph client.\n */\nexport const ClientOptionsSchema = z.object({\n instances: z.array(UrlOrPathSchema),\n accessToken: ApplicationAccessTokenSchema.optional(),\n subgraphName: z.string(),\n cache: z.enum([\"default\", \"force-cache\", \"no-cache\", \"no-store\", \"only-if-cached\", \"reload\"]).optional(),\n});\n\n/**\n * Type definition for client options derived from the ClientOptionsSchema\n */\nexport type ClientOptions = z.infer<typeof ClientOptionsSchema>;\n\n/**\n * Constructs the full URL for TheGraph GraphQL API based on the provided options\n *\n * @param options - The client options for configuring TheGraph client\n * @returns The complete GraphQL API URL as a string\n * @throws Will throw an error if no matching instance is found for the specified subgraph\n */\nfunction getFullUrl(options: ClientOptions): string {\n const instance = options.instances.find((instance) => instance.endsWith(`/${options.subgraphName}`));\n if (!instance) {\n throw new Error(`Instance for subgraph ${options.subgraphName} not found`);\n }\n return new URL(instance).toString();\n}\n\n/**\n * Creates a TheGraph GraphQL client with proper type safety using gql.tada\n *\n * @param options - Configuration options for the client including instance URLs,\n * access token and subgraph name\n * @param clientOptions - Optional GraphQL client configuration options\n * @returns An object containing:\n * - client: The configured GraphQL client instance\n * - graphql: The initialized gql.tada function for type-safe queries\n * @throws Will throw an error if the options fail validation against ClientOptionsSchema\n * @example\n * import { createTheGraphClient } from '@settlemint/sdk-thegraph';\n * import type { introspection } from '@schemas/the-graph-env-kits';\n * import { createLogger, requestLogger } from '@settlemint/sdk-utils/logging';\n *\n * const logger = createLogger();\n *\n * const { client, graphql } = createTheGraphClient<{\n * introspection: introspection;\n * disableMasking: true;\n * scalars: {\n * Bytes: string;\n * Int8: string;\n * BigInt: string;\n * BigDecimal: string;\n * Timestamp: string;\n * };\n * }>({\n * instances: JSON.parse(process.env.SETTLEMINT_THEGRAPH_SUBGRAPHS_ENDPOINTS || '[]'),\n * accessToken: process.env.SETTLEMINT_ACCESS_TOKEN,\n * subgraphName: 'kits'\n * }, {\n * fetch: requestLogger(logger, \"the-graph-kits\", fetch) as typeof fetch,\n * });\n *\n * // Making GraphQL queries\n * const query = graphql(`\n * query SearchAssets {\n * assets @fetchAll {\n * id\n * name\n * symbol\n * }\n * }\n * `);\n *\n * const result = await client.request(query);\n */\nexport function createTheGraphClient<const Setup extends AbstractSetupSchema>(\n options: ClientOptions,\n clientOptions?: RequestConfig,\n): {\n client: GraphQLClient;\n graphql: initGraphQLTada<Setup>;\n} {\n ensureServer();\n const validatedOptions = validate(ClientOptionsSchema, options);\n const graphql = initGraphQLTada<Setup>();\n const fullUrl = getFullUrl(validatedOptions);\n\n const client = new GraphQLClient(fullUrl, {\n ...clientOptions,\n headers: appendHeaders(clientOptions?.headers, { \"x-auth-token\": validatedOptions.accessToken }),\n });\n const originalRequest = client.request.bind(client);\n const paginatedClient = createTheGraphClientWithPagination({\n request: originalRequest,\n });\n client.request = paginatedClient.query;\n return {\n client,\n graphql,\n };\n}\n\nexport type { FragmentOf, ResultOf, VariablesOf } from \"gql.tada\";\nexport { readFragment } from \"gql.tada\";\nexport { createTheGraphClientWithPagination } from \"./utils/pagination.js\";\n"],"mappings":"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"}
|
package/dist/thegraph.cjs
CHANGED
|
@@ -22,15 +22,24 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
22
22
|
}) : target, mod));
|
|
23
23
|
|
|
24
24
|
//#endregion
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
25
|
+
let __settlemint_sdk_utils_http = require("@settlemint/sdk-utils/http");
|
|
26
|
+
__settlemint_sdk_utils_http = __toESM(__settlemint_sdk_utils_http);
|
|
27
|
+
let __settlemint_sdk_utils_runtime = require("@settlemint/sdk-utils/runtime");
|
|
28
|
+
__settlemint_sdk_utils_runtime = __toESM(__settlemint_sdk_utils_runtime);
|
|
29
|
+
let __settlemint_sdk_utils_validation = require("@settlemint/sdk-utils/validation");
|
|
30
|
+
__settlemint_sdk_utils_validation = __toESM(__settlemint_sdk_utils_validation);
|
|
31
|
+
let gql_tada = require("gql.tada");
|
|
32
|
+
gql_tada = __toESM(gql_tada);
|
|
33
|
+
let graphql_request = require("graphql-request");
|
|
34
|
+
graphql_request = __toESM(graphql_request);
|
|
35
|
+
let zod = require("zod");
|
|
36
|
+
zod = __toESM(zod);
|
|
37
|
+
let es_toolkit = require("es-toolkit");
|
|
38
|
+
es_toolkit = __toESM(es_toolkit);
|
|
39
|
+
let es_toolkit_compat = require("es-toolkit/compat");
|
|
40
|
+
es_toolkit_compat = __toESM(es_toolkit_compat);
|
|
41
|
+
let graphql = require("graphql");
|
|
42
|
+
graphql = __toESM(graphql);
|
|
34
43
|
|
|
35
44
|
//#region src/utils/pagination.ts
|
|
36
45
|
const THE_GRAPH_LIMIT = 500;
|
|
@@ -212,7 +221,6 @@ function createSingleFieldQuery(document, targetField, skip, first) {
|
|
|
212
221
|
} });
|
|
213
222
|
}
|
|
214
223
|
function createNonListQuery(document, listFields) {
|
|
215
|
-
let hasFields = false;
|
|
216
224
|
const pathStack = [];
|
|
217
225
|
const filtered = (0, graphql.visit)(document, { Field: {
|
|
218
226
|
enter: (node) => {
|
|
@@ -223,14 +231,28 @@ function createNonListQuery(document, listFields) {
|
|
|
223
231
|
pathStack.pop();
|
|
224
232
|
return null;
|
|
225
233
|
}
|
|
226
|
-
hasFields = true;
|
|
227
234
|
return undefined;
|
|
228
235
|
},
|
|
229
|
-
leave: () => {
|
|
236
|
+
leave: (node) => {
|
|
230
237
|
pathStack.pop();
|
|
238
|
+
if (node.selectionSet && node.selectionSet.selections.length === 0) {
|
|
239
|
+
return null;
|
|
240
|
+
}
|
|
241
|
+
return undefined;
|
|
231
242
|
}
|
|
232
243
|
} });
|
|
233
|
-
return
|
|
244
|
+
return filtered;
|
|
245
|
+
}
|
|
246
|
+
function countExecutableFields(document) {
|
|
247
|
+
let fieldCount = 0;
|
|
248
|
+
(0, graphql.visit)(document, { Field: (node) => {
|
|
249
|
+
if (!node.name.value.startsWith("__")) {
|
|
250
|
+
if (!node.selectionSet || node.selectionSet.selections.length > 0) {
|
|
251
|
+
fieldCount += 1;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
} });
|
|
255
|
+
return fieldCount;
|
|
234
256
|
}
|
|
235
257
|
function filterVariables(variables, document) {
|
|
236
258
|
if (!variables) return undefined;
|
|
@@ -314,6 +336,9 @@ function createTheGraphClientWithPagination(theGraphClient) {
|
|
|
314
336
|
}
|
|
315
337
|
const nonListQuery = createNonListQuery(processedDocument, listFields);
|
|
316
338
|
if (nonListQuery) {
|
|
339
|
+
if (countExecutableFields(nonListQuery) === 0) {
|
|
340
|
+
return result;
|
|
341
|
+
}
|
|
317
342
|
const nonListResult = await theGraphClient.request(nonListQuery, filterVariables(variables, nonListQuery) ?? {}, requestHeaders);
|
|
318
343
|
const merged = customMerge(nonListResult, result);
|
|
319
344
|
return merged;
|
package/dist/thegraph.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"thegraph.cjs","names":["result: Record<string, unknown>","fields: ListFieldWithFetchAllDirective[]","pathStack: string[]","firstValue: number | undefined","skipValue: number | undefined","otherArgs: ArgumentNode[]","Kind","newArgs: ArgumentNode[]","filtered: Variables","results: unknown[]","document: TadaDocumentNode<TResult, TVariables> | RequestDocument","variables: Omit<TVariables, \"skip\" | \"first\">","requestHeaders: HeadersInit | undefined","z","UrlOrPathSchema","ApplicationAccessTokenSchema","instance","graphql","GraphQLClient"],"sources":["../src/utils/pagination.ts","../src/thegraph.ts"],"sourcesContent":["import { sortBy } from \"es-toolkit\";\nimport { get, isArray, isEmpty, set } from \"es-toolkit/compat\";\nimport type { TadaDocumentNode } from \"gql.tada\";\nimport { type ArgumentNode, type DocumentNode, Kind, parse, visit } from \"graphql\";\nimport type { GraphQLClient, RequestDocument, RequestOptions, Variables } from \"graphql-request\";\n\n// Constants for TheGraph limits\nconst THE_GRAPH_LIMIT = 500;\nconst FIRST_ARG = \"first\";\nconst SKIP_ARG = \"skip\";\nconst FETCH_ALL_DIRECTIVE = \"fetchAll\";\n\ninterface ListFieldWithFetchAllDirective {\n path: string[];\n fieldName: string;\n firstValue?: number;\n skipValue?: number;\n otherArgs: ArgumentNode[];\n}\n\n/**\n * Detects and strips @fetchAll directives from a GraphQL document\n *\n * @param {DocumentNode} document - The GraphQL document to process\n * @returns {Object} Processed document and list of fields with @fetchAll\n *\n * @remarks\n * This function:\n * - Identifies fields decorated with @fetchAll directive\n * - Removes the directive from the AST (The Graph doesn't recognize it)\n * - Returns both the cleaned document and a list of fields to auto-paginate\n */\nfunction stripFetchAllDirective(document: DocumentNode | RequestDocument): {\n document: DocumentNode;\n fetchAllFields: Set<string>;\n} {\n const fetchAllFields = new Set<string>();\n const documentNode = typeof document === \"string\" ? parse(document) : document;\n const strippedDocument = visit(documentNode, {\n Field(node) {\n // Check if this field has the @fetchAll directive\n if (node.directives && node.directives.length > 0) {\n const hasFetchAll = node.directives.some((dir) => dir.name.value === FETCH_ALL_DIRECTIVE);\n\n if (hasFetchAll) {\n const fieldIdentifier = node.alias?.value || node.name.value;\n fetchAllFields.add(fieldIdentifier);\n\n // Return a new node without the @fetchAll directive\n return {\n ...node,\n directives: node.directives.filter((dir) => dir.name.value !== FETCH_ALL_DIRECTIVE),\n };\n }\n }\n\n return node;\n },\n });\n\n return {\n document: strippedDocument,\n fetchAllFields,\n };\n}\n\n/**\n * Custom merge function for deep object merging with special handling for lists\n *\n * @param {unknown} target - The target object or value to merge into\n * @param {unknown} source - The source object or value to merge from\n * @returns {unknown} Merged result with preservation of arrays and specific merge logic\n *\n * @remarks\n * Key behaviors:\n * - Preserves existing arrays without merging\n * - Handles null and undefined values\n * - Performs deep merge for nested objects\n * - Prioritizes source values for primitives\n *\n */\nfunction customMerge(target: unknown, source: unknown): unknown {\n if (source == null) return target;\n if (target == null) return source;\n\n // If source is an array, return it, don't merge arrays\n if (isArray(source)) {\n return source;\n }\n\n if (typeof target !== \"object\" || typeof source !== \"object\") {\n return source;\n }\n\n // Manually merge objects to ensure arrays are preserved\n const targetObj = target as Record<string, unknown>;\n const sourceObj = source as Record<string, unknown>;\n const result: Record<string, unknown> = { ...targetObj };\n\n for (const key in sourceObj) {\n if (Object.hasOwn(sourceObj, key)) {\n result[key] = key in result ? customMerge(result[key], sourceObj[key]) : sourceObj[key];\n }\n }\n\n return result;\n}\n\n// Extract all fields that have @fetchAll directive\nfunction extractFetchAllFields(\n document: DocumentNode,\n variables?: Variables,\n fetchAllFields?: Set<string>,\n): ListFieldWithFetchAllDirective[] {\n const fields: ListFieldWithFetchAllDirective[] = [];\n const pathStack: string[] = [];\n\n visit(document, {\n Field: {\n enter: (node) => {\n const fieldIdentifier = node.alias?.value || node.name.value;\n pathStack.push(fieldIdentifier);\n\n // Skip meta fields\n if (node.name.value.startsWith(\"__\")) {\n return;\n }\n\n // Check if this field has pagination arguments (first or skip)\n let firstValue: number | undefined;\n let skipValue: number | undefined;\n const otherArgs: ArgumentNode[] = [];\n\n if (node.arguments) {\n for (const arg of node.arguments) {\n if (arg.name.value === FIRST_ARG) {\n if (arg.value.kind === Kind.INT) {\n firstValue = Number.parseInt(arg.value.value, 10);\n } else if (arg.value.kind === Kind.VARIABLE && variables) {\n const varName = arg.value.name.value;\n const varValue = (variables as Record<string, unknown>)[varName];\n firstValue = typeof varValue === \"number\" ? varValue : undefined;\n }\n } else if (arg.name.value === SKIP_ARG) {\n if (arg.value.kind === Kind.INT) {\n skipValue = Number.parseInt(arg.value.value, 10);\n } else if (arg.value.kind === Kind.VARIABLE && variables) {\n const varName = arg.value.name.value;\n const varValue = (variables as Record<string, unknown>)[varName];\n skipValue = typeof varValue === \"number\" ? varValue : undefined;\n }\n } else {\n otherArgs.push(arg);\n }\n }\n }\n\n // Check if this field has @fetchAll directive\n const fieldIdentifierForDirective = node.alias?.value || node.name.value;\n const hasFetchAllDirective = fetchAllFields?.has(fieldIdentifierForDirective);\n\n if (hasFetchAllDirective) {\n // Do not allow nesting @fetchAll fields\n const parentFetchAllField = fields.find((field) => pathStack.join(\",\").startsWith(field.path.join(\",\")));\n if (parentFetchAllField) {\n throw new Error(\n `Nesting of @fetchAll directive is not supported: ${pathStack.join(\".\")} is a child of ${parentFetchAllField.path.join(\".\")}`,\n );\n }\n fields.push({\n path: [...pathStack],\n fieldName: node.name.value,\n firstValue: firstValue ?? THE_GRAPH_LIMIT,\n skipValue: skipValue ?? 0,\n otherArgs,\n });\n }\n },\n leave: () => {\n pathStack.pop();\n },\n },\n });\n\n return fields;\n}\n\n// Create a query for a single field with specific pagination\nfunction createSingleFieldQuery(\n document: DocumentNode,\n targetField: ListFieldWithFetchAllDirective,\n skip: number,\n first: number,\n): DocumentNode {\n const targetPath = [...targetField.path];\n const pathStack: string[] = [];\n\n return visit(document, {\n Field: {\n enter: (node) => {\n const fieldIdentifier = node.alias?.value || node.name.value;\n pathStack.push(fieldIdentifier);\n\n // Check if we're on the path to target field\n const onPath = pathStack.every((segment, i) => i >= targetPath.length || segment === targetPath[i]);\n\n if (!onPath) {\n pathStack.pop();\n return null; // Remove fields not on path\n }\n\n // If this is our target field, update pagination\n const isTarget =\n pathStack.length === targetPath.length && pathStack.every((segment, i) => segment === targetPath[i]);\n\n if (isTarget) {\n const newArgs: ArgumentNode[] = [...targetField.otherArgs];\n\n // Add pagination arguments\n newArgs.push(\n {\n kind: Kind.ARGUMENT,\n name: { kind: Kind.NAME, value: FIRST_ARG },\n value: { kind: Kind.INT, value: first.toString() },\n },\n {\n kind: Kind.ARGUMENT,\n name: { kind: Kind.NAME, value: SKIP_ARG },\n value: { kind: Kind.INT, value: skip.toString() },\n },\n );\n\n return { ...node, arguments: newArgs };\n }\n\n return undefined;\n },\n leave: () => {\n pathStack.pop();\n },\n },\n });\n}\n\n// Create query without list fields\nfunction createNonListQuery(document: DocumentNode, listFields: ListFieldWithFetchAllDirective[]): DocumentNode | null {\n let hasFields = false;\n const pathStack: string[] = [];\n\n const filtered = visit(document, {\n Field: {\n enter: (node) => {\n const fieldIdentifier = node.alias?.value || node.name.value;\n pathStack.push(fieldIdentifier);\n\n // Check if this field is a list field\n const isList = listFields.some(\n (field) =>\n field.path.length === pathStack.length && field.path.every((segment, i) => segment === pathStack[i]),\n );\n\n if (isList) {\n pathStack.pop();\n return null;\n }\n\n hasFields = true;\n return undefined;\n },\n leave: () => {\n pathStack.pop();\n },\n },\n });\n\n return hasFields ? filtered : null;\n}\n\n// Filter variables to only include used ones\nfunction filterVariables(variables: Variables | undefined, document: DocumentNode): Variables | undefined {\n if (!variables) return undefined;\n\n const usedVariables = new Set<string>();\n\n visit(document, {\n Variable: (node) => {\n usedVariables.add(node.name.value);\n },\n });\n\n const filtered: Variables = {};\n const varsObj = variables as Record<string, unknown>;\n for (const key of usedVariables) {\n if (key in varsObj) {\n (filtered as Record<string, unknown>)[key] = varsObj[key];\n }\n }\n\n return isEmpty(filtered) ? undefined : filtered;\n}\n\n/**\n * Creates a TheGraph client that supports pagination for list fields\n *\n * @param theGraphClient - The GraphQL client to use for requests\n * @returns A TheGraph client that supports pagination for list fields\n * @internal Used internally by createTheGraphClient\n */\nexport function createTheGraphClientWithPagination(theGraphClient: Pick<GraphQLClient, \"request\">) {\n // Execute pagination for a list field\n async function executeListFieldPagination(\n document: DocumentNode,\n variables: Variables | undefined,\n field: ListFieldWithFetchAllDirective,\n requestHeaders?: HeadersInit,\n ): Promise<unknown[]> {\n const results: unknown[] = [];\n let currentSkip = field.skipValue || 0;\n let hasMore = true;\n\n // For fields with pagination arguments, always attempt to fetch data\n // and continue if we get a full page (indicating more data might exist)\n const batchSize = Math.min(field.firstValue || THE_GRAPH_LIMIT, THE_GRAPH_LIMIT);\n\n while (hasMore) {\n const query = createSingleFieldQuery(document, field, currentSkip, batchSize);\n const existingVariables = filterVariables(variables, query) ?? {};\n const response = await theGraphClient.request(\n query,\n {\n ...existingVariables,\n first: batchSize,\n skip: currentSkip,\n },\n requestHeaders,\n );\n\n // Use array path format for es-toolkit's get function\n const data = get(response, field.path) ?? get(response, field.fieldName);\n\n const parentPath = field.path.slice(0, -1);\n const parentData = get(response, parentPath);\n if (isArray(parentData) && parentData.length > 0) {\n throw new Error(\n `Response is an array, but expected a single object for field ${parentPath.join(\".\")}. The @fetchAll directive is not supported inside a query that returns a list of items.`,\n );\n }\n\n if (isArray(data) && data.length > 0) {\n results.push(...data);\n\n // With @fetchAll, continue if we got a full batch\n hasMore = data.length === batchSize;\n } else {\n hasMore = false;\n }\n\n currentSkip += batchSize;\n }\n\n return results;\n }\n\n return {\n async query<TResult, TVariables extends Variables>(\n documentOrOptions: TadaDocumentNode<TResult, TVariables> | RequestDocument | RequestOptions<TVariables, TResult>,\n variablesRaw?: Omit<TVariables, \"skip\" | \"first\">,\n requestHeadersRaw?: HeadersInit,\n ): Promise<TResult> {\n let document: TadaDocumentNode<TResult, TVariables> | RequestDocument;\n let variables: Omit<TVariables, \"skip\" | \"first\">;\n let requestHeaders: HeadersInit | undefined;\n\n if (isRequestOptions(documentOrOptions)) {\n document = documentOrOptions.document;\n variables = (documentOrOptions.variables ?? {}) as TVariables;\n requestHeaders = documentOrOptions.requestHeaders;\n } else {\n document = documentOrOptions;\n variables = variablesRaw ?? ({} as TVariables);\n requestHeaders = requestHeadersRaw;\n }\n\n // First, detect and strip @fetchAll directives\n const { document: processedDocument, fetchAllFields } = stripFetchAllDirective(document);\n\n // Extract all list fields (including those with @fetchAll)\n const listFields = extractFetchAllFields(processedDocument, variables, fetchAllFields);\n\n // If no list fields, execute normally\n if (listFields.length === 0) {\n return theGraphClient.request(processedDocument, variables as Variables, requestHeaders);\n }\n\n // Execute paginated queries for all list fields\n const result: Record<string, unknown> = {};\n\n // Sort fields by depth to handle nested fields correctly\n const sortedFields = sortBy(listFields, [(field) => field.path.length]);\n\n // Process list fields in parallel for better performance\n const fieldDataPromises = sortedFields.map(async (field) => ({\n field,\n data: await executeListFieldPagination(processedDocument, variables, field, requestHeaders),\n }));\n\n const fieldResults = await Promise.all(fieldDataPromises);\n\n // Set results in order\n for (const { field, data } of fieldResults) {\n // Use array path format for es-toolkit's set function\n set(result, field.path, data);\n }\n\n // Execute non-list fields (single entity queries)\n const nonListQuery = createNonListQuery(processedDocument, listFields);\n\n if (nonListQuery) {\n const nonListResult = await theGraphClient.request(\n nonListQuery,\n filterVariables(variables, nonListQuery) ?? {},\n requestHeaders,\n );\n\n // Merge results, preserving list data\n const merged = customMerge(nonListResult, result);\n return merged as TResult;\n }\n\n return result as TResult;\n },\n } as const;\n}\n\nfunction isRequestOptions(args: unknown): args is RequestOptions<Variables, unknown> {\n return typeof args === \"object\" && args !== null && \"document\" in args;\n}\n","import { appendHeaders } from \"@settlemint/sdk-utils/http\";\nimport { ensureServer } from \"@settlemint/sdk-utils/runtime\";\nimport { ApplicationAccessTokenSchema, UrlOrPathSchema, validate } from \"@settlemint/sdk-utils/validation\";\nimport { type AbstractSetupSchema, initGraphQLTada } from \"gql.tada\";\nimport { GraphQLClient } from \"graphql-request\";\nimport { z } from \"zod\";\nimport { createTheGraphClientWithPagination } from \"./utils/pagination.js\";\n\n/**\n * Type definition for GraphQL client configuration options\n */\nexport type RequestConfig = ConstructorParameters<typeof GraphQLClient>[1];\n\n/**\n * Schema for validating client options for the TheGraph client.\n */\nexport const ClientOptionsSchema = z.object({\n instances: z.array(UrlOrPathSchema),\n accessToken: ApplicationAccessTokenSchema.optional(),\n subgraphName: z.string(),\n cache: z.enum([\"default\", \"force-cache\", \"no-cache\", \"no-store\", \"only-if-cached\", \"reload\"]).optional(),\n});\n\n/**\n * Type definition for client options derived from the ClientOptionsSchema\n */\nexport type ClientOptions = z.infer<typeof ClientOptionsSchema>;\n\n/**\n * Constructs the full URL for TheGraph GraphQL API based on the provided options\n *\n * @param options - The client options for configuring TheGraph client\n * @returns The complete GraphQL API URL as a string\n * @throws Will throw an error if no matching instance is found for the specified subgraph\n */\nfunction getFullUrl(options: ClientOptions): string {\n const instance = options.instances.find((instance) => instance.endsWith(`/${options.subgraphName}`));\n if (!instance) {\n throw new Error(`Instance for subgraph ${options.subgraphName} not found`);\n }\n return new URL(instance).toString();\n}\n\n/**\n * Creates a TheGraph GraphQL client with proper type safety using gql.tada\n *\n * @param options - Configuration options for the client including instance URLs,\n * access token and subgraph name\n * @param clientOptions - Optional GraphQL client configuration options\n * @returns An object containing:\n * - client: The configured GraphQL client instance\n * - graphql: The initialized gql.tada function for type-safe queries\n * @throws Will throw an error if the options fail validation against ClientOptionsSchema\n * @example\n * import { createTheGraphClient } from '@settlemint/sdk-thegraph';\n * import type { introspection } from '@schemas/the-graph-env-kits';\n * import { createLogger, requestLogger } from '@settlemint/sdk-utils/logging';\n *\n * const logger = createLogger();\n *\n * const { client, graphql } = createTheGraphClient<{\n * introspection: introspection;\n * disableMasking: true;\n * scalars: {\n * Bytes: string;\n * Int8: string;\n * BigInt: string;\n * BigDecimal: string;\n * Timestamp: string;\n * };\n * }>({\n * instances: JSON.parse(process.env.SETTLEMINT_THEGRAPH_SUBGRAPHS_ENDPOINTS || '[]'),\n * accessToken: process.env.SETTLEMINT_ACCESS_TOKEN,\n * subgraphName: 'kits'\n * }, {\n * fetch: requestLogger(logger, \"the-graph-kits\", fetch) as typeof fetch,\n * });\n *\n * // Making GraphQL queries\n * const query = graphql(`\n * query SearchAssets {\n * assets @fetchAll {\n * id\n * name\n * symbol\n * }\n * }\n * `);\n *\n * const result = await client.request(query);\n */\nexport function createTheGraphClient<const Setup extends AbstractSetupSchema>(\n options: ClientOptions,\n clientOptions?: RequestConfig,\n): {\n client: GraphQLClient;\n graphql: initGraphQLTada<Setup>;\n} {\n ensureServer();\n const validatedOptions = validate(ClientOptionsSchema, options);\n const graphql = initGraphQLTada<Setup>();\n const fullUrl = getFullUrl(validatedOptions);\n\n const client = new GraphQLClient(fullUrl, {\n ...clientOptions,\n headers: appendHeaders(clientOptions?.headers, { \"x-auth-token\": validatedOptions.accessToken }),\n });\n const originalRequest = client.request.bind(client);\n const paginatedClient = createTheGraphClientWithPagination({\n request: originalRequest,\n });\n client.request = paginatedClient.query;\n return {\n client,\n graphql,\n };\n}\n\nexport type { FragmentOf, ResultOf, VariablesOf } from \"gql.tada\";\nexport { readFragment } from \"gql.tada\";\nexport { createTheGraphClientWithPagination } from \"./utils/pagination.js\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAOA,MAAM,kBAAkB;AACxB,MAAM,YAAY;AAClB,MAAM,WAAW;AACjB,MAAM,sBAAsB;;;;;;;;;;;;;AAsB5B,SAAS,uBAAuB,UAG9B;CACA,MAAM,iBAAiB,IAAI;CAC3B,MAAM,eAAe,OAAO,aAAa,8BAAiB,YAAY;CACtE,MAAM,sCAAyB,cAAc,EAC3C,MAAM,MAAM;AAEV,MAAI,KAAK,cAAc,KAAK,WAAW,SAAS,GAAG;GACjD,MAAM,cAAc,KAAK,WAAW,MAAM,QAAQ,IAAI,KAAK,UAAU;AAErE,OAAI,aAAa;IACf,MAAM,kBAAkB,KAAK,OAAO,SAAS,KAAK,KAAK;AACvD,mBAAe,IAAI;AAGnB,WAAO;KACL,GAAG;KACH,YAAY,KAAK,WAAW,QAAQ,QAAQ,IAAI,KAAK,UAAU;;;;AAKrE,SAAO;;AAIX,QAAO;EACL,UAAU;EACV;;;;;;;;;;;;;;;;;;AAmBJ,SAAS,YAAY,QAAiB,QAA0B;AAC9D,KAAI,UAAU,KAAM,QAAO;AAC3B,KAAI,UAAU,KAAM,QAAO;AAG3B,oCAAY,SAAS;AACnB,SAAO;;AAGT,KAAI,OAAO,WAAW,YAAY,OAAO,WAAW,UAAU;AAC5D,SAAO;;CAIT,MAAM,YAAY;CAClB,MAAM,YAAY;CAClB,MAAMA,SAAkC,EAAE,GAAG;AAE7C,MAAK,MAAM,OAAO,WAAW;AAC3B,MAAI,OAAO,OAAO,WAAW,MAAM;AACjC,UAAO,OAAO,OAAO,SAAS,YAAY,OAAO,MAAM,UAAU,QAAQ,UAAU;;;AAIvF,QAAO;;AAIT,SAAS,sBACP,UACA,WACA,gBACkC;CAClC,MAAMC,SAA2C;CACjD,MAAMC,YAAsB;AAE5B,oBAAM,UAAU,EACd,OAAO;EACL,QAAQ,SAAS;GACf,MAAM,kBAAkB,KAAK,OAAO,SAAS,KAAK,KAAK;AACvD,aAAU,KAAK;AAGf,OAAI,KAAK,KAAK,MAAM,WAAW,OAAO;AACpC;;GAIF,IAAIC;GACJ,IAAIC;GACJ,MAAMC,YAA4B;AAElC,OAAI,KAAK,WAAW;AAClB,SAAK,MAAM,OAAO,KAAK,WAAW;AAChC,SAAI,IAAI,KAAK,UAAU,WAAW;AAChC,UAAI,IAAI,MAAM,SAASC,aAAK,KAAK;AAC/B,oBAAa,OAAO,SAAS,IAAI,MAAM,OAAO;iBACrC,IAAI,MAAM,SAASA,aAAK,YAAY,WAAW;OACxD,MAAM,UAAU,IAAI,MAAM,KAAK;OAC/B,MAAM,WAAY,UAAsC;AACxD,oBAAa,OAAO,aAAa,WAAW,WAAW;;gBAEhD,IAAI,KAAK,UAAU,UAAU;AACtC,UAAI,IAAI,MAAM,SAASA,aAAK,KAAK;AAC/B,mBAAY,OAAO,SAAS,IAAI,MAAM,OAAO;iBACpC,IAAI,MAAM,SAASA,aAAK,YAAY,WAAW;OACxD,MAAM,UAAU,IAAI,MAAM,KAAK;OAC/B,MAAM,WAAY,UAAsC;AACxD,mBAAY,OAAO,aAAa,WAAW,WAAW;;YAEnD;AACL,gBAAU,KAAK;;;;GAMrB,MAAM,8BAA8B,KAAK,OAAO,SAAS,KAAK,KAAK;GACnE,MAAM,uBAAuB,gBAAgB,IAAI;AAEjD,OAAI,sBAAsB;IAExB,MAAM,sBAAsB,OAAO,MAAM,UAAU,UAAU,KAAK,KAAK,WAAW,MAAM,KAAK,KAAK;AAClG,QAAI,qBAAqB;AACvB,WAAM,IAAI,MACR,oDAAoD,UAAU,KAAK,KAAK,iBAAiB,oBAAoB,KAAK,KAAK;;AAG3H,WAAO,KAAK;KACV,MAAM,CAAC,GAAG;KACV,WAAW,KAAK,KAAK;KACrB,YAAY,cAAc;KAC1B,WAAW,aAAa;KACxB;;;;EAIN,aAAa;AACX,aAAU;;;AAKhB,QAAO;;AAIT,SAAS,uBACP,UACA,aACA,MACA,OACc;CACd,MAAM,aAAa,CAAC,GAAG,YAAY;CACnC,MAAMJ,YAAsB;AAE5B,2BAAa,UAAU,EACrB,OAAO;EACL,QAAQ,SAAS;GACf,MAAM,kBAAkB,KAAK,OAAO,SAAS,KAAK,KAAK;AACvD,aAAU,KAAK;GAGf,MAAM,SAAS,UAAU,OAAO,SAAS,MAAM,KAAK,WAAW,UAAU,YAAY,WAAW;AAEhG,OAAI,CAAC,QAAQ;AACX,cAAU;AACV,WAAO;;GAIT,MAAM,WACJ,UAAU,WAAW,WAAW,UAAU,UAAU,OAAO,SAAS,MAAM,YAAY,WAAW;AAEnG,OAAI,UAAU;IACZ,MAAMK,UAA0B,CAAC,GAAG,YAAY;AAGhD,YAAQ,KACN;KACE,MAAMD,aAAK;KACX,MAAM;MAAE,MAAMA,aAAK;MAAM,OAAO;;KAChC,OAAO;MAAE,MAAMA,aAAK;MAAK,OAAO,MAAM;;OAExC;KACE,MAAMA,aAAK;KACX,MAAM;MAAE,MAAMA,aAAK;MAAM,OAAO;;KAChC,OAAO;MAAE,MAAMA,aAAK;MAAK,OAAO,KAAK;;;AAIzC,WAAO;KAAE,GAAG;KAAM,WAAW;;;AAG/B,UAAO;;EAET,aAAa;AACX,aAAU;;;;AAOlB,SAAS,mBAAmB,UAAwB,YAAmE;CACrH,IAAI,YAAY;CAChB,MAAMJ,YAAsB;CAE5B,MAAM,8BAAiB,UAAU,EAC/B,OAAO;EACL,QAAQ,SAAS;GACf,MAAM,kBAAkB,KAAK,OAAO,SAAS,KAAK,KAAK;AACvD,aAAU,KAAK;GAGf,MAAM,SAAS,WAAW,MACvB,UACC,MAAM,KAAK,WAAW,UAAU,UAAU,MAAM,KAAK,OAAO,SAAS,MAAM,YAAY,UAAU;AAGrG,OAAI,QAAQ;AACV,cAAU;AACV,WAAO;;AAGT,eAAY;AACZ,UAAO;;EAET,aAAa;AACX,aAAU;;;AAKhB,QAAO,YAAY,WAAW;;AAIhC,SAAS,gBAAgB,WAAkC,UAA+C;AACxG,KAAI,CAAC,UAAW,QAAO;CAEvB,MAAM,gBAAgB,IAAI;AAE1B,oBAAM,UAAU,EACd,WAAW,SAAS;AAClB,gBAAc,IAAI,KAAK,KAAK;;CAIhC,MAAMM,WAAsB;CAC5B,MAAM,UAAU;AAChB,MAAK,MAAM,OAAO,eAAe;AAC/B,MAAI,OAAO,SAAS;AAClB,GAAC,SAAqC,OAAO,QAAQ;;;AAIzD,uCAAe,YAAY,YAAY;;;;;;;;;AAUzC,SAAgB,mCAAmC,gBAAgD;CAEjG,eAAe,2BACb,UACA,WACA,OACA,gBACoB;EACpB,MAAMC,UAAqB;EAC3B,IAAI,cAAc,MAAM,aAAa;EACrC,IAAI,UAAU;EAId,MAAM,YAAY,KAAK,IAAI,MAAM,cAAc,iBAAiB;AAEhE,SAAO,SAAS;GACd,MAAM,QAAQ,uBAAuB,UAAU,OAAO,aAAa;GACnE,MAAM,oBAAoB,gBAAgB,WAAW,UAAU;GAC/D,MAAM,WAAW,MAAM,eAAe,QACpC,OACA;IACE,GAAG;IACH,OAAO;IACP,MAAM;MAER;GAIF,MAAM,kCAAW,UAAU,MAAM,oCAAa,UAAU,MAAM;GAE9D,MAAM,aAAa,MAAM,KAAK,MAAM,GAAG,CAAC;GACxC,MAAM,wCAAiB,UAAU;AACjC,sCAAY,eAAe,WAAW,SAAS,GAAG;AAChD,UAAM,IAAI,MACR,gEAAgE,WAAW,KAAK,KAAK;;AAIzF,sCAAY,SAAS,KAAK,SAAS,GAAG;AACpC,YAAQ,KAAK,GAAG;AAGhB,cAAU,KAAK,WAAW;UACrB;AACL,cAAU;;AAGZ,kBAAe;;AAGjB,SAAO;;AAGT,QAAO,EACL,MAAM,MACJ,mBACA,cACA,mBACkB;EAClB,IAAIC;EACJ,IAAIC;EACJ,IAAIC;AAEJ,MAAI,iBAAiB,oBAAoB;AACvC,cAAW,kBAAkB;AAC7B,eAAa,kBAAkB,aAAa;AAC5C,oBAAiB,kBAAkB;SAC9B;AACL,cAAW;AACX,eAAY,gBAAiB;AAC7B,oBAAiB;;EAInB,MAAM,EAAE,UAAU,mBAAmB,mBAAmB,uBAAuB;EAG/E,MAAM,aAAa,sBAAsB,mBAAmB,WAAW;AAGvE,MAAI,WAAW,WAAW,GAAG;AAC3B,UAAO,eAAe,QAAQ,mBAAmB,WAAwB;;EAI3E,MAAMZ,SAAkC;EAGxC,MAAM,sCAAsB,YAAY,EAAE,UAAU,MAAM,KAAK;EAG/D,MAAM,oBAAoB,aAAa,IAAI,OAAO,WAAW;GAC3D;GACA,MAAM,MAAM,2BAA2B,mBAAmB,WAAW,OAAO;;EAG9E,MAAM,eAAe,MAAM,QAAQ,IAAI;AAGvC,OAAK,MAAM,EAAE,OAAO,UAAU,cAAc;AAE1C,8BAAI,QAAQ,MAAM,MAAM;;EAI1B,MAAM,eAAe,mBAAmB,mBAAmB;AAE3D,MAAI,cAAc;GAChB,MAAM,gBAAgB,MAAM,eAAe,QACzC,cACA,gBAAgB,WAAW,iBAAiB,IAC5C;GAIF,MAAM,SAAS,YAAY,eAAe;AAC1C,UAAO;;AAGT,SAAO;;;AAKb,SAAS,iBAAiB,MAA2D;AACnF,QAAO,OAAO,SAAS,YAAY,SAAS,QAAQ,cAAc;;;;;;;;ACnapE,MAAa,sBAAsBa,MAAE,OAAO;CAC1C,WAAWA,MAAE,MAAMC;CACnB,aAAaC,+DAA6B;CAC1C,cAAcF,MAAE;CAChB,OAAOA,MAAE,KAAK;EAAC;EAAW;EAAe;EAAY;EAAY;EAAkB;IAAW;;;;;;;;;AAehG,SAAS,WAAW,SAAgC;CAClD,MAAM,WAAW,QAAQ,UAAU,MAAM,eAAaG,WAAS,SAAS,IAAI,QAAQ;AACpF,KAAI,CAAC,UAAU;AACb,QAAM,IAAI,MAAM,yBAAyB,QAAQ,aAAa;;AAEhE,QAAO,IAAI,IAAI,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmD3B,SAAgB,qBACd,SACA,eAIA;AACA;CACA,MAAM,mEAA4B,qBAAqB;CACvD,MAAMC;CACN,MAAM,UAAU,WAAW;CAE3B,MAAM,SAAS,IAAIC,8BAAc,SAAS;EACxC,GAAG;EACH,wDAAuB,eAAe,SAAS,EAAE,gBAAgB,iBAAiB;;CAEpF,MAAM,kBAAkB,OAAO,QAAQ,KAAK;CAC5C,MAAM,kBAAkB,mCAAmC,EACzD,SAAS;AAEX,QAAO,UAAU,gBAAgB;AACjC,QAAO;EACL;EACA"}
|
|
1
|
+
{"version":3,"file":"thegraph.cjs","names":["result: Record<string, unknown>","fields: ListFieldWithFetchAllDirective[]","pathStack: string[]","firstValue: number | undefined","skipValue: number | undefined","otherArgs: ArgumentNode[]","Kind","newArgs: ArgumentNode[]","filtered: Variables","results: unknown[]","document: TadaDocumentNode<TResult, TVariables> | RequestDocument","variables: Omit<TVariables, \"skip\" | \"first\">","requestHeaders: HeadersInit | undefined","z","UrlOrPathSchema","ApplicationAccessTokenSchema","instance","graphql","GraphQLClient"],"sources":["../src/utils/pagination.ts","../src/thegraph.ts"],"sourcesContent":["import { sortBy } from \"es-toolkit\";\nimport { get, isArray, isEmpty, set } from \"es-toolkit/compat\";\nimport type { TadaDocumentNode } from \"gql.tada\";\nimport { type ArgumentNode, type DocumentNode, type FieldNode, Kind, parse, visit } from \"graphql\";\nimport type { GraphQLClient, RequestDocument, RequestOptions, Variables } from \"graphql-request\";\n\n// Constants for TheGraph limits\nconst THE_GRAPH_LIMIT = 500;\nconst FIRST_ARG = \"first\";\nconst SKIP_ARG = \"skip\";\nconst FETCH_ALL_DIRECTIVE = \"fetchAll\";\n\ninterface ListFieldWithFetchAllDirective {\n path: string[];\n fieldName: string;\n firstValue?: number;\n skipValue?: number;\n otherArgs: ArgumentNode[];\n}\n\n/**\n * Detects and strips @fetchAll directives from a GraphQL document\n *\n * @param {DocumentNode} document - The GraphQL document to process\n * @returns {Object} Processed document and list of fields with @fetchAll\n *\n * @remarks\n * This function:\n * - Identifies fields decorated with @fetchAll directive\n * - Removes the directive from the AST (The Graph doesn't recognize it)\n * - Returns both the cleaned document and a list of fields to auto-paginate\n */\nfunction stripFetchAllDirective(document: DocumentNode | RequestDocument): {\n document: DocumentNode;\n fetchAllFields: Set<string>;\n} {\n const fetchAllFields = new Set<string>();\n const documentNode = typeof document === \"string\" ? parse(document) : document;\n const strippedDocument = visit(documentNode, {\n Field(node) {\n // Check if this field has the @fetchAll directive\n if (node.directives && node.directives.length > 0) {\n const hasFetchAll = node.directives.some((dir) => dir.name.value === FETCH_ALL_DIRECTIVE);\n\n if (hasFetchAll) {\n const fieldIdentifier = node.alias?.value || node.name.value;\n fetchAllFields.add(fieldIdentifier);\n\n // Return a new node without the @fetchAll directive\n return {\n ...node,\n directives: node.directives.filter((dir) => dir.name.value !== FETCH_ALL_DIRECTIVE),\n };\n }\n }\n\n return node;\n },\n });\n\n return {\n document: strippedDocument,\n fetchAllFields,\n };\n}\n\n/**\n * Custom merge function for deep object merging with special handling for lists\n *\n * @param {unknown} target - The target object or value to merge into\n * @param {unknown} source - The source object or value to merge from\n * @returns {unknown} Merged result with preservation of arrays and specific merge logic\n *\n * @remarks\n * Key behaviors:\n * - Preserves existing arrays without merging\n * - Handles null and undefined values\n * - Performs deep merge for nested objects\n * - Prioritizes source values for primitives\n *\n */\nfunction customMerge(target: unknown, source: unknown): unknown {\n if (source == null) return target;\n if (target == null) return source;\n\n // If source is an array, return it, don't merge arrays\n if (isArray(source)) {\n return source;\n }\n\n if (typeof target !== \"object\" || typeof source !== \"object\") {\n return source;\n }\n\n // Manually merge objects to ensure arrays are preserved\n const targetObj = target as Record<string, unknown>;\n const sourceObj = source as Record<string, unknown>;\n const result: Record<string, unknown> = { ...targetObj };\n\n for (const key in sourceObj) {\n if (Object.hasOwn(sourceObj, key)) {\n result[key] = key in result ? customMerge(result[key], sourceObj[key]) : sourceObj[key];\n }\n }\n\n return result;\n}\n\n// Extract all fields that have @fetchAll directive\nfunction extractFetchAllFields(\n document: DocumentNode,\n variables?: Variables,\n fetchAllFields?: Set<string>,\n): ListFieldWithFetchAllDirective[] {\n const fields: ListFieldWithFetchAllDirective[] = [];\n const pathStack: string[] = [];\n\n visit(document, {\n Field: {\n enter: (node) => {\n const fieldIdentifier = node.alias?.value || node.name.value;\n pathStack.push(fieldIdentifier);\n\n // Skip meta fields\n if (node.name.value.startsWith(\"__\")) {\n return;\n }\n\n // Check if this field has pagination arguments (first or skip)\n let firstValue: number | undefined;\n let skipValue: number | undefined;\n const otherArgs: ArgumentNode[] = [];\n\n if (node.arguments) {\n for (const arg of node.arguments) {\n if (arg.name.value === FIRST_ARG) {\n if (arg.value.kind === Kind.INT) {\n firstValue = Number.parseInt(arg.value.value, 10);\n } else if (arg.value.kind === Kind.VARIABLE && variables) {\n const varName = arg.value.name.value;\n const varValue = (variables as Record<string, unknown>)[varName];\n firstValue = typeof varValue === \"number\" ? varValue : undefined;\n }\n } else if (arg.name.value === SKIP_ARG) {\n if (arg.value.kind === Kind.INT) {\n skipValue = Number.parseInt(arg.value.value, 10);\n } else if (arg.value.kind === Kind.VARIABLE && variables) {\n const varName = arg.value.name.value;\n const varValue = (variables as Record<string, unknown>)[varName];\n skipValue = typeof varValue === \"number\" ? varValue : undefined;\n }\n } else {\n otherArgs.push(arg);\n }\n }\n }\n\n // Check if this field has @fetchAll directive\n const fieldIdentifierForDirective = node.alias?.value || node.name.value;\n const hasFetchAllDirective = fetchAllFields?.has(fieldIdentifierForDirective);\n\n if (hasFetchAllDirective) {\n // Do not allow nesting @fetchAll fields\n const parentFetchAllField = fields.find((field) => pathStack.join(\",\").startsWith(field.path.join(\",\")));\n if (parentFetchAllField) {\n throw new Error(\n `Nesting of @fetchAll directive is not supported: ${pathStack.join(\".\")} is a child of ${parentFetchAllField.path.join(\".\")}`,\n );\n }\n fields.push({\n path: [...pathStack],\n fieldName: node.name.value,\n firstValue: firstValue ?? THE_GRAPH_LIMIT,\n skipValue: skipValue ?? 0,\n otherArgs,\n });\n }\n },\n leave: () => {\n pathStack.pop();\n },\n },\n });\n\n return fields;\n}\n\n// Create a query for a single field with specific pagination\nfunction createSingleFieldQuery(\n document: DocumentNode,\n targetField: ListFieldWithFetchAllDirective,\n skip: number,\n first: number,\n): DocumentNode {\n const targetPath = [...targetField.path];\n const pathStack: string[] = [];\n\n return visit(document, {\n Field: {\n enter: (node) => {\n const fieldIdentifier = node.alias?.value || node.name.value;\n pathStack.push(fieldIdentifier);\n\n // Check if we're on the path to target field\n const onPath = pathStack.every((segment, i) => i >= targetPath.length || segment === targetPath[i]);\n\n if (!onPath) {\n pathStack.pop();\n return null; // Remove fields not on path\n }\n\n // If this is our target field, update pagination\n const isTarget =\n pathStack.length === targetPath.length && pathStack.every((segment, i) => segment === targetPath[i]);\n\n if (isTarget) {\n const newArgs: ArgumentNode[] = [...targetField.otherArgs];\n\n // Add pagination arguments\n newArgs.push(\n {\n kind: Kind.ARGUMENT,\n name: { kind: Kind.NAME, value: FIRST_ARG },\n value: { kind: Kind.INT, value: first.toString() },\n },\n {\n kind: Kind.ARGUMENT,\n name: { kind: Kind.NAME, value: SKIP_ARG },\n value: { kind: Kind.INT, value: skip.toString() },\n },\n );\n\n return { ...node, arguments: newArgs };\n }\n\n return undefined;\n },\n leave: () => {\n pathStack.pop();\n },\n },\n });\n}\n\n// Create query without list fields\nfunction createNonListQuery(document: DocumentNode, listFields: ListFieldWithFetchAllDirective[]): DocumentNode | null {\n const pathStack: string[] = [];\n\n const filtered = visit(document, {\n Field: {\n enter: (node) => {\n const fieldIdentifier = node.alias?.value || node.name.value;\n pathStack.push(fieldIdentifier);\n\n // Check if this field is a list field\n const isList = listFields.some(\n (field) =>\n field.path.length === pathStack.length && field.path.every((segment, i) => segment === pathStack[i]),\n );\n\n if (isList) {\n pathStack.pop();\n return null;\n }\n return undefined;\n },\n leave: (node: FieldNode) => {\n pathStack.pop();\n if (node.selectionSet && node.selectionSet.selections.length === 0) {\n return null;\n }\n return undefined;\n },\n },\n });\n\n return filtered;\n}\n\nfunction countExecutableFields(document: DocumentNode): number {\n let fieldCount = 0;\n\n visit(document, {\n Field: (node) => {\n if (!node.name.value.startsWith(\"__\")) {\n if (!node.selectionSet || node.selectionSet.selections.length > 0) {\n fieldCount += 1;\n }\n }\n },\n });\n\n return fieldCount;\n}\n\n// Filter variables to only include used ones\nfunction filterVariables(variables: Variables | undefined, document: DocumentNode): Variables | undefined {\n if (!variables) return undefined;\n\n const usedVariables = new Set<string>();\n\n visit(document, {\n Variable: (node) => {\n usedVariables.add(node.name.value);\n },\n });\n\n const filtered: Variables = {};\n const varsObj = variables as Record<string, unknown>;\n for (const key of usedVariables) {\n if (key in varsObj) {\n (filtered as Record<string, unknown>)[key] = varsObj[key];\n }\n }\n\n return isEmpty(filtered) ? undefined : filtered;\n}\n\n/**\n * Creates a TheGraph client that supports pagination for list fields\n *\n * @param theGraphClient - The GraphQL client to use for requests\n * @returns A TheGraph client that supports pagination for list fields\n * @internal Used internally by createTheGraphClient\n */\nexport function createTheGraphClientWithPagination(theGraphClient: Pick<GraphQLClient, \"request\">) {\n // Execute pagination for a list field\n async function executeListFieldPagination(\n document: DocumentNode,\n variables: Variables | undefined,\n field: ListFieldWithFetchAllDirective,\n requestHeaders?: HeadersInit,\n ): Promise<unknown[]> {\n const results: unknown[] = [];\n let currentSkip = field.skipValue || 0;\n let hasMore = true;\n\n // For fields with pagination arguments, always attempt to fetch data\n // and continue if we get a full page (indicating more data might exist)\n const batchSize = Math.min(field.firstValue || THE_GRAPH_LIMIT, THE_GRAPH_LIMIT);\n\n while (hasMore) {\n const query = createSingleFieldQuery(document, field, currentSkip, batchSize);\n const existingVariables = filterVariables(variables, query) ?? {};\n const response = await theGraphClient.request(\n query,\n {\n ...existingVariables,\n first: batchSize,\n skip: currentSkip,\n },\n requestHeaders,\n );\n\n // Use array path format for es-toolkit's get function\n const data = get(response, field.path) ?? get(response, field.fieldName);\n\n const parentPath = field.path.slice(0, -1);\n const parentData = get(response, parentPath);\n if (isArray(parentData) && parentData.length > 0) {\n throw new Error(\n `Response is an array, but expected a single object for field ${parentPath.join(\".\")}. The @fetchAll directive is not supported inside a query that returns a list of items.`,\n );\n }\n\n if (isArray(data) && data.length > 0) {\n results.push(...data);\n\n // With @fetchAll, continue if we got a full batch\n hasMore = data.length === batchSize;\n } else {\n hasMore = false;\n }\n\n currentSkip += batchSize;\n }\n\n return results;\n }\n\n return {\n async query<TResult, TVariables extends Variables>(\n documentOrOptions: TadaDocumentNode<TResult, TVariables> | RequestDocument | RequestOptions<TVariables, TResult>,\n variablesRaw?: Omit<TVariables, \"skip\" | \"first\">,\n requestHeadersRaw?: HeadersInit,\n ): Promise<TResult> {\n let document: TadaDocumentNode<TResult, TVariables> | RequestDocument;\n let variables: Omit<TVariables, \"skip\" | \"first\">;\n let requestHeaders: HeadersInit | undefined;\n\n if (isRequestOptions(documentOrOptions)) {\n document = documentOrOptions.document;\n variables = (documentOrOptions.variables ?? {}) as TVariables;\n requestHeaders = documentOrOptions.requestHeaders;\n } else {\n document = documentOrOptions;\n variables = variablesRaw ?? ({} as TVariables);\n requestHeaders = requestHeadersRaw;\n }\n\n // First, detect and strip @fetchAll directives\n const { document: processedDocument, fetchAllFields } = stripFetchAllDirective(document);\n\n // Extract all list fields (including those with @fetchAll)\n const listFields = extractFetchAllFields(processedDocument, variables, fetchAllFields);\n\n // If no list fields, execute normally\n if (listFields.length === 0) {\n return theGraphClient.request(processedDocument, variables as Variables, requestHeaders);\n }\n\n // Execute paginated queries for all list fields\n const result: Record<string, unknown> = {};\n\n // Sort fields by depth to handle nested fields correctly\n const sortedFields = sortBy(listFields, [(field) => field.path.length]);\n\n // Process list fields in parallel for better performance\n const fieldDataPromises = sortedFields.map(async (field) => ({\n field,\n data: await executeListFieldPagination(processedDocument, variables, field, requestHeaders),\n }));\n\n const fieldResults = await Promise.all(fieldDataPromises);\n\n // Set results in order\n for (const { field, data } of fieldResults) {\n // Use array path format for es-toolkit's set function\n set(result, field.path, data);\n }\n\n // Execute non-list fields (single entity queries)\n const nonListQuery = createNonListQuery(processedDocument, listFields);\n\n if (nonListQuery) {\n if (countExecutableFields(nonListQuery) === 0) {\n return result as TResult;\n }\n const nonListResult = await theGraphClient.request(\n nonListQuery,\n filterVariables(variables, nonListQuery) ?? {},\n requestHeaders,\n );\n\n // Merge results, preserving list data\n const merged = customMerge(nonListResult, result);\n return merged as TResult;\n }\n\n return result as TResult;\n },\n } as const;\n}\n\nfunction isRequestOptions(args: unknown): args is RequestOptions<Variables, unknown> {\n return typeof args === \"object\" && args !== null && \"document\" in args;\n}\n","import { appendHeaders } from \"@settlemint/sdk-utils/http\";\nimport { ensureServer } from \"@settlemint/sdk-utils/runtime\";\nimport { ApplicationAccessTokenSchema, UrlOrPathSchema, validate } from \"@settlemint/sdk-utils/validation\";\nimport { type AbstractSetupSchema, initGraphQLTada } from \"gql.tada\";\nimport { GraphQLClient } from \"graphql-request\";\nimport { z } from \"zod\";\nimport { createTheGraphClientWithPagination } from \"./utils/pagination.js\";\n\n/**\n * Type definition for GraphQL client configuration options\n */\nexport type RequestConfig = ConstructorParameters<typeof GraphQLClient>[1];\n\n/**\n * Schema for validating client options for the TheGraph client.\n */\nexport const ClientOptionsSchema = z.object({\n instances: z.array(UrlOrPathSchema),\n accessToken: ApplicationAccessTokenSchema.optional(),\n subgraphName: z.string(),\n cache: z.enum([\"default\", \"force-cache\", \"no-cache\", \"no-store\", \"only-if-cached\", \"reload\"]).optional(),\n});\n\n/**\n * Type definition for client options derived from the ClientOptionsSchema\n */\nexport type ClientOptions = z.infer<typeof ClientOptionsSchema>;\n\n/**\n * Constructs the full URL for TheGraph GraphQL API based on the provided options\n *\n * @param options - The client options for configuring TheGraph client\n * @returns The complete GraphQL API URL as a string\n * @throws Will throw an error if no matching instance is found for the specified subgraph\n */\nfunction getFullUrl(options: ClientOptions): string {\n const instance = options.instances.find((instance) => instance.endsWith(`/${options.subgraphName}`));\n if (!instance) {\n throw new Error(`Instance for subgraph ${options.subgraphName} not found`);\n }\n return new URL(instance).toString();\n}\n\n/**\n * Creates a TheGraph GraphQL client with proper type safety using gql.tada\n *\n * @param options - Configuration options for the client including instance URLs,\n * access token and subgraph name\n * @param clientOptions - Optional GraphQL client configuration options\n * @returns An object containing:\n * - client: The configured GraphQL client instance\n * - graphql: The initialized gql.tada function for type-safe queries\n * @throws Will throw an error if the options fail validation against ClientOptionsSchema\n * @example\n * import { createTheGraphClient } from '@settlemint/sdk-thegraph';\n * import type { introspection } from '@schemas/the-graph-env-kits';\n * import { createLogger, requestLogger } from '@settlemint/sdk-utils/logging';\n *\n * const logger = createLogger();\n *\n * const { client, graphql } = createTheGraphClient<{\n * introspection: introspection;\n * disableMasking: true;\n * scalars: {\n * Bytes: string;\n * Int8: string;\n * BigInt: string;\n * BigDecimal: string;\n * Timestamp: string;\n * };\n * }>({\n * instances: JSON.parse(process.env.SETTLEMINT_THEGRAPH_SUBGRAPHS_ENDPOINTS || '[]'),\n * accessToken: process.env.SETTLEMINT_ACCESS_TOKEN,\n * subgraphName: 'kits'\n * }, {\n * fetch: requestLogger(logger, \"the-graph-kits\", fetch) as typeof fetch,\n * });\n *\n * // Making GraphQL queries\n * const query = graphql(`\n * query SearchAssets {\n * assets @fetchAll {\n * id\n * name\n * symbol\n * }\n * }\n * `);\n *\n * const result = await client.request(query);\n */\nexport function createTheGraphClient<const Setup extends AbstractSetupSchema>(\n options: ClientOptions,\n clientOptions?: RequestConfig,\n): {\n client: GraphQLClient;\n graphql: initGraphQLTada<Setup>;\n} {\n ensureServer();\n const validatedOptions = validate(ClientOptionsSchema, options);\n const graphql = initGraphQLTada<Setup>();\n const fullUrl = getFullUrl(validatedOptions);\n\n const client = new GraphQLClient(fullUrl, {\n ...clientOptions,\n headers: appendHeaders(clientOptions?.headers, { \"x-auth-token\": validatedOptions.accessToken }),\n });\n const originalRequest = client.request.bind(client);\n const paginatedClient = createTheGraphClientWithPagination({\n request: originalRequest,\n });\n client.request = paginatedClient.query;\n return {\n client,\n graphql,\n };\n}\n\nexport type { FragmentOf, ResultOf, VariablesOf } from \"gql.tada\";\nexport { readFragment } from \"gql.tada\";\nexport { createTheGraphClientWithPagination } from \"./utils/pagination.js\";\n"],"mappings":"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"}
|
package/dist/thegraph.js
CHANGED
|
@@ -189,7 +189,6 @@ function createSingleFieldQuery(document, targetField, skip, first) {
|
|
|
189
189
|
} });
|
|
190
190
|
}
|
|
191
191
|
function createNonListQuery(document, listFields) {
|
|
192
|
-
let hasFields = false;
|
|
193
192
|
const pathStack = [];
|
|
194
193
|
const filtered = visit(document, { Field: {
|
|
195
194
|
enter: (node) => {
|
|
@@ -200,14 +199,28 @@ function createNonListQuery(document, listFields) {
|
|
|
200
199
|
pathStack.pop();
|
|
201
200
|
return null;
|
|
202
201
|
}
|
|
203
|
-
hasFields = true;
|
|
204
202
|
return undefined;
|
|
205
203
|
},
|
|
206
|
-
leave: () => {
|
|
204
|
+
leave: (node) => {
|
|
207
205
|
pathStack.pop();
|
|
206
|
+
if (node.selectionSet && node.selectionSet.selections.length === 0) {
|
|
207
|
+
return null;
|
|
208
|
+
}
|
|
209
|
+
return undefined;
|
|
208
210
|
}
|
|
209
211
|
} });
|
|
210
|
-
return
|
|
212
|
+
return filtered;
|
|
213
|
+
}
|
|
214
|
+
function countExecutableFields(document) {
|
|
215
|
+
let fieldCount = 0;
|
|
216
|
+
visit(document, { Field: (node) => {
|
|
217
|
+
if (!node.name.value.startsWith("__")) {
|
|
218
|
+
if (!node.selectionSet || node.selectionSet.selections.length > 0) {
|
|
219
|
+
fieldCount += 1;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
} });
|
|
223
|
+
return fieldCount;
|
|
211
224
|
}
|
|
212
225
|
function filterVariables(variables, document) {
|
|
213
226
|
if (!variables) return undefined;
|
|
@@ -291,6 +304,9 @@ function createTheGraphClientWithPagination(theGraphClient) {
|
|
|
291
304
|
}
|
|
292
305
|
const nonListQuery = createNonListQuery(processedDocument, listFields);
|
|
293
306
|
if (nonListQuery) {
|
|
307
|
+
if (countExecutableFields(nonListQuery) === 0) {
|
|
308
|
+
return result;
|
|
309
|
+
}
|
|
294
310
|
const nonListResult = await theGraphClient.request(nonListQuery, filterVariables(variables, nonListQuery) ?? {}, requestHeaders);
|
|
295
311
|
const merged = customMerge(nonListResult, result);
|
|
296
312
|
return merged;
|
package/dist/thegraph.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"thegraph.js","names":["result: Record<string, unknown>","fields: ListFieldWithFetchAllDirective[]","pathStack: string[]","firstValue: number | undefined","skipValue: number | undefined","otherArgs: ArgumentNode[]","newArgs: ArgumentNode[]","filtered: Variables","results: unknown[]","document: TadaDocumentNode<TResult, TVariables> | RequestDocument","variables: Omit<TVariables, \"skip\" | \"first\">","requestHeaders: HeadersInit | undefined","instance"],"sources":["../src/utils/pagination.ts","../src/thegraph.ts"],"sourcesContent":["import { sortBy } from \"es-toolkit\";\nimport { get, isArray, isEmpty, set } from \"es-toolkit/compat\";\nimport type { TadaDocumentNode } from \"gql.tada\";\nimport { type ArgumentNode, type DocumentNode, Kind, parse, visit } from \"graphql\";\nimport type { GraphQLClient, RequestDocument, RequestOptions, Variables } from \"graphql-request\";\n\n// Constants for TheGraph limits\nconst THE_GRAPH_LIMIT = 500;\nconst FIRST_ARG = \"first\";\nconst SKIP_ARG = \"skip\";\nconst FETCH_ALL_DIRECTIVE = \"fetchAll\";\n\ninterface ListFieldWithFetchAllDirective {\n path: string[];\n fieldName: string;\n firstValue?: number;\n skipValue?: number;\n otherArgs: ArgumentNode[];\n}\n\n/**\n * Detects and strips @fetchAll directives from a GraphQL document\n *\n * @param {DocumentNode} document - The GraphQL document to process\n * @returns {Object} Processed document and list of fields with @fetchAll\n *\n * @remarks\n * This function:\n * - Identifies fields decorated with @fetchAll directive\n * - Removes the directive from the AST (The Graph doesn't recognize it)\n * - Returns both the cleaned document and a list of fields to auto-paginate\n */\nfunction stripFetchAllDirective(document: DocumentNode | RequestDocument): {\n document: DocumentNode;\n fetchAllFields: Set<string>;\n} {\n const fetchAllFields = new Set<string>();\n const documentNode = typeof document === \"string\" ? parse(document) : document;\n const strippedDocument = visit(documentNode, {\n Field(node) {\n // Check if this field has the @fetchAll directive\n if (node.directives && node.directives.length > 0) {\n const hasFetchAll = node.directives.some((dir) => dir.name.value === FETCH_ALL_DIRECTIVE);\n\n if (hasFetchAll) {\n const fieldIdentifier = node.alias?.value || node.name.value;\n fetchAllFields.add(fieldIdentifier);\n\n // Return a new node without the @fetchAll directive\n return {\n ...node,\n directives: node.directives.filter((dir) => dir.name.value !== FETCH_ALL_DIRECTIVE),\n };\n }\n }\n\n return node;\n },\n });\n\n return {\n document: strippedDocument,\n fetchAllFields,\n };\n}\n\n/**\n * Custom merge function for deep object merging with special handling for lists\n *\n * @param {unknown} target - The target object or value to merge into\n * @param {unknown} source - The source object or value to merge from\n * @returns {unknown} Merged result with preservation of arrays and specific merge logic\n *\n * @remarks\n * Key behaviors:\n * - Preserves existing arrays without merging\n * - Handles null and undefined values\n * - Performs deep merge for nested objects\n * - Prioritizes source values for primitives\n *\n */\nfunction customMerge(target: unknown, source: unknown): unknown {\n if (source == null) return target;\n if (target == null) return source;\n\n // If source is an array, return it, don't merge arrays\n if (isArray(source)) {\n return source;\n }\n\n if (typeof target !== \"object\" || typeof source !== \"object\") {\n return source;\n }\n\n // Manually merge objects to ensure arrays are preserved\n const targetObj = target as Record<string, unknown>;\n const sourceObj = source as Record<string, unknown>;\n const result: Record<string, unknown> = { ...targetObj };\n\n for (const key in sourceObj) {\n if (Object.hasOwn(sourceObj, key)) {\n result[key] = key in result ? customMerge(result[key], sourceObj[key]) : sourceObj[key];\n }\n }\n\n return result;\n}\n\n// Extract all fields that have @fetchAll directive\nfunction extractFetchAllFields(\n document: DocumentNode,\n variables?: Variables,\n fetchAllFields?: Set<string>,\n): ListFieldWithFetchAllDirective[] {\n const fields: ListFieldWithFetchAllDirective[] = [];\n const pathStack: string[] = [];\n\n visit(document, {\n Field: {\n enter: (node) => {\n const fieldIdentifier = node.alias?.value || node.name.value;\n pathStack.push(fieldIdentifier);\n\n // Skip meta fields\n if (node.name.value.startsWith(\"__\")) {\n return;\n }\n\n // Check if this field has pagination arguments (first or skip)\n let firstValue: number | undefined;\n let skipValue: number | undefined;\n const otherArgs: ArgumentNode[] = [];\n\n if (node.arguments) {\n for (const arg of node.arguments) {\n if (arg.name.value === FIRST_ARG) {\n if (arg.value.kind === Kind.INT) {\n firstValue = Number.parseInt(arg.value.value, 10);\n } else if (arg.value.kind === Kind.VARIABLE && variables) {\n const varName = arg.value.name.value;\n const varValue = (variables as Record<string, unknown>)[varName];\n firstValue = typeof varValue === \"number\" ? varValue : undefined;\n }\n } else if (arg.name.value === SKIP_ARG) {\n if (arg.value.kind === Kind.INT) {\n skipValue = Number.parseInt(arg.value.value, 10);\n } else if (arg.value.kind === Kind.VARIABLE && variables) {\n const varName = arg.value.name.value;\n const varValue = (variables as Record<string, unknown>)[varName];\n skipValue = typeof varValue === \"number\" ? varValue : undefined;\n }\n } else {\n otherArgs.push(arg);\n }\n }\n }\n\n // Check if this field has @fetchAll directive\n const fieldIdentifierForDirective = node.alias?.value || node.name.value;\n const hasFetchAllDirective = fetchAllFields?.has(fieldIdentifierForDirective);\n\n if (hasFetchAllDirective) {\n // Do not allow nesting @fetchAll fields\n const parentFetchAllField = fields.find((field) => pathStack.join(\",\").startsWith(field.path.join(\",\")));\n if (parentFetchAllField) {\n throw new Error(\n `Nesting of @fetchAll directive is not supported: ${pathStack.join(\".\")} is a child of ${parentFetchAllField.path.join(\".\")}`,\n );\n }\n fields.push({\n path: [...pathStack],\n fieldName: node.name.value,\n firstValue: firstValue ?? THE_GRAPH_LIMIT,\n skipValue: skipValue ?? 0,\n otherArgs,\n });\n }\n },\n leave: () => {\n pathStack.pop();\n },\n },\n });\n\n return fields;\n}\n\n// Create a query for a single field with specific pagination\nfunction createSingleFieldQuery(\n document: DocumentNode,\n targetField: ListFieldWithFetchAllDirective,\n skip: number,\n first: number,\n): DocumentNode {\n const targetPath = [...targetField.path];\n const pathStack: string[] = [];\n\n return visit(document, {\n Field: {\n enter: (node) => {\n const fieldIdentifier = node.alias?.value || node.name.value;\n pathStack.push(fieldIdentifier);\n\n // Check if we're on the path to target field\n const onPath = pathStack.every((segment, i) => i >= targetPath.length || segment === targetPath[i]);\n\n if (!onPath) {\n pathStack.pop();\n return null; // Remove fields not on path\n }\n\n // If this is our target field, update pagination\n const isTarget =\n pathStack.length === targetPath.length && pathStack.every((segment, i) => segment === targetPath[i]);\n\n if (isTarget) {\n const newArgs: ArgumentNode[] = [...targetField.otherArgs];\n\n // Add pagination arguments\n newArgs.push(\n {\n kind: Kind.ARGUMENT,\n name: { kind: Kind.NAME, value: FIRST_ARG },\n value: { kind: Kind.INT, value: first.toString() },\n },\n {\n kind: Kind.ARGUMENT,\n name: { kind: Kind.NAME, value: SKIP_ARG },\n value: { kind: Kind.INT, value: skip.toString() },\n },\n );\n\n return { ...node, arguments: newArgs };\n }\n\n return undefined;\n },\n leave: () => {\n pathStack.pop();\n },\n },\n });\n}\n\n// Create query without list fields\nfunction createNonListQuery(document: DocumentNode, listFields: ListFieldWithFetchAllDirective[]): DocumentNode | null {\n let hasFields = false;\n const pathStack: string[] = [];\n\n const filtered = visit(document, {\n Field: {\n enter: (node) => {\n const fieldIdentifier = node.alias?.value || node.name.value;\n pathStack.push(fieldIdentifier);\n\n // Check if this field is a list field\n const isList = listFields.some(\n (field) =>\n field.path.length === pathStack.length && field.path.every((segment, i) => segment === pathStack[i]),\n );\n\n if (isList) {\n pathStack.pop();\n return null;\n }\n\n hasFields = true;\n return undefined;\n },\n leave: () => {\n pathStack.pop();\n },\n },\n });\n\n return hasFields ? filtered : null;\n}\n\n// Filter variables to only include used ones\nfunction filterVariables(variables: Variables | undefined, document: DocumentNode): Variables | undefined {\n if (!variables) return undefined;\n\n const usedVariables = new Set<string>();\n\n visit(document, {\n Variable: (node) => {\n usedVariables.add(node.name.value);\n },\n });\n\n const filtered: Variables = {};\n const varsObj = variables as Record<string, unknown>;\n for (const key of usedVariables) {\n if (key in varsObj) {\n (filtered as Record<string, unknown>)[key] = varsObj[key];\n }\n }\n\n return isEmpty(filtered) ? undefined : filtered;\n}\n\n/**\n * Creates a TheGraph client that supports pagination for list fields\n *\n * @param theGraphClient - The GraphQL client to use for requests\n * @returns A TheGraph client that supports pagination for list fields\n * @internal Used internally by createTheGraphClient\n */\nexport function createTheGraphClientWithPagination(theGraphClient: Pick<GraphQLClient, \"request\">) {\n // Execute pagination for a list field\n async function executeListFieldPagination(\n document: DocumentNode,\n variables: Variables | undefined,\n field: ListFieldWithFetchAllDirective,\n requestHeaders?: HeadersInit,\n ): Promise<unknown[]> {\n const results: unknown[] = [];\n let currentSkip = field.skipValue || 0;\n let hasMore = true;\n\n // For fields with pagination arguments, always attempt to fetch data\n // and continue if we get a full page (indicating more data might exist)\n const batchSize = Math.min(field.firstValue || THE_GRAPH_LIMIT, THE_GRAPH_LIMIT);\n\n while (hasMore) {\n const query = createSingleFieldQuery(document, field, currentSkip, batchSize);\n const existingVariables = filterVariables(variables, query) ?? {};\n const response = await theGraphClient.request(\n query,\n {\n ...existingVariables,\n first: batchSize,\n skip: currentSkip,\n },\n requestHeaders,\n );\n\n // Use array path format for es-toolkit's get function\n const data = get(response, field.path) ?? get(response, field.fieldName);\n\n const parentPath = field.path.slice(0, -1);\n const parentData = get(response, parentPath);\n if (isArray(parentData) && parentData.length > 0) {\n throw new Error(\n `Response is an array, but expected a single object for field ${parentPath.join(\".\")}. The @fetchAll directive is not supported inside a query that returns a list of items.`,\n );\n }\n\n if (isArray(data) && data.length > 0) {\n results.push(...data);\n\n // With @fetchAll, continue if we got a full batch\n hasMore = data.length === batchSize;\n } else {\n hasMore = false;\n }\n\n currentSkip += batchSize;\n }\n\n return results;\n }\n\n return {\n async query<TResult, TVariables extends Variables>(\n documentOrOptions: TadaDocumentNode<TResult, TVariables> | RequestDocument | RequestOptions<TVariables, TResult>,\n variablesRaw?: Omit<TVariables, \"skip\" | \"first\">,\n requestHeadersRaw?: HeadersInit,\n ): Promise<TResult> {\n let document: TadaDocumentNode<TResult, TVariables> | RequestDocument;\n let variables: Omit<TVariables, \"skip\" | \"first\">;\n let requestHeaders: HeadersInit | undefined;\n\n if (isRequestOptions(documentOrOptions)) {\n document = documentOrOptions.document;\n variables = (documentOrOptions.variables ?? {}) as TVariables;\n requestHeaders = documentOrOptions.requestHeaders;\n } else {\n document = documentOrOptions;\n variables = variablesRaw ?? ({} as TVariables);\n requestHeaders = requestHeadersRaw;\n }\n\n // First, detect and strip @fetchAll directives\n const { document: processedDocument, fetchAllFields } = stripFetchAllDirective(document);\n\n // Extract all list fields (including those with @fetchAll)\n const listFields = extractFetchAllFields(processedDocument, variables, fetchAllFields);\n\n // If no list fields, execute normally\n if (listFields.length === 0) {\n return theGraphClient.request(processedDocument, variables as Variables, requestHeaders);\n }\n\n // Execute paginated queries for all list fields\n const result: Record<string, unknown> = {};\n\n // Sort fields by depth to handle nested fields correctly\n const sortedFields = sortBy(listFields, [(field) => field.path.length]);\n\n // Process list fields in parallel for better performance\n const fieldDataPromises = sortedFields.map(async (field) => ({\n field,\n data: await executeListFieldPagination(processedDocument, variables, field, requestHeaders),\n }));\n\n const fieldResults = await Promise.all(fieldDataPromises);\n\n // Set results in order\n for (const { field, data } of fieldResults) {\n // Use array path format for es-toolkit's set function\n set(result, field.path, data);\n }\n\n // Execute non-list fields (single entity queries)\n const nonListQuery = createNonListQuery(processedDocument, listFields);\n\n if (nonListQuery) {\n const nonListResult = await theGraphClient.request(\n nonListQuery,\n filterVariables(variables, nonListQuery) ?? {},\n requestHeaders,\n );\n\n // Merge results, preserving list data\n const merged = customMerge(nonListResult, result);\n return merged as TResult;\n }\n\n return result as TResult;\n },\n } as const;\n}\n\nfunction isRequestOptions(args: unknown): args is RequestOptions<Variables, unknown> {\n return typeof args === \"object\" && args !== null && \"document\" in args;\n}\n","import { appendHeaders } from \"@settlemint/sdk-utils/http\";\nimport { ensureServer } from \"@settlemint/sdk-utils/runtime\";\nimport { ApplicationAccessTokenSchema, UrlOrPathSchema, validate } from \"@settlemint/sdk-utils/validation\";\nimport { type AbstractSetupSchema, initGraphQLTada } from \"gql.tada\";\nimport { GraphQLClient } from \"graphql-request\";\nimport { z } from \"zod\";\nimport { createTheGraphClientWithPagination } from \"./utils/pagination.js\";\n\n/**\n * Type definition for GraphQL client configuration options\n */\nexport type RequestConfig = ConstructorParameters<typeof GraphQLClient>[1];\n\n/**\n * Schema for validating client options for the TheGraph client.\n */\nexport const ClientOptionsSchema = z.object({\n instances: z.array(UrlOrPathSchema),\n accessToken: ApplicationAccessTokenSchema.optional(),\n subgraphName: z.string(),\n cache: z.enum([\"default\", \"force-cache\", \"no-cache\", \"no-store\", \"only-if-cached\", \"reload\"]).optional(),\n});\n\n/**\n * Type definition for client options derived from the ClientOptionsSchema\n */\nexport type ClientOptions = z.infer<typeof ClientOptionsSchema>;\n\n/**\n * Constructs the full URL for TheGraph GraphQL API based on the provided options\n *\n * @param options - The client options for configuring TheGraph client\n * @returns The complete GraphQL API URL as a string\n * @throws Will throw an error if no matching instance is found for the specified subgraph\n */\nfunction getFullUrl(options: ClientOptions): string {\n const instance = options.instances.find((instance) => instance.endsWith(`/${options.subgraphName}`));\n if (!instance) {\n throw new Error(`Instance for subgraph ${options.subgraphName} not found`);\n }\n return new URL(instance).toString();\n}\n\n/**\n * Creates a TheGraph GraphQL client with proper type safety using gql.tada\n *\n * @param options - Configuration options for the client including instance URLs,\n * access token and subgraph name\n * @param clientOptions - Optional GraphQL client configuration options\n * @returns An object containing:\n * - client: The configured GraphQL client instance\n * - graphql: The initialized gql.tada function for type-safe queries\n * @throws Will throw an error if the options fail validation against ClientOptionsSchema\n * @example\n * import { createTheGraphClient } from '@settlemint/sdk-thegraph';\n * import type { introspection } from '@schemas/the-graph-env-kits';\n * import { createLogger, requestLogger } from '@settlemint/sdk-utils/logging';\n *\n * const logger = createLogger();\n *\n * const { client, graphql } = createTheGraphClient<{\n * introspection: introspection;\n * disableMasking: true;\n * scalars: {\n * Bytes: string;\n * Int8: string;\n * BigInt: string;\n * BigDecimal: string;\n * Timestamp: string;\n * };\n * }>({\n * instances: JSON.parse(process.env.SETTLEMINT_THEGRAPH_SUBGRAPHS_ENDPOINTS || '[]'),\n * accessToken: process.env.SETTLEMINT_ACCESS_TOKEN,\n * subgraphName: 'kits'\n * }, {\n * fetch: requestLogger(logger, \"the-graph-kits\", fetch) as typeof fetch,\n * });\n *\n * // Making GraphQL queries\n * const query = graphql(`\n * query SearchAssets {\n * assets @fetchAll {\n * id\n * name\n * symbol\n * }\n * }\n * `);\n *\n * const result = await client.request(query);\n */\nexport function createTheGraphClient<const Setup extends AbstractSetupSchema>(\n options: ClientOptions,\n clientOptions?: RequestConfig,\n): {\n client: GraphQLClient;\n graphql: initGraphQLTada<Setup>;\n} {\n ensureServer();\n const validatedOptions = validate(ClientOptionsSchema, options);\n const graphql = initGraphQLTada<Setup>();\n const fullUrl = getFullUrl(validatedOptions);\n\n const client = new GraphQLClient(fullUrl, {\n ...clientOptions,\n headers: appendHeaders(clientOptions?.headers, { \"x-auth-token\": validatedOptions.accessToken }),\n });\n const originalRequest = client.request.bind(client);\n const paginatedClient = createTheGraphClientWithPagination({\n request: originalRequest,\n });\n client.request = paginatedClient.query;\n return {\n client,\n graphql,\n };\n}\n\nexport type { FragmentOf, ResultOf, VariablesOf } from \"gql.tada\";\nexport { readFragment } from \"gql.tada\";\nexport { createTheGraphClientWithPagination } from \"./utils/pagination.js\";\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"thegraph.js","names":["result: Record<string, unknown>","fields: ListFieldWithFetchAllDirective[]","pathStack: string[]","firstValue: number | undefined","skipValue: number | undefined","otherArgs: ArgumentNode[]","newArgs: ArgumentNode[]","filtered: Variables","results: unknown[]","document: TadaDocumentNode<TResult, TVariables> | RequestDocument","variables: Omit<TVariables, \"skip\" | \"first\">","requestHeaders: HeadersInit | undefined","instance"],"sources":["../src/utils/pagination.ts","../src/thegraph.ts"],"sourcesContent":["import { sortBy } from \"es-toolkit\";\nimport { get, isArray, isEmpty, set } from \"es-toolkit/compat\";\nimport type { TadaDocumentNode } from \"gql.tada\";\nimport { type ArgumentNode, type DocumentNode, type FieldNode, Kind, parse, visit } from \"graphql\";\nimport type { GraphQLClient, RequestDocument, RequestOptions, Variables } from \"graphql-request\";\n\n// Constants for TheGraph limits\nconst THE_GRAPH_LIMIT = 500;\nconst FIRST_ARG = \"first\";\nconst SKIP_ARG = \"skip\";\nconst FETCH_ALL_DIRECTIVE = \"fetchAll\";\n\ninterface ListFieldWithFetchAllDirective {\n path: string[];\n fieldName: string;\n firstValue?: number;\n skipValue?: number;\n otherArgs: ArgumentNode[];\n}\n\n/**\n * Detects and strips @fetchAll directives from a GraphQL document\n *\n * @param {DocumentNode} document - The GraphQL document to process\n * @returns {Object} Processed document and list of fields with @fetchAll\n *\n * @remarks\n * This function:\n * - Identifies fields decorated with @fetchAll directive\n * - Removes the directive from the AST (The Graph doesn't recognize it)\n * - Returns both the cleaned document and a list of fields to auto-paginate\n */\nfunction stripFetchAllDirective(document: DocumentNode | RequestDocument): {\n document: DocumentNode;\n fetchAllFields: Set<string>;\n} {\n const fetchAllFields = new Set<string>();\n const documentNode = typeof document === \"string\" ? parse(document) : document;\n const strippedDocument = visit(documentNode, {\n Field(node) {\n // Check if this field has the @fetchAll directive\n if (node.directives && node.directives.length > 0) {\n const hasFetchAll = node.directives.some((dir) => dir.name.value === FETCH_ALL_DIRECTIVE);\n\n if (hasFetchAll) {\n const fieldIdentifier = node.alias?.value || node.name.value;\n fetchAllFields.add(fieldIdentifier);\n\n // Return a new node without the @fetchAll directive\n return {\n ...node,\n directives: node.directives.filter((dir) => dir.name.value !== FETCH_ALL_DIRECTIVE),\n };\n }\n }\n\n return node;\n },\n });\n\n return {\n document: strippedDocument,\n fetchAllFields,\n };\n}\n\n/**\n * Custom merge function for deep object merging with special handling for lists\n *\n * @param {unknown} target - The target object or value to merge into\n * @param {unknown} source - The source object or value to merge from\n * @returns {unknown} Merged result with preservation of arrays and specific merge logic\n *\n * @remarks\n * Key behaviors:\n * - Preserves existing arrays without merging\n * - Handles null and undefined values\n * - Performs deep merge for nested objects\n * - Prioritizes source values for primitives\n *\n */\nfunction customMerge(target: unknown, source: unknown): unknown {\n if (source == null) return target;\n if (target == null) return source;\n\n // If source is an array, return it, don't merge arrays\n if (isArray(source)) {\n return source;\n }\n\n if (typeof target !== \"object\" || typeof source !== \"object\") {\n return source;\n }\n\n // Manually merge objects to ensure arrays are preserved\n const targetObj = target as Record<string, unknown>;\n const sourceObj = source as Record<string, unknown>;\n const result: Record<string, unknown> = { ...targetObj };\n\n for (const key in sourceObj) {\n if (Object.hasOwn(sourceObj, key)) {\n result[key] = key in result ? customMerge(result[key], sourceObj[key]) : sourceObj[key];\n }\n }\n\n return result;\n}\n\n// Extract all fields that have @fetchAll directive\nfunction extractFetchAllFields(\n document: DocumentNode,\n variables?: Variables,\n fetchAllFields?: Set<string>,\n): ListFieldWithFetchAllDirective[] {\n const fields: ListFieldWithFetchAllDirective[] = [];\n const pathStack: string[] = [];\n\n visit(document, {\n Field: {\n enter: (node) => {\n const fieldIdentifier = node.alias?.value || node.name.value;\n pathStack.push(fieldIdentifier);\n\n // Skip meta fields\n if (node.name.value.startsWith(\"__\")) {\n return;\n }\n\n // Check if this field has pagination arguments (first or skip)\n let firstValue: number | undefined;\n let skipValue: number | undefined;\n const otherArgs: ArgumentNode[] = [];\n\n if (node.arguments) {\n for (const arg of node.arguments) {\n if (arg.name.value === FIRST_ARG) {\n if (arg.value.kind === Kind.INT) {\n firstValue = Number.parseInt(arg.value.value, 10);\n } else if (arg.value.kind === Kind.VARIABLE && variables) {\n const varName = arg.value.name.value;\n const varValue = (variables as Record<string, unknown>)[varName];\n firstValue = typeof varValue === \"number\" ? varValue : undefined;\n }\n } else if (arg.name.value === SKIP_ARG) {\n if (arg.value.kind === Kind.INT) {\n skipValue = Number.parseInt(arg.value.value, 10);\n } else if (arg.value.kind === Kind.VARIABLE && variables) {\n const varName = arg.value.name.value;\n const varValue = (variables as Record<string, unknown>)[varName];\n skipValue = typeof varValue === \"number\" ? varValue : undefined;\n }\n } else {\n otherArgs.push(arg);\n }\n }\n }\n\n // Check if this field has @fetchAll directive\n const fieldIdentifierForDirective = node.alias?.value || node.name.value;\n const hasFetchAllDirective = fetchAllFields?.has(fieldIdentifierForDirective);\n\n if (hasFetchAllDirective) {\n // Do not allow nesting @fetchAll fields\n const parentFetchAllField = fields.find((field) => pathStack.join(\",\").startsWith(field.path.join(\",\")));\n if (parentFetchAllField) {\n throw new Error(\n `Nesting of @fetchAll directive is not supported: ${pathStack.join(\".\")} is a child of ${parentFetchAllField.path.join(\".\")}`,\n );\n }\n fields.push({\n path: [...pathStack],\n fieldName: node.name.value,\n firstValue: firstValue ?? THE_GRAPH_LIMIT,\n skipValue: skipValue ?? 0,\n otherArgs,\n });\n }\n },\n leave: () => {\n pathStack.pop();\n },\n },\n });\n\n return fields;\n}\n\n// Create a query for a single field with specific pagination\nfunction createSingleFieldQuery(\n document: DocumentNode,\n targetField: ListFieldWithFetchAllDirective,\n skip: number,\n first: number,\n): DocumentNode {\n const targetPath = [...targetField.path];\n const pathStack: string[] = [];\n\n return visit(document, {\n Field: {\n enter: (node) => {\n const fieldIdentifier = node.alias?.value || node.name.value;\n pathStack.push(fieldIdentifier);\n\n // Check if we're on the path to target field\n const onPath = pathStack.every((segment, i) => i >= targetPath.length || segment === targetPath[i]);\n\n if (!onPath) {\n pathStack.pop();\n return null; // Remove fields not on path\n }\n\n // If this is our target field, update pagination\n const isTarget =\n pathStack.length === targetPath.length && pathStack.every((segment, i) => segment === targetPath[i]);\n\n if (isTarget) {\n const newArgs: ArgumentNode[] = [...targetField.otherArgs];\n\n // Add pagination arguments\n newArgs.push(\n {\n kind: Kind.ARGUMENT,\n name: { kind: Kind.NAME, value: FIRST_ARG },\n value: { kind: Kind.INT, value: first.toString() },\n },\n {\n kind: Kind.ARGUMENT,\n name: { kind: Kind.NAME, value: SKIP_ARG },\n value: { kind: Kind.INT, value: skip.toString() },\n },\n );\n\n return { ...node, arguments: newArgs };\n }\n\n return undefined;\n },\n leave: () => {\n pathStack.pop();\n },\n },\n });\n}\n\n// Create query without list fields\nfunction createNonListQuery(document: DocumentNode, listFields: ListFieldWithFetchAllDirective[]): DocumentNode | null {\n const pathStack: string[] = [];\n\n const filtered = visit(document, {\n Field: {\n enter: (node) => {\n const fieldIdentifier = node.alias?.value || node.name.value;\n pathStack.push(fieldIdentifier);\n\n // Check if this field is a list field\n const isList = listFields.some(\n (field) =>\n field.path.length === pathStack.length && field.path.every((segment, i) => segment === pathStack[i]),\n );\n\n if (isList) {\n pathStack.pop();\n return null;\n }\n return undefined;\n },\n leave: (node: FieldNode) => {\n pathStack.pop();\n if (node.selectionSet && node.selectionSet.selections.length === 0) {\n return null;\n }\n return undefined;\n },\n },\n });\n\n return filtered;\n}\n\nfunction countExecutableFields(document: DocumentNode): number {\n let fieldCount = 0;\n\n visit(document, {\n Field: (node) => {\n if (!node.name.value.startsWith(\"__\")) {\n if (!node.selectionSet || node.selectionSet.selections.length > 0) {\n fieldCount += 1;\n }\n }\n },\n });\n\n return fieldCount;\n}\n\n// Filter variables to only include used ones\nfunction filterVariables(variables: Variables | undefined, document: DocumentNode): Variables | undefined {\n if (!variables) return undefined;\n\n const usedVariables = new Set<string>();\n\n visit(document, {\n Variable: (node) => {\n usedVariables.add(node.name.value);\n },\n });\n\n const filtered: Variables = {};\n const varsObj = variables as Record<string, unknown>;\n for (const key of usedVariables) {\n if (key in varsObj) {\n (filtered as Record<string, unknown>)[key] = varsObj[key];\n }\n }\n\n return isEmpty(filtered) ? undefined : filtered;\n}\n\n/**\n * Creates a TheGraph client that supports pagination for list fields\n *\n * @param theGraphClient - The GraphQL client to use for requests\n * @returns A TheGraph client that supports pagination for list fields\n * @internal Used internally by createTheGraphClient\n */\nexport function createTheGraphClientWithPagination(theGraphClient: Pick<GraphQLClient, \"request\">) {\n // Execute pagination for a list field\n async function executeListFieldPagination(\n document: DocumentNode,\n variables: Variables | undefined,\n field: ListFieldWithFetchAllDirective,\n requestHeaders?: HeadersInit,\n ): Promise<unknown[]> {\n const results: unknown[] = [];\n let currentSkip = field.skipValue || 0;\n let hasMore = true;\n\n // For fields with pagination arguments, always attempt to fetch data\n // and continue if we get a full page (indicating more data might exist)\n const batchSize = Math.min(field.firstValue || THE_GRAPH_LIMIT, THE_GRAPH_LIMIT);\n\n while (hasMore) {\n const query = createSingleFieldQuery(document, field, currentSkip, batchSize);\n const existingVariables = filterVariables(variables, query) ?? {};\n const response = await theGraphClient.request(\n query,\n {\n ...existingVariables,\n first: batchSize,\n skip: currentSkip,\n },\n requestHeaders,\n );\n\n // Use array path format for es-toolkit's get function\n const data = get(response, field.path) ?? get(response, field.fieldName);\n\n const parentPath = field.path.slice(0, -1);\n const parentData = get(response, parentPath);\n if (isArray(parentData) && parentData.length > 0) {\n throw new Error(\n `Response is an array, but expected a single object for field ${parentPath.join(\".\")}. The @fetchAll directive is not supported inside a query that returns a list of items.`,\n );\n }\n\n if (isArray(data) && data.length > 0) {\n results.push(...data);\n\n // With @fetchAll, continue if we got a full batch\n hasMore = data.length === batchSize;\n } else {\n hasMore = false;\n }\n\n currentSkip += batchSize;\n }\n\n return results;\n }\n\n return {\n async query<TResult, TVariables extends Variables>(\n documentOrOptions: TadaDocumentNode<TResult, TVariables> | RequestDocument | RequestOptions<TVariables, TResult>,\n variablesRaw?: Omit<TVariables, \"skip\" | \"first\">,\n requestHeadersRaw?: HeadersInit,\n ): Promise<TResult> {\n let document: TadaDocumentNode<TResult, TVariables> | RequestDocument;\n let variables: Omit<TVariables, \"skip\" | \"first\">;\n let requestHeaders: HeadersInit | undefined;\n\n if (isRequestOptions(documentOrOptions)) {\n document = documentOrOptions.document;\n variables = (documentOrOptions.variables ?? {}) as TVariables;\n requestHeaders = documentOrOptions.requestHeaders;\n } else {\n document = documentOrOptions;\n variables = variablesRaw ?? ({} as TVariables);\n requestHeaders = requestHeadersRaw;\n }\n\n // First, detect and strip @fetchAll directives\n const { document: processedDocument, fetchAllFields } = stripFetchAllDirective(document);\n\n // Extract all list fields (including those with @fetchAll)\n const listFields = extractFetchAllFields(processedDocument, variables, fetchAllFields);\n\n // If no list fields, execute normally\n if (listFields.length === 0) {\n return theGraphClient.request(processedDocument, variables as Variables, requestHeaders);\n }\n\n // Execute paginated queries for all list fields\n const result: Record<string, unknown> = {};\n\n // Sort fields by depth to handle nested fields correctly\n const sortedFields = sortBy(listFields, [(field) => field.path.length]);\n\n // Process list fields in parallel for better performance\n const fieldDataPromises = sortedFields.map(async (field) => ({\n field,\n data: await executeListFieldPagination(processedDocument, variables, field, requestHeaders),\n }));\n\n const fieldResults = await Promise.all(fieldDataPromises);\n\n // Set results in order\n for (const { field, data } of fieldResults) {\n // Use array path format for es-toolkit's set function\n set(result, field.path, data);\n }\n\n // Execute non-list fields (single entity queries)\n const nonListQuery = createNonListQuery(processedDocument, listFields);\n\n if (nonListQuery) {\n if (countExecutableFields(nonListQuery) === 0) {\n return result as TResult;\n }\n const nonListResult = await theGraphClient.request(\n nonListQuery,\n filterVariables(variables, nonListQuery) ?? {},\n requestHeaders,\n );\n\n // Merge results, preserving list data\n const merged = customMerge(nonListResult, result);\n return merged as TResult;\n }\n\n return result as TResult;\n },\n } as const;\n}\n\nfunction isRequestOptions(args: unknown): args is RequestOptions<Variables, unknown> {\n return typeof args === \"object\" && args !== null && \"document\" in args;\n}\n","import { appendHeaders } from \"@settlemint/sdk-utils/http\";\nimport { ensureServer } from \"@settlemint/sdk-utils/runtime\";\nimport { ApplicationAccessTokenSchema, UrlOrPathSchema, validate } from \"@settlemint/sdk-utils/validation\";\nimport { type AbstractSetupSchema, initGraphQLTada } from \"gql.tada\";\nimport { GraphQLClient } from \"graphql-request\";\nimport { z } from \"zod\";\nimport { createTheGraphClientWithPagination } from \"./utils/pagination.js\";\n\n/**\n * Type definition for GraphQL client configuration options\n */\nexport type RequestConfig = ConstructorParameters<typeof GraphQLClient>[1];\n\n/**\n * Schema for validating client options for the TheGraph client.\n */\nexport const ClientOptionsSchema = z.object({\n instances: z.array(UrlOrPathSchema),\n accessToken: ApplicationAccessTokenSchema.optional(),\n subgraphName: z.string(),\n cache: z.enum([\"default\", \"force-cache\", \"no-cache\", \"no-store\", \"only-if-cached\", \"reload\"]).optional(),\n});\n\n/**\n * Type definition for client options derived from the ClientOptionsSchema\n */\nexport type ClientOptions = z.infer<typeof ClientOptionsSchema>;\n\n/**\n * Constructs the full URL for TheGraph GraphQL API based on the provided options\n *\n * @param options - The client options for configuring TheGraph client\n * @returns The complete GraphQL API URL as a string\n * @throws Will throw an error if no matching instance is found for the specified subgraph\n */\nfunction getFullUrl(options: ClientOptions): string {\n const instance = options.instances.find((instance) => instance.endsWith(`/${options.subgraphName}`));\n if (!instance) {\n throw new Error(`Instance for subgraph ${options.subgraphName} not found`);\n }\n return new URL(instance).toString();\n}\n\n/**\n * Creates a TheGraph GraphQL client with proper type safety using gql.tada\n *\n * @param options - Configuration options for the client including instance URLs,\n * access token and subgraph name\n * @param clientOptions - Optional GraphQL client configuration options\n * @returns An object containing:\n * - client: The configured GraphQL client instance\n * - graphql: The initialized gql.tada function for type-safe queries\n * @throws Will throw an error if the options fail validation against ClientOptionsSchema\n * @example\n * import { createTheGraphClient } from '@settlemint/sdk-thegraph';\n * import type { introspection } from '@schemas/the-graph-env-kits';\n * import { createLogger, requestLogger } from '@settlemint/sdk-utils/logging';\n *\n * const logger = createLogger();\n *\n * const { client, graphql } = createTheGraphClient<{\n * introspection: introspection;\n * disableMasking: true;\n * scalars: {\n * Bytes: string;\n * Int8: string;\n * BigInt: string;\n * BigDecimal: string;\n * Timestamp: string;\n * };\n * }>({\n * instances: JSON.parse(process.env.SETTLEMINT_THEGRAPH_SUBGRAPHS_ENDPOINTS || '[]'),\n * accessToken: process.env.SETTLEMINT_ACCESS_TOKEN,\n * subgraphName: 'kits'\n * }, {\n * fetch: requestLogger(logger, \"the-graph-kits\", fetch) as typeof fetch,\n * });\n *\n * // Making GraphQL queries\n * const query = graphql(`\n * query SearchAssets {\n * assets @fetchAll {\n * id\n * name\n * symbol\n * }\n * }\n * `);\n *\n * const result = await client.request(query);\n */\nexport function createTheGraphClient<const Setup extends AbstractSetupSchema>(\n options: ClientOptions,\n clientOptions?: RequestConfig,\n): {\n client: GraphQLClient;\n graphql: initGraphQLTada<Setup>;\n} {\n ensureServer();\n const validatedOptions = validate(ClientOptionsSchema, options);\n const graphql = initGraphQLTada<Setup>();\n const fullUrl = getFullUrl(validatedOptions);\n\n const client = new GraphQLClient(fullUrl, {\n ...clientOptions,\n headers: appendHeaders(clientOptions?.headers, { \"x-auth-token\": validatedOptions.accessToken }),\n });\n const originalRequest = client.request.bind(client);\n const paginatedClient = createTheGraphClientWithPagination({\n request: originalRequest,\n });\n client.request = paginatedClient.query;\n return {\n client,\n graphql,\n };\n}\n\nexport type { FragmentOf, ResultOf, VariablesOf } from \"gql.tada\";\nexport { readFragment } from \"gql.tada\";\nexport { createTheGraphClientWithPagination } from \"./utils/pagination.js\";\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@settlemint/sdk-thegraph",
|
|
3
3
|
"description": "TheGraph integration module for SettleMint SDK, enabling querying and indexing of blockchain data through subgraphs",
|
|
4
|
-
"version": "2.6.
|
|
4
|
+
"version": "2.6.3-pr2085fb82",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": false,
|
|
7
7
|
"license": "FSL-1.1-MIT",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
},
|
|
42
42
|
"scripts": {
|
|
43
43
|
"build": "tsdown",
|
|
44
|
-
"dev": "tsdown --watch",
|
|
44
|
+
"dev": "tsdown --watch ./src",
|
|
45
45
|
"publint": "publint run --strict",
|
|
46
46
|
"attw": "attw --pack .",
|
|
47
47
|
"test": "bun test",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {},
|
|
55
55
|
"dependencies": {
|
|
56
|
-
"@settlemint/sdk-utils": "2.6.
|
|
56
|
+
"@settlemint/sdk-utils": "2.6.3-pr2085fb82",
|
|
57
57
|
"es-toolkit": "^1",
|
|
58
58
|
"gql.tada": "^1",
|
|
59
59
|
"graphql": "^16",
|