Skip to content

Commit f4f3dca

Browse files
schettnNico Schett
andauthored
feat: add graphiql option to config (#102)
* fix: update consola import * feat: allow enable/disable of graphiql, viewer and introspection * chore: create curly-foxes-tie.md * fix: make graphiql config optional and correct logic --------- Co-authored-by: Nico Schett <[email protected]>
1 parent d028d9e commit f4f3dca

File tree

14 files changed

+2382
-2247
lines changed

14 files changed

+2382
-2247
lines changed

.changeset/curly-foxes-tie.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
"create-pylon": patch
3+
"@getcronit/pylon-builder": patch
4+
"@getcronit/pylon-dev": patch
5+
"@getcronit/pylon": patch
6+
---
7+
8+
feat: add `graphiql` option to config
9+
fix: update internal consola imports

packages/create-pylon/src/detect-pm.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import * as fs from 'node:fs'
22
import * as path from 'node:path'
33
import process from 'node:process'
44
import {execSync} from 'node:child_process'
5-
import consola from 'consola'
5+
import {consola} from 'consola'
66

77
// Helper function to check if a command exists
88
function isCommandAvailable(command: string): boolean {

packages/create-pylon/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#!/usr/bin/env node
22

33
import {Option, program, type Command} from 'commander'
4-
import consola from 'consola'
4+
import {consola} from 'consola'
55
import {input, select, confirm} from '@inquirer/prompts'
66
import path from 'path'
77
import chalk from 'chalk'

packages/pylon-builder/src/bundler/bundler.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {Plugin, build} from 'esbuild'
55
import esbuildPluginTsc from 'esbuild-plugin-tsc'
66

77
import path from 'path'
8-
import consola from 'consola'
8+
import {consola} from 'consola'
99

1010
export interface BundlerBuildOptions {
1111
getBuildDefs: () => {

packages/pylon-builder/src/schema/schema-parser.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {
1414
Union as _Union,
1515
Enum as _Enum
1616
} from './type-definition-builder.js'
17-
import consola from 'consola'
17+
import {consola} from 'consola'
1818

1919
type Union = _Union & {
2020
description: string
@@ -448,7 +448,6 @@ export class SchemaParser {
448448
}
449449
schemaString += ` {\n`
450450

451-
452451
// loop over the fields in the type object
453452
for (const field of type.fields) {
454453
// build the argument list for the field if there is at least one argument

packages/pylon-dev/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import {build} from '@getcronit/pylon-builder'
44
import {fetchSchema, generateClient} from '@gqty/cli'
55
import {program, type Command} from 'commander'
6-
import consola from 'consola'
6+
import {consola} from 'consola'
77
import path from 'path'
88
import {version} from '../package.json'
99
import {ChildProcess, spawn} from 'child_process'

packages/pylon/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
"homepage": "https://pylon.cronit.io",
2323
"dependencies": {
2424
"@envelop/core": "^5.0.3",
25+
"@envelop/disable-introspection": "^8.0.0",
2526
"@getcronit/pylon-telemetry": "workspace:^",
2627
"@hono/sentry": "^1.2.0",
2728
"@sentry/bun": "^8.17.0",
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import {Plugin} from 'graphql-yoga'
2+
import {getContext} from '../../context'
3+
import {html} from 'hono/html'
4+
5+
export type ViewerPluginOptions<PluginContext extends Record<string, any>> = {
6+
disableIf?: () => boolean
7+
}
8+
9+
export const useViewer = <PluginContext extends Record<string, any> = {}>(
10+
options: ViewerPluginOptions<PluginContext> = {}
11+
): Plugin<PluginContext> => {
12+
return {
13+
async onRequest({url, endResponse}) {
14+
const c = getContext()
15+
16+
if (
17+
url.pathname === '/viewer' &&
18+
(typeof options.disableIf !== 'undefined'
19+
? options.disableIf() === false
20+
: true)
21+
) {
22+
const res = await c.html(html`
23+
<!DOCTYPE html>
24+
<html>
25+
<head>
26+
<title>Pylon Viewer</title>
27+
<script src="https://cdn.jsdelivr.net/npm/react@16/umd/react.production.min.js"></script>
28+
<script src="https://cdn.jsdelivr.net/npm/react-dom@16/umd/react-dom.production.min.js"></script>
29+
30+
<link
31+
rel="stylesheet"
32+
href="https://cdn.jsdelivr.net/npm/graphql-voyager/dist/voyager.css"
33+
/>
34+
<style>
35+
body {
36+
padding: 0;
37+
margin: 0;
38+
width: 100%;
39+
height: 100vh;
40+
overflow: hidden;
41+
}
42+
43+
#voyager {
44+
height: 100%;
45+
position: relative;
46+
}
47+
}
48+
</style>
49+
<script src="https://cdn.jsdelivr.net/npm/graphql-voyager/dist/voyager.min.js"></script>
50+
</head>
51+
<body>
52+
<div id="voyager">Loading...</div>
53+
<script>
54+
function introspectionProvider(introspectionQuery) {
55+
// ... do a call to server using introspectionQuery provided
56+
// or just return pre-fetched introspection
57+
58+
// Endpoint is current path instead of root/graphql
59+
const endpoint = window.location.pathname.replace(
60+
'/viewer',
61+
'/graphql'
62+
)
63+
64+
return fetch(endpoint, {
65+
method: 'post',
66+
headers: {
67+
'Content-Type': 'application/json'
68+
},
69+
body: JSON.stringify({query: introspectionQuery})
70+
}).then(response => response.json())
71+
}
72+
73+
// Render <Voyager />
74+
GraphQLVoyager.init(document.getElementById('voyager'), {
75+
introspection: introspectionProvider
76+
})
77+
</script>
78+
</body>
79+
</html>
80+
`)
81+
82+
return endResponse(res)
83+
}
84+
}
85+
}
86+
}

packages/pylon/src/app/handler/graphql-viewer-handler.ts

Lines changed: 0 additions & 64 deletions
This file was deleted.

packages/pylon/src/app/handler/pylon-handler.ts

Lines changed: 49 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
1-
import {createSchema, createYoga} from 'graphql-yoga'
21
import {GraphQLScalarType, Kind} from 'graphql'
32
import {
43
DateTimeISOResolver,
54
GraphQLVoid,
65
JSONObjectResolver,
76
JSONResolver
87
} from 'graphql-scalars'
8+
import {createSchema, createYoga} from 'graphql-yoga'
99

10-
import {useSentry} from '../envelop/use-sentry'
11-
import {Context} from '../../context'
12-
import {resolversToGraphQLResolvers} from '../../define-pylon'
13-
import {PylonConfig} from '../..'
10+
import {useDisableIntrospection} from '@envelop/disable-introspection'
1411
import {readFileSync} from 'fs'
1512
import path from 'path'
13+
import {PylonConfig} from '../..'
14+
import {Context, getContext} from '../../context'
15+
import {resolversToGraphQLResolvers} from '../../define-pylon'
16+
import {useSentry} from '../envelop/use-sentry'
17+
import {useViewer} from '../envelop/use-viewer'
1618

1719
interface PylonHandlerOptions {
1820
graphql: {
@@ -102,18 +104,52 @@ export const handler = (options: PylonHandlerOptions) => {
102104
}
103105
})
104106

107+
const resolveGraphiql = (config?: PylonConfig) => {
108+
const graphiqlOptions = {
109+
shouldPersistHeaders: true,
110+
title: 'Pylon Playground',
111+
defaultQuery: `# Welcome to the Pylon Playground!`
112+
}
113+
114+
if (typeof config?.graphiql === 'undefined') {
115+
return graphiqlOptions
116+
}
117+
118+
if (typeof config.graphiql === 'boolean') {
119+
return config.graphiql ? graphiqlOptions : false
120+
}
121+
122+
if (typeof config.graphiql === 'function') {
123+
const c = getContext()
124+
return config.graphiql(c) ? graphiqlOptions : false
125+
}
126+
127+
return false // fallback safeguard
128+
}
129+
105130
const yoga = createYoga({
106131
landingPage: false,
107-
graphiql: req => {
108-
return {
109-
shouldPersistHeaders: true,
110-
title: 'Pylon Playground',
111-
defaultQuery: `# Welcome to the Pylon Playground!`
112-
}
113-
},
114132
graphqlEndpoint: '/graphql',
115133
...config,
116-
plugins: [useSentry(), ...(config?.plugins || [])],
134+
graphiql: async () => resolveGraphiql(config),
135+
plugins: [
136+
useSentry(),
137+
useDisableIntrospection({
138+
disableIf: () => {
139+
const disable = resolveGraphiql(config) === false
140+
141+
return disable
142+
}
143+
}),
144+
useViewer({
145+
disableIf: () => {
146+
const disable = resolveGraphiql(config) === false
147+
148+
return disable
149+
}
150+
}),
151+
...(config?.plugins || [])
152+
],
117153
schema
118154
})
119155

0 commit comments

Comments
 (0)