@subsquid/openreader 1.0.2 → 2.1.0

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 (295) hide show
  1. package/bin/main.js +1 -1
  2. package/lib/context.d.ts +14 -0
  3. package/lib/context.d.ts.map +1 -0
  4. package/lib/context.js +3 -0
  5. package/lib/context.js.map +1 -0
  6. package/lib/db.d.ts +23 -0
  7. package/lib/db.d.ts.map +1 -0
  8. package/lib/db.js +57 -0
  9. package/lib/db.js.map +1 -0
  10. package/{dist → lib}/dialect.d.ts +0 -0
  11. package/{dist → lib}/dialect.d.ts.map +0 -0
  12. package/{dist → lib}/dialect.js +0 -0
  13. package/{dist → lib}/dialect.js.map +0 -0
  14. package/lib/ir/args.d.ts +47 -0
  15. package/lib/ir/args.d.ts.map +1 -0
  16. package/lib/ir/args.js +3 -0
  17. package/lib/ir/args.js.map +1 -0
  18. package/lib/ir/connection.d.ts +30 -0
  19. package/lib/ir/connection.d.ts.map +1 -0
  20. package/lib/ir/connection.js +17 -0
  21. package/lib/ir/connection.js.map +1 -0
  22. package/lib/ir/fields.d.ts +22 -0
  23. package/lib/ir/fields.d.ts.map +1 -0
  24. package/lib/ir/fields.js +3 -0
  25. package/lib/ir/fields.js.map +1 -0
  26. package/lib/limit.size.d.ts +8 -0
  27. package/lib/limit.size.d.ts.map +1 -0
  28. package/lib/limit.size.js +107 -0
  29. package/lib/limit.size.js.map +1 -0
  30. package/{dist → lib}/main.d.ts +0 -0
  31. package/{dist → lib}/main.d.ts.map +0 -0
  32. package/lib/main.js +59 -0
  33. package/lib/main.js.map +1 -0
  34. package/{dist → lib}/model.d.ts +11 -1
  35. package/lib/model.d.ts.map +1 -0
  36. package/{dist → lib}/model.js +0 -0
  37. package/{dist → lib}/model.js.map +0 -0
  38. package/{dist/gql/schema.d.ts → lib/model.schema.d.ts} +2 -2
  39. package/lib/model.schema.d.ts.map +1 -0
  40. package/{dist/gql/schema.js → lib/model.schema.js} +62 -8
  41. package/lib/model.schema.js.map +1 -0
  42. package/{dist → lib}/model.tools.d.ts +0 -0
  43. package/{dist → lib}/model.tools.d.ts.map +0 -0
  44. package/{dist → lib}/model.tools.js +0 -0
  45. package/{dist → lib}/model.tools.js.map +0 -0
  46. package/{dist → lib/opencrud}/orderBy.d.ts +2 -5
  47. package/lib/opencrud/orderBy.d.ts.map +1 -0
  48. package/{dist → lib/opencrud}/orderBy.js +1 -1
  49. package/lib/opencrud/orderBy.js.map +1 -0
  50. package/lib/opencrud/schema.d.ts +31 -0
  51. package/lib/opencrud/schema.d.ts.map +1 -0
  52. package/lib/opencrud/schema.js +527 -0
  53. package/lib/opencrud/schema.js.map +1 -0
  54. package/lib/opencrud/tree.d.ts +8 -0
  55. package/lib/opencrud/tree.d.ts.map +1 -0
  56. package/lib/opencrud/tree.js +131 -0
  57. package/lib/opencrud/tree.js.map +1 -0
  58. package/lib/opencrud/where.d.ts +7 -0
  59. package/lib/opencrud/where.d.ts.map +1 -0
  60. package/lib/opencrud/where.js +141 -0
  61. package/lib/opencrud/where.js.map +1 -0
  62. package/{dist/gql → lib}/scalars/BigInt.d.ts +0 -0
  63. package/lib/scalars/BigInt.d.ts.map +1 -0
  64. package/{dist/gql → lib}/scalars/BigInt.js +1 -1
  65. package/lib/scalars/BigInt.js.map +1 -0
  66. package/{dist/gql → lib}/scalars/Bytes.d.ts +0 -0
  67. package/lib/scalars/Bytes.d.ts.map +1 -0
  68. package/{dist/gql → lib}/scalars/Bytes.js +2 -2
  69. package/lib/scalars/Bytes.js.map +1 -0
  70. package/{dist/gql → lib}/scalars/DateTime.d.ts +0 -0
  71. package/lib/scalars/DateTime.d.ts.map +1 -0
  72. package/{dist/gql → lib}/scalars/DateTime.js +1 -1
  73. package/lib/scalars/DateTime.js.map +1 -0
  74. package/{dist/gql → lib}/scalars/JSON.d.ts +0 -0
  75. package/lib/scalars/JSON.d.ts.map +1 -0
  76. package/{dist/gql → lib}/scalars/JSON.js +0 -0
  77. package/lib/scalars/JSON.js.map +1 -0
  78. package/{dist/gql → lib}/scalars/index.d.ts +0 -0
  79. package/lib/scalars/index.d.ts.map +1 -0
  80. package/{dist/gql → lib}/scalars/index.js +0 -0
  81. package/lib/scalars/index.js.map +1 -0
  82. package/lib/server.d.ts +42 -0
  83. package/lib/server.d.ts.map +1 -0
  84. package/lib/server.js +171 -0
  85. package/lib/server.js.map +1 -0
  86. package/lib/sql/cursor.d.ts +52 -0
  87. package/lib/sql/cursor.d.ts.map +1 -0
  88. package/lib/sql/cursor.js +234 -0
  89. package/lib/sql/cursor.js.map +1 -0
  90. package/lib/sql/mapping.d.ts +4 -0
  91. package/lib/sql/mapping.d.ts.map +1 -0
  92. package/lib/sql/mapping.js +71 -0
  93. package/lib/sql/mapping.js.map +1 -0
  94. package/lib/sql/printer.d.ts +37 -0
  95. package/lib/sql/printer.d.ts.map +1 -0
  96. package/lib/sql/printer.js +311 -0
  97. package/lib/sql/printer.js.map +1 -0
  98. package/lib/sql/query.d.ts +46 -0
  99. package/lib/sql/query.d.ts.map +1 -0
  100. package/lib/sql/query.js +134 -0
  101. package/lib/sql/query.js.map +1 -0
  102. package/lib/sql/util.d.ts +30 -0
  103. package/lib/sql/util.d.ts.map +1 -0
  104. package/lib/sql/util.js +75 -0
  105. package/lib/sql/util.js.map +1 -0
  106. package/lib/subscription.d.ts +18 -0
  107. package/lib/subscription.d.ts.map +1 -0
  108. package/lib/subscription.js +47 -0
  109. package/lib/subscription.js.map +1 -0
  110. package/{dist → lib}/test/basic.test.d.ts +0 -0
  111. package/{dist → lib}/test/basic.test.d.ts.map +0 -0
  112. package/{dist → lib}/test/basic.test.js +0 -0
  113. package/{dist → lib}/test/basic.test.js.map +0 -0
  114. package/{dist → lib}/test/connection.test.d.ts +0 -0
  115. package/{dist → lib}/test/connection.test.d.ts.map +0 -0
  116. package/{dist → lib}/test/connection.test.js +0 -0
  117. package/{dist → lib}/test/connection.test.js.map +0 -0
  118. package/{dist → lib}/test/fts.test.d.ts +0 -0
  119. package/{dist → lib}/test/fts.test.d.ts.map +0 -0
  120. package/{dist → lib}/test/fts.test.js +1 -1
  121. package/lib/test/fts.test.js.map +1 -0
  122. package/{dist → lib}/test/isNull.test.d.ts +0 -0
  123. package/{dist → lib}/test/isNull.test.d.ts.map +0 -0
  124. package/{dist → lib}/test/isNull.test.js +0 -0
  125. package/{dist → lib}/test/isNull.test.js.map +0 -0
  126. package/lib/test/limits.test.d.ts +2 -0
  127. package/lib/test/limits.test.d.ts.map +1 -0
  128. package/lib/test/limits.test.js +159 -0
  129. package/lib/test/limits.test.js.map +1 -0
  130. package/{dist → lib}/test/lists.test.d.ts +0 -0
  131. package/{dist → lib}/test/lists.test.d.ts.map +0 -0
  132. package/{dist → lib}/test/lists.test.js +0 -0
  133. package/{dist → lib}/test/lists.test.js.map +0 -0
  134. package/{dist → lib}/test/lookup.test.d.ts +0 -0
  135. package/{dist → lib}/test/lookup.test.d.ts.map +0 -0
  136. package/{dist → lib}/test/lookup.test.js +0 -0
  137. package/{dist → lib}/test/lookup.test.js.map +0 -0
  138. package/{dist → lib}/test/regressions.test.d.ts +0 -0
  139. package/{dist → lib}/test/regressions.test.d.ts.map +0 -0
  140. package/{dist → lib}/test/regressions.test.js +0 -0
  141. package/{dist → lib}/test/regressions.test.js.map +0 -0
  142. package/{dist → lib}/test/scalars.test.d.ts +0 -0
  143. package/{dist → lib}/test/scalars.test.d.ts.map +0 -0
  144. package/{dist → lib}/test/scalars.test.js +0 -0
  145. package/{dist → lib}/test/scalars.test.js.map +0 -0
  146. package/lib/test/setup.d.ts +17 -0
  147. package/lib/test/setup.d.ts.map +1 -0
  148. package/{dist → lib}/test/setup.js +18 -13
  149. package/lib/test/setup.js.map +1 -0
  150. package/lib/test/subscription.test.d.ts +2 -0
  151. package/lib/test/subscription.test.d.ts.map +1 -0
  152. package/lib/test/subscription.test.js +99 -0
  153. package/lib/test/subscription.test.js.map +1 -0
  154. package/{dist → lib}/test/tools.test.d.ts +0 -0
  155. package/{dist → lib}/test/tools.test.d.ts.map +0 -0
  156. package/{dist → lib}/test/tools.test.js +0 -0
  157. package/{dist → lib}/test/tools.test.js.map +0 -0
  158. package/{dist → lib}/test/typed-json.test.d.ts +0 -0
  159. package/{dist → lib}/test/typed-json.test.d.ts.map +0 -0
  160. package/{dist → lib}/test/typed-json.test.js +0 -0
  161. package/{dist → lib}/test/typed-json.test.js.map +0 -0
  162. package/{dist → lib}/test/unions.test.d.ts +0 -0
  163. package/{dist → lib}/test/unions.test.d.ts.map +0 -0
  164. package/{dist → lib}/test/unions.test.js +0 -0
  165. package/{dist → lib}/test/unions.test.js.map +0 -0
  166. package/{dist → lib}/test/where.test.d.ts +0 -0
  167. package/{dist → lib}/test/where.test.d.ts.map +0 -0
  168. package/{dist → lib}/test/where.test.js +0 -0
  169. package/{dist → lib}/test/where.test.js.map +0 -0
  170. package/{dist → lib}/tools.d.ts +0 -0
  171. package/{dist → lib}/tools.d.ts.map +0 -0
  172. package/{dist → lib}/tools.js +3 -3
  173. package/{dist → lib}/tools.js.map +1 -1
  174. package/lib/util/error-handling.d.ts +11 -0
  175. package/lib/util/error-handling.d.ts.map +1 -0
  176. package/lib/util/error-handling.js +42 -0
  177. package/lib/util/error-handling.js.map +1 -0
  178. package/lib/util/execute.d.ts +5 -0
  179. package/lib/util/execute.d.ts.map +1 -0
  180. package/lib/util/execute.js +28 -0
  181. package/lib/util/execute.js.map +1 -0
  182. package/lib/util/lazy-transaction.d.ts +10 -0
  183. package/lib/util/lazy-transaction.d.ts.map +1 -0
  184. package/lib/util/lazy-transaction.js +43 -0
  185. package/lib/util/lazy-transaction.js.map +1 -0
  186. package/lib/util/limit.d.ts +11 -0
  187. package/lib/util/limit.d.ts.map +1 -0
  188. package/lib/util/limit.js +39 -0
  189. package/lib/util/limit.js.map +1 -0
  190. package/lib/util/resolve-tree.d.ts +14 -0
  191. package/lib/util/resolve-tree.d.ts.map +1 -0
  192. package/lib/util/resolve-tree.js +52 -0
  193. package/lib/util/resolve-tree.js.map +1 -0
  194. package/{dist → lib/util}/util.d.ts +2 -3
  195. package/lib/util/util.d.ts.map +1 -0
  196. package/{dist → lib/util}/util.js +9 -13
  197. package/lib/util/util.js.map +1 -0
  198. package/package.json +18 -10
  199. package/src/context.ts +17 -0
  200. package/src/db.ts +46 -57
  201. package/src/ir/args.ts +85 -0
  202. package/src/ir/connection.ts +48 -0
  203. package/src/ir/fields.ts +40 -0
  204. package/src/limit.size.ts +130 -0
  205. package/src/main.ts +74 -42
  206. package/src/{gql/schema.ts → model.schema.ts} +72 -9
  207. package/src/model.ts +13 -1
  208. package/src/{orderBy.ts → opencrud/orderBy.ts} +3 -10
  209. package/src/opencrud/schema.ts +639 -0
  210. package/src/opencrud/tree.ts +144 -0
  211. package/src/opencrud/where.ts +141 -0
  212. package/src/{gql/scalars → scalars}/BigInt.ts +1 -1
  213. package/src/{gql/scalars → scalars}/Bytes.ts +4 -4
  214. package/src/{gql/scalars → scalars}/DateTime.ts +1 -1
  215. package/src/{gql/scalars → scalars}/JSON.ts +0 -0
  216. package/src/{gql/scalars → scalars}/index.ts +0 -0
  217. package/src/server.ts +175 -55
  218. package/src/sql/cursor.ts +291 -0
  219. package/src/sql/mapping.ts +66 -0
  220. package/src/sql/printer.ts +328 -0
  221. package/src/sql/query.ts +194 -0
  222. package/src/sql/util.ts +89 -0
  223. package/src/subscription.ts +46 -0
  224. package/src/test/fts.test.ts +1 -1
  225. package/src/test/limits.test.ts +163 -0
  226. package/src/test/setup.ts +16 -11
  227. package/src/test/subscription.test.ts +98 -0
  228. package/src/tools.ts +1 -1
  229. package/src/util/error-handling.ts +40 -0
  230. package/src/util/execute.ts +53 -0
  231. package/src/util/lazy-transaction.ts +49 -0
  232. package/src/util/limit.ts +34 -0
  233. package/src/util/resolve-tree.ts +65 -0
  234. package/src/{util.ts → util/util.ts} +9 -14
  235. package/dist/db.d.ts +0 -28
  236. package/dist/db.d.ts.map +0 -1
  237. package/dist/db.js +0 -69
  238. package/dist/db.js.map +0 -1
  239. package/dist/gql/opencrud.d.ts +0 -6
  240. package/dist/gql/opencrud.d.ts.map +0 -1
  241. package/dist/gql/opencrud.js +0 -326
  242. package/dist/gql/opencrud.js.map +0 -1
  243. package/dist/gql/scalars/BigInt.d.ts.map +0 -1
  244. package/dist/gql/scalars/BigInt.js.map +0 -1
  245. package/dist/gql/scalars/Bytes.d.ts.map +0 -1
  246. package/dist/gql/scalars/Bytes.js.map +0 -1
  247. package/dist/gql/scalars/DateTime.d.ts.map +0 -1
  248. package/dist/gql/scalars/DateTime.js.map +0 -1
  249. package/dist/gql/scalars/JSON.d.ts.map +0 -1
  250. package/dist/gql/scalars/JSON.js.map +0 -1
  251. package/dist/gql/scalars/index.d.ts.map +0 -1
  252. package/dist/gql/scalars/index.js.map +0 -1
  253. package/dist/gql/schema.d.ts.map +0 -1
  254. package/dist/gql/schema.js.map +0 -1
  255. package/dist/main.js +0 -44
  256. package/dist/main.js.map +0 -1
  257. package/dist/model.d.ts.map +0 -1
  258. package/dist/orderBy.d.ts.map +0 -1
  259. package/dist/orderBy.js.map +0 -1
  260. package/dist/queryBuilder.d.ts +0 -56
  261. package/dist/queryBuilder.d.ts.map +0 -1
  262. package/dist/queryBuilder.js +0 -733
  263. package/dist/queryBuilder.js.map +0 -1
  264. package/dist/relayConnection.d.ts +0 -37
  265. package/dist/relayConnection.d.ts.map +0 -1
  266. package/dist/relayConnection.js +0 -43
  267. package/dist/relayConnection.js.map +0 -1
  268. package/dist/requestedFields.d.ts +0 -33
  269. package/dist/requestedFields.d.ts.map +0 -1
  270. package/dist/requestedFields.js +0 -179
  271. package/dist/requestedFields.js.map +0 -1
  272. package/dist/resolver.d.ts +0 -9
  273. package/dist/resolver.d.ts.map +0 -1
  274. package/dist/resolver.js +0 -158
  275. package/dist/resolver.js.map +0 -1
  276. package/dist/server.d.ts +0 -22
  277. package/dist/server.d.ts.map +0 -1
  278. package/dist/server.js +0 -96
  279. package/dist/server.js.map +0 -1
  280. package/dist/test/fts.test.js.map +0 -1
  281. package/dist/test/setup.d.ts +0 -14
  282. package/dist/test/setup.d.ts.map +0 -1
  283. package/dist/test/setup.js.map +0 -1
  284. package/dist/util.d.ts.map +0 -1
  285. package/dist/util.js.map +0 -1
  286. package/dist/where.d.ts +0 -9
  287. package/dist/where.d.ts.map +0 -1
  288. package/dist/where.js +0 -101
  289. package/dist/where.js.map +0 -1
  290. package/src/gql/opencrud.ts +0 -350
  291. package/src/queryBuilder.ts +0 -891
  292. package/src/relayConnection.ts +0 -80
  293. package/src/requestedFields.ts +0 -246
  294. package/src/resolver.ts +0 -201
  295. package/src/where.ts +0 -119
