@sentio/sdk 2.39.3 → 2.39.4-rc.10

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 (47) hide show
  1. package/lib/eth/provider.d.ts.map +1 -1
  2. package/lib/eth/provider.js +3 -3
  3. package/lib/eth/provider.js.map +1 -1
  4. package/lib/store/codegen.d.ts +0 -2
  5. package/lib/store/codegen.d.ts.map +1 -1
  6. package/lib/store/codegen.js +147 -158
  7. package/lib/store/codegen.js.map +1 -1
  8. package/lib/store/convert.d.ts +32 -0
  9. package/lib/store/convert.d.ts.map +1 -0
  10. package/lib/store/convert.js +293 -0
  11. package/lib/store/convert.js.map +1 -0
  12. package/lib/store/convert.test.d.ts +2 -0
  13. package/lib/store/convert.test.d.ts.map +1 -0
  14. package/lib/store/convert.test.js.map +1 -0
  15. package/lib/store/decorators.d.ts +24 -7
  16. package/lib/store/decorators.d.ts.map +1 -1
  17. package/lib/store/decorators.js +160 -16
  18. package/lib/store/decorators.js.map +1 -1
  19. package/lib/store/index.d.ts +1 -1
  20. package/lib/store/index.d.ts.map +1 -1
  21. package/lib/store/index.js +1 -1
  22. package/lib/store/index.js.map +1 -1
  23. package/lib/store/schema.d.ts.map +1 -1
  24. package/lib/store/schema.js +39 -1
  25. package/lib/store/schema.js.map +1 -1
  26. package/lib/store/store.d.ts +23 -4
  27. package/lib/store/store.d.ts.map +1 -1
  28. package/lib/store/store.js +119 -59
  29. package/lib/store/store.js.map +1 -1
  30. package/lib/store/types.d.ts +22 -5
  31. package/lib/store/types.d.ts.map +1 -1
  32. package/lib/store/types.js +14 -1
  33. package/lib/store/types.js.map +1 -1
  34. package/package.json +4 -3
  35. package/src/eth/provider.ts +8 -3
  36. package/src/store/codegen.ts +183 -185
  37. package/src/store/convert.ts +321 -0
  38. package/src/store/decorators.ts +185 -16
  39. package/src/store/index.ts +1 -1
  40. package/src/store/schema.ts +41 -1
  41. package/src/store/store.ts +157 -56
  42. package/src/store/types.ts +25 -6
  43. package/lib/store/entity.d.ts +0 -17
  44. package/lib/store/entity.d.ts.map +0 -1
  45. package/lib/store/entity.js +0 -61
  46. package/lib/store/entity.js.map +0 -1
  47. package/src/store/entity.ts +0 -71
