@subsquid/fuel-stream 0.0.1

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.
Files changed (56) hide show
  1. package/lib/archive/data-schema.d.ts +332 -0
  2. package/lib/archive/data-schema.d.ts.map +1 -0
  3. package/lib/archive/data-schema.js +293 -0
  4. package/lib/archive/data-schema.js.map +1 -0
  5. package/lib/archive/source.d.ts +14 -0
  6. package/lib/archive/source.d.ts.map +1 -0
  7. package/lib/archive/source.js +82 -0
  8. package/lib/archive/source.js.map +1 -0
  9. package/lib/data/data-partial.d.ts +20 -0
  10. package/lib/data/data-partial.d.ts.map +1 -0
  11. package/lib/data/data-partial.js +3 -0
  12. package/lib/data/data-partial.js.map +1 -0
  13. package/lib/data/data-request.d.ts +35 -0
  14. package/lib/data/data-request.d.ts.map +1 -0
  15. package/lib/data/data-request.js +3 -0
  16. package/lib/data/data-request.js.map +1 -0
  17. package/lib/data/model.d.ts +45 -0
  18. package/lib/data/model.d.ts.map +1 -0
  19. package/lib/data/model.js +19 -0
  20. package/lib/data/model.js.map +1 -0
  21. package/lib/data/util.d.ts +20 -0
  22. package/lib/data/util.d.ts.map +1 -0
  23. package/lib/data/util.js +3 -0
  24. package/lib/data/util.js.map +1 -0
  25. package/lib/fields.d.ts +6 -0
  26. package/lib/fields.d.ts.map +1 -0
  27. package/lib/fields.js +32 -0
  28. package/lib/fields.js.map +1 -0
  29. package/lib/filter.d.ts +6 -0
  30. package/lib/filter.d.ts.map +1 -0
  31. package/lib/filter.js +172 -0
  32. package/lib/filter.js.map +1 -0
  33. package/lib/graphql.d.ts +14 -0
  34. package/lib/graphql.d.ts.map +1 -0
  35. package/lib/graphql.js +40 -0
  36. package/lib/graphql.js.map +1 -0
  37. package/lib/index.d.ts +3 -0
  38. package/lib/index.d.ts.map +1 -0
  39. package/lib/index.js +19 -0
  40. package/lib/index.js.map +1 -0
  41. package/lib/source.d.ts +118 -0
  42. package/lib/source.d.ts.map +1 -0
  43. package/lib/source.js +296 -0
  44. package/lib/source.js.map +1 -0
  45. package/package.json +34 -0
  46. package/src/archive/data-schema.ts +320 -0
  47. package/src/archive/source.ts +88 -0
  48. package/src/data/data-partial.ts +25 -0
  49. package/src/data/data-request.ts +44 -0
  50. package/src/data/model.ts +114 -0
  51. package/src/data/util.ts +42 -0
  52. package/src/fields.ts +35 -0
  53. package/src/filter.ts +229 -0
  54. package/src/graphql.ts +51 -0
  55. package/src/index.ts +2 -0
  56. package/src/source.ts +416 -0
