vyriy 0.5.2 → 0.5.4

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 (71) hide show
  1. package/README.md +54 -36
  2. package/args.js +16 -0
  3. package/bin/vyriy.js +1 -1
  4. package/cli.js +68 -0
  5. package/package.json +52 -7
  6. package/types.d.ts +11 -0
  7. package/cli/args.js +0 -29
  8. package/cli/cli.js +0 -28
  9. package/cli/types.d.ts +0 -13
  10. package/commands/check-env.d.ts +0 -2
  11. package/commands/check-env.js +0 -120
  12. package/commands/create/index.d.ts +0 -2
  13. package/commands/create/index.js +0 -130
  14. package/commands/create/plan/index.d.ts +0 -4
  15. package/commands/create/plan/index.js +0 -3
  16. package/commands/create/plan/plan.d.ts +0 -7
  17. package/commands/create/plan/plan.js +0 -35
  18. package/commands/create/plan/question.d.ts +0 -2
  19. package/commands/create/plan/question.js +0 -25
  20. package/commands/create/plan/types.d.ts +0 -12
  21. package/commands/create/preset/api.d.ts +0 -2
  22. package/commands/create/preset/api.js +0 -63
  23. package/commands/create/preset/base.d.ts +0 -2
  24. package/commands/create/preset/base.js +0 -159
  25. package/commands/create/preset/fullstack.d.ts +0 -2
  26. package/commands/create/preset/fullstack.js +0 -158
  27. package/commands/create/preset/gql.d.ts +0 -2
  28. package/commands/create/preset/gql.js +0 -744
  29. package/commands/create/preset/index.d.ts +0 -52
  30. package/commands/create/preset/index.js +0 -62
  31. package/commands/create/preset/library.d.ts +0 -2
  32. package/commands/create/preset/library.js +0 -247
  33. package/commands/create/preset/mfe.d.ts +0 -2
  34. package/commands/create/preset/mfe.js +0 -326
  35. package/commands/create/preset/rest.d.ts +0 -2
  36. package/commands/create/preset/rest.js +0 -242
  37. package/commands/create/preset/shared.d.ts +0 -116
  38. package/commands/create/preset/shared.js +0 -245
  39. package/commands/create/preset/spa.d.ts +0 -2
  40. package/commands/create/preset/spa.js +0 -132
  41. package/commands/create/preset/ssg.d.ts +0 -2
  42. package/commands/create/preset/ssg.js +0 -171
  43. package/commands/create/preset/ssr.d.ts +0 -2
  44. package/commands/create/preset/ssr.js +0 -179
  45. package/commands/create/preset/types.d.ts +0 -9
  46. package/commands/create/prompt/conflict-strategy.d.ts +0 -5
  47. package/commands/create/prompt/conflict-strategy.js +0 -22
  48. package/commands/create/prompt/index.d.ts +0 -6
  49. package/commands/create/prompt/index.js +0 -5
  50. package/commands/create/prompt/preset.d.ts +0 -4
  51. package/commands/create/prompt/preset.js +0 -11
  52. package/commands/create/prompt/prompt.d.ts +0 -2
  53. package/commands/create/prompt/prompt.js +0 -4
  54. package/commands/create/prompt/resolve-option.d.ts +0 -6
  55. package/commands/create/prompt/resolve-option.js +0 -8
  56. package/commands/create/prompt/scope.d.ts +0 -2
  57. package/commands/create/prompt/scope.js +0 -2
  58. package/commands/create/prompt/types.d.ts +0 -4
  59. package/commands/dist.d.ts +0 -2
  60. package/commands/dist.js +0 -287
  61. package/commands/help.d.ts +0 -3
  62. package/commands/help.js +0 -24
  63. package/commands/index.d.ts +0 -5
  64. package/commands/index.js +0 -5
  65. package/commands/types.d.ts +0 -44
  66. package/commands/version.d.ts +0 -2
  67. package/commands/version.js +0 -6
  68. /package/{cli/args.d.ts → args.d.ts} +0 -0
  69. /package/{cli/cli.d.ts → cli.d.ts} +0 -0
  70. /package/{cli/index.d.ts → index.d.ts} +0 -0
  71. /package/{cli/index.js → index.js} +0 -0