@@ -0,0 +1,48 @@
1
+ import {OrderBy, Where} from "./args"
2
+ import {FieldRequest} from "./fields"
3
+
4
+
5
+ export interface RelayConnectionRequest {
6
+ orderBy: OrderBy
7
+ where?: Where
8
+ first?: number
9
+ after?: string
10
+ edgeNode?: FieldRequest[]
11
+ edgeCursor?: boolean
12
+ pageInfo?: boolean
13
+ totalCount?: boolean
14
+ }
15
+
16
+
17
+ export interface RelayConnectionResponse {
18
+ edges?: RelayConnectionEdge[]
19
+ pageInfo?: Partial<RelayConnectionPageInfo>
20
+ totalCount?: number
21
+ }
22
+
23
+
24
+ export interface RelayConnectionEdge {
25
+ node?: unknown
26
+ cursor?: string
27
+ }
28
+
29
+
30
+ export interface RelayConnectionPageInfo {
31
+ hasNextPage: boolean
32
+ hasPreviousPage: boolean
33
+ startCursor: string
34
+ endCursor: string
35
+ }
36
+
37
+
38
+ export function decodeRelayConnectionCursor(cursor: string): number | undefined {
39
+ if (!/^\d+$/.test(cursor)) return undefined
40
+ let val = parseInt(cursor, 10)
41
+ if (Number.isSafeInteger(val) && val > 0) return val
42
+ return undefined
43
+ }
44
+
45
+
46
+ export function encodeRelayConnectionCursor(val: number): string {
47
+ return '' + val
48
+ }
@@ -0,0 +1,40 @@
1
+ import {
2
+ EnumPropType,
3
+ FkPropType,
4
+ ListLookupPropType,
5
+ ListPropType,
6
+ LookupPropType,
7
+ ObjectPropType, Prop,
8
+ PropType,
9
+ ScalarPropType,
10
+ UnionPropType
11
+ } from "../model"
12
+ import {EntityListArguments} from "./args"
13
+
14
+
15
+ export type FieldRequest = EntityListRequest | ObjectRequest | OpaqueRequest
16
+
17
+
18
+ type Base<T> = T extends PropType ? {
19
+ field: string
20
+ kind: T['kind']
21
+ type: T
22
+ prop: Prop
23
+ aliases: string[]
24
+ ifType?: string
25
+ index: number
26
+ } : never
27
+
28
+
29
+ export type EntityListRequest = Base<ListLookupPropType> & {
30
+ children: FieldRequest[]
31
+ args?: EntityListArguments
32
+ }
33
+
34
+
35
+ export type ObjectRequest = Base<FkPropType | LookupPropType | ObjectPropType | UnionPropType> & {
36
+ children: FieldRequest[]
37
+ }
38
+
39
+
40
+ export type OpaqueRequest = Base<ScalarPropType | EnumPropType | ListPropType>
@@ -0,0 +1,130 @@
1
+ import {unexpectedCase} from '@subsquid/util-internal'
2
+ import {Where} from './ir/args'
3
+ import {RelayConnectionRequest} from './ir/connection'
4
+ import {FieldRequest} from './ir/fields'
5
+ import {Model} from './model'
6
+ import {getEntity} from './model.tools'
7
+
8
+
9
+ export function getSize(model: Model, fields: FieldRequest[]): number {
10
+ let total = 0
11
+ for (let req of fields) {
12
+ let size = getFieldSize(model, req)
13
+ if (Number.isFinite(size)) {
14
+ total += size * req.aliases.length
15
+ } else {
16
+ return Infinity
17
+ }
18
+ }
19
+ return total
20
+ }
21
+
22
+
23
+ function getFieldSize(model: Model, req: FieldRequest): number {
24
+ switch(req.kind) {
25
+ case "scalar":
26
+ case "list":
27
+ return req.prop.byteWeight || 1
28
+ case "enum":
29
+ return 1
30
+ case "object":
31
+ case "fk":
32
+ case "lookup":
33
+ case "union":
34
+ return getSize(model, req.children) + 1
35
+ case "list-lookup":
36
+ return getEntityListSize(
37
+ model,
38
+ req.type.entity,
39
+ req.children,
40
+ Math.min(req.args?.limit ?? Infinity, req.prop.cardinality ?? Infinity),
41
+ req.args?.where
42
+ ) + 1
43
+ default:
44
+ throw unexpectedCase()
45
+ }
46
+ }
47
+
48
+
49
+ export function getEntityListSize(
50
+ model: Model,
51
+ entityName: string,
52
+ fields: FieldRequest[],
53
+ limit?: number,
54
+ where?: Where
55
+ ): number {
56
+ let cardinality = Math.min(
57
+ getEntityCardinality(model, entityName),
58
+ limit ?? Infinity,
59
+ getWhereCardinality(where)
60
+ )
61
+ if (Number.isFinite(cardinality)) {
62
+ return cardinality * Math.max(getSize(model, fields), 1)
63
+ } else {
64
+ return Infinity
65
+ }
66
+ }
67
+
68
+
69
+ function getWhereCardinality(where?: Where): number {
70
+ if (where == null) return Infinity
71
+ switch(where.op) {
72
+ case 'AND': {
73
+ let min = Infinity
74
+ for (let co of where.args) {
75
+ min = Math.min(min, getWhereCardinality(co))
76
+ }
77
+ return min
78
+ }
79
+ case 'OR': {
80
+ if (where.args.length == 0) return Infinity
81
+ let max = 0
82
+ for (let co of where.args) {
83
+ max = Math.max(max, getWhereCardinality(co))
84
+ }
85
+ return max
86
+ }
87
+ case 'eq':
88
+ if (where.field == 'id') {
89
+ return 1
90
+ } else {
91
+ return Infinity
92
+ }
93
+ case 'in':
94
+ if (where.field == 'id') {
95
+ return where.values.length
96
+ } else {
97
+ return Infinity
98
+ }
99
+ default:
100
+ return Infinity
101
+ }
102
+ }
103
+
104
+
105
+ export function getRelaySize(model: Model, entityName: string, req: RelayConnectionRequest): number {
106
+ let total = 0
107
+ let limit = Math.min(
108
+ getEntityCardinality(model, entityName),
109
+ req.first ?? 100,
110
+ getWhereCardinality(req.where)
111
+ )
112
+ if (req.edgeNode) {
113
+ total += limit * Math.max(getSize(model, req.edgeNode), 1)
114
+ }
115
+ if (req.edgeCursor) {
116
+ total += limit
117
+ }
118
+ if (req.pageInfo) {
119
+ total += 4
120
+ }
121
+ if (req.totalCount) {
122
+ total += 1
123
+ }
124
+ return total
125
+ }
126
+
127
+
128
+ function getEntityCardinality(model: Model, entityName: string): number {
129
+ return getEntity(model, entityName).cardinality ?? Infinity
130
+ }
package/src/main.ts CHANGED
@@ -1,53 +1,85 @@
1
- import {Pool} from "pg"
2
- import {createPoolConfig} from "./db"
3
- import {Dialect} from "./dialect"
4
- import {serve} from "./server"
5
- import {loadModel} from "./tools"
1
+ import {createLogger} from '@subsquid/logger'
2
+ import {runProgram} from '@subsquid/util-internal'
3
+ import {nat, Url} from '@subsquid/util-internal-commander'
4
+ import {waitForInterruption} from '@subsquid/util-internal-http-server'
5
+ import {Command, Option} from 'commander'
6
+ import {Pool} from 'pg'
7
+ import {Dialect} from './dialect'
8
+ import {serve} from './server'
9
+ import {loadModel} from './tools'
6
10
 
