@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.
- package/lib/eth/provider.d.ts.map +1 -1
- package/lib/eth/provider.js +3 -3
- package/lib/eth/provider.js.map +1 -1
- package/lib/store/codegen.d.ts +0 -2
- package/lib/store/codegen.d.ts.map +1 -1
- package/lib/store/codegen.js +147 -158
- package/lib/store/codegen.js.map +1 -1
- package/lib/store/convert.d.ts +32 -0
- package/lib/store/convert.d.ts.map +1 -0
- package/lib/store/convert.js +293 -0
- package/lib/store/convert.js.map +1 -0
- package/lib/store/convert.test.d.ts +2 -0
- package/lib/store/convert.test.d.ts.map +1 -0
- package/lib/store/convert.test.js.map +1 -0
- package/lib/store/decorators.d.ts +24 -7
- package/lib/store/decorators.d.ts.map +1 -1
- package/lib/store/decorators.js +160 -16
- package/lib/store/decorators.js.map +1 -1
- package/lib/store/index.d.ts +1 -1
- package/lib/store/index.d.ts.map +1 -1
- package/lib/store/index.js +1 -1
- package/lib/store/index.js.map +1 -1
- package/lib/store/schema.d.ts.map +1 -1
- package/lib/store/schema.js +39 -1
- package/lib/store/schema.js.map +1 -1
- package/lib/store/store.d.ts +23 -4
- package/lib/store/store.d.ts.map +1 -1
- package/lib/store/store.js +119 -59
- package/lib/store/store.js.map +1 -1
- package/lib/store/types.d.ts +22 -5
- package/lib/store/types.d.ts.map +1 -1
- package/lib/store/types.js +14 -1
- package/lib/store/types.js.map +1 -1
- package/package.json +4 -3
- package/src/eth/provider.ts +8 -3
- package/src/store/codegen.ts +183 -185
- package/src/store/convert.ts +321 -0
- package/src/store/decorators.ts +185 -16
- package/src/store/index.ts +1 -1
- package/src/store/schema.ts +41 -1
- package/src/store/store.ts +157 -56
- package/src/store/types.ts +25 -6
- package/lib/store/entity.d.ts +0 -17
- package/lib/store/entity.d.ts.map +0 -1
- package/lib/store/entity.js +0 -61
- package/lib/store/entity.js.map +0 -1
- 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
|
+
}
|
package/src/store/decorators.ts
CHANGED
@@ -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
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
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
|
11
|
-
return function (target: 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
|
15
|
-
return function (target: 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
|
19
|
-
return function (target: 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
|
23
|
-
|
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
|
27
|
-
return function (target: 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
|
31
|
-
return function (target: any,
|
197
|
+
export function Many(entity: string) {
|
198
|
+
return function (target: any, propertyKey: string) {
|
199
|
+
column(array_(objectId_(entity)))(target, propertyKey)
|
200
|
+
}
|
32
201
|
}
|
package/src/store/index.ts
CHANGED
package/src/store/schema.ts
CHANGED
@@ -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', '
|
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
|
|