@0xobelisk/graphql-server 1.2.0-pre.100

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 (87) hide show
  1. package/.turbo/turbo-build.log +8 -0
  2. package/DUAL_POOL_CONFIG.md +188 -0
  3. package/Dockerfile +35 -0
  4. package/LICENSE +92 -0
  5. package/README.md +487 -0
  6. package/dist/cli.d.ts +3 -0
  7. package/dist/cli.d.ts.map +1 -0
  8. package/dist/cli.js +206 -0
  9. package/dist/cli.js.map +1 -0
  10. package/dist/config/subscription-config.d.ts +80 -0
  11. package/dist/config/subscription-config.d.ts.map +1 -0
  12. package/dist/config/subscription-config.js +158 -0
  13. package/dist/config/subscription-config.js.map +1 -0
  14. package/dist/index.d.ts +1 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +11 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/plugins/all-fields-filter-plugin.d.ts +4 -0
  19. package/dist/plugins/all-fields-filter-plugin.d.ts.map +1 -0
  20. package/dist/plugins/all-fields-filter-plugin.js +132 -0
  21. package/dist/plugins/all-fields-filter-plugin.js.map +1 -0
  22. package/dist/plugins/database-introspector.d.ts +23 -0
  23. package/dist/plugins/database-introspector.d.ts.map +1 -0
  24. package/dist/plugins/database-introspector.js +96 -0
  25. package/dist/plugins/database-introspector.js.map +1 -0
  26. package/dist/plugins/enhanced-playground.d.ts +9 -0
  27. package/dist/plugins/enhanced-playground.d.ts.map +1 -0
  28. package/dist/plugins/enhanced-playground.js +113 -0
  29. package/dist/plugins/enhanced-playground.js.map +1 -0
  30. package/dist/plugins/enhanced-server-manager.d.ts +29 -0
  31. package/dist/plugins/enhanced-server-manager.d.ts.map +1 -0
  32. package/dist/plugins/enhanced-server-manager.js +262 -0
  33. package/dist/plugins/enhanced-server-manager.js.map +1 -0
  34. package/dist/plugins/index.d.ts +9 -0
  35. package/dist/plugins/index.d.ts.map +1 -0
  36. package/dist/plugins/index.js +26 -0
  37. package/dist/plugins/index.js.map +1 -0
  38. package/dist/plugins/postgraphile-config.d.ts +94 -0
  39. package/dist/plugins/postgraphile-config.d.ts.map +1 -0
  40. package/dist/plugins/postgraphile-config.js +138 -0
  41. package/dist/plugins/postgraphile-config.js.map +1 -0
  42. package/dist/plugins/query-filter.d.ts +4 -0
  43. package/dist/plugins/query-filter.d.ts.map +1 -0
  44. package/dist/plugins/query-filter.js +42 -0
  45. package/dist/plugins/query-filter.js.map +1 -0
  46. package/dist/plugins/simple-naming.d.ts +4 -0
  47. package/dist/plugins/simple-naming.d.ts.map +1 -0
  48. package/dist/plugins/simple-naming.js +79 -0
  49. package/dist/plugins/simple-naming.js.map +1 -0
  50. package/dist/plugins/welcome-page.d.ts +11 -0
  51. package/dist/plugins/welcome-page.d.ts.map +1 -0
  52. package/dist/plugins/welcome-page.js +203 -0
  53. package/dist/plugins/welcome-page.js.map +1 -0
  54. package/dist/server.d.ts +21 -0
  55. package/dist/server.d.ts.map +1 -0
  56. package/dist/server.js +265 -0
  57. package/dist/server.js.map +1 -0
  58. package/dist/universal-subscriptions.d.ts +32 -0
  59. package/dist/universal-subscriptions.d.ts.map +1 -0
  60. package/dist/universal-subscriptions.js +318 -0
  61. package/dist/universal-subscriptions.js.map +1 -0
  62. package/dist/utils/logger/index.d.ts +80 -0
  63. package/dist/utils/logger/index.d.ts.map +1 -0
  64. package/dist/utils/logger/index.js +230 -0
  65. package/dist/utils/logger/index.js.map +1 -0
  66. package/docker-compose.yml +46 -0
  67. package/eslint.config.mjs +3 -0
  68. package/package.json +78 -0
  69. package/src/cli.ts +232 -0
  70. package/src/config/subscription-config.ts +243 -0
  71. package/src/index.ts +11 -0
  72. package/src/plugins/README.md +138 -0
  73. package/src/plugins/all-fields-filter-plugin.ts +158 -0
  74. package/src/plugins/database-introspector.ts +126 -0
  75. package/src/plugins/enhanced-playground.ts +121 -0
  76. package/src/plugins/enhanced-server-manager.ts +314 -0
  77. package/src/plugins/index.ts +9 -0
  78. package/src/plugins/postgraphile-config.ts +182 -0
  79. package/src/plugins/query-filter.ts +50 -0
  80. package/src/plugins/simple-naming.ts +105 -0
  81. package/src/plugins/welcome-page.ts +218 -0
  82. package/src/server.ts +324 -0
  83. package/src/universal-subscriptions.ts +397 -0
  84. package/src/utils/logger/README.md +209 -0
  85. package/src/utils/logger/index.ts +275 -0
  86. package/sui-indexer-schema.graphql +3691 -0
  87. package/tsconfig.json +28 -0
