@stonecrop/nuxt-grafserv 0.7.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/README.md +180 -0
- package/dist/module.d.mts +61 -0
- package/dist/module.json +9 -0
- package/dist/module.mjs +117 -0
- package/dist/runtime/cache.d.ts +26 -0
- package/dist/runtime/cache.js +31 -0
- package/dist/runtime/handler.d.ts +9 -0
- package/dist/runtime/handler.js +133 -0
- package/dist/types.d.mts +7 -0
- package/package.json +99 -0
package/README.md
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# Nuxt Grafserv
|
|
2
|
+
|
|
3
|
+
[![npm version][npm-version-src]][npm-version-href]
|
|
4
|
+
[![npm downloads][npm-downloads-src]][npm-downloads-href]
|
|
5
|
+
[![License][license-src]][license-href]
|
|
6
|
+
[![Nuxt][nuxt-src]][nuxt-href]
|
|
7
|
+
|
|
8
|
+
Pluggable Grafserv GraphQL server as a Nuxt Module. Uses the Grafast execution engine for high-performance GraphQL.
|
|
9
|
+
|
|
10
|
+
- [✨ Release Notes](/CHANGELOG.md)
|
|
11
|
+
|
|
12
|
+
## Features
|
|
13
|
+
|
|
14
|
+
- 🚀 Grafserv Server Integration
|
|
15
|
+
- ⚡️ Grafast Execution Engine (faster than graphql-js)
|
|
16
|
+
- 🔄 Schema Stitching Support
|
|
17
|
+
- 🛠 Middleware Support
|
|
18
|
+
- 📝 TypeScript Support
|
|
19
|
+
- 🔍 GraphiQL Interface
|
|
20
|
+
- ⚡️ Hot Module Reloading
|
|
21
|
+
|
|
22
|
+
## Quick Setup
|
|
23
|
+
|
|
24
|
+
1. Add `@stonecrop/nuxt-grafserv` dependency to your project:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
# Using pnpm
|
|
28
|
+
pnpm add @stonecrop/nuxt-grafserv
|
|
29
|
+
|
|
30
|
+
# Using yarn
|
|
31
|
+
yarn add @stonecrop/nuxt-grafserv
|
|
32
|
+
|
|
33
|
+
# Using npm
|
|
34
|
+
npm install @stonecrop/nuxt-grafserv
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
2. Add `@stonecrop/nuxt-grafserv` to the `modules` section of `nuxt.config.ts`:
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
export default defineNuxtConfig({
|
|
41
|
+
modules: ['@stonecrop/nuxt-grafserv'],
|
|
42
|
+
grafserv: {
|
|
43
|
+
schema: './server/**/*.graphql',
|
|
44
|
+
resolvers: './server/resolvers.ts',
|
|
45
|
+
url: '/graphql/',
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Configuration
|
|
51
|
+
|
|
52
|
+
Here's a full example of all available options:
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
export default defineNuxtConfig({
|
|
56
|
+
modules: ['@stonecrop/nuxt-grafserv'],
|
|
57
|
+
grafserv: {
|
|
58
|
+
// Path to your GraphQL schema files
|
|
59
|
+
schema: './server/**/*.graphql',
|
|
60
|
+
|
|
61
|
+
// Path to your resolvers
|
|
62
|
+
resolvers: './server/resolvers.ts',
|
|
63
|
+
|
|
64
|
+
// GraphQL endpoint URL (default: /graphql/)
|
|
65
|
+
url: '/graphql/',
|
|
66
|
+
|
|
67
|
+
// Enable GraphiQL IDE (default: true in dev, false in prod)
|
|
68
|
+
graphiql: true,
|
|
69
|
+
|
|
70
|
+
// Middleware functions
|
|
71
|
+
middleware: [
|
|
72
|
+
async (ctx, next) => {
|
|
73
|
+
const start = Date.now()
|
|
74
|
+
const result = await next()
|
|
75
|
+
console.log(`Request took ${Date.now() - start}ms`)
|
|
76
|
+
return result
|
|
77
|
+
}
|
|
78
|
+
],
|
|
79
|
+
|
|
80
|
+
// Grafserv-specific options
|
|
81
|
+
grafserv: {
|
|
82
|
+
websockets: false,
|
|
83
|
+
introspection: true
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
})
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Basic Usage
|
|
90
|
+
|
|
91
|
+
1. Create your GraphQL schema (`server/schema.graphql`):
|
|
92
|
+
|
|
93
|
+
```graphql
|
|
94
|
+
type Query {
|
|
95
|
+
hello: String!
|
|
96
|
+
ping: Boolean!
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
type Mutation {
|
|
100
|
+
echo(message: String!): String!
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
2. Create your resolvers (`server/resolvers.ts`):
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
export default {
|
|
108
|
+
Query: {
|
|
109
|
+
hello: () => 'world',
|
|
110
|
+
ping: () => true
|
|
111
|
+
},
|
|
112
|
+
Mutation: {
|
|
113
|
+
echo: (_: unknown, { message }: { message: string }) => message
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Middleware
|
|
119
|
+
|
|
120
|
+
Add middleware functions to process requests:
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
grafserv: {
|
|
124
|
+
middleware: [
|
|
125
|
+
// Logging middleware
|
|
126
|
+
async (ctx, next) => {
|
|
127
|
+
const start = Date.now()
|
|
128
|
+
const result = await next()
|
|
129
|
+
console.log(`Request took ${Date.now() - start}ms`)
|
|
130
|
+
return result
|
|
131
|
+
},
|
|
132
|
+
// Authentication middleware
|
|
133
|
+
async (ctx, next) => {
|
|
134
|
+
const token = ctx.req.headers.get('authorization')
|
|
135
|
+
if (!token) throw new Error('Unauthorized')
|
|
136
|
+
return next()
|
|
137
|
+
}
|
|
138
|
+
]
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Development
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
# Install dependencies
|
|
146
|
+
pnpm install
|
|
147
|
+
|
|
148
|
+
# Generate type stubs
|
|
149
|
+
pnpm run dev:prepare
|
|
150
|
+
|
|
151
|
+
# Develop with the playground
|
|
152
|
+
pnpm run dev
|
|
153
|
+
|
|
154
|
+
# Build the module
|
|
155
|
+
pnpm run build
|
|
156
|
+
|
|
157
|
+
# Run ESLint
|
|
158
|
+
pnpm run lint
|
|
159
|
+
|
|
160
|
+
# Run tests
|
|
161
|
+
pnpm run test
|
|
162
|
+
pnpm run test:watch
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## License
|
|
166
|
+
|
|
167
|
+
[MIT License](./LICENSE)
|
|
168
|
+
|
|
169
|
+
<!-- Badges -->
|
|
170
|
+
[npm-version-src]: https://img.shields.io/npm/v/@stonecrop/nuxt-grafserv/latest.svg?style=flat&colorA=020420&colorB=00DC82
|
|
171
|
+
[npm-version-href]: https://npmjs.com/package/@stonecrop/nuxt-grafserv
|
|
172
|
+
|
|
173
|
+
[npm-downloads-src]: https://img.shields.io/npm/dm/@stonecrop/nuxt-grafserv.svg?style=flat&colorA=020420&colorB=00DC82
|
|
174
|
+
[npm-downloads-href]: https://npm.chart.dev/@stonecrop/nuxt-grafserv
|
|
175
|
+
|
|
176
|
+
[license-src]: https://img.shields.io/npm/l/@stonecrop/nuxt-grafserv.svg?style=flat&colorA=020420&colorB=00DC82
|
|
177
|
+
[license-href]: https://npmjs.com/package/@stonecrop/nuxt-grafserv
|
|
178
|
+
|
|
179
|
+
[nuxt-src]: https://img.shields.io/badge/Nuxt-020420?logo=nuxt.js
|
|
180
|
+
[nuxt-href]: https://nuxt.com
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { NuxtModule } from '@nuxt/schema';
|
|
2
|
+
import { GraphQLSchema } from 'graphql';
|
|
3
|
+
import { GraphileConfig } from 'graphile-config';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Context provided to GraphQL resolvers
|
|
7
|
+
*/
|
|
8
|
+
type GrafastContext = {
|
|
9
|
+
req: Request;
|
|
10
|
+
params: Record<string, string>;
|
|
11
|
+
[key: string]: unknown;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Middleware function type for request processing
|
|
15
|
+
*/
|
|
16
|
+
type MiddlewareFunction = (context: GrafastContext, next: () => Promise<GrafastContext>) => Promise<GrafastContext>;
|
|
17
|
+
/**
|
|
18
|
+
* Schema provider function - returns a GraphQL schema
|
|
19
|
+
*/
|
|
20
|
+
type SchemaProvider = () => GraphQLSchema | Promise<GraphQLSchema>;
|
|
21
|
+
/**
|
|
22
|
+
* Configuration for the Grafast module
|
|
23
|
+
*/
|
|
24
|
+
interface ModuleOptions {
|
|
25
|
+
/** Path to schema file(s) or a schema provider function */
|
|
26
|
+
schema?: string | string[] | SchemaProvider;
|
|
27
|
+
/** Path to resolvers file (for .graphql schema files) */
|
|
28
|
+
resolvers?: string;
|
|
29
|
+
/** GraphQL endpoint URL (default: '/graphql/') */
|
|
30
|
+
url?: string;
|
|
31
|
+
/** Whether to enable GraphiQL IDE (default: true in dev, false in prod) */
|
|
32
|
+
graphiql?: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Path to middleware file that exports an array of middleware functions.
|
|
35
|
+
* This is the recommended approach as it preserves imports/dependencies.
|
|
36
|
+
* Example: './server/middleware.ts'
|
|
37
|
+
*/
|
|
38
|
+
middlewarePath?: string;
|
|
39
|
+
/**
|
|
40
|
+
* Middleware functions to process requests (inline).
|
|
41
|
+
* Note: Inline middleware cannot reference external modules.
|
|
42
|
+
* For middleware with dependencies, use middlewarePath instead.
|
|
43
|
+
* @deprecated Use middlewarePath for middleware with external dependencies
|
|
44
|
+
*/
|
|
45
|
+
middleware?: MiddlewareFunction[];
|
|
46
|
+
/** Custom Graphile preset to extend (for advanced grafast configuration) */
|
|
47
|
+
preset?: GraphileConfig.Preset;
|
|
48
|
+
/** Additional Graphile plugins */
|
|
49
|
+
plugins?: GraphileConfig.Plugin[];
|
|
50
|
+
/** Grafserv options */
|
|
51
|
+
grafserv?: {
|
|
52
|
+
/** Whether to enable the GraphQL websocket endpoint */
|
|
53
|
+
websockets?: boolean;
|
|
54
|
+
/** Whether to enable introspection (default: true in dev) */
|
|
55
|
+
introspection?: boolean;
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
declare const module$1: NuxtModule<ModuleOptions>;
|
|
60
|
+
|
|
61
|
+
export { module$1 as default };
|
package/dist/module.json
ADDED
package/dist/module.mjs
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { useLogger, defineNuxtModule, createResolver } from '@nuxt/kit';
|
|
3
|
+
|
|
4
|
+
const logger = useLogger("nuxt-grafserv");
|
|
5
|
+
const module$1 = defineNuxtModule({
|
|
6
|
+
meta: {
|
|
7
|
+
name: "nuxt-grafserv",
|
|
8
|
+
configKey: "grafserv"
|
|
9
|
+
},
|
|
10
|
+
defaults: (_nuxt) => ({
|
|
11
|
+
schema: "./server/**/*.graphql",
|
|
12
|
+
resolvers: "./server/resolvers.ts",
|
|
13
|
+
url: "/graphql/",
|
|
14
|
+
graphiql: void 0,
|
|
15
|
+
// Will default based on dev mode
|
|
16
|
+
middleware: [],
|
|
17
|
+
plugins: [],
|
|
18
|
+
grafserv: {
|
|
19
|
+
websockets: false,
|
|
20
|
+
introspection: void 0
|
|
21
|
+
// Will default based on dev mode
|
|
22
|
+
}
|
|
23
|
+
}),
|
|
24
|
+
setup(options, nuxt) {
|
|
25
|
+
const { resolve } = createResolver(import.meta.url);
|
|
26
|
+
nuxt.hook("nitro:config", (config) => {
|
|
27
|
+
const resolverPath = options.resolvers ? join(nuxt.options.srcDir, options.resolvers) : void 0;
|
|
28
|
+
config.runtimeConfig = config.runtimeConfig || {};
|
|
29
|
+
config.runtimeConfig.grafserv = {
|
|
30
|
+
...options,
|
|
31
|
+
// Resolve schema paths
|
|
32
|
+
schema: typeof options.schema === "string" ? join(nuxt.options.srcDir, options.schema) : Array.isArray(options.schema) ? options.schema.map((s) => join(nuxt.options.srcDir, s)) : options.schema
|
|
33
|
+
// function passed through
|
|
34
|
+
};
|
|
35
|
+
config.virtual = config.virtual || {};
|
|
36
|
+
if (resolverPath) {
|
|
37
|
+
config.virtual["#internal/grafserv/resolvers"] = `export { default } from '${resolverPath}'`;
|
|
38
|
+
}
|
|
39
|
+
let middlewareCode;
|
|
40
|
+
if (options.middlewarePath) {
|
|
41
|
+
const middlewarePath = join(nuxt.options.srcDir, options.middlewarePath);
|
|
42
|
+
middlewareCode = `export { default } from '${middlewarePath}'`;
|
|
43
|
+
} else if (options.middleware?.length) {
|
|
44
|
+
logger.warn("Inline middleware is deprecated. Use middlewarePath for middleware with external dependencies.");
|
|
45
|
+
middlewareCode = `export default [${options.middleware.map((fn) => fn.toString()).join(",")}]`;
|
|
46
|
+
} else {
|
|
47
|
+
middlewareCode = "export default []";
|
|
48
|
+
}
|
|
49
|
+
config.virtual["#internal/grafserv/middleware"] = middlewareCode;
|
|
50
|
+
config.externals = config.externals || {};
|
|
51
|
+
config.externals.inline = config.externals.inline || [];
|
|
52
|
+
config.externals.inline.push(
|
|
53
|
+
"grafast",
|
|
54
|
+
"grafserv",
|
|
55
|
+
"grafserv/h3/v1",
|
|
56
|
+
"graphile-config",
|
|
57
|
+
"@graphql-tools/schema",
|
|
58
|
+
"@graphql-tools/load",
|
|
59
|
+
"@graphql-tools/graphql-file-loader"
|
|
60
|
+
);
|
|
61
|
+
});
|
|
62
|
+
nuxt.hook("nitro:config", (config) => {
|
|
63
|
+
config.handlers = config.handlers || [];
|
|
64
|
+
config.handlers.push({
|
|
65
|
+
route: options.url || "/graphql/",
|
|
66
|
+
handler: resolve("./runtime/handler")
|
|
67
|
+
});
|
|
68
|
+
config.handlers.push({
|
|
69
|
+
route: "/graphql/cache",
|
|
70
|
+
handler: resolve("./runtime/cache")
|
|
71
|
+
});
|
|
72
|
+
config.handlers.push({
|
|
73
|
+
route: "/ruru-static/**",
|
|
74
|
+
handler: resolve("./runtime/handler")
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
if (options.url) {
|
|
78
|
+
nuxt.hook("devtools:customTabs", (tabs) => {
|
|
79
|
+
tabs.push({
|
|
80
|
+
name: "nuxt-grafserv",
|
|
81
|
+
title: "GraphQL (Grafserv)",
|
|
82
|
+
icon: "simple-icons:graphql",
|
|
83
|
+
view: {
|
|
84
|
+
type: "iframe",
|
|
85
|
+
src: options.url
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
if (nuxt.options.dev) {
|
|
91
|
+
let cacheClearing = false;
|
|
92
|
+
nuxt.hook("builder:watch", async (event, path) => {
|
|
93
|
+
const isSchemaFile = path.endsWith(".graphql");
|
|
94
|
+
const isResolverFile = options.resolvers && path.includes(options.resolvers.replace("./", ""));
|
|
95
|
+
if (isSchemaFile || isResolverFile) {
|
|
96
|
+
logger.info(`${path} changed`);
|
|
97
|
+
if (!cacheClearing) {
|
|
98
|
+
cacheClearing = true;
|
|
99
|
+
try {
|
|
100
|
+
logger.info("Clearing Grafserv cache...");
|
|
101
|
+
const { clearGrafservCache } = await import(resolve("./runtime/handler"));
|
|
102
|
+
await clearGrafservCache();
|
|
103
|
+
logger.success("Cache cleared, schema reloaded");
|
|
104
|
+
} catch (error) {
|
|
105
|
+
logger.error("Failed to clear cache:", error);
|
|
106
|
+
} finally {
|
|
107
|
+
cacheClearing = false;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
logger.success("[@stonecrop/nuxt-grafserv] Module initialized");
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
export { module$1 as default };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cache management endpoint for the Grafserv module.
|
|
3
|
+
*
|
|
4
|
+
* Actions:
|
|
5
|
+
* - clear: Clear the Grafserv instance cache
|
|
6
|
+
* - status: Get cache status information
|
|
7
|
+
*/
|
|
8
|
+
declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<{
|
|
9
|
+
success: boolean;
|
|
10
|
+
message: string;
|
|
11
|
+
data?: undefined;
|
|
12
|
+
availableActions?: undefined;
|
|
13
|
+
} | {
|
|
14
|
+
success: boolean;
|
|
15
|
+
data: {
|
|
16
|
+
message: string;
|
|
17
|
+
};
|
|
18
|
+
message?: undefined;
|
|
19
|
+
availableActions?: undefined;
|
|
20
|
+
} | {
|
|
21
|
+
success: boolean;
|
|
22
|
+
message: string;
|
|
23
|
+
availableActions: string[];
|
|
24
|
+
data?: undefined;
|
|
25
|
+
}>>;
|
|
26
|
+
export default _default;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { defineEventHandler, getQuery, createError } from "h3";
|
|
2
|
+
import { clearGrafservCache } from "./handler.js";
|
|
3
|
+
export default defineEventHandler(async (event) => {
|
|
4
|
+
const allowCacheApi = process.env.NODE_ENV === "development";
|
|
5
|
+
if (!allowCacheApi) {
|
|
6
|
+
throw createError({
|
|
7
|
+
statusCode: 403,
|
|
8
|
+
statusMessage: "Cache API is disabled in production"
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
const query = getQuery(event);
|
|
12
|
+
const action = query.action;
|
|
13
|
+
switch (action) {
|
|
14
|
+
case "clear":
|
|
15
|
+
await clearGrafservCache();
|
|
16
|
+
return { success: true, message: "Grafserv cache cleared" };
|
|
17
|
+
case "status":
|
|
18
|
+
return {
|
|
19
|
+
success: true,
|
|
20
|
+
data: {
|
|
21
|
+
message: "Grafserv cache status - schema and server instance cached in memory"
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
default:
|
|
25
|
+
return {
|
|
26
|
+
success: false,
|
|
27
|
+
message: "Invalid action",
|
|
28
|
+
availableActions: ["clear", "status"]
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Clear the cached instances (useful for development hot reload)
|
|
3
|
+
*/
|
|
4
|
+
export declare function clearGrafservCache(): Promise<void>;
|
|
5
|
+
/**
|
|
6
|
+
* Main H3 event handler for GraphQL requests
|
|
7
|
+
*/
|
|
8
|
+
declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<Buffer<ArrayBufferLike> | import("grafserv").JSONValue | undefined>>;
|
|
9
|
+
export default _default;
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { grafserv } from "grafserv/h3/v1";
|
|
2
|
+
import { makeGrafastSchema } from "grafast";
|
|
3
|
+
import { GraphQLFileLoader } from "@graphql-tools/graphql-file-loader";
|
|
4
|
+
import { loadTypedefs } from "@graphql-tools/load";
|
|
5
|
+
import { defineEventHandler } from "h3";
|
|
6
|
+
import { useRuntimeConfig } from "nitropack/runtime";
|
|
7
|
+
let middlewareFunctions = null;
|
|
8
|
+
async function getMiddleware() {
|
|
9
|
+
if (middlewareFunctions !== null) {
|
|
10
|
+
return middlewareFunctions;
|
|
11
|
+
}
|
|
12
|
+
try {
|
|
13
|
+
const mod = await import("#internal/grafserv/middleware");
|
|
14
|
+
middlewareFunctions = mod.default || [];
|
|
15
|
+
} catch {
|
|
16
|
+
middlewareFunctions = [];
|
|
17
|
+
}
|
|
18
|
+
return middlewareFunctions;
|
|
19
|
+
}
|
|
20
|
+
let grafservInstance = null;
|
|
21
|
+
let cachedSchema = null;
|
|
22
|
+
async function loadTypeDefsFromFiles(schemaPath) {
|
|
23
|
+
const paths = Array.isArray(schemaPath) ? schemaPath : [schemaPath];
|
|
24
|
+
const sources = await loadTypedefs(paths, {
|
|
25
|
+
loaders: [new GraphQLFileLoader()]
|
|
26
|
+
});
|
|
27
|
+
return sources.map((source) => source.document).filter(Boolean);
|
|
28
|
+
}
|
|
29
|
+
async function getSchema(options) {
|
|
30
|
+
if (cachedSchema) {
|
|
31
|
+
return cachedSchema;
|
|
32
|
+
}
|
|
33
|
+
let schema;
|
|
34
|
+
if (typeof options.schema === "function") {
|
|
35
|
+
schema = await options.schema();
|
|
36
|
+
} else if (options.schema) {
|
|
37
|
+
const typeDefDocs = await loadTypeDefsFromFiles(options.schema);
|
|
38
|
+
let plans = {};
|
|
39
|
+
if (options.resolvers) {
|
|
40
|
+
try {
|
|
41
|
+
const resolverModule = await import("#internal/grafserv/resolvers");
|
|
42
|
+
const resolvers = resolverModule.default || resolverModule;
|
|
43
|
+
console.log("[@stonecrop/nuxt-grafserv] Resolvers loaded:", Object.keys(resolvers));
|
|
44
|
+
for (const typeName of Object.keys(resolvers)) {
|
|
45
|
+
plans[typeName] = {};
|
|
46
|
+
const typeResolvers = resolvers[typeName];
|
|
47
|
+
for (const fieldName of Object.keys(typeResolvers)) {
|
|
48
|
+
const resolver = typeResolvers[fieldName];
|
|
49
|
+
if (typeof resolver === "function") {
|
|
50
|
+
plans[typeName][fieldName] = { resolve: resolver };
|
|
51
|
+
} else {
|
|
52
|
+
plans[typeName][fieldName] = resolver;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
} catch (e) {
|
|
57
|
+
console.warn("[@stonecrop/nuxt-grafserv] Could not load resolvers:", e);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
schema = makeGrafastSchema({
|
|
61
|
+
typeDefs: typeDefDocs,
|
|
62
|
+
plans
|
|
63
|
+
});
|
|
64
|
+
} else {
|
|
65
|
+
throw new Error("[@stonecrop/nuxt-grafserv] No schema provided. Configure schema path or provider function.");
|
|
66
|
+
}
|
|
67
|
+
cachedSchema = schema;
|
|
68
|
+
return schema;
|
|
69
|
+
}
|
|
70
|
+
async function getGrafservInstance(options) {
|
|
71
|
+
if (grafservInstance) {
|
|
72
|
+
return grafservInstance;
|
|
73
|
+
}
|
|
74
|
+
const schema = await getSchema(options);
|
|
75
|
+
const isDev = process.env.NODE_ENV === "development";
|
|
76
|
+
grafservInstance = grafserv({
|
|
77
|
+
schema,
|
|
78
|
+
graphiql: options.graphiql ?? isDev,
|
|
79
|
+
websockets: options.grafserv?.websockets ?? false
|
|
80
|
+
});
|
|
81
|
+
console.log("[@stonecrop/nuxt-grafserv] Grafserv instance created");
|
|
82
|
+
return grafservInstance;
|
|
83
|
+
}
|
|
84
|
+
export async function clearGrafservCache() {
|
|
85
|
+
grafservInstance = null;
|
|
86
|
+
cachedSchema = null;
|
|
87
|
+
console.log("[@stonecrop/nuxt-grafserv] Cache cleared");
|
|
88
|
+
}
|
|
89
|
+
async function applyMiddleware(context, middleware) {
|
|
90
|
+
if (!middleware || middleware.length === 0) {
|
|
91
|
+
return context;
|
|
92
|
+
}
|
|
93
|
+
const applyNext = async (index) => {
|
|
94
|
+
if (index >= middleware.length) {
|
|
95
|
+
return context;
|
|
96
|
+
}
|
|
97
|
+
return middleware[index](context, () => applyNext(index + 1));
|
|
98
|
+
};
|
|
99
|
+
return applyNext(0);
|
|
100
|
+
}
|
|
101
|
+
export default defineEventHandler(async (event) => {
|
|
102
|
+
const config = useRuntimeConfig();
|
|
103
|
+
const options = config.grafserv;
|
|
104
|
+
try {
|
|
105
|
+
if (event.node.req.url?.includes("/__grafserv_cache_clear") && process.env.NODE_ENV === "development") {
|
|
106
|
+
await clearGrafservCache();
|
|
107
|
+
return { success: true, message: "Cache cleared" };
|
|
108
|
+
}
|
|
109
|
+
const { req } = event.node;
|
|
110
|
+
const context = {
|
|
111
|
+
req: new Request(new URL(req.url || "/", `http://${req.headers.host || "localhost"}`), {
|
|
112
|
+
method: req.method,
|
|
113
|
+
headers: req.headers
|
|
114
|
+
}),
|
|
115
|
+
params: event.context.params || {}
|
|
116
|
+
};
|
|
117
|
+
const middleware = await getMiddleware();
|
|
118
|
+
await applyMiddleware(context, middleware);
|
|
119
|
+
const serv = await getGrafservInstance(options);
|
|
120
|
+
const url = event.node.req.url || "";
|
|
121
|
+
const method = event.node.req.method || "GET";
|
|
122
|
+
if (url.includes("/ruru-static/")) {
|
|
123
|
+
return serv.handleGraphiqlStaticEvent(event);
|
|
124
|
+
}
|
|
125
|
+
if (method === "GET" && url.match(/\/graphql\/?(\?.*)?$/)) {
|
|
126
|
+
return serv.handleGraphiqlEvent(event);
|
|
127
|
+
}
|
|
128
|
+
return serv.handleGraphQLEvent(event);
|
|
129
|
+
} catch (error) {
|
|
130
|
+
console.error("[@stonecrop/nuxt-grafserv] Error in GraphQL handler:", error);
|
|
131
|
+
throw error;
|
|
132
|
+
}
|
|
133
|
+
});
|
package/dist/types.d.mts
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@stonecrop/nuxt-grafserv",
|
|
3
|
+
"version": "0.7.0",
|
|
4
|
+
"description": "Pluggable Grafserv GraphQL server as Nuxt Module",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"author": {
|
|
8
|
+
"name": "Tyler Matteson",
|
|
9
|
+
"email": "tyler@agritheory.com"
|
|
10
|
+
},
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "https://github.com/agritheory/stonecrop",
|
|
14
|
+
"directory": "nuxt_grafserv"
|
|
15
|
+
},
|
|
16
|
+
"bugs": {
|
|
17
|
+
"url": "https://github.com/agritheory/stonecrop/issues"
|
|
18
|
+
},
|
|
19
|
+
"exports": {
|
|
20
|
+
".": {
|
|
21
|
+
"import": "./dist/module.mjs",
|
|
22
|
+
"types": "./dist/types.d.mts"
|
|
23
|
+
},
|
|
24
|
+
"./runtime/*": "./dist/runtime/*"
|
|
25
|
+
},
|
|
26
|
+
"main": "./dist/module.mjs",
|
|
27
|
+
"types": "./dist/types.d.mts",
|
|
28
|
+
"typesVersions": {
|
|
29
|
+
"*": {
|
|
30
|
+
".": [
|
|
31
|
+
"./dist/types.d.mts"
|
|
32
|
+
]
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"files": [
|
|
36
|
+
"dist"
|
|
37
|
+
],
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@graphql-tools/graphql-file-loader": "^8.1.9",
|
|
40
|
+
"@graphql-tools/load": "^8.1.8",
|
|
41
|
+
"@graphql-tools/stitch": "^10.1.8",
|
|
42
|
+
"@graphql-tools/wrap": "^11.1.4",
|
|
43
|
+
"grafast": "^1.0.0-rc.3",
|
|
44
|
+
"grafserv": "^1.0.0-rc.3",
|
|
45
|
+
"graphile-config": "^1.0.0-rc.2",
|
|
46
|
+
"graphql": "^16.12.0",
|
|
47
|
+
"@stonecrop/graphql-middleware": "0.7.0"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@casl/ability": "^6.7.5",
|
|
51
|
+
"@eslint/js": "^9.39.2",
|
|
52
|
+
"@nuxt/devtools": "^3.1.1",
|
|
53
|
+
"@nuxt/eslint-config": "^1.12.1",
|
|
54
|
+
"@nuxt/kit": "^4.2.2",
|
|
55
|
+
"@nuxt/module-builder": "^1.0.2",
|
|
56
|
+
"@nuxt/schema": "^4.2.2",
|
|
57
|
+
"@nuxt/test-utils": "^3.23.0",
|
|
58
|
+
"@vitest/coverage-istanbul": "^4.0.17",
|
|
59
|
+
"@types/node": "^22.19.5",
|
|
60
|
+
"zod": "^3.25.76",
|
|
61
|
+
"h3": "^1.15.4",
|
|
62
|
+
"nitropack": "^2.13.0",
|
|
63
|
+
"changelogen": "^0.6.2",
|
|
64
|
+
"eslint": "^9.39.2",
|
|
65
|
+
"jsdom": "^27.4.0",
|
|
66
|
+
"nuxt": "^4.2.2",
|
|
67
|
+
"typescript": "^5.9.3",
|
|
68
|
+
"vite": "^7.3.1",
|
|
69
|
+
"vitest": "^4.0.17",
|
|
70
|
+
"vue-tsc": "^3.2.2",
|
|
71
|
+
"@stonecrop/casl-middleware": "0.7.0",
|
|
72
|
+
"@stonecrop/rockfoil": "0.7.0"
|
|
73
|
+
},
|
|
74
|
+
"peerDependencies": {
|
|
75
|
+
"@stonecrop/casl-middleware": "0.7.0",
|
|
76
|
+
"@stonecrop/rockfoil": "0.7.0"
|
|
77
|
+
},
|
|
78
|
+
"peerDependenciesMeta": {
|
|
79
|
+
"@stonecrop/casl-middleware": {
|
|
80
|
+
"optional": true
|
|
81
|
+
},
|
|
82
|
+
"@stonecrop/rockfoil": {
|
|
83
|
+
"optional": true
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
"scripts": {
|
|
87
|
+
"_phase:build": "rushx dev:prepare && rushx prepack",
|
|
88
|
+
"build": "rushx dev:prepare && rushx prepack",
|
|
89
|
+
"build:module": "rushx dev:prepare && rushx prepack",
|
|
90
|
+
"dev": "nuxi dev playground",
|
|
91
|
+
"dev:build": "nuxi build playground",
|
|
92
|
+
"dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxi prepare playground",
|
|
93
|
+
"lint": "eslint .",
|
|
94
|
+
"test": "vitest run",
|
|
95
|
+
"test:ci": "vitest run --run",
|
|
96
|
+
"test:watch": "vitest watch",
|
|
97
|
+
"test:types": "vue-tsc --noEmit && cd playground && vue-tsc --noEmit"
|
|
98
|
+
}
|
|
99
|
+
}
|