@@ -0,0 +1,320 @@
1
+ import {weakMemo} from '@subsquid/util-internal'
2
+ import {
3
+ array,
4
+ BIG_NAT,
5
+ BOOLEAN,
6
+ BYTES,
7
+ NAT,
8
+ object,
9
+ option,
10
+ STRING,
11
+ taggedUnion,
12
+ oneOf,
13
+ constant
14
+ } from '@subsquid/util-internal-validation'
15
+ import {FieldSelection} from '../data/model'
16
+ import {Selector} from '../data/util'
17
+
18
+
19
+ export const getDataSchema = weakMemo((fields: FieldSelection) => {
20
+ let BlockHeader = object({
21
+ number: NAT,
22
+ hash: BYTES,
23
+ ...project(fields.block, {
24
+ daHeight: BIG_NAT,
25
+ transactionsRoot: BYTES,
26
+ transactionsCount: BIG_NAT,
27
+ messageReceiptRoot: BYTES,
28
+ messageReceiptCount: BIG_NAT,
29
+ applicationHash: BYTES,
30
+ prevRoot: BYTES,
31
+ time: BIG_NAT,
32
+ })
33
+ })
34
+
35
+ let Transaction = object({
36
+ index: NAT,
37
+ ...project(fields.transaction, {
38
+ bytecodeLength: option(BIG_NAT),
39
+ bytecodeWitnessIndex: option(NAT),
40
+ gasPrice: option(BIG_NAT),
41
+ hash: BYTES,
42
+ inputAssetIds: option(array(BYTES)),
43
+ policies: option(object({
44
+ gasPrice: option(BIG_NAT),
45
+ witnessLimit: option(BIG_NAT),
46
+ maturity: option(NAT),
47
+ maxFee: option(BIG_NAT)
48
+ })),
49
+ inputContract: option(object({
50
+ utxoId: BYTES,
51
+ balanceRoot: BYTES,
52
+ stateRoot: BYTES,
53
+ txPointer: STRING,
54
+ contract: BYTES
55
+ })),
56
+ inputContracts: option(array(BYTES)),
57
+ isCreate: BOOLEAN,
58
+ isMint: BOOLEAN,
59
+ isScript: BOOLEAN,
60
+ maturity: option(NAT),
61
+ mintAmount: option(BIG_NAT),
62
+ mintAssetId: option(BYTES),
63
+ outputContract: option(object({
64
+ inputIndex: NAT,
65
+ balanceRoot: BYTES,
66
+ stateRoot: BYTES
67
+ })),
68
+ rawPayload: option(BYTES),
69
+ receiptsRoot: option(BYTES),
70
+ salt: option(BYTES),
71
+ script: option(BYTES),
72
+ scriptData: option(BYTES),
73
+ scriptGasLimit: option(BIG_NAT),
74
+ storageSlots: option(array(BYTES)),
75
+ txPointer: option(STRING),
76
+ type: oneOf({
77
+ script: constant('Script'),
78
+ create: constant('Create'),
79
+ mint: constant('Mint'),
80
+ }),
81
+ witnesses: option(array(BYTES)),
82
+ })
83
+ })
84
+
85
+ let Receipt = object({
86
+ transactionIndex: NAT,
87
+ index: NAT,
88
+ ...project(fields.receipt, {
89
+ amount: option(BIG_NAT),
90
+ assetId: option(BYTES),
91
+ contract: option(BYTES),
92
+ contractId: option(BYTES),
93
+ data: option(BYTES),
94
+ digest: option(BYTES),
95
+ gas: option(BIG_NAT),
96
+ gasUsed: option(BIG_NAT),
97
+ is: option(BIG_NAT),
98
+ len: option(BIG_NAT),
99
+ nonce: option(BYTES),
100
+ param1: option(BIG_NAT),
101
+ param2: option(BIG_NAT),
102
+ pc: option(BIG_NAT),
103
+ ptr: option(BIG_NAT),
104
+ ra: option(BIG_NAT),
105
+ rb: option(BIG_NAT),
106
+ rc: option(BIG_NAT),
107
+ rd: option(BIG_NAT),
108
+ reason: option(BIG_NAT),
109
+ receiptType: oneOf({
110
+ CALL: constant('CALL'),
111
+ RETURN: constant('RETURN'),
112
+ RETURN_DATA: constant('RETURN_DATA'),
113
+ PANIC: constant('PANIC'),
114
+ REVERT: constant('REVERT'),
115
+ LOG: constant('LOG'),
116
+ LOG_DATA: constant('LOG_DATA'),
117
+ TRANSFER: constant('TRANSFER'),
118
+ TRANSFER_OUT: constant('TRANSFER_OUT'),
119
+ SCRIPT_RESULT: constant('SCRIPT_RESULT'),
120
+ MESSAGE_OUT: constant('MESSAGE_OUT'),
121
+ MINT: constant('MINT'),
122
+ BURN: constant('BURN'),
123
+ }),
124
+ recipient: option(BYTES),
125
+ result: option(BIG_NAT),
126
+ sender: option(BYTES),
127
+ subId: option(BYTES),
128
+ to: option(BYTES),
129
+ toAddress: option(BYTES),
130
+ val: option(BIG_NAT),
131
+ })
132
+ })
133
+
134
+ let InputCoin = object({
135
+ transactionIndex: NAT,
136
+ index: NAT,
137
+ ...project({
138
+ amount: fields.input?.coinAmount,
139
+ assetId: fields.input?.coinAssetId,
140
+ maturity: fields.input?.coinMaturity,
141
+ owner: fields.input?.coinOwner,
142
+ predicate: fields.input?.coinPredicate,
143
+ predicateData: fields.input?.coinPredicateData,
144
+ predicateGasUsed: fields.input?.coinPredicateGasUsed,
145
+ txPointer: fields.input?.coinTxPointer,
146
+ utxoId: fields.input?.coinUtxoId,
147
+ witnessIndex: fields.input?.coinWitnessIndex
148
+ }, {
149
+ utxoId: BYTES,
150
+ owner: BYTES,
151
+ amount: BIG_NAT,
152
+ assetId: BYTES,
153
+ txPointer: STRING,
154
+ witnessIndex: NAT,
155
+ maturity: NAT,
156
+ predicateGasUsed: BIG_NAT,
157
+ predicate: BYTES,
158
+ predicateData: BYTES,
159
+ })
160
+ })
161
+
162
+ let InputContract = object({
163
+ transactionIndex: NAT,
164
+ index: NAT,
165
+ ...project({
166
+ utxoId: fields.input?.contractUtxoId,
167
+ balanceRoot: fields.input?.contractBalanceRoot,
168
+ contract: fields.input?.contractContract,
169
+ stateRoot: fields.input?.contractStateRoot,
170
+ txPointer: fields.input?.contractTxPointer
171
+ }, {
172
+ utxoId: BYTES,
173
+ balanceRoot: BYTES,
174
+ stateRoot: BYTES,
175
+ txPointer: STRING,
176
+ contract: BYTES,
177
+ })
178
+ })
179
+
180
+ let InputMessage = object({
181
+ transactionIndex: NAT,
182
+ index: NAT,
183
+ ...project({
184
+ sender: fields.input?.messageSender,
185
+ amount: fields.input?.messageAmount,
186
+ data: fields.input?.messageData,
187
+ nonce: fields.input?.messageNonce,
188
+ predicate: fields.input?.messagePredicate,
189
+ predicateData: fields.input?.messagePredicateData,
190
+ predicateGasUsed: fields.input?.messagePredicateGasUsed,
191
+ recipient: fields.input?.messageRecipient,
192
+ witnessIndex: fields.input?.messageWitnessIndex
193
+ }, {
194
+ sender: BYTES,
195
+ recipient: BYTES,
196
+ amount: BIG_NAT,
197
+ nonce: BYTES,
198
+ witnessIndex: NAT,
199
+ predicateGasUsed: BIG_NAT,
200
+ data: BYTES,
201
+ predicate: BYTES,
202
+ predicateData: BYTES,
203
+ })
204
+ })
205
+
206
+ let Input = taggedUnion('type', {
207
+ InputCoin,
208
+ InputContract,
209
+ InputMessage
210
+ })
211
+
212
+ let CoinOutput = object({
213
+ transactionIndex: NAT,
214
+ index: NAT,
215
+ ...project({
216
+ amount: fields.output?.coinAmount,
217
+ assetId: fields.output?.coinAssetId,
218
+ to: fields.output?.coinTo
219
+ }, {
220
+ to: BYTES,
221
+ amount: BIG_NAT,
222
+ assetId: BYTES
223
+ })
224
+ })
225
+
226
+ let ContractOutput = object({
227
+ transactionIndex: NAT,
228
+ index: NAT,
229
+ ...project({
230
+ inputIndex: fields.output?.contractInputIndex,
231
+ balanceRoot: fields.output?.contractBalanceRoot,
232
+ stateRoot: fields.output?.contractStateRoot
233
+ }, {
234
+ inputIndex: NAT,
235
+ balanceRoot: BYTES,
236
+ stateRoot: BYTES
237
+ })
238
+ })
239
+
240
+ let ChangeOutput = object({
241
+ transactionIndex: NAT,
242
+ index: NAT,
243
+ ...project({
244
+ amount: fields.output?.changeAmount,
245
+ assetId: fields.output?.changeAssetId,
246
+ to: fields.output?.changeTo
247
+ }, {
248
+ to: BYTES,
249
+ amount: BIG_NAT,
250
+ assetId: BYTES
251
+ })
252
+ })
253
+
254
+ let VariableOutput = object({
255
+ transactionIndex: NAT,
256
+ index: NAT,
257
+ ...project({
258
+ to: fields.output?.variableTo,
259
+ amount: fields.output?.variableAmount,
260
+ assetId: fields.output?.variableAssetId
261
+ }, {
262
+ to: BYTES,
263
+ amount: BIG_NAT,
264
+ assetId: BYTES
265
+ })
266
+ })
267
+
268
+ let ContractCreated = object({
269
+ transactionIndex: NAT,
270
+ index: NAT,
271
+ ...project({
272
+ contract: fields.output?.contractCreatedContract,
273
+ stateRoot: fields.output?.contractCreatedStateRoot
274
+ }, {
275
+ contract: object({
276
+ id: BYTES,
277
+ bytecode: BYTES,
278
+ salt: BYTES
279
+ }),
280
+ stateRoot: BYTES
281
+ })
282
+ })
283
+
284
+ let Ouput = taggedUnion('type', {
285
+ CoinOutput,
286
+ ContractOutput,
287
+ ChangeOutput,
288
+ VariableOutput,
289
+ ContractCreated
290
+ })
291
+
292
+ return object({
293
+ header: BlockHeader,
294
+ transactions: option(array(Transaction)),
295
+ receipts: option(array(Receipt)),
296
+ inputs: option(array(Input)),
297
+ outputs: option(array(Ouput)),
298
+ })
299
+ })
300
+
301
+
302
+ function project<T>(fields: Selector<keyof T> | undefined, obj: T): Partial<T> {
303
+ if (fields == null) return {}
304
+ let result: Partial<T> = {}
305
+ let key: keyof T
306
+ for (key in obj) {
307
+ if (fields[key]) {
308
+ result[key] = obj[key]
309
+ }
310
+ }
311
+ return result
312
+ }
313
+
314
+
315
+ export function isEmpty(obj: object): boolean {
316
+ for (let _ in obj) {
317
+ return false
318
+ }
319
+ return true
320
+ }
@@ -0,0 +1,88 @@
1
+ import {BlockHeader} from '@subsquid/fuel-normalization'
2
+ import {assertNotNull} from '@subsquid/util-internal'
3
+ import {ArchiveClient} from '@subsquid/util-internal-archive-client'
4
+ import {archiveIngest} from '@subsquid/util-internal-ingest-tools'
5
+ import {getRequestAt, mapRangeRequestList, RangeRequestList} from '@subsquid/util-internal-range'
6
+ import {array, cast} from '@subsquid/util-internal-validation'
7
+ import assert from 'assert'
8
+ import {DataRequest} from '../data/data-request'
9
+ import {getDataSchema} from './data-schema'
10
+ import {PartialBlock} from '../data/data-partial'
11
+
12
+
13
+ export class FuelGateway {
14
+ constructor(private client: ArchiveClient) {}
15
+
16
+ getFinalizedHeight(): Promise<number> {
17
+ return this.client.getHeight()
18
+ }
19
+
20
+ async getBlockHash(height: number): Promise<string | undefined> {
21
+ let blocks = await this.client.query({
22
+ fromBlock: height,
23
+ toBlock: height,
24
+ includeAllBlocks: true
25
+ })
26
+ assert(blocks.length == 1)
27
+ return blocks[0].header.hash
28
+ }
29
+
30
+ async getBlockHeader(height: number): Promise<BlockHeader> {
31
+ let blocks = await this.client.query({
32
+ type: 'fuel',
33
+ fromBlock: height,
34
+ toBlock: height,
35
+ includeAllBlocks: true,
36
+ fields: {
37
+ block: {
38
+ daHeight: true,
39
+ transactionsRoot: true,
40
+ transactionsCount: true,
41
+ messageReceiptRoot: true,
42
+ messageReceiptCount: true,
43
+ prevRoot: true,
44
+ time: true,
45
+ applicationHash: true
46
+ }
47
+ }
48
+ })
49
+ assert(blocks.length == 1)
50
+ let {number, ...rest} = blocks[0].header
51
+ return {
52
+ height: number,
53
+ ...rest
54
+ } as BlockHeader
55
+ }
56
+
57
+ async *getBlockStream(requests: RangeRequestList<DataRequest>, stopOnHead?: boolean | undefined): AsyncIterable<PartialBlock[]> {
58
+ let archiveRequests = mapRangeRequestList(requests, req => {
59
+ let {fields, ...items} = req
60
+ return {
61
+ type: 'fuel',
62
+ fields,
63
+ ...items
64
+ }
65
+ })
66
+
67
+ for await (let batch of archiveIngest({
68
+ client: this.client,
69
+ requests: archiveRequests,
70
+ stopOnHead
71
+ })) {
72
+ let req = getRequestAt(requests, batch.blocks[0].header.number)
73
+
74
+ let blocks = cast(
75
+ array(getDataSchema(assertNotNull(req?.fields))),
76
+ batch.blocks
77
+ ).map(b => {
78
+ let {header: {number, ...hdr}, ...items} = b
79
+ return {
80
+ header: {height: number, ...hdr},
81
+ ...items
82
+ }
83
+ })
84
+
85
+ yield blocks
86
+ }
87
+ }
88
+ }
@@ -0,0 +1,25 @@
1
+ import type * as data from '@subsquid/fuel-data/lib/data'
2
+ import type {MakePartial} from './util'
3
+
4
+
5
+ export type BlockRequiredFields = 'height' | 'hash'
6
+ export type TransactionRequiredFields = 'index'
7
+ export type ReceiptRequiredFields = 'transactionIndex' | 'index'
8
+ export type InputRequiredFields = 'transactionIndex' | 'index' | 'type'
9
+ export type OutputRequiredFields = 'transactionIndex' | 'index' | 'type'
10
+
11
+
12
+ export type PartialBlockHeader = MakePartial<data.BlockHeader, BlockRequiredFields>
13
+ export type PartialTransaction = MakePartial<data.Transaction, TransactionRequiredFields>
14
+ export type PartialReceipt = MakePartial<data.Receipt, ReceiptRequiredFields>
15
+ export type PartialInput = MakePartial<data.TransactionInput, InputRequiredFields>
16
+ export type PartialOutput = MakePartial<data.TransactionOutput, OutputRequiredFields>
17
+
18
+
19
+ export interface PartialBlock {
20
+ header: PartialBlockHeader
21
+ transactions?: PartialTransaction[]
22
+ receipts?: PartialReceipt[]
23
+ inputs?: PartialInput[]
24
+ outputs?: PartialOutput[]
25
+ }
@@ -0,0 +1,44 @@
1
+ import {ReceiptType, TransactionType, InputType, OutputType} from '@subsquid/fuel-data/lib/data'
2
+ import {FieldSelection, Bytes} from './model'
3
+
4
+
5
+ export interface DataRequest {
6
+ fields?: FieldSelection
7
+ includeAllBlocks?: boolean
8
+ transactions?: TransactionRequest[]
9
+ receipts?: ReceiptRequest[]
10
+ inputs?: InputRequest[]
11
+ outputs?: OutputRequest[]
12
+ }
13
+
14
+
15
+ export interface TransactionRequest {
16
+ type?: TransactionType[]
17
+ receipts?: boolean
18
+ inputs?: boolean
19
+ outputs?: boolean
20
+ }
21
+
22
+
23
+ export interface ReceiptRequest {
24
+ type?: ReceiptType[]
25
+ logDataContract?: Bytes[]
26
+ transaction?: boolean
27
+ }
28
+
29
+
30
+ export interface InputRequest {
31
+ type?: InputType[]
32
+ coinOwner?: Bytes[]
33
+ coinAssetId?: Bytes[]
34
+ contractContract?: Bytes[]
35
+ messageSender?: Bytes[]
36
+ messageRecipient?: Bytes[]
37
+ transaction?: boolean
38
+ }
39
+
40
+
41
+ export interface OutputRequest {
42
+ type?: OutputType[]
43
+ transaction?: boolean
44
+ }
@@ -0,0 +1,114 @@
1
+ import type * as data from '@subsquid/fuel-data/lib/data'
2
+ import type {
3
+ BlockRequiredFields,
4
+ ReceiptRequiredFields,
5
+ InputRequiredFields,
6
+ OutputRequiredFields,
7
+ TransactionRequiredFields
8
+ } from './data-partial'
9
+ import type {GetFields, Select, Selector, Simplify} from './util'
10
+
11
+
12
+ /**
13
+ * Hex encoded binary string
14
+ */
15
+ export type Bytes = string
16
+
17
+
18
+ type AddPrefix<Prefix extends string, S extends string> = `${Prefix}${Capitalize<S>}`
19
+
20
+
21
+ export interface FieldSelection {
22
+ block?: Selector<Exclude<keyof data.BlockHeader, BlockRequiredFields>>
23
+ transaction?: Selector<Exclude<keyof data.Transaction, TransactionRequiredFields>>
24
+ receipt?: Selector<Exclude<keyof data.Receipt, ReceiptRequiredFields>>
25
+ input?: Selector<
26
+ AddPrefix<'coin', Exclude<keyof data.InputCoin, InputRequiredFields>> |
27
+ AddPrefix<'contract', Exclude<keyof data.InputContract, InputRequiredFields>> |
28
+ AddPrefix<'message', Exclude<keyof data.InputMessage, InputRequiredFields>>
29
+ >
30
+ output?: Selector<
31
+ AddPrefix<'coin', Exclude<keyof data.CoinOutput, OutputRequiredFields>> |
32
+ AddPrefix<'contract', Exclude<keyof data.ContractOutput, OutputRequiredFields>> |
33
+ AddPrefix<'change', Exclude<keyof data.ChangeOutput, OutputRequiredFields>> |
34
+ AddPrefix<'variable', Exclude<keyof data.VariableOutput, OutputRequiredFields>> |
35
+ AddPrefix<'contractCreated', Exclude<keyof data.ContractCreated, OutputRequiredFields>>
36
+ >
37
+ }
38
+
39
+
40
+ export const DEFAULT_FIELDS = {
41
+ block: {
42
+ time: true
43
+ },
44
+ transaction: {
45
+ hash: true,
46
+ type: true,
47
+ status: true
48
+ },
49
+ receipt: {
50
+ receiptType: true
51
+ },
52
+ input: {},
53
+ output: {},
54
+ } as const
55
+
56
+
57
+ type Item<
58
+ Data,
59
+ RequiredFields extends keyof Data,
60
+ F extends FieldSelection,
61
+ K extends keyof FieldSelection
62
+ > = Simplify<
63
+ Pick<Data, RequiredFields> &
64
+ Select<Data, GetFields<FieldSelection, typeof DEFAULT_FIELDS, F, K>>
65
+ >
66
+
67
+
68
+ export type BlockHeader<F extends FieldSelection = {}> = Item<
69
+ data.BlockHeader,
70
+ BlockRequiredFields,
71
+ F,
72
+ 'block'
73
+ >
74
+
75
+
76
+ export type Transaction<F extends FieldSelection = {}> = Item<
77
+ data.Transaction,
78
+ TransactionRequiredFields,
79
+ F,
80
+ 'transaction'
81
+ >
82
+
83
+
84
+ export type Receipt<F extends FieldSelection = {}> = Item<
85
+ data.Receipt,
86
+ ReceiptRequiredFields,
87
+ F,
88
+ 'receipt'
89
+ >
90
+
91
+
92
+ export type Input<F extends FieldSelection = {}> = Item<
93
+ data.TransactionInput,
94
+ InputRequiredFields,
95
+ F,
96
+ 'input'
97
+ >
98
+
99
+
100
+ export type Output<F extends FieldSelection = {}> = Item<
101
+ data.TransactionOutput,
102
+ OutputRequiredFields,
103
+ F,
104
+ 'output'
105
+ >
106
+
107
+
108
+ export interface Block<F extends FieldSelection = {}> {
109
+ header: BlockHeader<F>
110
+ transactions: Transaction<F>[]
111
+ receipts: Receipt<F>[]
112
+ inputs: Input<F>[]
113
+ outputs: Output<F>[]
114
+ }
@@ -0,0 +1,42 @@
1
+ export type Simplify<T> = {
2
+ [K in keyof T]: T[K]
3
+ } & {}
4
+
5
+
6
+ export type ExcludeUndefined<T> = {
7
+ [K in keyof T as undefined extends T[K] ? never : K]: T[K]
8
+ } & {}
9
+
10
+
11
+ export type GetFields<
12
+ FieldSelectionType,
13
+ Defaults extends FieldSelectionType,
14
+ Selection extends FieldSelectionType,
15
+ K extends keyof FieldSelectionType
16
+ > = TrueFields<MergeDefault<Selection[K], Defaults[K]>>
17
+
18
+
19
+ type MergeDefault<T, D> = Simplify<
20
+ undefined extends T ? D : Omit<D, keyof ExcludeUndefined<T>> & ExcludeUndefined<T>
21
+ >
22
+
23
+
24
+ type TrueFields<F> = keyof {
25
+ [K in keyof F as true extends F[K] ? K : never]: true
26
+ }
27
+
28
+
29
+ export type Select<T, Fields> = T extends any ? Simplify<Pick<T, Extract<keyof T, Fields>>> : never
30
+
31
+
32
+ export type Selector<Fields extends string | number | symbol> = {
33
+ [P in Fields]?: boolean
34
+ }
35
+
36
+
37
+ export type MakePartial<T, Required extends keyof T> = Simplify<
38
+ Pick<T, Required> &
39
+ {
40
+ [K in keyof T as K extends Required ? never : K]+?: T[K]
41
+ }
42
+ >
package/src/fields.ts ADDED
@@ -0,0 +1,35 @@
1
+ import {DEFAULT_FIELDS, FieldSelection} from './data/model'
2
+ import {Selector} from './data/util'
3
+
4
+
5
+ /**
6
+ * Get effective set of selected fields.
7
+ */
8
+ export function getFields(fields: FieldSelection | undefined): FieldSelection {
9
+ return {
10
+ block: merge(DEFAULT_FIELDS.block, fields?.block),
11
+ transaction: merge(DEFAULT_FIELDS.transaction, fields?.transaction),
12
+ receipt: merge(DEFAULT_FIELDS.receipt, fields?.receipt),
13
+ input: merge(DEFAULT_FIELDS.input, fields?.input),
14
+ output: merge(DEFAULT_FIELDS.output, fields?.output),
15
+ }
16
+ }
17
+
18
+
19
+ function merge<Keys extends string>(def: Selector<Keys>, requested: Selector<Keys> = {}): Selector<Keys> {
20
+ let fields: Selector<Keys> = {}
21
+
22
+ for (let key in def) {
23
+ if (requested[key] !== false) {
24
+ fields[key] = def[key]
25
+ }
26
+ }
27
+
28
+ for (let key in requested) {
29
+ if (requested[key]) {
30
+ fields[key] = true
31
+ }
32
+ }
33
+
34
+ return fields
35
+ }