@@ -0,0 +1,158 @@
1
+ import { Plugin } from 'postgraphile';
2
+
3
+ // All fields filter plugin - ensure all fields support filtering
4
+ export const AllFieldsFilterPlugin: Plugin = (builder) => {
5
+ // Extend filter input type, add filter support for all fields
6
+ builder.hook('GraphQLInputObjectType:fields', (fields, build, context) => {
7
+ const {
8
+ scope: { isPgConnectionFilter, pgIntrospection: table }
9
+ } = context;
10
+
11
+ // Only handle connection filters
12
+ if (!isPgConnectionFilter || !table || table.kind !== 'class') {
13
+ return fields;
14
+ }
15
+
16
+ const enhancedFields = { ...fields };
17
+
18
+ // Add filters for each field of the table
19
+ table.attributes.forEach((attr: any) => {
20
+ const fieldName = build.inflection.column(attr);
21
+
22
+ // Skip fields that already exist
23
+ if (enhancedFields[fieldName]) {
24
+ return;
25
+ }
26
+
27
+ // Determine filter type based on field type
28
+ let filterType;
29
+ const pgType = attr.type;
30
+
31
+ // Special handling for BigInt type
32
+ if (pgType.name === 'int8' || pgType.name === 'bigint') {
33
+ // For BigInt type, try to use StringFilter (because BigInt is represented as string in GraphQL)
34
+ filterType = build.getTypeByName('StringFilter');
35
+ } else {
36
+ // Map PostgreSQL types to GraphQL filter types
37
+ switch (pgType.category) {
38
+ case 'S': // String type
39
+ filterType = build.getTypeByName('StringFilter');
40
+ break;
41
+ case 'N': // Numeric type
42
+ if (pgType.name.includes('int')) {
43
+ filterType = build.getTypeByName('IntFilter');
44
+ } else {
45
+ filterType = build.getTypeByName('FloatFilter');
46
+ }
47
+ break;
48
+ case 'B': // Boolean type
49
+ filterType = build.getTypeByName('BooleanFilter');
50
+ break;
51
+ case 'D': // Date/time type
52
+ filterType = build.getTypeByName('DatetimeFilter');
53
+ break;
54
+ default:
55
+ // For other types, use string filter as default
56
+ filterType = build.getTypeByName('StringFilter');
57
+ }
58
+ }
59
+
60
+ // If specific filter type not found, use string filter
61
+ if (!filterType) {
62
+ filterType = build.getTypeByName('StringFilter');
63
+ }
64
+
65
+ // Add field filter
66
+ if (filterType) {
67
+ enhancedFields[fieldName] = {
68
+ type: filterType,
69
+ description: `Filter by the object's \`${attr.name}\` field.`
70
+ };
71
+ }
72
+ });
73
+
74
+ return enhancedFields;
75
+ });
76
+
77
+ // Ensure sorting options are generated for all fields
78
+ builder.hook('GraphQLEnumType:values', (values, build, context) => {
79
+ const {
80
+ scope: { isPgRowSortEnum, pgIntrospection: table }
81
+ } = context;
82
+
83
+ if (!isPgRowSortEnum || !table || table.kind !== 'class') {
84
+ return values;
85
+ }
86
+
87
+ const enhancedValues = { ...values };
88
+
89
+ // Add ASC and DESC sorting options for each field
90
+ table.attributes.forEach((attr: any) => {
91
+ const columnName = build.inflection.column(attr);
92
+ const enumName = build.inflection.constantCase(columnName);
93
+
94
+ // Add ascending sort
95
+ const ascKey = `${enumName}_ASC`;
96
+ if (!enhancedValues[ascKey]) {
97
+ enhancedValues[ascKey] = {
98
+ value: {
99
+ alias: `${attr.name.toLowerCase()}_ASC`,
100
+ specs: [[attr.name, true]]
101
+ },
102
+ description: `Sorts by ${attr.name} in ascending order.`
103
+ };
104
+ }
105
+
106
+ // Add descending sort
107
+ const descKey = `${enumName}_DESC`;
108
+ if (!enhancedValues[descKey]) {
109
+ enhancedValues[descKey] = {
110
+ value: {
111
+ alias: `${attr.name.toLowerCase()}_DESC`,
112
+ specs: [[attr.name, false]]
113
+ },
114
+ description: `Sorts by ${attr.name} in descending order.`
115
+ };
116
+ }
117
+ });
118
+
119
+ return enhancedValues;
120
+ });
121
+
122
+ // Extend condition filters to support all fields
123
+ builder.hook('GraphQLInputObjectType:fields', (fields, build, context) => {
124
+ const {
125
+ scope: { isPgCondition, pgIntrospection: table }
126
+ } = context;
127
+
128
+ if (!isPgCondition || !table || table.kind !== 'class') {
129
+ return fields;
130
+ }
131
+
132
+ const enhancedFields = { ...fields };
133
+
134
+ // Add condition filters for each field
135
+ table.attributes.forEach((attr: any) => {
136
+ const fieldName = build.inflection.column(attr);
137
+
138
+ // Skip fields that already exist
139
+ if (enhancedFields[fieldName]) {
140
+ return;
141
+ }
142
+
143
+ // Get GraphQL type
144
+ const gqlType = build.pgGetGqlTypeByTypeIdAndModifier(attr.typeId, attr.typeModifier);
145
+
146
+ if (gqlType) {
147
+ enhancedFields[fieldName] = {
148
+ type: gqlType,
149
+ description: `Checks for equality with the object's \`${attr.name}\` field.`
150
+ };
151
+ }
152
+ });
153
+
154
+ return enhancedFields;
155
+ });
156
+ };
157
+
158
+ export default AllFieldsFilterPlugin;
@@ -0,0 +1,126 @@
1
+ import { Pool } from 'pg';
2
+
3
+ // Database table structure interface
4
+ export interface TableField {
5
+ field_name: string;
6
+ field_type: string;
7
+ field_index: number | null;
8
+ is_key: boolean;
9
+ }
10
+
11
+ export interface DynamicTable {
12
+ table_name: string;
13
+ fields: TableField[];
14
+ }
15
+
16
+ // Scan database table structure
17
+ export class DatabaseIntrospector {
18
+ constructor(private pool: Pool, private schema: string = 'public') {}
19
+
20
+ // Get all dynamically created store_* tables
21
+ async getStoreTables(): Promise<string[]> {
22
+ const result = await this.pool.query(
23
+ `
24
+ SELECT table_name
25
+ FROM information_schema.tables
26
+ WHERE table_schema = $1
27
+ AND table_name LIKE 'store_%'
28
+ ORDER BY table_name
29
+ `,
30
+ [this.schema]
31
+ );
32
+
33
+ return result.rows.map((row) => row.table_name);
34
+ }
35
+
36
+ // Get system tables (dubhe related tables)
37
+ async getSystemTables(): Promise<string[]> {
38
+ const result = await this.pool.query(
39
+ `
40
+ SELECT table_name
41
+ FROM information_schema.tables
42
+ WHERE table_schema = $1
43
+ AND (table_name = 'table_fields')
44
+ ORDER BY table_name
45
+ `,
46
+ [this.schema]
47
+ );
48
+
49
+ return result.rows.map((row) => row.table_name);
50
+ }
51
+
52
+ // Get dynamic table field information from table_fields table
53
+ async getDynamicTableFields(tableName: string): Promise<TableField[]> {
54
+ // Extract table name (remove store_ prefix)
55
+ const baseTableName = tableName.replace('store_', '');
56
+
57
+ const result = await this.pool.query(
58
+ `
59
+ SELECT field_name, field_type, field_index, is_key
60
+ FROM table_fields
61
+ WHERE table_name = $1
62
+ ORDER BY is_key DESC, field_index ASC
63
+ `,
64
+ [baseTableName]
65
+ );
66
+
67
+ return result.rows;
68
+ }
69
+
70
+ // Get field information from system tables
71
+ async getSystemTableFields(tableName: string): Promise<TableField[]> {
72
+ const result = await this.pool.query(
73
+ `
74
+ SELECT
75
+ column_name as field_name,
76
+ data_type as field_type,
77
+ ordinal_position as field_index,
78
+ CASE WHEN column_name = 'entity_id' THEN true ELSE false END as is_key
79
+ FROM information_schema.columns
80
+ WHERE table_schema = $1 AND table_name = $2
81
+ ORDER BY ordinal_position
82
+ `,
83
+ [this.schema, tableName]
84
+ );
85
+
86
+ return result.rows;
87
+ }
88
+
89
+ // Get complete information for all tables
90
+ async getAllTables(): Promise<DynamicTable[]> {
91
+ const storeTables = await this.getStoreTables();
92
+ const systemTables = await this.getSystemTables();
93
+ const allTables: DynamicTable[] = [];
94
+
95
+ // Process dynamic tables
96
+ for (const tableName of storeTables) {
97
+ const fields = await this.getDynamicTableFields(tableName);
98
+ allTables.push({
99
+ table_name: tableName,
100
+ fields
101
+ });
102
+ }
103
+
104
+ // Process system tables
105
+ for (const tableName of systemTables) {
106
+ const fields = await this.getSystemTableFields(tableName);
107
+ allTables.push({
108
+ table_name: tableName,
109
+ fields
110
+ });
111
+ }
112
+
113
+ return allTables;
114
+ }
115
+
116
+ // Test database connection
117
+ async testConnection(): Promise<boolean> {
118
+ try {
119
+ await this.pool.query('SELECT NOW() as current_time');
120
+ return true;
121
+ } catch (error) {
122
+ console.error('Database connection test failed:', error);
123
+ return false;
124
+ }
125
+ }
126
+ }
@@ -0,0 +1,121 @@
1
+ // Enhanced GraphQL Playground plugin
2
+ // Provides better visual experience based on GraphiQL and Explorer plugin
3
+
4
+ import type { IncomingMessage, ServerResponse } from 'http';
5
+
6
+ export interface PlaygroundOptions {
7
+ url: string;
8
+ subscriptionUrl?: string;
9
+ title?: string;
10
+ subtitle?: string;
11
+ }
12
+
13
+ export function createEnhancedPlayground(
14
+ options: PlaygroundOptions
15
+ ): (req: IncomingMessage, res: ServerResponse, config?: any) => string {
16
+ return (_req: IncomingMessage, _res: ServerResponse, _config?: any) => {
17
+ // This content is sourced from https://github.com/graphql/graphiql/blob/main/examples/graphiql-cdn/index.html
18
+ return `<!--
19
+ * Copyright (c) 2025 GraphQL Contributors
20
+ * All rights reserved.
21
+ *
22
+ * This source code is licensed under the license found in the
23
+ * LICENSE file in the root directory of this source tree.
24
+ -->
25
+ <!doctype html>
26
+ <html lang="en">
27
+ <head>
28
+ <meta charset="UTF-8" />
29
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
30
+ <title>GraphiQL 5 with React 19 and GraphiQL Explorer</title>
31
+ <style>
32
+ body {
33
+ margin: 0;
34
+ }
35
+
36
+ #graphiql {
37
+ height: 100dvh;
38
+ }
39
+
40
+ .loading {
41
+ height: 100%;
42
+ display: flex;
43
+ align-items: center;
44
+ justify-content: center;
45
+ font-size: 4rem;
46
+ }
47
+ </style>
48
+ <link rel="stylesheet" href="https://esm.sh/graphiql/dist/style.css" />
49
+ <link
50
+ rel="stylesheet"
51
+ href="https://esm.sh/@graphiql/plugin-explorer/dist/style.css"
52
+ />
53
+ <!-- Note: the ?standalone flag bundles the module along with all of its 'dependencies', excluding peerDependencies', into a single JavaScript file. -->
54
+ <script type="importmap">
55
+ {
56
+ "imports": {
57
+ "react": "https://esm.sh/react@19.1.0",
58
+ "react/jsx-runtime": "https://esm.sh/react@19.1.0/jsx-runtime",
59
+
60
+ "react-dom": "https://esm.sh/react-dom@19.1.0",
61
+ "react-dom/client": "https://esm.sh/react-dom@19.1.0/client",
62
+
63
+ "graphiql": "https://esm.sh/graphiql?standalone&external=react,react-dom,@graphiql/react,graphql",
64
+ "@graphiql/plugin-explorer": "https://esm.sh/@graphiql/plugin-explorer?standalone&external=react,@graphiql/react,graphql",
65
+ "@graphiql/react": "https://esm.sh/@graphiql/react?standalone&external=react,react-dom,graphql",
66
+
67
+ "@graphiql/toolkit": "https://esm.sh/@graphiql/toolkit?standalone&external=graphql",
68
+ "graphql": "https://esm.sh/graphql@16.11.0"
69
+ }
70
+ }
71
+ </script>
72
+ <script type="module">
73
+ // Import React and ReactDOM
74
+ import React from 'react';
75
+ import ReactDOM from 'react-dom/client';
76
+ // Import GraphiQL and the Explorer plugin
77
+ import { GraphiQL, HISTORY_PLUGIN } from 'graphiql';
78
+ import { createGraphiQLFetcher } from '@graphiql/toolkit';
79
+ import { explorerPlugin } from '@graphiql/plugin-explorer';
80
+
81
+ import createJSONWorker from 'https://esm.sh/monaco-editor/esm/vs/language/json/json.worker.js?worker';
82
+ import createGraphQLWorker from 'https://esm.sh/monaco-graphql/esm/graphql.worker.js?worker';
83
+ import createEditorWorker from 'https://esm.sh/monaco-editor/esm/vs/editor/editor.worker.js?worker';
84
+
85
+ globalThis.MonacoEnvironment = {
86
+ getWorker(_workerId, label) {
87
+ console.info('MonacoEnvironment.getWorker', { label });
88
+ switch (label) {
89
+ case 'json':
90
+ return createJSONWorker();
91
+ case 'graphql':
92
+ return createGraphQLWorker();
93
+ }
94
+ return createEditorWorker();
95
+ },
96
+ };
97
+
98
+ const fetcher = createGraphiQLFetcher(${JSON.stringify(options)});
99
+ const plugins = [HISTORY_PLUGIN, explorerPlugin()];
100
+
101
+ function App() {
102
+ return React.createElement(GraphiQL, {
103
+ fetcher,
104
+ plugins,
105
+ defaultEditorToolsVisibility: true,
106
+ });
107
+ }
108
+
109
+ const container = document.getElementById('graphiql');
110
+ const root = ReactDOM.createRoot(container);
111
+ root.render(React.createElement(App));
112
+ </script>
113
+ </head>
114
+ <body>
115
+ <div id="graphiql">
116
+ <div class="loading">Loading…</div>
117
+ </div>
118
+ </body>
119
+ </html>`;
120
+ };
121
+ }