7
11
 
8
- module.exports = function main() {
9
- let args = process.argv.slice(2)
12
+ const LOG = createLogger('sqd:openreader')
10
13
 
11
- if (args.indexOf('--help') >= 0) {
12
- help()
13
- process.exit(1)
14
- }
15
14
 
16
- if (args.length != 1) {
17
- help()
18
- process.exit(1)
19
- }
15
+ runProgram(async () => {
16
+ let program = new Command()
20
17
 
21
- let model = loadModel(args[0])
22
- let db = new Pool(createPoolConfig())
23
- let port = process.env.GRAPHQL_SERVER_PORT || 3000
24
- let dialect: Dialect = process.env.DB_TYPE == 'cockroach' ? 'cockroach' : 'postgres'
25
-
26
- serve({model, db, port, dialect}).then(
27
- () => {
28
- console.log('OpenReader is listening on port ' + port)
29
- },
30
- err => {
31
- console.error(err)
32
- process.exit(1)
33
- }
18
+ program.description(`
19
+ GraphQL server for postgres-compatible databases
20
+ `.trim())
21
+
22
+ program.requiredOption('-s, --schema <file>', 'a path to a file or folder with database description')
23
+ program.requiredOption('-d, --db-url <url>', 'database connection string', Url(['postgres:']))
24
+ program.addOption(
25
+ new Option('-t, --db-type <type>', 'database type').choices(['postgres', 'cockroach']).default('postgres')
34
26
  )
35
- }
27
+ program.option('-p, --port <number>', 'port to listen on', nat, 3000)
28
+ program.option('--max-request-size <kb>', 'max request size in kilobytes', nat, 256)
29
+ program.option('--max-root-fields <count>', 'max number of root fields in a query', nat)
30
+ program.option('--max-response-size <nodes>', 'max response size measured in nodes', nat)
31
+ program.option('--sql-statement-timeout <ms>', 'sql statement timeout in ms', nat)
32
+ program.option('--subscriptions', 'enable gql subscriptions')
33
+ program.option('--subscription-poll-interval <ms>', 'subscription poll interval in ms', nat, 1000)
34
+ program.option('--subscription-sql-statement-timeout <ms>', 'sql statement timeout for polling queries', nat)
35
+ program.option('--subscription-max-response-size <nodes>', 'max response size measured in nodes', nat)
36
36
 
37
+ let opts = program.parse().opts() as {
38
+ schema: string
39
+ dbUrl: string
40
+ dbType: Dialect
41
+ port: number
42
+ maxRequestSize: number
43
+ maxRootFields?: number
44
+ maxResponseSize?: number
45
+ sqlStatementTimeout?: number
46
+ subscriptions?: boolean
47
+ subscriptionPollInterval: number
48
+ subscriptionSqlStatementTimeout?: number
49
+ subscriptionMaxResponseSize?: number
50
+ }
37
51
 
38
- function help() {
39
- console.error(`
40
- Usage: openreader SCHEMA
52
+ let model = loadModel(opts.schema)
53
+
54
+ let connection = new Pool({
55
+ connectionString: opts.dbUrl,
56
+ statement_timeout: opts.sqlStatementTimeout || undefined
57
+ })
58
+
59
+ let subscriptionConnection: Pool | undefined
60
+ if (opts.subscriptions) {
61
+ subscriptionConnection = new Pool({
62
+ connectionString: opts.dbUrl,
63
+ statement_timeout: opts.subscriptionSqlStatementTimeout || opts.sqlStatementTimeout || undefined
64
+ })
65
+ }
41
66
 
42
- OpenCRUD GraphQL server.
67
+ let server = await serve({
68
+ model,
69
+ dialect: opts.dbType,
70
+ connection,
71
+ port: opts.port,
72
+ log: LOG,
73
+ maxRequestSizeBytes: opts.maxRequestSize * 1024,
74
+ maxRootFields: opts.maxRootFields,
75
+ maxResponseNodes: opts.maxResponseSize,
76
+ subscriptions: opts.subscriptions,
77
+ subscriptionPollInterval: opts.subscriptionPollInterval,
78
+ subscriptionConnection,
79
+ subscriptionMaxResponseNodes: opts.subscriptionMaxResponseSize
80
+ })
43
81
 
44
- Can be configured using the following environment variables:
82
+ LOG.info(`listening on port ${server.port}`)
45
83
 
46
- DB_NAME
47
- DB_USER
48
- DB_PASS
49
- DB_HOST
50
- DB_PORT
51
- GRAPHQL_SERVER_PORT
52
- `)
53
- }
84
+ return waitForInterruption(server)
85
+ }, err => LOG.fatal(err))
@@ -18,8 +18,8 @@ import {
18
18
  parse,
19
19
  validateSchema
20
20
  } from "graphql"
21
- import {Index, Model, Prop, PropType} from "../model"
22
- import {validateModel} from "../model.tools"
21
+ import {Index, Model, Prop, PropType, Scalar} from "./model"
22
+ import {validateModel} from "./model.tools"
23
23
  import {customScalars} from "./scalars"
24
24
 
25
25
 
@@ -29,6 +29,8 @@ const baseSchema = buildASTSchema(parse(`
29
29
  directive @unique on FIELD_DEFINITION
30
30
  directive @index(fields: [String!] unique: Boolean) on OBJECT | FIELD_DEFINITION
31
31
  directive @fulltext(query: String!) on FIELD_DEFINITION
32
+ directive @cardinality(value: Int!) on OBJECT | FIELD_DEFINITION
33
+ directive @byteWeight(value: Float!) on FIELD_DEFINITION
32
34
  directive @variant on OBJECT # legacy
33
35
  directive @jsonField on OBJECT # legacy
34
36
  scalar ID
@@ -75,11 +77,12 @@ function addEntityOrJsonObjectOrInterface(model: Model, type: GraphQLObjectType
75
77
  let properties: Record<string, Prop> = {}
76
78
  let interfaces: string[] = []
77
79
  let indexes: Index[] = type instanceof GraphQLObjectType ? checkEntityIndexes(type) : []
80
+ let cardinality = checkEntityCardinality(type)
78
81
  let description = type.description || undefined
79
82
 
80
83
  switch(kind) {
81
84
  case 'entity':
82
- model[type.name] = {kind, properties, description, interfaces, indexes}
85
+ model[type.name] = {kind, properties, description, interfaces, indexes, ...cardinality}
83
86
  break
84
87
  case 'object':
85
88
  model[type.name] = {kind, properties, description, interfaces}
@@ -120,6 +123,10 @@ function addEntityOrJsonObjectOrInterface(model: Model, type: GraphQLObjectType
120
123
  let derivedFrom = checkDerivedFrom(type, f)
121
124
  let index = checkFieldIndex(type, f)
122
125
  let unique = index?.unique || false
126
+ let limits = {
127
+ ...checkByteWeightDirective(type, f),
128
+ ...checkCardinalityLimitDirective(type, f)
129
+ }
123
130
 
124
131
  if (index) {
125
132
  indexes.push(index)
@@ -137,10 +144,11 @@ function addEntityOrJsonObjectOrInterface(model: Model, type: GraphQLObjectType
137
144
  properties[key] = {
138
145
  type: wrapWithList(list.nulls, {
139
146
  kind: 'scalar',
140
- name: fieldType.name
147
+ name: fieldType.name as Scalar
141
148
  }),
142
149
  nullable,
143
- description
150
+ description,
151
+ ...limits
144
152
  }
145
153
  } else if (fieldType instanceof GraphQLEnumType) {
146
154
  addEnum(model, fieldType)
@@ -150,7 +158,8 @@ function addEntityOrJsonObjectOrInterface(model: Model, type: GraphQLObjectType
150
158
  name: fieldType.name
151
159
  }),
152
160
  nullable,
153
- description
161
+ description,
162
+ ...limits
154
163
  }
155
164
  } else if (fieldType instanceof GraphQLUnionType) {
156
165
  addUnion(model, fieldType)
@@ -160,7 +169,8 @@ function addEntityOrJsonObjectOrInterface(model: Model, type: GraphQLObjectType
160
169
  name: fieldType.name
161
170
  }),
162
171
  nullable,
163
- description
172
+ description,
173
+ ...limits
164
174
  }
165
175
  } else if (fieldType instanceof GraphQLObjectType) {
166
176
  if (isEntityType(fieldType)) {
@@ -205,7 +215,8 @@ function addEntityOrJsonObjectOrInterface(model: Model, type: GraphQLObjectType
205
215
  field: derivedFrom.field
206
216
  },
207
217
  nullable: false,
208
- description
218
+ description,
219
+ ...limits
209
220
  }
210
221
  break
211
222
  default:
@@ -219,7 +230,8 @@ function addEntityOrJsonObjectOrInterface(model: Model, type: GraphQLObjectType
219
230
  name: fieldType.name
220
231
  }),
221
232
  nullable,
222
- description
233
+ description,
234
+ ...limits
223
235
  }
224
236
  }
225
237
  } else {
@@ -456,6 +468,57 @@ function checkDerivedFrom(type: GraphQLNamedType, f: GraphQLField<any, any>): {f
456
468
  }
457
469
 
458
470
 
471
+ function checkEntityCardinality(type: GraphQLObjectType | GraphQLInterfaceType): {cardinality?: number} {
472
+ let directives = type.astNode?.directives?.filter(d => d.name.value == 'cardinality') || []
473
+ if (directives.length > 0 && !isEntityType(type)) {
474
+ throw new SchemaError(`@cardinality directive can be only applied to entities, but were applied to ${type.name}`)
475
+ }
476
+ if (directives.length > 1) throw new SchemaError(
477
+ `Multiple @cardinality directives where applied to ${type.name}`
478
+ )
479
+ if (directives.length == 0) return {}
480
+ let arg = assertNotNull(directives[0].arguments?.find(arg => arg.name.value == 'value'))
481
+ assert(arg.value.kind == 'IntValue')
482
+ let cardinality = parseInt(arg.value.value, 10)
483
+ if (cardinality < 0) throw new SchemaError(
484
+ `Incorrect @cardinality where applied to ${type.name}. Cardinality value must be positive.`
485
+ )
486
+ return {cardinality}
487
+ }
488
+
489
+
490
+ function checkCardinalityLimitDirective(type: GraphQLNamedType, f: GraphQLField<any, any>): {cardinality?: number} {
491
+ let directives = f.astNode?.directives?.filter(d => d.name.value == 'cardinality') || []
492
+ if (directives.length > 1) throw new SchemaError(
493
+ `Multiple @cardinality directives where applied to ${type.name}.${f.name}`
494
+ )
495
+ if (directives.length == 0) return {}
496
+ let arg = assertNotNull(directives[0].arguments?.find(arg => arg.name.value == 'value'))
497
+ assert(arg.value.kind == 'IntValue')
498
+ let cardinality = parseInt(arg.value.value, 10)
499
+ if (cardinality < 0) throw new SchemaError(
500
+ `Incorrect @cardinality where applied to ${type.name}.${f.name}. Cardinality value must be positive.`
501
+ )
502
+ return {cardinality}
503
+ }
504
+
505
+
506
+ function checkByteWeightDirective(type: GraphQLNamedType, f: GraphQLField<any, any>): {byteWeight?: number} {
507
+ let directives = f.astNode?.directives?.filter(d => d.name.value == 'byteWeight') || []
508
+ if (directives.length > 1) throw new SchemaError(
509
+ `Multiple @byteWeight directives where applied to ${type.name}.${f.name}`
510
+ )
511
+ if (directives.length == 0) return {}
512
+ let arg = assertNotNull(directives[0].arguments?.find(arg => arg.name.value == 'value'))
513
+ assert(arg.value.kind == 'FloatValue')
514
+ let byteWeight = parseFloat(arg.value.value)
515
+ if (byteWeight < 0) throw new SchemaError(
516
+ `Incorrect @byteWeight where applied to ${type.name}.${f.name}. Byte weight value must be positive.`
517
+ )
518
+ return {byteWeight}
519
+ }
520
+
521
+
459
522
  function unsupportedFieldTypeError(propName: string): Error {
460
523
  return new SchemaError(`Property ${propName} has unsupported type`)
461
524
  }
package/src/model.ts CHANGED
@@ -9,6 +9,7 @@ export interface Entity extends TypeMeta {
9
9
  properties: Record<Name, Prop>
10
10
  interfaces?: Name[]
11
11
  indexes?: Index[]
12
+ cardinality?: number
12
13
  }
13
14
 
14
15
 
@@ -50,6 +51,14 @@ export interface Prop {
50
51
  * Whether the values in the column must be unique. Applicable only to entities.
51
52
  */
52
53
  unique?: boolean
54
+ /**
55
+ * Characteristic number of elements in a `list-lookup` or in a `list` of objects
56
+ */
57
+ cardinality?: number
58
+ /**
59
+ * Relative byte size of a scalar value or scalar list
60
+ */
61
+ byteWeight?: number
53
62
  }
54
63
 
55
64
 
@@ -66,10 +75,13 @@ export type PropType =
66
75
 
67
76
  export interface ScalarPropType {
68
77
  kind: 'scalar'
69
- name: Name
78
+ name: Scalar
70
79
  }
71
80
 
72
81
 
82
+ export type Scalar = 'ID' | 'String' | 'Int' | 'Float' | 'Boolean' | 'DateTime' | 'BigInt' | 'JSON' | 'Bytes'
83
+
84
+
73
85
  export interface EnumPropType {
74
86
  kind: 'enum'
75
87
  name: Name
@@ -1,14 +1,7 @@
1
1
  import assert from "assert"
2
- import type {Model} from "./model"
3
- import {getUnionProps} from "./model.tools"
4
-
5
-
6
- export type SortOrder = 'ASC' | 'DESC'
7
-
8
-
9
- export interface OrderBy {
10
- [field: string]: SortOrder | OrderBy
11
- }
2
+ import type {Model} from "../model"
3
+ import {getUnionProps} from "../model.tools"
4
+ import {OrderBy} from "../ir/args"
12
5
 
13
6
 
14
7
  /**