@@ -0,0 +1,321 @@
1
+ import { RichValue, RichValue_NullValue } from '@sentio/protos'
2
+ import type { String, Int, Float, ID, Bytes, Timestamp, Boolean } from './types.js'
3
+ import { BigDecimal } from '@sentio/bigdecimal'
4
+
5
+ export interface ValueConverter<T> {
6
+ from: (value: T) => RichValue
7
+ to: (value: RichValue) => T
8
+ required?: boolean
9
+ isArray?: boolean
10
+ isRelation?: boolean
11
+ relationName?: string
12
+ }
13
+
14
+ export const ValueRequiredError = new Error('Value is required but received null or undefined')
15
+
16
+ export function required_<T>(converter: ValueConverter<T | undefined>): ValueConverter<T> {
17
+ const { from, to, ...rest } = converter
18
+ return {
19
+ from: (value: T | undefined) => {
20
+ if (value == null) {
21
+ throw ValueRequiredError
22
+ }
23
+ return from(value)
24
+ },
25
+ to: (value: RichValue) => {
26
+ if (value == null || value.nullValue) {
27
+ throw ValueRequiredError
28
+ }
29
+ return to(value)!
30
+ },
31
+ ...rest,
32
+ required: true
33
+ }
34
+ }
35
+
36
+ export function array_<T>(converter: ValueConverter<T>): ValueConverter<T[]> {
37
+ return {
38
+ from: (value: T[]) => {
39
+ return {
40
+ listValue: {
41
+ values: value.map(converter.from)
42
+ }
43
+ }
44
+ },
45
+ to: (value: RichValue) => {
46
+ return value.listValue?.values.map(converter.to) || []
47
+ },
48
+ isArray: true,
49
+ isRelation: converter.isRelation,
50
+ relationName: converter.relationName
51
+ }
52
+ }
53
+
54
+ export function enumerate_<T extends string | number>(values: Record<T, string>): ValueConverter<T> {
55
+ return {
56
+ from: (value?: T) => {
57
+ if (value == null) {
58
+ return {
59
+ nullValue: RichValue_NullValue.NULL_VALUE
60
+ }
61
+ }
62
+ return {
63
+ stringValue: values[value]
64
+ }
65
+ },
66
+ to(v: RichValue): T {
67
+ return v.stringValue as T
68
+ }
69
+ }
70
+ }
71
+
72
+ export function objectId_<T>(entityName: string): ValueConverter<T | ID> {
73
+ return {
74
+ from: (value: T | ID) => {
75
+ if (typeof value == 'string') {
76
+ return {
77
+ stringValue: value
78
+ }
79
+ }
80
+ if (value instanceof Uint8Array) {
81
+ return {
82
+ stringValue: `0x${Buffer.from(value).toString('hex')}`
83
+ }
84
+ }
85
+
86
+ if (typeof value == 'object') {
87
+ const entity = value as any
88
+ return {
89
+ stringValue: entity.id.toString()
90
+ }
91
+ }
92
+ return {
93
+ nullValue: RichValue_NullValue.NULL_VALUE
94
+ }
95
+ },
96
+ to(v) {
97
+ return v.stringValue as T | ID
98
+ },
99
+ isRelation: true,
100
+ relationName: entityName
101
+ }
102
+ }
103
+
104
+ /* eslint-disable */
105
+ export const StringConverter: ValueConverter<String | undefined> = {
106
+ from: (value?: String) => {
107
+ if (value == null) {
108
+ return {
109
+ nullValue: RichValue_NullValue.NULL_VALUE
110
+ }
111
+ }
112
+ return {
113
+ stringValue: value
114
+ }
115
+ },
116
+ to(v) {
117
+ return v.stringValue
118
+ }
119
+ }
120
+
121
+ export const IntConverter: ValueConverter<Int | undefined> = {
122
+ from: (value?: Int) => {
123
+ if (value == null) {
124
+ return {
125
+ nullValue: RichValue_NullValue.NULL_VALUE
126
+ }
127
+ }
128
+ return {
129
+ intValue: value
130
+ }
131
+ },
132
+ to(v) {
133
+ return v.intValue
134
+ }
135
+ }
136
+
137
+ export const FloatConverter: ValueConverter<Float | undefined> = {
138
+ from: (value?: Float) => {
139
+ if (value == null) {
140
+ return {
141
+ nullValue: RichValue_NullValue.NULL_VALUE
142
+ }
143
+ }
144
+ return {
145
+ floatValue: value
146
+ }
147
+ },
148
+ to(v) {
149
+ return v.floatValue
150
+ }
151
+ }
152
+
153
+ export const BooleanConverter: ValueConverter<Boolean | undefined> = {
154
+ from: (value?: Boolean) => {
155
+ if (value == null) {
156
+ return {
157
+ nullValue: RichValue_NullValue.NULL_VALUE
158
+ }
159
+ }
160
+ return {
161
+ boolValue: value
162
+ }
163
+ },
164
+ to(v) {
165
+ return v.boolValue
166
+ }
167
+ }
168
+
169
+ export const TimestampConverter: ValueConverter<Timestamp | undefined> = {
170
+ from: (value: Timestamp | undefined) => {
171
+ if (value == null) {
172
+ return {
173
+ nullValue: RichValue_NullValue.NULL_VALUE
174
+ }
175
+ }
176
+ return {
177
+ timestampValue: value
178
+ }
179
+ },
180
+ to(v) {
181
+ return v.timestampValue
182
+ }
183
+ }
184
+
185
+ export const BytesConverter: ValueConverter<Bytes | undefined> = {
186
+ from: (value?: Bytes) => {
187
+ if (value == null) {
188
+ return {
189
+ nullValue: RichValue_NullValue.NULL_VALUE
190
+ }
191
+ }
192
+ return {
193
+ bytesValue: value
194
+ }
195
+ },
196
+ to(v) {
197
+ return v.bytesValue
198
+ }
199
+ }
200
+
201
+ export const IDConverter: ValueConverter<ID | undefined> = {
202
+ from(value: ID | undefined): RichValue {
203
+ if (typeof value == 'string') {
204
+ return {
205
+ stringValue: value
206
+ }
207
+ }
208
+ if (value instanceof Uint8Array) {
209
+ return {
210
+ stringValue: `0x${Buffer.from(value).toString('hex')}`
211
+ }
212
+ }
213
+ return {
214
+ nullValue: RichValue_NullValue.NULL_VALUE
215
+ }
216
+ },
217
+ to(value: RichValue): ID | undefined {
218
+ if (value.stringValue) {
219
+ return value.stringValue as ID
220
+ }
221
+ if (value.bytesValue) {
222
+ const v = `0x${Buffer.from(value.bytesValue).toString('hex')}`
223
+ return v as ID
224
+ }
225
+ return undefined
226
+ }
227
+ }
228
+
229
+ export const BigDecimalConverter: ValueConverter<BigDecimal | undefined> = {
230
+ from: (value?: BigDecimal): RichValue => {
231
+ if (value == null) {
232
+ return {
233
+ nullValue: RichValue_NullValue.NULL_VALUE
234
+ }
235
+ }
236
+ const s = (value.c || []).join('')
237
+ const exp = -(s.length - (value.e ?? 0) - 1)
238
+
239
+ return {
240
+ bigdecimalValue: {
241
+ value: toBigInteger(BigInt(s)),
242
+ exp: exp
243
+ }
244
+ }
245
+ },
246
+ to(v) {
247
+ const d = v.bigdecimalValue
248
+ if (d) {
249
+ const i = bytesToBigInt(d.value!.data)
250
+ let ret = new BigDecimal(i.toString())
251
+ if (d.exp < 0) {
252
+ ret = ret.dividedBy(new BigDecimal(10).pow(-d.exp))
253
+ } else {
254
+ ret = ret.multipliedBy(new BigDecimal(10).pow(d.exp))
255
+ }
256
+ return ret.multipliedBy(d.value?.negative ? -1 : 1)
257
+ }
258
+ return undefined
259
+ }
260
+ }
261
+
262
+ export const BigIntConverter: ValueConverter<bigint | undefined> = {
263
+ from: (value?: bigint) => {
264
+ if (value == null) {
265
+ return {
266
+ nullValue: RichValue_NullValue.NULL_VALUE
267
+ }
268
+ }
269
+ return {
270
+ bigintValue: toBigInteger(value)
271
+ }
272
+ },
273
+ to(v) {
274
+ if (v.bigintValue) {
275
+ let res = bytesToBigInt(v.bigintValue?.data)
276
+ if (v.bigintValue.negative) {
277
+ res = -res
278
+ }
279
+ return res
280
+ }
281
+ return undefined
282
+ }
283
+ }
284
+
285
+ export function bytesToBigInt(bytes: Uint8Array) {
286
+ let intValue = BigInt(0)
287
+ for (let i = 0; i < bytes.length; i++) {
288
+ intValue = intValue * BigInt(256) + BigInt(bytes[i])
289
+ }
290
+ return intValue
291
+ }
292
+
293
+ export function toBigInteger(a: bigint) {
294
+ const negative = a < 0
295
+ if (negative) {
296
+ a = -a
297
+ }
298
+ let hex = a.toString(16)
299
+ if (hex.length % 2 === 1) {
300
+ hex = '0' + hex
301
+ }
302
+ const buffer = Buffer.from(hex, 'hex')
303
+
304
+ return {
305
+ negative: negative,
306
+ data: new Uint8Array(buffer)
307
+ }
308
+ }
309
+
310
+ export const TypeConverters: Record<string, ValueConverter<any>> = {
311
+ BigDecimal: BigDecimalConverter,
312
+ BigInt: BigIntConverter,
313
+ String: StringConverter,
314
+ Boolean: BooleanConverter,
315
+ Uint8Array: BytesConverter,
316
+ ID: IDConverter,
317
+ Bytes: BytesConverter,
318
+ Int: IntConverter,
319
+ Float: FloatConverter,
320
+ Timestamp: TimestampConverter
321
+ }
@@ -1,32 +1,201 @@
1
+ import 'reflect-metadata'
2
+ import {
3
+ array_,
4
+ BigDecimalConverter,
5
+ BigIntConverter,
6
+ BooleanConverter,
7
+ BytesConverter,
8
+ FloatConverter,
9
+ IDConverter,
10
+ IntConverter,
11
+ objectId_,
12
+ required_,
13
+ StringConverter,
14
+ TimestampConverter,
15
+ TypeConverters,
16
+ ValueConverter
17
+ } from './convert.js'
18
+ import { RichStruct } from '@sentio/protos'
19
+ import { getStore } from './store.js'
20
+
1
21
  type Constructor = { new (...args: any[]): any }