@@ -1,744 +0,0 @@
1
- import packageJson from '../../../package.json' with { type: 'json' };
2
- import { base } from './base.js';
3
- export const gql = (options) => ({
4
- ...base(options),
5
- 'package.json': JSON.stringify({
6
- name: options.name,
7
- version: '0.0.0',
8
- description: options.description,
9
- private: true,
10
- type: 'module',
11
- agents: './AGENTS.md',
12
- packageManager: packageJson.packageManager,
13
- engines: {
14
- node: packageJson.engines.node,
15
- },
16
- workspaces: [
17
- 'packages/*',
18
- 'workspaces/*',
19
- ],
20
- scripts: {
21
- storybook: 'cross-env STORYBOOK_DISABLE_TELEMETRY=1 storybook dev -p 6006 --disable-telemetry',
22
- check: 'run-s lint build test',
23
- fix: "run-s 'fix:*'",
24
- start: "run-p 'start:*'",
25
- lint: "run-s 'lint:*'",
26
- build: "run-s 'build:*'",
27
- test: "run-s 'test:*'",
28
- 'fix:prettier': 'prettier . --write',
29
- 'fix:eslint': 'eslint . --fix',
30
- 'start:graphql': 'sh workspaces/graphql/bin/start.sh',
31
- 'lint:ts': 'tsc',
32
- 'lint:prettier': 'prettier . --check',
33
- 'lint:eslint': 'eslint .',
34
- 'build:graphql': 'rimraf dist && sh workspaces/graphql/bin/build.sh',
35
- 'build:storybook': 'cross-env STORYBOOK_DISABLE_TELEMETRY=1 storybook build --quiet --disable-telemetry',
36
- 'test:jest': 'jest',
37
- postinstall: 'husky',
38
- },
39
- dependencies: {
40
- '@vyriy/typescript-config': `^${packageJson.version}`,
41
- typescript: packageJson.peerDependencies.typescript,
42
- '@vyriy/prettier-config': `^${packageJson.version}`,
43
- prettier: packageJson.peerDependencies.prettier,
44
- '@vyriy/eslint-config': `^${packageJson.version}`,
45
- eslint: packageJson.peerDependencies.eslint,
46
- '@vyriy/jest-config': `^${packageJson.version}`,
47
- jest: packageJson.peerDependencies.jest,
48
- '@vyriy/storybook-config': `^${packageJson.version}`,
49
- storybook: packageJson.peerDependencies.storybook,
50
- '@vyriy/path': `^${packageJson.version}`,
51
- husky: packageJson.peerDependencies.husky,
52
- 'npm-run-all2': packageJson.peerDependencies['npm-run-all2'],
53
- 'cross-env': packageJson.peerDependencies['cross-env'],
54
- rimraf: packageJson.peerDependencies.rimraf,
55
- '@vyriy/webpack-config': `^${packageJson.version}`,
56
- '@vyriy/handler': `^${packageJson.version}`,
57
- '@vyriy/server': `^${packageJson.version}`,
58
- '@vyriy/router': `^${packageJson.version}`,
59
- tsx: packageJson.peerDependencies.tsx,
60
- 'webpack-cli': packageJson.peerDependencies['webpack-cli'],
61
- graphql: packageJson.peerDependencies.graphql,
62
- '@vyriy/html': `^${packageJson.version}`,
63
- },
64
- }, null, 2) + '\n',
65
- 'packages/graphql/doc.mdx': `import { Meta, Markdown } from '@storybook/addon-docs/blocks';
66
- import ReadMe from './README.md?raw';
67
-
68
- <Meta title="Packages/GraphQL" />
69
-
70
- <Markdown>{ReadMe}</Markdown>
71
- `,
72
- 'packages/graphql/graphiql.test.ts': `import { describe, expect, it } from '@jest/globals';
73
-
74
- import { graphiql } from './graphiql.js';
75
-
76
- describe('packages/graphql/graphiql.ts', () => {
77
- it('returns the embedded GraphiQL HTML page', () => {
78
- const page = graphiql();
79
-
80
- expect(page).toContain('<html lang="en">');
81
- expect(page).toContain('<title>GraphiQL</title>');
82
- expect(page).toContain('<meta charset="UTF-8"');
83
- expect(page).toContain('<div id="graphiql"><div class="loading">Loading');
84
- expect(page).toContain('https://esm.sh/graphiql@5.2.2/dist/style.css');
85
- expect(page).toContain('https://esm.sh/@graphiql/plugin-explorer@5.1.1/dist/style.css');
86
- expect(page).toContain('<script type="importmap">');
87
- expect(page).toContain('"graphiql": "https://esm.sh/graphiql@5.2.2?standalone');
88
- expect(page).toContain('const fetcher = createGraphiQLFetcher({');
89
- expect(page).toContain("url: 'http://localhost:3000'");
90
- expect(page).toContain('root.render(React.createElement(App));');
91
- });
92
- });
93
- `,
94
- 'packages/graphql/graphiql.ts': `import { html, minify } from '@vyriy/html';
95
-
96
- export const graphiql = () =>
97
- minify(
98
- html({
99
- htmlAttributes: 'lang="en"',
100
- title: '<title>GraphiQL</title>',
101
- meta: '<meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" />',
102
- link: [
103
- \`<link
104
- rel="stylesheet"
105
- href="https://esm.sh/graphiql@5.2.2/dist/style.css"
106
- integrity="sha384-f6GHLfCwoa4MFYUMd3rieGOsIVAte/evKbJhMigNdzUf52U9bV2JQBMQLke0ua+2"
107
- crossorigin="anonymous"
108
- />\`,
109
- \`<link
110
- rel="stylesheet"
111
- href="https://esm.sh/@graphiql/plugin-explorer@5.1.1/dist/style.css"
112
- integrity="sha384-vTFGj0krVqwFXLB7kq/VHR0/j2+cCT/B63rge2mULaqnib2OX7DVLUVksTlqvMab"
113
- crossorigin="anonymous"
114
- />\`,
115
- ].join(''),
116
- style: \`<style>
117
- body {
118
- margin: 0;
119
- }
120
-
121
- #graphiql {
122
- height: 100dvh;
123
- }
124
-
125
- .loading {
126
- height: 100%;
127
- display: flex;
128
- align-items: center;
129
- justify-content: center;
130
- font-size: 4rem;
131
- }
132
- </style>\`,
133
- script: [
134
- \`<script type="importmap">
135
- {
136
- "imports": {
137
- "react": "https://esm.sh/react@19.2.5",
138
- "react/": "https://esm.sh/react@19.2.5/",
139
- "react-dom": "https://esm.sh/react-dom@19.2.5",
140
- "react-dom/": "https://esm.sh/react-dom@19.2.5/",
141
- "graphiql": "https://esm.sh/graphiql@5.2.2?standalone&external=react,react-dom,@graphiql/react,graphql",
142
- "graphiql/": "https://esm.sh/graphiql@5.2.2/",
143
- "@graphiql/plugin-explorer": "https://esm.sh/@graphiql/plugin-explorer@5.1.1?standalone&external=react,@graphiql/react,graphql",
144
- "@graphiql/react": "https://esm.sh/@graphiql/react@0.37.3?standalone&external=react,react-dom,graphql,@graphiql/toolkit,@emotion/is-prop-valid",
145
- "@graphiql/toolkit": "https://esm.sh/@graphiql/toolkit@0.11.3?standalone&external=graphql",
146
- "graphql": "https://esm.sh/graphql@16.13.2",
147
- "@emotion/is-prop-valid": "data:text/javascript,"
148
- },
149
- "integrity": {
150
- "https://esm.sh/react@19.2.5": "sha384-ZNmUQ9QQgyl95nnD/FJTBQn2ZEPTbWtMuWCXTKWNuF6Si7nC+6bvSgk5LWu+ELHn",
151
- "https://esm.sh/react-dom@19.2.5": "sha384-qtNxBzn9gBs3CmJItMuvIVyjW3VIU0/rzGhCm9MippVU1BpR/c4VgaFYDIg/FrY2",
152
- "https://esm.sh/graphiql@5.2.2": "sha384-MBVZMq1pmz8DwpwIWPWLk2tmS6tGiSi6WwbXvy9NhuDYASAAWd2m96xbxLqszig9",
153
- "https://esm.sh/graphiql@5.2.2?standalone&external=react,react-dom,@graphiql/react,graphql": "sha384-SzHBEbcQfhvmwqh5Vtat9k7b/kIzmdVO3KMzQiAYwcxCA9x7vZwFRUgjzN1AeV3q",
154
- "https://esm.sh/@graphiql/plugin-explorer@5.1.1": "sha384-83REbLb9KtIhL/6J1n91SLoP0648KOKZLIDdHRx/a0E7T3ajq6PzKz+815SCfN52",
155
- "https://esm.sh/@graphiql/react@0.37.3?standalone&external=react,react-dom,graphql,@graphiql/toolkit,@emotion/is-prop-valid": "sha384-iZsbTy9B0VcX2BOTdqMuX0uJ9Hff5GbG2QeOt4OeMp0GHza76dwQaYQYNYkZkIVq",
156
- "https://esm.sh/@graphiql/toolkit@0.11.3?standalone&external=graphql": "sha384-ZsnupyYmzpNjF1Z/81zwi4nV352n4P7vm0JOFKiYnAwVGOf9twnEMnnxmxabMBXe",
157
- "https://esm.sh/graphql@16.13.2": "sha384-TQg9alwG3P9fzBErDW011vKuyTnrwpBZsl3SdMAh6DwBcv9ezFOl0djGI/68VOyy"
158
- }
159
- }
160
- </script>\`,
161
- \`<script type="module">
162
- import React from 'react';
163
- import ReactDOM from 'react-dom/client';
164
- import { GraphiQL, HISTORY_PLUGIN } from 'graphiql';
165
- import { createGraphiQLFetcher } from '@graphiql/toolkit';
166
- import { explorerPlugin } from '@graphiql/plugin-explorer';
167
- import 'graphiql/setup-workers/esm.sh';
168
-
169
- const fetcher = createGraphiQLFetcher({
170
- url: 'http://localhost:3000',
171
- });
172
- const plugins = [HISTORY_PLUGIN, explorerPlugin()];
173
-
174
- function App() {
175
- return React.createElement(GraphiQL, {
176
- fetcher,
177
- plugins,
178
- defaultEditorToolsVisibility: true,
179
- });
180
- }
181
-
182
- const container = document.getElementById('graphiql');
183
- const root = ReactDOM.createRoot(container);
184
- root.render(React.createElement(App));
185
- </script>\`,
186
- ].join(''),
187
- body: '<div id="graphiql"><div class="loading">Loading…</div></div>',
188
- }),
189
- );
190
- `,
191
- 'packages/graphql/index.test.ts': `import { describe, expect, it } from '@jest/globals';
192
-
193
- import { router } from './router.js';
194
-
195
- describe('packages/graphql/index.ts', () => {
196
- it('re-exports the GraphQL router', async () => {
197
- await expect(import('./index.js')).resolves.toMatchObject({
198
- router,
199
- });
200
- });
201
- });
202
- `,
203
- 'packages/graphql/index.ts': "export * from './router.js';\n",
204
- 'packages/graphql/package.json': `{
205
- "name": "@p/graphql",
206
- "type": "module",
207
- "private": true
208
- }
209
- `,
210
- 'packages/graphql/README.md': `# @p/graphql
211
-
212
- Reusable GraphQL API package for the application.
213
-
214
- ## Exports
215
-
216
- - \`router\` - an \`@vyriy/router\` instance that serves the GraphiQL page and GraphQL HTTP requests.
217
-
218
- ## Routes
219
-
220
- ### \`GET /\`
221
-
222
- Returns an embedded GraphiQL page for exploring the schema in a browser.
223
-
224
- ### \`POST /\`
225
-
226
- The router expects a JSON body with a GraphQL request:
227
-
228
- \`\`\`json
229
- {
230
- "query": "{ hello }",
231
- "variables": {},
232
- "operationName": "Hello"
233
- }
234
- \`\`\`
235
-
236
- Invalid JSON or an empty body returns \`400\` with \`Invalid JSON body\`. A body without \`query\` returns \`400\` with \`Missing GraphQL query\`.
237
-
238
- ## Schema
239
-
240
- The package schema currently supports:
241
-
242
- - \`hello: String\`
243
- - \`test: Test\`
244
- - \`ping(message: String): String\`
245
-
246
- Example mutation:
247
-
248
- \`\`\`graphql
249
- mutation Ping($message: String) {
250
- ping(message: $message)
251
- }
252
- \`\`\`
253
- `,
254
- 'packages/graphql/router.test.ts': `import { describe, expect, it } from '@jest/globals';
255
- import type { APIGatewayProxyEvent, APIGatewayProxyResult } from '@vyriy/router';
256
-
257
- import { router } from './router.js';
258
-
259
- type GraphQLBody = {
260
- data?: Record<string, unknown> | null;
261
- errors?: Array<{
262
- message: string;
263
- }>;
264
- };
265
-
266
- const getPostEvent = (
267
- body: string | null,
268
- headers: APIGatewayProxyEvent['headers'] | null = {
269
- authorization: 'Bearer token',
270
- },
271
- ): APIGatewayProxyEvent =>
272
- ({
273
- body,
274
- headers,
275
- httpMethod: 'POST',
276
- path: '/',
277
- pathParameters: null,
278
- queryStringParameters: null,
279
- }) as unknown as APIGatewayProxyEvent;
280
-
281
- const getEvent = (path = '/'): APIGatewayProxyEvent =>
282
- ({
283
- body: null,
284
- headers: {},
285
- httpMethod: 'GET',
286
- path,
287
- pathParameters: null,
288
- queryStringParameters: null,
289
- }) as unknown as APIGatewayProxyEvent;
290
-
291
- const parseBody = <Value>(response: APIGatewayProxyResult): Value => JSON.parse(response.body) as Value;
292
-
293
- describe('packages/graphql/router.ts', () => {
294
- it('serves the embedded GraphiQL page', async () => {
295
- const response = await router.route(getEvent());
296
-
297
- expect(response.statusCode).toBe(200);
298
- expect(response.headers).toEqual({
299
- 'Content-Type': 'text/html; charset=UTF-8',
300
- });
301
- expect(response.body).toContain('<title>GraphiQL</title>');
302
- expect(response.body).toContain('<div id="graphiql">');
303
- });
304
-
305
- it('executes GraphQL requests', async () => {
306
- const response = await router.route(
307
- getPostEvent(
308
- JSON.stringify({
309
- query: 'query Hello { hello }',
310
- operationName: 'Hello',
311
- }),
312
- ),
313
- );
314
-
315
- expect(response.statusCode).toBe(200);
316
- expect(parseBody<GraphQLBody>(response)).toEqual({
317
- data: {
318
- hello: 'Hello from GraphQL',
319
- },
320
- });
321
- });
322
-
323
- it('passes variables to GraphQL execution', async () => {
324
- const response = await router.route(
325
- getPostEvent(
326
- JSON.stringify({
327
- query: 'mutation Ping($message: String) { ping(message: $message) }',
328
- variables: {
329
- message: 'from router',
330
- },
331
- }),
332
- ),
333
- );
334
-
335
- expect(response.statusCode).toBe(200);
336
- expect(parseBody<GraphQLBody>(response)).toEqual({
337
- data: {
338
- ping: 'pong: from router',
339
- },
340
- });
341
- });
342
-
343
- it('executes GraphQL requests without request headers', async () => {
344
- const response = await router.route(
345
- getPostEvent(
346
- JSON.stringify({
347
- query: '{ test { ok message } }',
348
- }),
349
- null,
350
- ),
351
- );
352
-
353
- expect(response.statusCode).toBe(200);
354
- expect(parseBody<GraphQLBody>(response)).toEqual({
355
- data: {
356
- test: {
357
- ok: true,
358
- message: 'GraphQL works',
359
- },
360
- },
361
- });
362
- });
363
-
364
- it('rejects missing bodies', async () => {
365
- await expect(router.route(getPostEvent(null))).resolves.toEqual({
366
- body: JSON.stringify({
367
- errors: [{ message: 'Invalid JSON body' }],
368
- }),
369
- statusCode: 400,
370
- });
371
- });
372
-
373
- it('rejects invalid JSON bodies', async () => {
374
- await expect(router.route(getPostEvent('{'))).resolves.toEqual({
375
- body: JSON.stringify({
376
- errors: [{ message: 'Invalid JSON body' }],
377
- }),
378
- statusCode: 400,
379
- });
380
- });
381
-
382
- it('rejects bodies without a GraphQL query', async () => {
383
- await expect(router.route(getPostEvent(JSON.stringify({ variables: {} })))).resolves.toEqual({
384
- body: JSON.stringify({
385
- errors: [{ message: 'Missing GraphQL query' }],
386
- }),
387
- statusCode: 400,
388
- });
389
- });
390
- });
391
- `,
392
- 'packages/graphql/router.ts': `import { createRouter } from '@vyriy/router';
393
-
394
- import { graphql } from 'graphql';
395
-
396
- import { graphiql } from './graphiql.js';
397
- import { schema } from './schema.js';
398
-
399
- export const router = createRouter();
400
-
401
- router.get('/', () => ({
402
- headers: {
403
- 'Content-Type': 'text/html; charset=UTF-8',
404
- },
405
- body: graphiql(),
406
- }));
407
-
408
- router.post('/', async (params) => {
409
- let payload: {
410
- query?: string;
411
- variables?: Record<string, unknown>;
412
- operationName?: string;
413
- };
414
-
415
- if (!params.body) {
416
- return {
417
- statusCode: 400,
418
- body: JSON.stringify({
419
- errors: [{ message: 'Invalid JSON body' }],
420
- }),
421
- };
422
- }
423
-
424
- try {
425
- payload = JSON.parse(params.body) as typeof payload;
426
- } catch {
427
- return {
428
- statusCode: 400,
429
- body: JSON.stringify({
430
- errors: [{ message: 'Invalid JSON body' }],
431
- }),
432
- };
433
- }
434
-
435
- if (!payload.query) {
436
- return {
437
- statusCode: 400,
438
- body: JSON.stringify({
439
- errors: [{ message: 'Missing GraphQL query' }],
440
- }),
441
- };
442
- }
443
-
444
- return {
445
- body: JSON.stringify(
446
- await graphql({
447
- schema,
448
- source: payload.query,
449
- variableValues: payload.variables,
450
- operationName: payload.operationName,
451
- contextValue: {
452
- event: params.event,
453
- headers: params.headers ?? {},
454
- },
455
- }),
456
- ),
457
- };
458
- });
459
- `,
460
- 'packages/graphql/schema.test.ts': `import { describe, expect, it } from '@jest/globals';
461
- import { graphql } from 'graphql';
462
-
463
- import { schema } from './schema.js';
464
-
465
- describe('packages/graphql/schema.ts', () => {
466
- it('resolves query fields', async () => {
467
- await expect(
468
- graphql({
469
- schema,
470
- source: '{ hello test { ok message } }',
471
- }),
472
- ).resolves.toEqual({
473
- data: {
474
- hello: 'Hello from GraphQL',
475
- test: {
476
- ok: true,
477
- message: 'GraphQL works',
478
- },
479
- },
480
- });
481
- });
482
-
483
- it('resolves the ping mutation', async () => {
484
- await expect(
485
- graphql({
486
- schema,
487
- source: 'mutation Ping($message: String) { ping(message: $message) }',
488
- variableValues: {
489
- message: 'hello',
490
- },
491
- }),
492
- ).resolves.toEqual({
493
- data: {
494
- ping: 'pong: hello',
495
- },
496
- });
497
- });
498
- });
499
- `,
500
- 'packages/graphql/schema.ts': `import { GraphQLSchema, GraphQLObjectType, GraphQLString, GraphQLBoolean } from 'graphql';
501
-
502
- const TestType = new GraphQLObjectType({
503
- name: 'Test',
504
- fields: {
505
- ok: {
506
- type: GraphQLBoolean,
507
- },
508
- message: {
509
- type: GraphQLString,
510
- },
511
- },
512
- });
513
-
514
- const QueryType = new GraphQLObjectType({
515
- name: 'Query',
516
- fields: {
517
- hello: {
518
- type: GraphQLString,
519
- resolve: () => 'Hello from GraphQL',
520
- },
521
-
522
- test: {
523
- type: TestType,
524
- resolve: () => ({
525
- ok: true,
526
- message: 'GraphQL works',
527
- }),
528
- },
529
- },
530
- });
531
-
532
- const MutationType = new GraphQLObjectType({
533
- name: 'Mutation',
534
- fields: {
535
- ping: {
536
- type: GraphQLString,
537
- args: {
538
- message: {
539
- type: GraphQLString,
540
- },
541
- },
542
- resolve: (_source, args: { message: string }) => {
543
- return \`pong: \${args.message}\`;
544
- },
545
- },
546
- },
547
- });
548
-
549
- export const schema = new GraphQLSchema({
550
- query: QueryType,
551
- mutation: MutationType,
552
- });
553
- `,
554
- 'workspaces/graphql/bin/build.sh': `#!/usr/bin/env sh
555
-
556
- set -e
557
-
558
- scriptdir="$PWD/workspaces/graphql";
559
- distdir="dist/graphql";
560
-
561
- NODE_ENV=production npx webpack --config $scriptdir/webpack.config.ts
562
-
563
- cp $scriptdir/package.json $distdir/package.json
564
- npm pkg delete "type" --prefix $distdir
565
- npm pkg delete "private" --prefix $distdir
566
- `,
567
- 'workspaces/graphql/bin/start.sh': `#!/usr/bin/env sh
568
-
569
- set -e
570
-
571
- scriptdir="$PWD/workspaces/graphql";
572
-
573
- NODE_ENV=production LOG_LEVEL=info tsx $scriptdir/index.ts
574
- `,
575
- 'workspaces/graphql/doc.mdx': `import { Meta, Markdown } from '@storybook/addon-docs/blocks';
576
- import ReadMe from './README.md?raw';
577
-
578
- <Meta title="Workspaces/Graphql" />
579
-
580
- <Markdown>{ReadMe}</Markdown>
581
- `,
582
- 'workspaces/graphql/index.test.ts': `import { describe, expect, it, jest } from '@jest/globals';
583
- import type { APIGatewayProxyEvent } from '@vyriy/router';
584
-
585
- const apiMock = jest.fn((handler) => ({
586
- handler,
587
- }));
588
- const serverMock = jest.fn();
589
-
590
- jest.mock('@vyriy/handler', () => ({
591
- api: apiMock,
592
- }));
593
-
594
- jest.mock('@vyriy/server', () => ({
595
- server: serverMock,
596
- }));
597
-
598
- describe('workspaces/graphql/index.ts', () => {
599
- const getPostEvent = (body: string | null, path = '/'): APIGatewayProxyEvent =>
600
- ({
601
- body,
602
- headers: {},
603
- httpMethod: 'POST',
604
- path,
605
- pathParameters: null,
606
- queryStringParameters: null,
607
- }) as unknown as APIGatewayProxyEvent;
608
-
609
- const getEvent = (path = '/'): APIGatewayProxyEvent =>
610
- ({
611
- body: null,
612
- headers: {},
613
- httpMethod: 'GET',
614
- path,
615
- pathParameters: null,
616
- queryStringParameters: null,
617
- }) as unknown as APIGatewayProxyEvent;
618
-
619
- it('starts the server with the API router handler', async () => {
620
- await import('./index.js');
621
-
622
- expect(apiMock).toHaveBeenCalledTimes(1);
623
- expect(serverMock).toHaveBeenCalledTimes(1);
624
- expect(serverMock).toHaveBeenCalledWith(apiMock.mock.results[0]?.value);
625
-
626
- const handler = apiMock.mock.calls[0]?.[0] as (event: APIGatewayProxyEvent) => Promise<{
627
- body: string;
628
- headers?: Record<string, string>;
629
- statusCode: number;
630
- }>;
631
-
632
- await expect(
633
- handler(
634
- getPostEvent(
635
- JSON.stringify({
636
- query: '{ hello test { ok message } }',
637
- }),
638
- ),
639
- ),
640
- ).resolves.toEqual({
641
- body: JSON.stringify({
642
- data: {
643
- hello: 'Hello from GraphQL',
644
- test: {
645
- ok: true,
646
- message: 'GraphQL works',
647
- },
648
- },
649
- }),
650
- statusCode: 200,
651
- });
652
-
653
- const graphiqlResponse = await handler(getEvent());
654
-
655
- expect(graphiqlResponse).toEqual({
656
- body: expect.stringContaining('<title>GraphiQL</title>'),
657
- headers: {
658
- 'Content-Type': 'text/html; charset=UTF-8',
659
- },
660
- statusCode: 200,
661
- });
662
-
663
- await expect(handler(getEvent('/healthcheck'))).resolves.toEqual({
664
- body: JSON.stringify({
665
- message: 'Not Found',
666
- }),
667
- statusCode: 404,
668
- });
669
- });
670
- });
671
- `,
672
- 'workspaces/graphql/index.ts': `import { server } from '@vyriy/server';
673
- import { api } from '@vyriy/handler';
674
-
675
- import { router } from '@p/graphql';
676
-
677
- server(api(async (event) => router.route(event)));
678
- `,
679
- 'workspaces/graphql/package.json': `{
680
- "name": "@w/graphql",
681
- "type": "module",
682
- "private": true
683
- }
684
- `,
685
- 'workspaces/graphql/README.md': `# @w/graphql
686
-
687
- GraphQL runtime workspace for the application.
688
-
689
- This workspace starts the HTTP server and mounts the reusable \`@p/graphql\` router through \`@vyriy/handler\` and \`@vyriy/server\`.
690
-
691
- ## Routes
692
-
693
- - \`GET /\` - serves the embedded GraphiQL page.
694
- - \`POST /\` - executes GraphQL requests against the package schema.
695
-
696
- Example request body:
697
-
698
- \`\`\`json
699
- {
700
- "query": "{ hello test { ok message } }"
701
- }
702
- \`\`\`
703
-
704
- ## Development
705
-
706
- Start the local GraphQL server:
707
-
708
- \`\`\`bash
709
- yarn start:graphql
710
- \`\`\`
711
-
712
- Build the deployable bundle:
713
-
714
- \`\`\`bash
715
- yarn build:graphql
716
- \`\`\`
717
-
718
- The build output is written to \`dist/graphql\`.
719
-
720
- ## Implementation
721
-
722
- The workspace entrypoint is \`index.ts\`. It wraps the shared router with \`api()\` and passes it to \`server()\`:
723
-
724
- \`\`\`ts
725
- server(api(async (event) => router.route(event)));
726
- \`\`\`
727
- `,
728
- 'workspaces/graphql/webpack.config.ts': `import { path } from '@vyriy/path';
729
- import { ssr, external } from '@vyriy/webpack-config';
730
-
731
- export default ssr(
732
- '@w/graphql',
733
- {
734
- path: path('dist', 'graphql'),
735
- filename: 'index.js',
736
- library: { type: 'commonjs2' },
737
- },
738
- (config) => ({
739
- ...config,
740
- externals: [external({ allowlist: [/^@p/, /^@w/, /^@vyriy/] })],
741
- }),
742
- );
743
- `,
744
- });