@ensnode/ponder-subgraph 0.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.
- package/LICENSE +21 -0
- package/README.md +3 -0
- package/dist/graphql.d.ts +65 -0
- package/dist/graphql.js +814 -0
- package/dist/graphql.js.map +1 -0
- package/dist/helpers.d.ts +4 -0
- package/dist/helpers.js +11 -0
- package/dist/helpers.js.map +1 -0
- package/dist/middleware.d.ts +20 -0
- package/dist/middleware.js +867 -0
- package/dist/middleware.js.map +1 -0
- package/package.json +58 -0
package/dist/graphql.js
ADDED
|
@@ -0,0 +1,814 @@
|
|
|
1
|
+
// src/graphql.ts
|
|
2
|
+
import DataLoader from "dataloader";
|
|
3
|
+
import {
|
|
4
|
+
Many,
|
|
5
|
+
One,
|
|
6
|
+
and,
|
|
7
|
+
arrayContained,
|
|
8
|
+
arrayContains,
|
|
9
|
+
asc,
|
|
10
|
+
createTableRelationsHelpers,
|
|
11
|
+
desc,
|
|
12
|
+
eq,
|
|
13
|
+
extractTablesRelationalConfig,
|
|
14
|
+
getTableColumns,
|
|
15
|
+
getTableUniqueName,
|
|
16
|
+
gt,
|
|
17
|
+
gte,
|
|
18
|
+
inArray,
|
|
19
|
+
is,
|
|
20
|
+
isNotNull,
|
|
21
|
+
isNull,
|
|
22
|
+
like,
|
|
23
|
+
lt,
|
|
24
|
+
lte,
|
|
25
|
+
ne,
|
|
26
|
+
not,
|
|
27
|
+
notInArray,
|
|
28
|
+
notLike,
|
|
29
|
+
or,
|
|
30
|
+
relations,
|
|
31
|
+
sql
|
|
32
|
+
} from "drizzle-orm";
|
|
33
|
+
import {
|
|
34
|
+
PgDialect,
|
|
35
|
+
PgEnumColumn,
|
|
36
|
+
PgInteger,
|
|
37
|
+
PgSerial,
|
|
38
|
+
isPgEnum,
|
|
39
|
+
pgTable
|
|
40
|
+
} from "drizzle-orm/pg-core";
|
|
41
|
+
import {
|
|
42
|
+
GraphQLBoolean,
|
|
43
|
+
GraphQLEnumType,
|
|
44
|
+
GraphQLFloat,
|
|
45
|
+
GraphQLInputObjectType,
|
|
46
|
+
GraphQLInt,
|
|
47
|
+
GraphQLInterfaceType,
|
|
48
|
+
GraphQLList,
|
|
49
|
+
GraphQLNonNull,
|
|
50
|
+
GraphQLObjectType,
|
|
51
|
+
GraphQLScalarType,
|
|
52
|
+
GraphQLSchema,
|
|
53
|
+
GraphQLString
|
|
54
|
+
} from "graphql";
|
|
55
|
+
import { GraphQLJSON } from "graphql-scalars";
|
|
56
|
+
|
|
57
|
+
// src/helpers.ts
|
|
58
|
+
var intersectionOf = (arrays) => arrays.reduce((a, b) => a.filter((c) => b.includes(c)));
|
|
59
|
+
var capitalize = (str) => {
|
|
60
|
+
if (!str) return str;
|
|
61
|
+
return `${str.charAt(0).toUpperCase()}${str.slice(1)}`;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// src/serialize.ts
|
|
65
|
+
function serialize(value) {
|
|
66
|
+
return JSON.stringify(
|
|
67
|
+
value,
|
|
68
|
+
(_, v) => typeof v === "bigint" ? { __type: "bigint", value: v.toString() } : v
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
function deserialize(value) {
|
|
72
|
+
return JSON.parse(
|
|
73
|
+
value,
|
|
74
|
+
(_, value_) => (value_ == null ? void 0 : value_.__type) === "bigint" ? BigInt(value_.value) : value_
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// src/graphql.ts
|
|
79
|
+
var onchain = Symbol.for("ponder:onchain");
|
|
80
|
+
var DEFAULT_LIMIT = 100;
|
|
81
|
+
var MAX_LIMIT = 1e3;
|
|
82
|
+
var OrderDirectionEnum = new GraphQLEnumType({
|
|
83
|
+
name: "OrderDirection",
|
|
84
|
+
values: {
|
|
85
|
+
asc: { value: "asc" },
|
|
86
|
+
desc: { value: "desc" }
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
function buildGraphQLSchema(_schema, polymorphicConfig = { types: {}, fields: {} }) {
|
|
90
|
+
const schema = { ..._schema };
|
|
91
|
+
const _tablesConfig = extractTablesRelationalConfig(schema, createTableRelationsHelpers);
|
|
92
|
+
const polymorphicTableConfigs = Object.fromEntries(
|
|
93
|
+
Object.entries(polymorphicConfig.types).map(([interfaceTypeName, implementingTables]) => [
|
|
94
|
+
interfaceTypeName,
|
|
95
|
+
implementingTables.map((table) => getTableUniqueName(table)).map((tableName) => _tablesConfig.tables[_tablesConfig.tableNamesMap[tableName]])
|
|
96
|
+
])
|
|
97
|
+
);
|
|
98
|
+
Object.assign(
|
|
99
|
+
schema,
|
|
100
|
+
...Object.keys(polymorphicConfig.types).map(
|
|
101
|
+
(interfaceTypeName) => getIntersectionTableSchema(interfaceTypeName, polymorphicTableConfigs[interfaceTypeName])
|
|
102
|
+
)
|
|
103
|
+
);
|
|
104
|
+
const polymorphicFields = Object.entries(polymorphicConfig.fields).map(([fieldPath, interfaceTypeName]) => [
|
|
105
|
+
fieldPath.split("."),
|
|
106
|
+
interfaceTypeName
|
|
107
|
+
]);
|
|
108
|
+
const tablesConfig = extractTablesRelationalConfig(schema, createTableRelationsHelpers);
|
|
109
|
+
const tables = Object.values(tablesConfig.tables);
|
|
110
|
+
const enums = Object.entries(schema).filter(
|
|
111
|
+
(el) => isPgEnum(el[1])
|
|
112
|
+
);
|
|
113
|
+
const enumTypes = {};
|
|
114
|
+
for (const [enumTsName, enumObject] of enums) {
|
|
115
|
+
enumTypes[enumObject.enumName] = new GraphQLEnumType({
|
|
116
|
+
name: enumTsName,
|
|
117
|
+
values: enumObject.enumValues.reduce(
|
|
118
|
+
(acc, cur) => ({ ...acc, [cur]: {} }),
|
|
119
|
+
{}
|
|
120
|
+
)
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
const entityOrderByEnums = {};
|
|
124
|
+
for (const table of tables) {
|
|
125
|
+
const values = Object.keys(table.columns).reduce(
|
|
126
|
+
(acc, columnName) => ({
|
|
127
|
+
...acc,
|
|
128
|
+
[columnName]: { value: columnName }
|
|
129
|
+
}),
|
|
130
|
+
{}
|
|
131
|
+
);
|
|
132
|
+
entityOrderByEnums[table.tsName] = new GraphQLEnumType({
|
|
133
|
+
name: `${getSubgraphEntityName(table.tsName)}_orderBy`,
|
|
134
|
+
values
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
const entityFilterTypes = {};
|
|
138
|
+
for (const table of tables) {
|
|
139
|
+
const filterType = new GraphQLInputObjectType({
|
|
140
|
+
name: `${getSubgraphEntityName(table.tsName)}_filter`,
|
|
141
|
+
fields: () => {
|
|
142
|
+
const filterFields = {
|
|
143
|
+
// Logical operators
|
|
144
|
+
// NOTE: lower case and/or
|
|
145
|
+
and: { type: new GraphQLList(filterType) },
|
|
146
|
+
or: { type: new GraphQLList(filterType) }
|
|
147
|
+
};
|
|
148
|
+
for (const [columnName, column] of Object.entries(table.columns)) {
|
|
149
|
+
const type = columnToGraphQLCore(column, enumTypes);
|
|
150
|
+
if (type instanceof GraphQLList) {
|
|
151
|
+
const baseType = innerType(type);
|
|
152
|
+
conditionSuffixes.universal.forEach((suffix) => {
|
|
153
|
+
filterFields[`${columnName}${suffix}`] = {
|
|
154
|
+
type: new GraphQLList(baseType)
|
|
155
|
+
};
|
|
156
|
+
});
|
|
157
|
+
conditionSuffixes.plural.forEach((suffix) => {
|
|
158
|
+
filterFields[`${columnName}${suffix}`] = { type: baseType };
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
if (type instanceof GraphQLScalarType || type instanceof GraphQLEnumType) {
|
|
162
|
+
if (type.name === "JSON") continue;
|
|
163
|
+
conditionSuffixes.universal.forEach((suffix) => {
|
|
164
|
+
filterFields[`${columnName}${suffix}`] = {
|
|
165
|
+
type
|
|
166
|
+
};
|
|
167
|
+
});
|
|
168
|
+
conditionSuffixes.singular.forEach((suffix) => {
|
|
169
|
+
filterFields[`${columnName}${suffix}`] = {
|
|
170
|
+
type: new GraphQLList(type)
|
|
171
|
+
};
|
|
172
|
+
});
|
|
173
|
+
if (["String", "ID"].includes(type.name)) {
|
|
174
|
+
conditionSuffixes.string.forEach((suffix) => {
|
|
175
|
+
filterFields[`${columnName}${suffix}`] = {
|
|
176
|
+
type
|
|
177
|
+
};
|
|
178
|
+
});
|
|
179
|
+
conditionSuffixes.numeric.forEach((suffix) => {
|
|
180
|
+
filterFields[`${columnName}${suffix}`] = {
|
|
181
|
+
type
|
|
182
|
+
};
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
if (["Int", "Float", "BigInt"].includes(type.name)) {
|
|
186
|
+
conditionSuffixes.numeric.forEach((suffix) => {
|
|
187
|
+
filterFields[`${columnName}${suffix}`] = {
|
|
188
|
+
type
|
|
189
|
+
};
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
for (const [relationName, relation] of Object.entries(table.relations)) {
|
|
195
|
+
if (is(relation, One)) {
|
|
196
|
+
conditionSuffixes.universal.forEach((suffix) => {
|
|
197
|
+
filterFields[`${relation.fieldName}${suffix}`] = {
|
|
198
|
+
type: GraphQLString
|
|
199
|
+
};
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return filterFields;
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
entityFilterTypes[table.tsName] = filterType;
|
|
207
|
+
}
|
|
208
|
+
const entityTypes = {};
|
|
209
|
+
const interfaceTypes = {};
|
|
210
|
+
const entityPageTypes = {};
|
|
211
|
+
for (const interfaceTypeName of Object.keys(polymorphicTableConfigs)) {
|
|
212
|
+
const table = tablesConfig.tables[interfaceTypeName];
|
|
213
|
+
interfaceTypes[interfaceTypeName] = new GraphQLInterfaceType({
|
|
214
|
+
name: interfaceTypeName,
|
|
215
|
+
fields: () => {
|
|
216
|
+
const fieldConfigMap = {};
|
|
217
|
+
for (const [columnName, column] of Object.entries(table.columns)) {
|
|
218
|
+
const type = columnToGraphQLCore(column, enumTypes);
|
|
219
|
+
fieldConfigMap[columnName] = {
|
|
220
|
+
type: column.notNull ? new GraphQLNonNull(type) : type
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
return fieldConfigMap;
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
for (const table of tables) {
|
|
228
|
+
if (isInterfaceType(polymorphicConfig, table.tsName)) continue;
|
|
229
|
+
const entityTypeName = getSubgraphEntityName(table.tsName);
|
|
230
|
+
entityTypes[table.tsName] = new GraphQLObjectType({
|
|
231
|
+
name: entityTypeName,
|
|
232
|
+
interfaces: Object.entries(polymorphicTableConfigs).filter(
|
|
233
|
+
([, implementingTables]) => implementingTables.map((table2) => table2.tsName).includes(table.tsName)
|
|
234
|
+
).map(([interfaceTypeName]) => interfaceTypes[interfaceTypeName]),
|
|
235
|
+
fields: () => {
|
|
236
|
+
var _a, _b, _c, _d;
|
|
237
|
+
const fieldConfigMap = {};
|
|
238
|
+
for (const [columnName, column] of Object.entries(table.columns)) {
|
|
239
|
+
const type = columnToGraphQLCore(column, enumTypes);
|
|
240
|
+
fieldConfigMap[columnName] = {
|
|
241
|
+
type: column.notNull ? new GraphQLNonNull(type) : type
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
const relations2 = Object.entries(table.relations);
|
|
245
|
+
for (const [relationName, relation] of relations2) {
|
|
246
|
+
const referencedTable = tables.find(
|
|
247
|
+
(table2) => table2.dbName === relation.referencedTableName
|
|
248
|
+
);
|
|
249
|
+
if (!referencedTable)
|
|
250
|
+
throw new Error(
|
|
251
|
+
`Internal error: Referenced table "${relation.referencedTableName}" not found`
|
|
252
|
+
);
|
|
253
|
+
const referencedEntityType = entityTypes[referencedTable.tsName];
|
|
254
|
+
const referencedEntityPageType = entityPageTypes[referencedTable.tsName];
|
|
255
|
+
const referencedEntityFilterType = entityFilterTypes[referencedTable.tsName];
|
|
256
|
+
if (referencedEntityType === void 0 || referencedEntityPageType === void 0 || referencedEntityFilterType === void 0)
|
|
257
|
+
throw new Error(
|
|
258
|
+
`Internal error: Referenced entity types not found for table "${referencedTable.tsName}" `
|
|
259
|
+
);
|
|
260
|
+
if (is(relation, One)) {
|
|
261
|
+
const fields = ((_a = relation.config) == null ? void 0 : _a.fields) ?? [];
|
|
262
|
+
const references = ((_b = relation.config) == null ? void 0 : _b.references) ?? [];
|
|
263
|
+
if (fields.length !== references.length) {
|
|
264
|
+
throw new Error(
|
|
265
|
+
"Internal error: Fields and references arrays must be the same length"
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
fieldConfigMap[relationName] = {
|
|
269
|
+
// Note: There is a `relation.isNullable` field here but it appears
|
|
270
|
+
// to be internal / incorrect. Until we have support for foriegn
|
|
271
|
+
// key constraints, all `one` relations must be nullable.
|
|
272
|
+
type: referencedEntityType,
|
|
273
|
+
resolve: (parent, _args, context) => {
|
|
274
|
+
const loader = context.getDataLoader({ table: referencedTable });
|
|
275
|
+
const rowFragment = {};
|
|
276
|
+
for (let i = 0; i < references.length; i++) {
|
|
277
|
+
const referenceColumn = references[i];
|
|
278
|
+
const fieldColumn = fields[i];
|
|
279
|
+
const fieldColumnTsName = getColumnTsName(fieldColumn);
|
|
280
|
+
const referenceColumnTsName = getColumnTsName(referenceColumn);
|
|
281
|
+
rowFragment[referenceColumnTsName] = parent[fieldColumnTsName];
|
|
282
|
+
}
|
|
283
|
+
const encodedId = encodeRowFragment(rowFragment);
|
|
284
|
+
return loader.load(encodedId);
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
} else if (is(relation, Many)) {
|
|
288
|
+
const oneRelation = Object.values(referencedTable.relations).find(
|
|
289
|
+
(relation2) => relation2.relationName === relationName || is(relation2, One) && relation2.referencedTableName === table.dbName
|
|
290
|
+
);
|
|
291
|
+
if (!oneRelation)
|
|
292
|
+
throw new Error(
|
|
293
|
+
`Internal error: Relation "${relationName}" not found in table "${referencedTable.tsName}"`
|
|
294
|
+
);
|
|
295
|
+
const fields = ((_c = oneRelation.config) == null ? void 0 : _c.fields) ?? [];
|
|
296
|
+
const references = ((_d = oneRelation.config) == null ? void 0 : _d.references) ?? [];
|
|
297
|
+
const referencedEntityOrderByType = entityOrderByEnums[referencedTable.tsName];
|
|
298
|
+
if (!referencedEntityOrderByType)
|
|
299
|
+
throw new Error(`Entity_orderBy Enum not found for ${referencedTable.tsName}`);
|
|
300
|
+
fieldConfigMap[relationName] = {
|
|
301
|
+
type: referencedEntityPageType,
|
|
302
|
+
args: {
|
|
303
|
+
where: { type: referencedEntityFilterType },
|
|
304
|
+
orderBy: { type: referencedEntityOrderByType },
|
|
305
|
+
orderDirection: { type: OrderDirectionEnum },
|
|
306
|
+
first: { type: GraphQLInt },
|
|
307
|
+
skip: { type: GraphQLInt }
|
|
308
|
+
},
|
|
309
|
+
resolve: (parent, args, context, info) => {
|
|
310
|
+
const relationalConditions = [];
|
|
311
|
+
for (let i = 0; i < references.length; i++) {
|
|
312
|
+
const column = fields[i];
|
|
313
|
+
const value = parent[references[i].name];
|
|
314
|
+
relationalConditions.push(eq(column, value));
|
|
315
|
+
}
|
|
316
|
+
return executePluralQuery(
|
|
317
|
+
referencedTable,
|
|
318
|
+
schema[referencedTable.tsName],
|
|
319
|
+
context.drizzle,
|
|
320
|
+
args,
|
|
321
|
+
relationalConditions
|
|
322
|
+
);
|
|
323
|
+
}
|
|
324
|
+
};
|
|
325
|
+
} else {
|
|
326
|
+
throw new Error(
|
|
327
|
+
`Internal error: Relation "${relationName}" is unsupported, expected One or Many`
|
|
328
|
+
);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
polymorphicFields.filter(([[parent]]) => parent === entityTypeName).forEach(([[, fieldName], interfaceTypeName]) => {
|
|
332
|
+
fieldConfigMap[fieldName] = definePolymorphicPluralField({
|
|
333
|
+
schema,
|
|
334
|
+
interfaceType: interfaceTypes[interfaceTypeName],
|
|
335
|
+
filterType: entityFilterTypes[interfaceTypeName],
|
|
336
|
+
orderByType: entityOrderByEnums[interfaceTypeName],
|
|
337
|
+
intersectionTableConfig: tablesConfig.tables[interfaceTypeName],
|
|
338
|
+
implementingTableConfigs: polymorphicTableConfigs[interfaceTypeName]
|
|
339
|
+
});
|
|
340
|
+
});
|
|
341
|
+
return fieldConfigMap;
|
|
342
|
+
}
|
|
343
|
+
});
|
|
344
|
+
entityPageTypes[table.tsName] = new GraphQLNonNull(
|
|
345
|
+
new GraphQLList(new GraphQLNonNull(entityTypes[table.tsName]))
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
const queryFields = {};
|
|
349
|
+
for (const table of tables) {
|
|
350
|
+
if (isInterfaceType(polymorphicConfig, table.tsName)) continue;
|
|
351
|
+
const entityType = entityTypes[table.tsName];
|
|
352
|
+
const entityPageType = entityPageTypes[table.tsName];
|
|
353
|
+
const entityFilterType = entityFilterTypes[table.tsName];
|
|
354
|
+
const singularFieldName = table.tsName.charAt(0).toLowerCase() + table.tsName.slice(1);
|
|
355
|
+
const pluralFieldName = `${singularFieldName}s`;
|
|
356
|
+
queryFields[singularFieldName] = {
|
|
357
|
+
type: entityType,
|
|
358
|
+
// Find the primary key columns and GraphQL core types and include them
|
|
359
|
+
// as arguments to the singular query type.
|
|
360
|
+
args: Object.fromEntries(
|
|
361
|
+
table.primaryKey.map((column) => [
|
|
362
|
+
getColumnTsName(column),
|
|
363
|
+
{
|
|
364
|
+
type: new GraphQLNonNull(columnToGraphQLCore(column, enumTypes))
|
|
365
|
+
}
|
|
366
|
+
])
|
|
367
|
+
),
|
|
368
|
+
resolve: async (_parent, args, context) => {
|
|
369
|
+
const loader = context.getDataLoader({ table });
|
|
370
|
+
const encodedId = encodeRowFragment(args);
|
|
371
|
+
return loader.load(encodedId);
|
|
372
|
+
}
|
|
373
|
+
};
|
|
374
|
+
const entityOrderByType = entityOrderByEnums[table.tsName];
|
|
375
|
+
if (!entityOrderByType) throw new Error(`Entity_orderBy Enum not found for ${table.tsName}`);
|
|
376
|
+
queryFields[pluralFieldName] = {
|
|
377
|
+
type: entityPageType,
|
|
378
|
+
args: {
|
|
379
|
+
where: { type: entityFilterType },
|
|
380
|
+
orderBy: { type: entityOrderByType },
|
|
381
|
+
orderDirection: { type: OrderDirectionEnum },
|
|
382
|
+
first: { type: GraphQLInt },
|
|
383
|
+
skip: { type: GraphQLInt }
|
|
384
|
+
},
|
|
385
|
+
resolve: async (_parent, args, context, info) => {
|
|
386
|
+
return executePluralQuery(table, schema[table.tsName], context.drizzle, args);
|
|
387
|
+
}
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
polymorphicFields.filter(([[parent]]) => parent === "Query").forEach(([[, fieldName], interfaceTypeName]) => {
|
|
391
|
+
queryFields[fieldName] = definePolymorphicPluralField({
|
|
392
|
+
schema,
|
|
393
|
+
interfaceType: interfaceTypes[interfaceTypeName],
|
|
394
|
+
filterType: entityFilterTypes[interfaceTypeName],
|
|
395
|
+
orderByType: entityOrderByEnums[interfaceTypeName],
|
|
396
|
+
intersectionTableConfig: tablesConfig.tables[interfaceTypeName],
|
|
397
|
+
implementingTableConfigs: polymorphicTableConfigs[interfaceTypeName]
|
|
398
|
+
});
|
|
399
|
+
});
|
|
400
|
+
return new GraphQLSchema({
|
|
401
|
+
// Include these here so they are listed first in the printed schema.
|
|
402
|
+
types: [GraphQLJSON, GraphQLBigInt, GraphQLPageInfo],
|
|
403
|
+
query: new GraphQLObjectType({
|
|
404
|
+
name: "Query",
|
|
405
|
+
fields: queryFields
|
|
406
|
+
})
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
var GraphQLPageInfo = new GraphQLObjectType({
|
|
410
|
+
name: "PageInfo",
|
|
411
|
+
fields: {
|
|
412
|
+
hasNextPage: { type: new GraphQLNonNull(GraphQLBoolean) },
|
|
413
|
+
hasPreviousPage: { type: new GraphQLNonNull(GraphQLBoolean) },
|
|
414
|
+
startCursor: { type: GraphQLString },
|
|
415
|
+
endCursor: { type: GraphQLString }
|
|
416
|
+
}
|
|
417
|
+
});
|
|
418
|
+
var GraphQLBigInt = new GraphQLScalarType({
|
|
419
|
+
name: "BigInt",
|
|
420
|
+
serialize: (value) => String(value),
|
|
421
|
+
parseValue: (value) => BigInt(value),
|
|
422
|
+
parseLiteral: (value) => {
|
|
423
|
+
if (value.kind === "StringValue") {
|
|
424
|
+
return BigInt(value.value);
|
|
425
|
+
} else {
|
|
426
|
+
throw new Error(
|
|
427
|
+
`Invalid value kind provided for field of type BigInt: ${value.kind}. Expected: StringValue`
|
|
428
|
+
);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
});
|
|
432
|
+
var columnToGraphQLCore = (column, enumTypes) => {
|
|
433
|
+
if (column.columnType === "PgEvmBigint") {
|
|
434
|
+
return GraphQLBigInt;
|
|
435
|
+
}
|
|
436
|
+
if (column instanceof PgEnumColumn) {
|
|
437
|
+
if (column.enum === void 0) {
|
|
438
|
+
throw new Error(
|
|
439
|
+
`Internal error: Expected enum column "${getColumnTsName(column)}" to have an "enum" property`
|
|
440
|
+
);
|
|
441
|
+
}
|
|
442
|
+
const enumType = enumTypes[column.enum.enumName];
|
|
443
|
+
if (enumType === void 0) {
|
|
444
|
+
throw new Error(
|
|
445
|
+
`Internal error: Expected to find a GraphQL enum named "${column.enum.enumName}"`
|
|
446
|
+
);
|
|
447
|
+
}
|
|
448
|
+
return enumType;
|
|
449
|
+
}
|
|
450
|
+
switch (column.dataType) {
|
|
451
|
+
case "boolean":
|
|
452
|
+
return GraphQLBoolean;
|
|
453
|
+
case "json":
|
|
454
|
+
return GraphQLJSON;
|
|
455
|
+
case "date":
|
|
456
|
+
return GraphQLString;
|
|
457
|
+
case "string":
|
|
458
|
+
return GraphQLString;
|
|
459
|
+
case "bigint":
|
|
460
|
+
return GraphQLString;
|
|
461
|
+
case "number":
|
|
462
|
+
return is(column, PgInteger) || is(column, PgSerial) ? GraphQLInt : GraphQLFloat;
|
|
463
|
+
case "buffer":
|
|
464
|
+
return new GraphQLList(new GraphQLNonNull(GraphQLInt));
|
|
465
|
+
case "array": {
|
|
466
|
+
if (column.columnType === "PgVector") {
|
|
467
|
+
return new GraphQLList(new GraphQLNonNull(GraphQLFloat));
|
|
468
|
+
}
|
|
469
|
+
if (column.columnType === "PgGeometry") {
|
|
470
|
+
return new GraphQLList(new GraphQLNonNull(GraphQLFloat));
|
|
471
|
+
}
|
|
472
|
+
const innerType2 = columnToGraphQLCore(column.baseColumn, enumTypes);
|
|
473
|
+
return new GraphQLList(new GraphQLNonNull(innerType2));
|
|
474
|
+
}
|
|
475
|
+
default:
|
|
476
|
+
throw new Error(`Type ${column.dataType} is not implemented`);
|
|
477
|
+
}
|
|
478
|
+
};
|
|
479
|
+
var innerType = (type) => {
|
|
480
|
+
if (type instanceof GraphQLScalarType || type instanceof GraphQLEnumType) return type;
|
|
481
|
+
if (type instanceof GraphQLList || type instanceof GraphQLNonNull) return innerType(type.ofType);
|
|
482
|
+
throw new Error(`Type ${type.toString()} is not implemented`);
|
|
483
|
+
};
|
|
484
|
+
async function executePluralQuery(table, from, drizzle, args, extraConditions = []) {
|
|
485
|
+
const limit = args.first ?? DEFAULT_LIMIT;
|
|
486
|
+
if (limit > MAX_LIMIT) {
|
|
487
|
+
throw new Error(`Invalid limit. Got ${limit}, expected <=${MAX_LIMIT}.`);
|
|
488
|
+
}
|
|
489
|
+
const skip = args.skip ?? 0;
|
|
490
|
+
const orderBySchema = buildOrderBySchema(table, args);
|
|
491
|
+
const orderBy = orderBySchema.map(([columnName, direction]) => {
|
|
492
|
+
const column = table.columns[columnName];
|
|
493
|
+
if (column === void 0) {
|
|
494
|
+
throw new Error(`Unknown column "${columnName}" used in orderBy argument`);
|
|
495
|
+
}
|
|
496
|
+
return direction === "asc" ? asc(column) : desc(column);
|
|
497
|
+
});
|
|
498
|
+
const whereConditions = buildWhereConditions(args.where, table);
|
|
499
|
+
const query = drizzle.select().from(from).where(and(...whereConditions, ...extraConditions)).orderBy(...orderBy).limit(limit).offset(skip);
|
|
500
|
+
const startTime = performance.now();
|
|
501
|
+
const rows = await query;
|
|
502
|
+
const queryDurationSeconds = (performance.now() - startTime) / 1e3;
|
|
503
|
+
const isSlowQuery = queryDurationSeconds > 2;
|
|
504
|
+
if (isSlowQuery) {
|
|
505
|
+
console.warn(`Slow Query Detected (${queryDurationSeconds.toFixed(4)}s)`);
|
|
506
|
+
console.warn(new PgDialect().sqlToQuery(query.getSQL()).sql);
|
|
507
|
+
console.log("\n");
|
|
508
|
+
}
|
|
509
|
+
return rows;
|
|
510
|
+
}
|
|
511
|
+
var conditionSuffixes = {
|
|
512
|
+
universal: ["", "_not"],
|
|
513
|
+
singular: ["_in", "_not_in"],
|
|
514
|
+
plural: ["_has", "_not_has"],
|
|
515
|
+
numeric: ["_gt", "_lt", "_gte", "_lte"],
|
|
516
|
+
string: [
|
|
517
|
+
"_contains",
|
|
518
|
+
"_not_contains",
|
|
519
|
+
"_starts_with",
|
|
520
|
+
"_ends_with",
|
|
521
|
+
"_not_starts_with",
|
|
522
|
+
"_not_ends_with"
|
|
523
|
+
]
|
|
524
|
+
};
|
|
525
|
+
var conditionSuffixesByLengthDesc = Object.values(conditionSuffixes).flat().sort((a, b) => b.length - a.length);
|
|
526
|
+
function buildWhereConditions(where, table) {
|
|
527
|
+
const conditions = [];
|
|
528
|
+
if (where === void 0) return conditions;
|
|
529
|
+
for (const [whereKey, rawValue] of Object.entries(where)) {
|
|
530
|
+
if (whereKey === "and" || whereKey === "or") {
|
|
531
|
+
if (!Array.isArray(rawValue)) {
|
|
532
|
+
throw new Error(
|
|
533
|
+
`Invalid query: Expected an array for the ${whereKey} operator. Got: ${rawValue}`
|
|
534
|
+
);
|
|
535
|
+
}
|
|
536
|
+
const nestedConditions = rawValue.flatMap(
|
|
537
|
+
(subWhere) => buildWhereConditions(subWhere, table)
|
|
538
|
+
);
|
|
539
|
+
if (nestedConditions.length > 0) {
|
|
540
|
+
conditions.push(whereKey === "and" ? and(...nestedConditions) : or(...nestedConditions));
|
|
541
|
+
}
|
|
542
|
+
continue;
|
|
543
|
+
}
|
|
544
|
+
const conditionSuffix = conditionSuffixesByLengthDesc.find((s) => whereKey.endsWith(s));
|
|
545
|
+
if (conditionSuffix === void 0) {
|
|
546
|
+
throw new Error(`Invariant violation: Condition suffix not found for where key ${whereKey}`);
|
|
547
|
+
}
|
|
548
|
+
const columnName = whereKey.slice(0, whereKey.length - conditionSuffix.length);
|
|
549
|
+
const column = columnName in table.relations ? (
|
|
550
|
+
// if the referenced name is a relation, the relevant column is this table's `${relationName}Id`
|
|
551
|
+
table.columns[`${columnName}Id`]
|
|
552
|
+
) : (
|
|
553
|
+
// otherwise validate that the column name is present in the table
|
|
554
|
+
table.columns[columnName]
|
|
555
|
+
);
|
|
556
|
+
if (column === void 0) {
|
|
557
|
+
throw new Error(`Invalid query: Where clause contains unknown column ${columnName}`);
|
|
558
|
+
}
|
|
559
|
+
switch (conditionSuffix) {
|
|
560
|
+
case "":
|
|
561
|
+
if (column.columnType === "PgArray") {
|
|
562
|
+
conditions.push(and(arrayContains(column, rawValue), arrayContained(column, rawValue)));
|
|
563
|
+
} else {
|
|
564
|
+
if (rawValue === null) {
|
|
565
|
+
conditions.push(isNull(column));
|
|
566
|
+
} else {
|
|
567
|
+
conditions.push(eq(column, rawValue));
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
break;
|
|
571
|
+
case "_not":
|
|
572
|
+
if (column.columnType === "PgArray") {
|
|
573
|
+
conditions.push(
|
|
574
|
+
not(and(arrayContains(column, rawValue), arrayContained(column, rawValue)))
|
|
575
|
+
);
|
|
576
|
+
} else {
|
|
577
|
+
if (rawValue === null) {
|
|
578
|
+
conditions.push(isNotNull(column));
|
|
579
|
+
} else {
|
|
580
|
+
conditions.push(ne(column, rawValue));
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
break;
|
|
584
|
+
case "_in":
|
|
585
|
+
conditions.push(inArray(column, rawValue));
|
|
586
|
+
break;
|
|
587
|
+
case "_not_in":
|
|
588
|
+
conditions.push(notInArray(column, rawValue));
|
|
589
|
+
break;
|
|
590
|
+
case "_has":
|
|
591
|
+
conditions.push(arrayContains(column, [rawValue]));
|
|
592
|
+
break;
|
|
593
|
+
case "_not_has":
|
|
594
|
+
conditions.push(not(arrayContains(column, [rawValue])));
|
|
595
|
+
break;
|
|
596
|
+
case "_gt":
|
|
597
|
+
conditions.push(gt(column, rawValue));
|
|
598
|
+
break;
|
|
599
|
+
case "_lt":
|
|
600
|
+
conditions.push(lt(column, rawValue));
|
|
601
|
+
break;
|
|
602
|
+
case "_gte":
|
|
603
|
+
conditions.push(gte(column, rawValue));
|
|
604
|
+
break;
|
|
605
|
+
case "_lte":
|
|
606
|
+
conditions.push(lte(column, rawValue));
|
|
607
|
+
break;
|
|
608
|
+
case "_contains":
|
|
609
|
+
conditions.push(like(column, `%${rawValue}%`));
|
|
610
|
+
break;
|
|
611
|
+
case "_not_contains":
|
|
612
|
+
conditions.push(notLike(column, `%${rawValue}%`));
|
|
613
|
+
break;
|
|
614
|
+
case "_starts_with":
|
|
615
|
+
conditions.push(like(column, `${rawValue}%`));
|
|
616
|
+
break;
|
|
617
|
+
case "_ends_with":
|
|
618
|
+
conditions.push(like(column, `%${rawValue}`));
|
|
619
|
+
break;
|
|
620
|
+
case "_not_starts_with":
|
|
621
|
+
conditions.push(notLike(column, `${rawValue}%`));
|
|
622
|
+
break;
|
|
623
|
+
case "_not_ends_with":
|
|
624
|
+
conditions.push(notLike(column, `%${rawValue}`));
|
|
625
|
+
break;
|
|
626
|
+
default:
|
|
627
|
+
throw new Error(`Invalid Condition Suffix ${conditionSuffix}`);
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
return conditions;
|
|
631
|
+
}
|
|
632
|
+
function buildOrderBySchema(table, args) {
|
|
633
|
+
const userDirection = args.orderDirection ?? "asc";
|
|
634
|
+
const userColumns = args.orderBy !== void 0 ? [[args.orderBy, userDirection]] : [];
|
|
635
|
+
const pkColumns = table.primaryKey.map((column) => [getColumnTsName(column), userDirection]);
|
|
636
|
+
const missingPkColumns = pkColumns.filter(
|
|
637
|
+
(pkColumn) => !userColumns.some((userColumn) => userColumn[0] === pkColumn[0])
|
|
638
|
+
);
|
|
639
|
+
return [...userColumns, ...missingPkColumns];
|
|
640
|
+
}
|
|
641
|
+
function buildDataLoaderCache({ drizzle }) {
|
|
642
|
+
const dataLoaderMap = /* @__PURE__ */ new Map();
|
|
643
|
+
return ({ table }) => {
|
|
644
|
+
const baseQuery = drizzle.query[table.tsName];
|
|
645
|
+
if (baseQuery === void 0)
|
|
646
|
+
throw new Error(`Internal error: Unknown table "${table.tsName}" in data loader cache`);
|
|
647
|
+
let dataLoader = dataLoaderMap.get(table);
|
|
648
|
+
if (dataLoader === void 0) {
|
|
649
|
+
dataLoader = new DataLoader(
|
|
650
|
+
async (encodedIds) => {
|
|
651
|
+
const decodedRowFragments = encodedIds.map(decodeRowFragment);
|
|
652
|
+
const idConditions = decodedRowFragments.map(
|
|
653
|
+
(decodedRowFragment) => and(...buildWhereConditions(decodedRowFragment, table))
|
|
654
|
+
);
|
|
655
|
+
const rows = await baseQuery.findMany({
|
|
656
|
+
where: or(...idConditions),
|
|
657
|
+
limit: encodedIds.length
|
|
658
|
+
});
|
|
659
|
+
return decodedRowFragments.map(
|
|
660
|
+
(fragment) => rows.find(
|
|
661
|
+
(row) => Object.entries(fragment).every(([col, val]) => row[col] === val)
|
|
662
|
+
)
|
|
663
|
+
);
|
|
664
|
+
},
|
|
665
|
+
{ maxBatchSize: 1e3 }
|
|
666
|
+
);
|
|
667
|
+
dataLoaderMap.set(table, dataLoader);
|
|
668
|
+
}
|
|
669
|
+
return dataLoader;
|
|
670
|
+
};
|
|
671
|
+
}
|
|
672
|
+
function getColumnTsName(column) {
|
|
673
|
+
const tableColumns = getTableColumns(column.table);
|
|
674
|
+
return Object.entries(tableColumns).find(([_, c]) => c.name === column.name)[0];
|
|
675
|
+
}
|
|
676
|
+
function encodeRowFragment(rowFragment) {
|
|
677
|
+
return Buffer.from(serialize(rowFragment)).toString("base64");
|
|
678
|
+
}
|
|
679
|
+
function decodeRowFragment(encodedRowFragment) {
|
|
680
|
+
return deserialize(Buffer.from(encodedRowFragment, "base64").toString());
|
|
681
|
+
}
|
|
682
|
+
function getSubgraphEntityName(tsName) {
|
|
683
|
+
return capitalize(tsName);
|
|
684
|
+
}
|
|
685
|
+
function isInterfaceType(polymorphicConfig, columnName) {
|
|
686
|
+
return columnName in polymorphicConfig.types;
|
|
687
|
+
}
|
|
688
|
+
function getIntersectionTableSchema(interfaceTypeName, tableConfigs) {
|
|
689
|
+
if (tableConfigs.length === 0) throw new Error("Must have some tables to intersect");
|
|
690
|
+
const baseColumns = tableConfigs[0].columns;
|
|
691
|
+
const baseRelations = tableConfigs[0].relations;
|
|
692
|
+
const commonColumnNames = intersectionOf(
|
|
693
|
+
tableConfigs.map((table) => Object.keys(table.columns))
|
|
694
|
+
//
|
|
695
|
+
);
|
|
696
|
+
const commonRelationsNames = intersectionOf(
|
|
697
|
+
tableConfigs.map((table) => Object.keys(table.relations))
|
|
698
|
+
);
|
|
699
|
+
const intersectionTable = pgTable("intersection_table", (t) => {
|
|
700
|
+
function getColumnBuilder(column) {
|
|
701
|
+
const sqlType = column.getSQLType();
|
|
702
|
+
if (sqlType === "numeric(78)") {
|
|
703
|
+
return t.numeric({ precision: 78 });
|
|
704
|
+
}
|
|
705
|
+
const built = t[sqlType.split("(")[0]]();
|
|
706
|
+
return column.primary ? built.primaryKey() : built;
|
|
707
|
+
}
|
|
708
|
+
const columnMap = {};
|
|
709
|
+
for (const columnName of commonColumnNames) {
|
|
710
|
+
const baseColumn = baseColumns[columnName];
|
|
711
|
+
columnMap[columnName] = getColumnBuilder(baseColumn).notNull(baseColumn.notNull);
|
|
712
|
+
}
|
|
713
|
+
return columnMap;
|
|
714
|
+
});
|
|
715
|
+
const intersectionTableRelations = relations(
|
|
716
|
+
intersectionTable,
|
|
717
|
+
({ one }) => commonRelationsNames.reduce((memo, relationName) => {
|
|
718
|
+
const relation = baseRelations[relationName];
|
|
719
|
+
if (is(relation, One)) {
|
|
720
|
+
memo[relationName] = one(relation.referencedTable, relation.config);
|
|
721
|
+
} else if (is(relation, Many)) {
|
|
722
|
+
}
|
|
723
|
+
return memo;
|
|
724
|
+
}, {})
|
|
725
|
+
);
|
|
726
|
+
return {
|
|
727
|
+
[interfaceTypeName]: intersectionTable,
|
|
728
|
+
[`${interfaceTypeName}Relations`]: intersectionTableRelations
|
|
729
|
+
};
|
|
730
|
+
}
|
|
731
|
+
function getColumnsUnion(tables) {
|
|
732
|
+
return tables.reduce(
|
|
733
|
+
(memo, table) => ({
|
|
734
|
+
...memo,
|
|
735
|
+
...table.columns
|
|
736
|
+
}),
|
|
737
|
+
{}
|
|
738
|
+
);
|
|
739
|
+
}
|
|
740
|
+
function buildUnionAllQuery(drizzle, schema, tables, where = {}) {
|
|
741
|
+
const allColumns = getColumnsUnion(tables);
|
|
742
|
+
const allColumnNames = Object.keys(allColumns).sort();
|
|
743
|
+
const subqueries = tables.map((table) => {
|
|
744
|
+
const selectAllColumnsIncludingNulls = allColumnNames.reduce((memo, columnName) => {
|
|
745
|
+
var _a;
|
|
746
|
+
const column = allColumns[columnName];
|
|
747
|
+
return {
|
|
748
|
+
...memo,
|
|
749
|
+
[columnName]: sql.raw(`${((_a = table.columns[columnName]) == null ? void 0 : _a.name) ?? "NULL"}::${column.getSQLType()}`).as(column.name)
|
|
750
|
+
};
|
|
751
|
+
}, {});
|
|
752
|
+
const relationalConditions = Object.entries(where).map(
|
|
753
|
+
([foreignKeyName, foreignKeyValue]) => eq(table.columns[foreignKeyName], foreignKeyValue)
|
|
754
|
+
);
|
|
755
|
+
return drizzle.select({
|
|
756
|
+
...selectAllColumnsIncludingNulls,
|
|
757
|
+
// inject __typename into each subquery
|
|
758
|
+
__typename: sql.raw(`'${getSubgraphEntityName(table.tsName)}'`).as("__typename")
|
|
759
|
+
}).from(schema[table.tsName]).where(and(...relationalConditions)).$dynamic();
|
|
760
|
+
});
|
|
761
|
+
return subqueries.reduce((memo, fragment, i) => i === 0 ? fragment : memo.unionAll(fragment)).as("intersection_table");
|
|
762
|
+
}
|
|
763
|
+
function definePolymorphicPluralField({
|
|
764
|
+
schema,
|
|
765
|
+
interfaceType,
|
|
766
|
+
filterType,
|
|
767
|
+
orderByType,
|
|
768
|
+
intersectionTableConfig,
|
|
769
|
+
implementingTableConfigs
|
|
770
|
+
}) {
|
|
771
|
+
return {
|
|
772
|
+
type: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(interfaceType))),
|
|
773
|
+
args: {
|
|
774
|
+
where: { type: filterType },
|
|
775
|
+
orderBy: { type: orderByType },
|
|
776
|
+
orderDirection: { type: OrderDirectionEnum },
|
|
777
|
+
first: { type: GraphQLInt },
|
|
778
|
+
skip: { type: GraphQLInt }
|
|
779
|
+
},
|
|
780
|
+
resolve: async (parent, args, { drizzle }, info) => {
|
|
781
|
+
const foreignKeyName = getForeignKeyFieldName(intersectionTableConfig, info.parentType.name);
|
|
782
|
+
const relationalFilter = foreignKeyName ? { [foreignKeyName]: parent.id } : {};
|
|
783
|
+
const subquery = buildUnionAllQuery(
|
|
784
|
+
drizzle,
|
|
785
|
+
schema,
|
|
786
|
+
implementingTableConfigs,
|
|
787
|
+
relationalFilter
|
|
788
|
+
);
|
|
789
|
+
return executePluralQuery(intersectionTableConfig, subquery, drizzle, args);
|
|
790
|
+
}
|
|
791
|
+
};
|
|
792
|
+
}
|
|
793
|
+
function getForeignKeyFieldName(table, parentTypeName) {
|
|
794
|
+
var _a, _b, _c;
|
|
795
|
+
const relationName = Object.keys(table.relations).find(
|
|
796
|
+
(relationName2) => getSubgraphEntityName(relationName2) === parentTypeName
|
|
797
|
+
);
|
|
798
|
+
if (!relationName) return;
|
|
799
|
+
const relation = table.relations[relationName];
|
|
800
|
+
if (!is(relation, One)) return;
|
|
801
|
+
const fkEntry = Object.entries(((_c = (_b = (_a = relation.config) == null ? void 0 : _a.fields) == null ? void 0 : _b[0]) == null ? void 0 : _c.table) ?? {}).find(
|
|
802
|
+
([_, column]) => {
|
|
803
|
+
var _a2, _b2, _c2;
|
|
804
|
+
return column.name === ((_c2 = (_b2 = (_a2 = relation.config) == null ? void 0 : _a2.fields) == null ? void 0 : _b2[0]) == null ? void 0 : _c2.name);
|
|
805
|
+
}
|
|
806
|
+
);
|
|
807
|
+
return fkEntry == null ? void 0 : fkEntry[0];
|
|
808
|
+
}
|
|
809
|
+
export {
|
|
810
|
+
buildDataLoaderCache,
|
|
811
|
+
buildGraphQLSchema,
|
|
812
|
+
onchain
|
|
813
|
+
};
|
|
814
|
+
//# sourceMappingURL=graphql.js.map
|