2
22
 
3
- export function entity(name: string) {
4
- return function <T extends Constructor>(constructor: T, context: any) {
5
- constructor.prototype.entityName = name
6
- return constructor
23
+ function handleError(entity: string, key: string, fn: () => any) {
24
+ try {
25
+ return fn()
26
+ } catch (e) {
27
+ throw new Error(`Get property "${key}" from Entity "${entity}" failed: ${e.message}`)
28
+ }
29
+ }
30
+
31
+ export function Entity(name: string) {
32
+ return function <T extends Constructor>(BaseClass: T) {
33
+ Object.defineProperty(BaseClass, 'entityName', {
34
+ value: name
35
+ })
36
+
37
+ const meta: Record<string, ValueConverter<any>> = (BaseClass as any).meta || {}
38
+ const target = BaseClass.prototype
39
+ for (const [propertyKey, type] of Object.entries(meta)) {
40
+ if (type.isRelation && type.relationName) {
41
+ const relationName = type.relationName
42
+ const idGetter = function (this: any) {
43
+ return handleError(this.constructor.entityName, propertyKey, () => type!.to(this._data.fields[propertyKey]))
44
+ }
45
+ const idSetter = function (this: any, value: any) {
46
+ this._data.fields[propertyKey] = handleError(this.constructor.entityName, propertyKey, () =>
47
+ type!.from(value)
48
+ )
49
+ }
50
+ const idKey = type.isArray ? propertyKey + 'IDs' : propertyKey + 'ID'
51
+
52
+ Reflect.defineProperty(target, idKey, {
53
+ get: idGetter,
54
+ set: idSetter
55
+ })
56
+ Reflect.defineProperty(target, propertyKey, {
57
+ get: function () {
58
+ const ids = idGetter.call(this)
59
+ if (Array.isArray(ids)) {
60
+ return Promise.all(
61
+ ids.map((id) => {
62
+ return getStore()?.get(relationName, id)
63
+ })
64
+ )
65
+ } else {
66
+ return getStore()?.get(relationName, ids)
67
+ }
68
+ },
69
+ set: function (value) {
70
+ if (value instanceof Promise) {
71
+ value.then((v) => {
72
+ idSetter.call(this, v)
73
+ })
74
+ } else {
75
+ idSetter.call(this, value)
76
+ }
77
+ }
78
+ })
79
+ } else {
80
+ Reflect.defineProperty(target, propertyKey, {
81
+ configurable: true,
82
+ get: function () {
83
+ return handleError(this.constructor.entityName, propertyKey, () => type!.to(this._data.fields[propertyKey]))
84
+ },
85
+ set: function (value) {
86
+ this._data.fields[propertyKey] = handleError(this.constructor.entityName, propertyKey, () =>
87
+ type!.from(value)
88
+ )
89
+ }
90
+ })
91
+ }
92
+ }
93
+
94
+ return class extends BaseClass {
95
+ readonly _data: RichStruct = { fields: {} }
96
+ constructor(...args: any[]) {
97
+ super()
98
+ for (const key of Object.getOwnPropertyNames(this)) {
99
+ if (BaseClass.prototype.hasOwnProperty(key)) {
100
+ delete this[key]
101
+ }
102
+ }
103
+ if (args[0]) {
104
+ Object.assign(this, args[0])
105
+ }
106
+ }
107
+ }
7
108
  }
8
109
  }
9
110
 
10
- export function derivedFrom(field: string) {
11
- return function (target: any, context: any) {}
111
+ export function Column(type: string) {
112
+ return function (target: any, propertyKey: string) {
113
+ let required = false
114
+ if (type.endsWith('!')) {
115
+ required = true
116
+ type = type.slice(0, -1)
117
+ }
118
+ let typeConverter = TypeConverters[type]
119
+ if (!typeConverter) {
120
+ throw new Error(`Unsupported type ${type}`)
121
+ }
122
+ if (required) {
123
+ typeConverter = required_(typeConverter)
124
+ }
125
+ column(typeConverter)(target, propertyKey)
126
+ }
12
127
  }
13
128
 
14
- export function unique(field: string) {
15
- return function (target: any, context: any) {}
129
+ export function ListColumn(type: string = '') {
130
+ return function (target: any, propertyKey: string) {
131
+ let required = false
132
+ if (type.endsWith('!')) {
133
+ required = true
134
+ type = type.slice(0, -1)
135
+ }
136
+
137
+ let typeConverter = TypeConverters[type]
138
+ if (!typeConverter) {
139
+ // support list of list
140
+ const meta = target.constructor.meta || {}
141
+ const type = meta[propertyKey]
142
+ if (type) {
143
+ typeConverter = type
144
+ } else {
145
+ throw new Error(`Can't find inner type convert`)
146
+ }
147
+ }
148
+ if (required) {
149
+ typeConverter = required_(typeConverter)
150
+ }
151
+ column(array_(typeConverter))(target, propertyKey)
152
+ }
16
153
  }
17
154
 
18
- export function index(field: string[]) {
19
- return function (target: any, context: any) {}
155
+ export function column(type?: ValueConverter<any>) {
156
+ return function (target: any, propertyKey: string) {
157
+ const reflectType = Reflect.getMetadata('design:type', target, propertyKey)
158
+ if (!type) {
159
+ const typeName = reflectType.name
160
+ type = TypeConverters[typeName]
161
+ if (!type) {
162
+ throw new Error(`Unsupported type ${typeName}`)
163
+ }
164
+ }
165
+
166
+ const meta = target.constructor.meta || {}
167
+ meta[propertyKey] = type
168
+ target.constructor.meta = meta
169
+ }
20
170
  }
21
171
 
22
- export function fulltext(query: string) {
23
- return function (target: any, context: any) {}
172
+ export const IDColumn = column(IDConverter)
173
+ export const IntColumn = column(IntConverter)
174
+ export const FloatColumn = column(FloatConverter)
175
+ export const BigDecimalColumn = column(BigDecimalConverter)
176
+ export const BigIntColumn = column(BigIntConverter)
177
+ export const StringColumn = column(StringConverter)
178
+ export const BooleanColumn = column(BooleanConverter)
179
+ export const TimestampColumn = column(TimestampConverter)
180
+ export const BytesColumn = column(BytesConverter)
181
+
182
+ export function Required(target: any, propertyKey: string) {
183
+ const meta = target.constructor.meta || {}
184
+ const type = meta[propertyKey]
185
+ if (type) {
186
+ meta[propertyKey] = required_(type)
187
+ }
188
+ target.constructor.meta = meta
24
189
  }
25
190
 
26
- export function cardinality(value: number) {
27
- return function (target: any, context: any) {}
191
+ export function One(entity: string) {
192
+ return function (target: any, propertyKey: string) {
193
+ column(objectId_(entity))(target, propertyKey)
194
+ }
28
195
  }
29
196
 
30
- export function byteWeight(value: number) {
31
- return function (target: any, context: any) {}
197
+ export function Many(entity: string) {
198
+ return function (target: any, propertyKey: string) {
199
+ column(array_(objectId_(entity)))(target, propertyKey)
200
+ }
32
201
  }
@@ -1,5 +1,5 @@
1
1
  export * from './decorators.js'
2
2
  export * from './types.js'
3
- export * from './entity.js'
4
3
  export * from './store.js'
5
4
  export * from './context.js'
5
+ export * from './convert.js'
@@ -1,7 +1,8 @@
1
1
  import { buildASTSchema, DocumentNode, extendSchema, GraphQLSchema, parse, validateSchema } from 'graphql/index.js'
2
2
  import * as fs from 'node:fs'
3
+ import { GraphQLObjectType, GraphQLOutputType, isListType, isNonNullType } from 'graphql'
3
4
 
4
- const customScalars = ['BigInt', 'BigDecimal', 'DateTime', 'JSON', 'Bytes', 'ID']
5
+ const customScalars = ['BigInt', 'BigDecimal', 'Timestamp', 'JSON', 'Bytes', 'ID']
5
6
 
6
7
  const baseSchema = buildASTSchema(
7
8
  parse(`
@@ -19,12 +20,51 @@ const baseSchema = buildASTSchema(
19
20
  `)
20
21
  )
21
22
 
23
+ function getElemType(type: GraphQLOutputType) {
24
+ if (isNonNullType(type)) {
25
+ return getElemType(type.ofType)
26
+ }
27
+ if (isListType(type)) {
28
+ return getElemType(type.ofType)
29
+ }
30
+ return type
31
+ }
32
+
33
+ function checkRelations(schema: GraphQLSchema) {
34
+ for (const t of Object.values(schema.getTypeMap())) {
35
+ if (t.name.startsWith('__')) {
36
+ continue
37
+ }
38
+ if (t instanceof GraphQLObjectType) {
39
+ for (const f of Object.values(t.getFields())) {
40
+ if (f.astNode) {
41
+ for (const d of f.astNode.directives ?? []) {
42
+ if (d.name.value === 'derivedFrom') {
43
+ const arg = (d.arguments ?? []).find((a) => a.name.value === 'field')
44
+ if (!arg || !arg.value || arg.value.kind !== 'StringValue') {
45
+ throw new Error(`@derivedFrom directive must have a 'field' argument`)
46
+ }
47
+ const fieldName = arg.value.value
48
+ const targetType = getElemType(f.type) as GraphQLObjectType
49
+ // Check if the target type has a field with the same name
50
+ if (!targetType.getFields()[fieldName]) {
51
+ throw new Error(`Field '${fieldName}' not found in type '${targetType.name}'`)
52
+ }
53
+ }
54
+ }
55
+ }
56
+ }
57
+ }
58
+ }
59
+ }
60
+
22
61
  export function buildSchema(doc: DocumentNode): GraphQLSchema {
23
62
  const schema = extendSchema(baseSchema, doc)
24
63
  const errors = validateSchema(schema).filter((err) => !/query root/i.test(err.message))
25
64
  if (errors.length > 0) {
26
65
  throw errors[0]
27
66
  }
67
+ checkRelations(schema)
28
68
  return schema
29
69
  }
30
70