@solana/rpc-graphql 2.0.0-experimental.b153bb7 → 2.0.0-experimental.b419cd0

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.
@@ -1,4 +1,6 @@
1
1
  import { graphql, Kind } from 'graphql';
2
+ import DataLoader from 'dataloader';
3
+ import fastStableStringify from 'fast-stable-stringify';
2
4
  import { makeExecutableSchema } from '@graphql-tools/schema';
3
5
 
4
6
  // src/rpc.ts
@@ -34,8 +36,8 @@ function createGraphQLCache() {
34
36
  };
35
37
  }
36
38
 
37
- // src/loaders/account.ts
38
- function onlyAddressRequested(info) {
39
+ // src/loaders/common/resolve-info.ts
40
+ function onlyPresentFieldRequested(fieldName, info) {
39
41
  if (info && info.fieldNodes[0].selectionSet) {
40
42
  const selectionSet = info.fieldNodes[0].selectionSet;
41
43
  const requestedFields = selectionSet.selections.map((field) => {
@@ -44,12 +46,24 @@ function onlyAddressRequested(info) {
44
46
  }
45
47
  return null;
46
48
  });
47
- if (requestedFields && requestedFields.length === 1 && requestedFields[0] === "address") {
49
+ if (requestedFields && requestedFields.length === 1 && requestedFields[0] === fieldName) {
48
50
  return true;
49
51
  }
50
52
  }
51
53
  return false;
52
54
  }
55
+
56
+ // src/loaders/account.ts
57
+ function normalizeArgs(args) {
58
+ const { address, commitment, dataSlice, encoding, minContextSlot } = args;
59
+ return {
60
+ address,
61
+ commitment: commitment ?? "confirmed",
62
+ dataSlice,
63
+ encoding: encoding ?? "jsonParsed",
64
+ minContextSlot
65
+ };
66
+ }
53
67
  function refineJsonParsedAccountData(jsonParsedAccountData) {
54
68
  const meta = {
55
69
  program: jsonParsedAccountData.program,
@@ -75,86 +89,44 @@ function processQueryResponse({ address, account, encoding }) {
75
89
  data: refinedData
76
90
  };
77
91
  }
78
- async function loadAccount({ address, encoding = "jsonParsed", ...config }, cache, rpc, info) {
79
- if (onlyAddressRequested(info)) {
80
- return { address };
81
- }
82
- const requestConfig = { encoding, ...config };
83
- const cached = cache.get(address, requestConfig);
84
- if (cached !== null) {
85
- return cached;
86
- }
87
- const account = await rpc.getAccountInfo(address, requestConfig).send().then((res) => res.value).catch((e) => {
92
+ async function loadAccount(rpc, { address, ...config }) {
93
+ const { encoding } = config;
94
+ const account = await rpc.getAccountInfo(address, config).send().then((res) => res.value).catch((e) => {
88
95
  throw e;
89
96
  });
90
97
  if (account === null) {
91
98
  return { address };
92
99
  }
93
- const queryResponse = processQueryResponse({ account, address, encoding });
94
- cache.insert(address, requestConfig, queryResponse);
95
- return queryResponse;
100
+ return processQueryResponse({ account, address, encoding });
96
101
  }
97
-
98
- // src/loaders/block.ts
99
- async function loadBlock({ slot, encoding = "jsonParsed", ...config }, cache, rpc, _info) {
100
- const requestConfig = {
101
- encoding,
102
- ...config,
103
- // Always use 0 to avoid silly errors
104
- maxSupportedTransactionVersion: 0
102
+ function createAccountBatchLoadFn(rpc) {
103
+ const resolveAccountUsingRpc = loadAccount.bind(null, rpc);
104
+ return async (accountQueryArgs) => {
105
+ return await Promise.all(accountQueryArgs.map(async (args) => await resolveAccountUsingRpc(args)));
105
106
  };
106
- const cached = cache.get(slot, config);
107
- if (cached !== null) {
108
- return cached;
109
- }
110
- const block = await rpc.getBlock(slot, requestConfig).send();
111
- if (block === null) {
112
- return null;
113
- }
114
- cache.insert(slot, config, block);
115
- return block;
116
107
  }
117
-
118
- // src/loaders/program-accounts.ts
119
- function processQueryResponse2({ encoding, programAccounts }) {
120
- return programAccounts.map((programAccount) => {
121
- const [refinedData, responseEncoding] = Array.isArray(programAccount.account.data) ? encoding === "jsonParsed" ? [programAccount.account.data[0], "base64"] : [programAccount.account.data[0], encoding] : [refineJsonParsedAccountData(programAccount.account.data), "jsonParsed"];
122
- const pubkey = programAccount.pubkey;
123
- const responseBase = {
124
- ...programAccount.account,
125
- address: pubkey,
126
- encoding: responseEncoding
127
- };
128
- return typeof refinedData === "object" && "meta" in refinedData ? {
129
- ...responseBase,
130
- data: refinedData.data,
131
- meta: refinedData.meta
132
- } : {
133
- ...responseBase,
134
- data: refinedData
135
- };
136
- });
137
- }
138
- async function loadProgramAccounts({ programAddress, encoding = "jsonParsed", ...config }, cache, rpc, _info) {
139
- const requestConfig = { encoding, ...config };
140
- const cached = cache.get(programAddress, requestConfig);
141
- if (cached !== null) {
142
- return cached;
143
- }
144
- const programAccounts = await rpc.getProgramAccounts(programAddress, requestConfig).send().then((res) => {
145
- if ("value" in res) {
146
- return res.value;
108
+ function createAccountLoader(rpc) {
109
+ const loader = new DataLoader(createAccountBatchLoadFn(rpc), { cacheKeyFn: fastStableStringify });
110
+ return {
111
+ load: async (args, info) => {
112
+ if (onlyPresentFieldRequested("address", info)) {
113
+ return { address: args.address };
114
+ }
115
+ return loader.load(normalizeArgs(args));
147
116
  }
148
- return res;
149
- }).catch((e) => {
150
- throw e;
151
- });
152
- const queryResponse = processQueryResponse2({ encoding, programAccounts });
153
- cache.insert(programAddress, requestConfig, queryResponse);
154
- return queryResponse;
117
+ };
155
118
  }
156
119
 
157
120
  // src/loaders/transaction.ts
121
+ function normalizeArgs2(args) {
122
+ const { commitment, encoding } = args;
123
+ return {
124
+ commitment: commitment ?? "confirmed",
125
+ encoding: encoding ?? "jsonParsed",
126
+ // Always use 0 to avoid silly errors
127
+ maxSupportedTransactionVersion: 0
128
+ };
129
+ }
158
130
  function refineJsonParsedInstructionData(jsonParsedInstructionData) {
159
131
  if ("parsed" in jsonParsedInstructionData) {
160
132
  const meta = {
@@ -192,36 +164,36 @@ function refineJsonParsedTransactionMeta(jsonParsedTransactionMeta) {
192
164
  innerInstructions: refinedInnerInstructions
193
165
  };
194
166
  }
195
- function processQueryResponse3({ encoding, transaction }) {
196
- const [transactionData, transactionMeta, responseEncoding] = Array.isArray(transaction.transaction) ? [transaction.transaction[0], transaction.meta, encoding] : [
197
- refineJsonParsedTransactionData(transaction.transaction),
198
- refineJsonParsedTransactionMeta(transaction.meta),
199
- encoding
200
- ];
167
+ function refineJsonParsedTransaction({ encoding, transaction }) {
168
+ const [transactionData, transactionMeta] = Array.isArray(transaction.transaction) ? [transaction.transaction[0], transaction.meta] : [refineJsonParsedTransactionData(transaction.transaction), refineJsonParsedTransactionMeta(transaction.meta)];
201
169
  return {
202
- ...transaction,
203
170
  data: transactionData,
204
- encoding: responseEncoding,
205
- meta: transactionMeta
206
- };
207
- }
208
- async function loadTransaction({ signature, encoding = "jsonParsed", ...config }, cache, rpc, _info) {
209
- const requestConfig = {
210
171
  encoding,
211
- ...config,
212
- // Always use 0 to avoid silly errors
213
- maxSupportedTransactionVersion: 0
172
+ meta: transactionMeta,
173
+ slot: transaction.slot,
174
+ version: transaction.version
214
175
  };
176
+ }
177
+ function processQueryResponse2({ encoding, transaction }) {
178
+ return refineJsonParsedTransaction({ encoding, transaction });
179
+ }
180
+ async function loadTransaction({ signature, ...config }, cache, rpc, _info) {
181
+ const requestConfig = normalizeArgs2(config);
182
+ const { encoding } = requestConfig;
215
183
  const cached = cache.get(signature, requestConfig);
216
184
  if (cached !== null) {
217
185
  return cached;
218
186
  }
219
- let transaction = await rpc.getTransaction(signature, requestConfig).send();
187
+ let transaction = await rpc.getTransaction(signature, requestConfig).send().catch((e) => {
188
+ throw e;
189
+ });
220
190
  if (transaction === null) {
221
191
  return null;
222
192
  }
223
193
  if (encoding !== "jsonParsed") {
224
- const transactionJsonParsed = await rpc.getTransaction(signature, requestConfig).send();
194
+ const transactionJsonParsed = await rpc.getTransaction(signature, requestConfig).send().catch((e) => {
195
+ throw e;
196
+ });
225
197
  if (transactionJsonParsed === null) {
226
198
  return null;
227
199
  }
@@ -231,22 +203,140 @@ async function loadTransaction({ signature, encoding = "jsonParsed", ...config }
231
203
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
232
204
  };
233
205
  }
234
- const queryResponse = processQueryResponse3({ encoding, transaction });
206
+ const queryResponse = processQueryResponse2({ encoding, transaction });
235
207
  cache.insert(signature, requestConfig, queryResponse);
236
208
  return queryResponse;
237
209
  }
238
210
 
211
+ // src/loaders/block.ts
212
+ function normalizeArgs3(args) {
213
+ const { commitment, encoding, slot, transactionDetails } = args;
214
+ return {
215
+ commitment: commitment ?? "confirmed",
216
+ encoding: encoding ?? "jsonParsed",
217
+ // Always use 0 to avoid silly errors
218
+ maxSupportedTransactionVersion: 0,
219
+ slot,
220
+ transactionDetails: transactionDetails ?? "full"
221
+ };
222
+ }
223
+ function refineJsonParsedTransactionForAccounts({ transaction }) {
224
+ return {
225
+ data: transaction.transaction,
226
+ meta: transaction.meta,
227
+ version: transaction.version
228
+ };
229
+ }
230
+ function processQueryResponse3({
231
+ encoding,
232
+ block,
233
+ transactionDetails
234
+ }) {
235
+ if (typeof block === "object" && "transactions" in block) {
236
+ const refinedBlock = {
237
+ ...block,
238
+ transactions: block.transactions.map((transaction) => {
239
+ if (transactionDetails === "accounts") {
240
+ return refineJsonParsedTransactionForAccounts({ transaction });
241
+ } else {
242
+ return refineJsonParsedTransaction({ encoding, transaction });
243
+ }
244
+ })
245
+ };
246
+ block = refinedBlock;
247
+ }
248
+ return {
249
+ ...block,
250
+ encoding,
251
+ transactionDetails
252
+ };
253
+ }
254
+ async function loadBlock(rpc, { slot, ...config }) {
255
+ const { encoding, transactionDetails } = config;
256
+ const block = await rpc.getBlock(slot, config).send().catch((e) => {
257
+ throw e;
258
+ });
259
+ if (block === null) {
260
+ return { slot };
261
+ }
262
+ const queryResponse = processQueryResponse3({ block, encoding, transactionDetails });
263
+ return queryResponse;
264
+ }
265
+ function createBlockBatchLoadFn(rpc) {
266
+ const resolveBlockUsingRpc = loadBlock.bind(null, rpc);
267
+ return async (blockQueryArgs) => {
268
+ return await Promise.all(blockQueryArgs.map(async (args) => await resolveBlockUsingRpc(args)));
269
+ };
270
+ }
271
+ function createBlockLoader(rpc) {
272
+ const loader = new DataLoader(createBlockBatchLoadFn(rpc), { cacheKeyFn: fastStableStringify });
273
+ return {
274
+ load: async (args, info) => {
275
+ if (onlyPresentFieldRequested("slot", info)) {
276
+ return { slot: args.slot };
277
+ }
278
+ return loader.load(normalizeArgs3(args));
279
+ }
280
+ };
281
+ }
282
+
283
+ // src/loaders/program-accounts.ts
284
+ function normalizeArgs4(args) {
285
+ const { commitment, dataSlice, encoding, filters, minContextSlot } = args;
286
+ return {
287
+ commitment: commitment ?? "confirmed",
288
+ dataSlice,
289
+ encoding: encoding ?? "jsonParsed",
290
+ filters,
291
+ minContextSlot
292
+ };
293
+ }
294
+ function processQueryResponse4({ encoding, programAccounts }) {
295
+ return programAccounts.map((programAccount) => {
296
+ const [refinedData, responseEncoding] = Array.isArray(programAccount.account.data) ? encoding === "jsonParsed" ? [programAccount.account.data[0], "base64"] : [programAccount.account.data[0], encoding] : [refineJsonParsedAccountData(programAccount.account.data), "jsonParsed"];
297
+ const pubkey = programAccount.pubkey;
298
+ const responseBase = {
299
+ ...programAccount.account,
300
+ address: pubkey,
301
+ encoding: responseEncoding
302
+ };
303
+ return typeof refinedData === "object" && "meta" in refinedData ? {
304
+ ...responseBase,
305
+ data: refinedData.data,
306
+ meta: refinedData.meta
307
+ } : {
308
+ ...responseBase,
309
+ data: refinedData
310
+ };
311
+ });
312
+ }
313
+ async function loadProgramAccounts({ programAddress, ...config }, cache, rpc, _info) {
314
+ const requestConfig = normalizeArgs4(config);
315
+ const { encoding } = requestConfig;
316
+ const cached = cache.get(programAddress, requestConfig);
317
+ if (cached !== null) {
318
+ return cached;
319
+ }
320
+ const programAccounts = await rpc.getProgramAccounts(programAddress, requestConfig).send().then((res) => {
321
+ if ("value" in res) {
322
+ return res.value;
323
+ }
324
+ return res;
325
+ }).catch((e) => {
326
+ throw e;
327
+ });
328
+ const queryResponse = processQueryResponse4({ encoding, programAccounts });
329
+ cache.insert(programAddress, requestConfig, queryResponse);
330
+ return queryResponse;
331
+ }
332
+
239
333
  // src/context.ts
240
334
  function createSolanaGraphQLContext(rpc) {
241
335
  const cache = createGraphQLCache();
242
336
  return {
337
+ accountLoader: createAccountLoader(rpc),
338
+ blockLoader: createBlockLoader(rpc),
243
339
  cache,
244
- loadAccount(args, info) {
245
- return loadAccount(args, this.cache, this.rpc, info);
246
- },
247
- loadBlock(args, info) {
248
- return loadBlock(args, this.cache, this.rpc);
249
- },
250
340
  loadProgramAccounts(args, info) {
251
341
  return loadProgramAccounts(args, this.cache, this.rpc);
252
342
  },
@@ -259,7 +349,7 @@ function createSolanaGraphQLContext(rpc) {
259
349
 
260
350
  // src/resolvers/account.ts
261
351
  var resolveAccount = (fieldName) => {
262
- return (parent, args, context, info) => parent[fieldName] === null ? null : context.loadAccount({ ...args, address: parent[fieldName] }, info);
352
+ return (parent, args, context, info) => parent[fieldName] === null ? null : context.accountLoader.load({ ...args, address: parent[fieldName] }, info);
263
353
  };
264
354
 
265
355
  // src/schema/account.ts
@@ -600,59 +690,79 @@ var blockTypeDefs = (
600
690
 
601
691
  # Block interface
602
692
  interface Block {
603
- blockHeight: BigInt
604
- blockTime: BigInt
605
693
  blockhash: String
694
+ blockHeight: BigInt
695
+ blockTime: Int
606
696
  parentSlot: BigInt
607
697
  previousBlockhash: String
608
698
  rewards: [Reward]
699
+ transactionDetails: String
609
700
  }
610
701
 
611
702
  # A block with account transaction details
612
- type BlockWithAccounts {
613
- blockHeight: BigInt
614
- blockTime: BigInt
703
+ type BlockWithAccounts implements Block {
615
704
  blockhash: String
705
+ blockHeight: BigInt
706
+ blockTime: Int
616
707
  parentSlot: BigInt
617
708
  previousBlockhash: String
618
709
  rewards: [Reward]
619
710
  transactions: [BlockTransactionAccounts]
711
+ transactionDetails: String
620
712
  }
621
713
 
622
714
  # A block with full transaction details
623
- type BlockFull {
624
- blockHeight: BigInt
625
- blockTime: BigInt
715
+ type BlockWithFull implements Block {
626
716
  blockhash: String
717
+ blockHeight: BigInt
718
+ blockTime: Int
627
719
  parentSlot: BigInt
628
720
  previousBlockhash: String
629
721
  rewards: [Reward]
630
722
  transactions: [Transaction]
723
+ transactionDetails: String
631
724
  }
632
725
 
633
726
  # A block with none transaction details
634
- type BlockWithNone {
635
- blockHeight: BigInt
636
- blockTime: BigInt
727
+ type BlockWithNone implements Block {
637
728
  blockhash: String
729
+ blockHeight: BigInt
730
+ blockTime: Int
638
731
  parentSlot: BigInt
639
732
  previousBlockhash: String
640
733
  rewards: [Reward]
734
+ transactionDetails: String
641
735
  }
642
736
 
643
737
  # A block with signature transaction details
644
- type BlockWithSignatures {
645
- blockHeight: BigInt
646
- blockTime: BigInt
738
+ type BlockWithSignatures implements Block {
647
739
  blockhash: String
740
+ blockHeight: BigInt
741
+ blockTime: Int
648
742
  parentSlot: BigInt
649
743
  previousBlockhash: String
650
744
  rewards: [Reward]
651
745
  signatures: [String]
746
+ transactionDetails: String
652
747
  }
653
748
  `
654
749
  );
655
- var blockResolvers = {};
750
+ var blockResolvers = {
751
+ Block: {
752
+ __resolveType(block) {
753
+ switch (block.transactionDetails) {
754
+ case "accounts":
755
+ return "BlockWithAccounts";
756
+ case "none":
757
+ return "BlockWithNone";
758
+ case "signatures":
759
+ return "BlockWithSignatures";
760
+ default:
761
+ return "BlockWithFull";
762
+ }
763
+ }
764
+ }
765
+ };
656
766
 
657
767
  // src/schema/common/inputs.ts
658
768
  var inputTypeDefs = (
@@ -2873,10 +2983,10 @@ var schemaTypeDefs = (
2873
2983
  var schemaResolvers = {
2874
2984
  Query: {
2875
2985
  account(_, args, context, info) {
2876
- return context.loadAccount(args, info);
2986
+ return context.accountLoader.load(args, info);
2877
2987
  },
2878
2988
  block(_, args, context, info) {
2879
- return context.loadBlock(args, info);
2989
+ return context.blockLoader.load(args, info);
2880
2990
  },
2881
2991
  programAccounts(_, args, context, info) {
2882
2992
  return context.loadProgramAccounts(args, info);