@buenojs/bueno 0.8.5 → 0.8.7
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/.claude/settings.local.json +9 -0
- package/README.md +259 -15
- package/dist/cache/index.d.ts +187 -0
- package/dist/cache/index.d.ts.map +1 -0
- package/dist/cli/bin.d.ts +8 -0
- package/dist/cli/bin.d.ts.map +1 -0
- package/dist/cli/bin.js +484 -156
- package/dist/cli/commands/add-frontend.d.ts +7 -0
- package/dist/cli/commands/add-frontend.d.ts.map +1 -0
- package/dist/cli/commands/build.d.ts +7 -0
- package/dist/cli/commands/build.d.ts.map +1 -0
- package/dist/cli/commands/dev.d.ts +7 -0
- package/dist/cli/commands/dev.d.ts.map +1 -0
- package/dist/cli/commands/generate.d.ts +7 -0
- package/dist/cli/commands/generate.d.ts.map +1 -0
- package/dist/cli/commands/help.d.ts +7 -0
- package/dist/cli/commands/help.d.ts.map +1 -0
- package/dist/cli/commands/index.d.ts +59 -0
- package/dist/cli/commands/index.d.ts.map +1 -0
- package/dist/cli/commands/migration.d.ts +7 -0
- package/dist/cli/commands/migration.d.ts.map +1 -0
- package/dist/cli/commands/new.d.ts +7 -0
- package/dist/cli/commands/new.d.ts.map +1 -0
- package/dist/cli/commands/start.d.ts +7 -0
- package/dist/cli/commands/start.d.ts.map +1 -0
- package/dist/cli/core/args.d.ts +61 -0
- package/dist/cli/core/args.d.ts.map +1 -0
- package/dist/cli/core/console.d.ts +135 -0
- package/dist/cli/core/console.d.ts.map +1 -0
- package/dist/cli/core/index.d.ts +10 -0
- package/dist/cli/core/index.d.ts.map +1 -0
- package/dist/cli/core/prompt.d.ts +63 -0
- package/dist/cli/core/prompt.d.ts.map +1 -0
- package/dist/cli/core/spinner.d.ts +111 -0
- package/dist/cli/core/spinner.d.ts.map +1 -0
- package/dist/cli/index.d.ts +47 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/templates/database/index.d.ts +24 -0
- package/dist/cli/templates/database/index.d.ts.map +1 -0
- package/dist/cli/templates/database/mysql.d.ts +6 -0
- package/dist/cli/templates/database/mysql.d.ts.map +1 -0
- package/dist/cli/templates/database/none.d.ts +8 -0
- package/dist/cli/templates/database/none.d.ts.map +1 -0
- package/dist/cli/templates/database/postgresql.d.ts +6 -0
- package/dist/cli/templates/database/postgresql.d.ts.map +1 -0
- package/dist/cli/templates/database/sqlite.d.ts +6 -0
- package/dist/cli/templates/database/sqlite.d.ts.map +1 -0
- package/dist/cli/templates/deploy.d.ts +41 -0
- package/dist/cli/templates/deploy.d.ts.map +1 -0
- package/dist/cli/templates/docker.d.ts +30 -0
- package/dist/cli/templates/docker.d.ts.map +1 -0
- package/dist/cli/templates/frontend/index.d.ts +25 -0
- package/dist/cli/templates/frontend/index.d.ts.map +1 -0
- package/dist/cli/templates/frontend/none.d.ts +8 -0
- package/dist/cli/templates/frontend/none.d.ts.map +1 -0
- package/dist/cli/templates/frontend/react.d.ts +6 -0
- package/dist/cli/templates/frontend/react.d.ts.map +1 -0
- package/dist/cli/templates/frontend/solid.d.ts +6 -0
- package/dist/cli/templates/frontend/solid.d.ts.map +1 -0
- package/dist/cli/templates/frontend/svelte.d.ts +6 -0
- package/dist/cli/templates/frontend/svelte.d.ts.map +1 -0
- package/dist/cli/templates/frontend/vue.d.ts +6 -0
- package/dist/cli/templates/frontend/vue.d.ts.map +1 -0
- package/dist/cli/templates/generators/index.d.ts +29 -0
- package/dist/cli/templates/generators/index.d.ts.map +1 -0
- package/dist/cli/templates/generators/types.d.ts +32 -0
- package/dist/cli/templates/generators/types.d.ts.map +1 -0
- package/dist/cli/templates/index.d.ts +12 -0
- package/dist/cli/templates/index.d.ts.map +1 -0
- package/dist/cli/templates/project/api.d.ts +6 -0
- package/dist/cli/templates/project/api.d.ts.map +1 -0
- package/dist/cli/templates/project/default.d.ts +6 -0
- package/dist/cli/templates/project/default.d.ts.map +1 -0
- package/dist/cli/templates/project/fullstack.d.ts +14 -0
- package/dist/cli/templates/project/fullstack.d.ts.map +1 -0
- package/dist/cli/templates/project/index.d.ts +26 -0
- package/dist/cli/templates/project/index.d.ts.map +1 -0
- package/dist/cli/templates/project/minimal.d.ts +6 -0
- package/dist/cli/templates/project/minimal.d.ts.map +1 -0
- package/dist/cli/templates/project/types.d.ts +80 -0
- package/dist/cli/templates/project/types.d.ts.map +1 -0
- package/dist/cli/templates/project/website.d.ts +8 -0
- package/dist/cli/templates/project/website.d.ts.map +1 -0
- package/dist/cli/utils/fs.d.ts +137 -0
- package/dist/cli/utils/fs.d.ts.map +1 -0
- package/dist/cli/utils/index.d.ts +9 -0
- package/dist/cli/utils/index.d.ts.map +1 -0
- package/dist/cli/utils/strings.d.ts +86 -0
- package/dist/cli/utils/strings.d.ts.map +1 -0
- package/dist/cli/utils/version.d.ts +15 -0
- package/dist/cli/utils/version.d.ts.map +1 -0
- package/dist/config/env-validation.d.ts +49 -0
- package/dist/config/env-validation.d.ts.map +1 -0
- package/dist/config/env.d.ts +167 -0
- package/dist/config/env.d.ts.map +1 -0
- package/dist/config/index.d.ts +168 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/loader.d.ts +81 -0
- package/dist/config/loader.d.ts.map +1 -0
- package/dist/config/merge.d.ts +66 -0
- package/dist/config/merge.d.ts.map +1 -0
- package/dist/config/types.d.ts +322 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/validation.d.ts +100 -0
- package/dist/config/validation.d.ts.map +1 -0
- package/dist/container/forward-ref.d.ts +116 -0
- package/dist/container/forward-ref.d.ts.map +1 -0
- package/dist/container/index.d.ts +95 -0
- package/dist/container/index.d.ts.map +1 -0
- package/dist/container/index.js +26 -3
- package/dist/context/index.d.ts +143 -0
- package/dist/context/index.d.ts.map +1 -0
- package/dist/database/index.d.ts +219 -0
- package/dist/database/index.d.ts.map +1 -0
- package/dist/database/migrations/index.d.ts +146 -0
- package/dist/database/migrations/index.d.ts.map +1 -0
- package/dist/database/orm/builder.d.ts +122 -0
- package/dist/database/orm/builder.d.ts.map +1 -0
- package/dist/database/orm/casts/index.d.ts +16 -0
- package/dist/database/orm/casts/index.d.ts.map +1 -0
- package/dist/database/orm/casts/types.d.ts +16 -0
- package/dist/database/orm/casts/types.d.ts.map +1 -0
- package/dist/database/orm/compiler.d.ts +90 -0
- package/dist/database/orm/compiler.d.ts.map +1 -0
- package/dist/database/orm/hooks/index.d.ts +53 -0
- package/dist/database/orm/hooks/index.d.ts.map +1 -0
- package/dist/database/orm/index.d.ts +21 -0
- package/dist/database/orm/index.d.ts.map +1 -0
- package/dist/database/orm/model-registry.d.ts +33 -0
- package/dist/database/orm/model-registry.d.ts.map +1 -0
- package/dist/database/orm/model.d.ts +245 -0
- package/dist/database/orm/model.d.ts.map +1 -0
- package/dist/database/orm/relationships/base.d.ts +69 -0
- package/dist/database/orm/relationships/base.d.ts.map +1 -0
- package/dist/database/orm/relationships/belongs-to-many.d.ts +47 -0
- package/dist/database/orm/relationships/belongs-to-many.d.ts.map +1 -0
- package/dist/database/orm/relationships/belongs-to.d.ts +17 -0
- package/dist/database/orm/relationships/belongs-to.d.ts.map +1 -0
- package/dist/database/orm/relationships/has-many.d.ts +14 -0
- package/dist/database/orm/relationships/has-many.d.ts.map +1 -0
- package/dist/database/orm/relationships/has-one.d.ts +14 -0
- package/dist/database/orm/relationships/has-one.d.ts.map +1 -0
- package/dist/database/orm/relationships/index.d.ts +10 -0
- package/dist/database/orm/relationships/index.d.ts.map +1 -0
- package/dist/database/orm/scopes/index.d.ts +36 -0
- package/dist/database/orm/scopes/index.d.ts.map +1 -0
- package/dist/database/schema/index.d.ts +155 -0
- package/dist/database/schema/index.d.ts.map +1 -0
- package/dist/events/__tests__/event-system.test.d.ts +2 -0
- package/dist/events/__tests__/event-system.test.d.ts.map +1 -0
- package/dist/events/config.d.ts +16 -0
- package/dist/events/config.d.ts.map +1 -0
- package/dist/events/example-usage.d.ts +12 -0
- package/dist/events/example-usage.d.ts.map +1 -0
- package/dist/events/index.d.ts +27 -0
- package/dist/events/index.d.ts.map +1 -0
- package/dist/events/manager.d.ts +33 -0
- package/dist/events/manager.d.ts.map +1 -0
- package/dist/events/registry.d.ts +31 -0
- package/dist/events/registry.d.ts.map +1 -0
- package/dist/events/types.d.ts +105 -0
- package/dist/events/types.d.ts.map +1 -0
- package/dist/frontend/api-routes.d.ts +189 -0
- package/dist/frontend/api-routes.d.ts.map +1 -0
- package/dist/frontend/bundler.d.ts +99 -0
- package/dist/frontend/bundler.d.ts.map +1 -0
- package/dist/frontend/console-client.d.ts +11 -0
- package/dist/frontend/console-client.d.ts.map +1 -0
- package/dist/frontend/console-stream.d.ts +138 -0
- package/dist/frontend/console-stream.d.ts.map +1 -0
- package/dist/frontend/dev-server.d.ts +174 -0
- package/dist/frontend/dev-server.d.ts.map +1 -0
- package/dist/frontend/file-router.d.ts +170 -0
- package/dist/frontend/file-router.d.ts.map +1 -0
- package/dist/frontend/frameworks/index.d.ts +41 -0
- package/dist/frontend/frameworks/index.d.ts.map +1 -0
- package/dist/frontend/frameworks/react.d.ts +32 -0
- package/dist/frontend/frameworks/react.d.ts.map +1 -0
- package/dist/frontend/frameworks/solid.d.ts +42 -0
- package/dist/frontend/frameworks/solid.d.ts.map +1 -0
- package/dist/frontend/frameworks/svelte.d.ts +57 -0
- package/dist/frontend/frameworks/svelte.d.ts.map +1 -0
- package/dist/frontend/frameworks/vue.d.ts +36 -0
- package/dist/frontend/frameworks/vue.d.ts.map +1 -0
- package/dist/frontend/hmr-client.d.ts +22 -0
- package/dist/frontend/hmr-client.d.ts.map +1 -0
- package/dist/frontend/hmr.d.ts +185 -0
- package/dist/frontend/hmr.d.ts.map +1 -0
- package/dist/frontend/index.d.ts +34 -0
- package/dist/frontend/index.d.ts.map +1 -0
- package/dist/frontend/islands.d.ts +135 -0
- package/dist/frontend/islands.d.ts.map +1 -0
- package/dist/frontend/isr.d.ts +143 -0
- package/dist/frontend/isr.d.ts.map +1 -0
- package/dist/frontend/layout.d.ts +140 -0
- package/dist/frontend/layout.d.ts.map +1 -0
- package/dist/frontend/ssr/react.d.ts +118 -0
- package/dist/frontend/ssr/react.d.ts.map +1 -0
- package/dist/frontend/ssr/solid.d.ts +141 -0
- package/dist/frontend/ssr/solid.d.ts.map +1 -0
- package/dist/frontend/ssr/svelte.d.ts +158 -0
- package/dist/frontend/ssr/svelte.d.ts.map +1 -0
- package/dist/frontend/ssr/vue.d.ts +161 -0
- package/dist/frontend/ssr/vue.d.ts.map +1 -0
- package/dist/frontend/ssr.d.ts +147 -0
- package/dist/frontend/ssr.d.ts.map +1 -0
- package/dist/frontend/types.d.ts +1902 -0
- package/dist/frontend/types.d.ts.map +1 -0
- package/dist/graphql/built-in-engine.d.ts +36 -0
- package/dist/graphql/built-in-engine.d.ts.map +1 -0
- package/dist/graphql/context-builder.d.ts +44 -0
- package/dist/graphql/context-builder.d.ts.map +1 -0
- package/dist/graphql/decorators.d.ts +162 -0
- package/dist/graphql/decorators.d.ts.map +1 -0
- package/dist/graphql/execution-pipeline.d.ts +67 -0
- package/dist/graphql/execution-pipeline.d.ts.map +1 -0
- package/dist/graphql/graphql-module.d.ts +70 -0
- package/dist/graphql/graphql-module.d.ts.map +1 -0
- package/dist/graphql/index.d.ts +48 -0
- package/dist/graphql/index.d.ts.map +1 -0
- package/dist/graphql/index.js +2156 -0
- package/dist/graphql/metadata.d.ts +37 -0
- package/dist/graphql/metadata.d.ts.map +1 -0
- package/dist/graphql/schema-builder.d.ts +34 -0
- package/dist/graphql/schema-builder.d.ts.map +1 -0
- package/dist/graphql/subscription-handler.d.ts +47 -0
- package/dist/graphql/subscription-handler.d.ts.map +1 -0
- package/dist/graphql/types.d.ts +252 -0
- package/dist/graphql/types.d.ts.map +1 -0
- package/dist/health/index.d.ts +176 -0
- package/dist/health/index.d.ts.map +1 -0
- package/dist/i18n/engine.d.ts +105 -0
- package/dist/i18n/engine.d.ts.map +1 -0
- package/dist/i18n/index.d.ts +13 -0
- package/dist/i18n/index.d.ts.map +1 -0
- package/dist/i18n/loader.d.ts +79 -0
- package/dist/i18n/loader.d.ts.map +1 -0
- package/dist/i18n/middleware.d.ts +96 -0
- package/dist/i18n/middleware.d.ts.map +1 -0
- package/dist/i18n/negotiator.d.ts +84 -0
- package/dist/i18n/negotiator.d.ts.map +1 -0
- package/dist/i18n/types.d.ts +129 -0
- package/dist/i18n/types.d.ts.map +1 -0
- package/dist/index.d.ts +48 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +520 -434
- package/dist/jobs/drivers/memory.d.ts +38 -0
- package/dist/jobs/drivers/memory.d.ts.map +1 -0
- package/dist/jobs/drivers/redis.d.ts +34 -0
- package/dist/jobs/drivers/redis.d.ts.map +1 -0
- package/dist/jobs/index.d.ts +12 -0
- package/dist/jobs/index.d.ts.map +1 -0
- package/dist/jobs/queue.d.ts +93 -0
- package/dist/jobs/queue.d.ts.map +1 -0
- package/dist/jobs/types.d.ts +193 -0
- package/dist/jobs/types.d.ts.map +1 -0
- package/dist/jobs/worker.d.ts +91 -0
- package/dist/jobs/worker.d.ts.map +1 -0
- package/dist/lock/index.d.ts +141 -0
- package/dist/lock/index.d.ts.map +1 -0
- package/dist/logger/index.d.ts +156 -0
- package/dist/logger/index.d.ts.map +1 -0
- package/dist/logger/transports/index.d.ts +371 -0
- package/dist/logger/transports/index.d.ts.map +1 -0
- package/dist/metrics/index.d.ts +163 -0
- package/dist/metrics/index.d.ts.map +1 -0
- package/dist/middleware/built-in.d.ts +50 -0
- package/dist/middleware/built-in.d.ts.map +1 -0
- package/dist/middleware/index.d.ts +40 -0
- package/dist/middleware/index.d.ts.map +1 -0
- package/dist/migrations/index.d.ts +10 -0
- package/dist/migrations/index.d.ts.map +1 -0
- package/dist/modules/filters.d.ts +150 -0
- package/dist/modules/filters.d.ts.map +1 -0
- package/dist/modules/guards.d.ts +188 -0
- package/dist/modules/guards.d.ts.map +1 -0
- package/dist/modules/index.d.ts +266 -0
- package/dist/modules/index.d.ts.map +1 -0
- package/dist/modules/index.js +514 -449
- package/dist/modules/interceptors.d.ts +242 -0
- package/dist/modules/interceptors.d.ts.map +1 -0
- package/dist/modules/lazy.d.ts +187 -0
- package/dist/modules/lazy.d.ts.map +1 -0
- package/dist/modules/lifecycle.d.ts +221 -0
- package/dist/modules/lifecycle.d.ts.map +1 -0
- package/dist/modules/metadata.d.ts +32 -0
- package/dist/modules/metadata.d.ts.map +1 -0
- package/dist/modules/pipes.d.ts +287 -0
- package/dist/modules/pipes.d.ts.map +1 -0
- package/dist/notification/channels/base.d.ts +32 -0
- package/dist/notification/channels/base.d.ts.map +1 -0
- package/dist/notification/channels/email.d.ts +37 -0
- package/dist/notification/channels/email.d.ts.map +1 -0
- package/dist/notification/channels/push.d.ts +37 -0
- package/dist/notification/channels/push.d.ts.map +1 -0
- package/dist/notification/channels/sms.d.ts +37 -0
- package/dist/notification/channels/sms.d.ts.map +1 -0
- package/dist/notification/channels/whatsapp.d.ts +37 -0
- package/dist/notification/channels/whatsapp.d.ts.map +1 -0
- package/dist/notification/index.d.ts +15 -0
- package/dist/notification/index.d.ts.map +1 -0
- package/dist/notification/service.d.ts +100 -0
- package/dist/notification/service.d.ts.map +1 -0
- package/dist/notification/types.d.ts +253 -0
- package/dist/notification/types.d.ts.map +1 -0
- package/dist/observability/__tests__/observability.test.d.ts +2 -0
- package/dist/observability/__tests__/observability.test.d.ts.map +1 -0
- package/dist/observability/breadcrumbs.d.ts +48 -0
- package/dist/observability/breadcrumbs.d.ts.map +1 -0
- package/dist/observability/index.d.ts +95 -0
- package/dist/observability/index.d.ts.map +1 -0
- package/dist/observability/interceptor.d.ts +19 -0
- package/dist/observability/interceptor.d.ts.map +1 -0
- package/dist/observability/service.d.ts +101 -0
- package/dist/observability/service.d.ts.map +1 -0
- package/dist/observability/trace.d.ts +21 -0
- package/dist/observability/trace.d.ts.map +1 -0
- package/dist/observability/types.d.ts +172 -0
- package/dist/observability/types.d.ts.map +1 -0
- package/dist/openapi/__tests__/decorators.test.d.ts +2 -0
- package/dist/openapi/__tests__/decorators.test.d.ts.map +1 -0
- package/dist/openapi/__tests__/document-builder.test.d.ts +2 -0
- package/dist/openapi/__tests__/document-builder.test.d.ts.map +1 -0
- package/dist/openapi/__tests__/route-scanner.test.d.ts +2 -0
- package/dist/openapi/__tests__/route-scanner.test.d.ts.map +1 -0
- package/dist/openapi/__tests__/schema-generator.test.d.ts +2 -0
- package/dist/openapi/__tests__/schema-generator.test.d.ts.map +1 -0
- package/dist/openapi/decorators.d.ts +173 -0
- package/dist/openapi/decorators.d.ts.map +1 -0
- package/dist/openapi/document-builder.d.ts +82 -0
- package/dist/openapi/document-builder.d.ts.map +1 -0
- package/dist/openapi/index.d.ts +48 -0
- package/dist/openapi/index.d.ts.map +1 -0
- package/dist/openapi/index.js +59 -40
- package/dist/openapi/metadata.d.ts +36 -0
- package/dist/openapi/metadata.d.ts.map +1 -0
- package/dist/openapi/route-scanner.d.ts +34 -0
- package/dist/openapi/route-scanner.d.ts.map +1 -0
- package/dist/openapi/schema-generator.d.ts +53 -0
- package/dist/openapi/schema-generator.d.ts.map +1 -0
- package/dist/openapi/swagger-module.d.ts +57 -0
- package/dist/openapi/swagger-module.d.ts.map +1 -0
- package/dist/openapi/types.d.ts +344 -0
- package/dist/openapi/types.d.ts.map +1 -0
- package/dist/orm/index.d.ts +10 -0
- package/dist/orm/index.d.ts.map +1 -0
- package/dist/router/index.d.ts +73 -0
- package/dist/router/index.d.ts.map +1 -0
- package/dist/router/linear.d.ts +54 -0
- package/dist/router/linear.d.ts.map +1 -0
- package/dist/router/regex.d.ts +49 -0
- package/dist/router/regex.d.ts.map +1 -0
- package/dist/router/tree.d.ts +112 -0
- package/dist/router/tree.d.ts.map +1 -0
- package/dist/rpc/index.d.ts +321 -0
- package/dist/rpc/index.d.ts.map +1 -0
- package/dist/schema/index.d.ts +10 -0
- package/dist/schema/index.d.ts.map +1 -0
- package/dist/security/index.d.ts +126 -0
- package/dist/security/index.d.ts.map +1 -0
- package/dist/ssg/index.d.ts +73 -0
- package/dist/ssg/index.d.ts.map +1 -0
- package/dist/storage/index.d.ts +99 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/telemetry/index.d.ts +376 -0
- package/dist/telemetry/index.d.ts.map +1 -0
- package/dist/template/index.d.ts +7 -0
- package/dist/template/index.d.ts.map +1 -0
- package/dist/templates/engine.d.ts +60 -0
- package/dist/templates/engine.d.ts.map +1 -0
- package/dist/templates/index.d.ts +9 -0
- package/dist/templates/index.d.ts.map +1 -0
- package/dist/templates/loader.d.ts +45 -0
- package/dist/templates/loader.d.ts.map +1 -0
- package/dist/templates/renderers/markdown.d.ts +46 -0
- package/dist/templates/renderers/markdown.d.ts.map +1 -0
- package/dist/templates/renderers/simple.d.ts +35 -0
- package/dist/templates/renderers/simple.d.ts.map +1 -0
- package/dist/templates/types.d.ts +138 -0
- package/dist/templates/types.d.ts.map +1 -0
- package/dist/testing/index.d.ts +539 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/types/index.d.ts +116 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/validation/index.d.ts +89 -0
- package/dist/validation/index.d.ts.map +1 -0
- package/dist/validation/schemas.d.ts +243 -0
- package/dist/validation/schemas.d.ts.map +1 -0
- package/dist/websocket/index.d.ts +252 -0
- package/dist/websocket/index.d.ts.map +1 -0
- package/llms.txt +231 -0
- package/package.json +6 -2
- package/src/cli/ARCHITECTURE.md +3 -3
- package/src/cli/commands/add-frontend.ts +444 -0
- package/src/cli/commands/new.ts +23 -0
- package/src/cli/index.ts +1 -0
- package/src/cli/templates/frontend/react.ts +2 -1
- package/src/cli/templates/frontend/solid.ts +2 -1
- package/src/cli/templates/frontend/svelte.ts +2 -1
- package/src/cli/templates/frontend/vue.ts +2 -1
- package/src/cli/templates/project/api.ts +1 -1
- package/src/cli/templates/project/default.ts +1 -1
- package/src/cli/templates/project/fullstack.ts +14 -104
- package/src/cli/templates/project/website.ts +63 -12
- package/src/config/types.ts +21 -0
- package/src/graphql/built-in-engine.ts +598 -0
- package/src/graphql/context-builder.ts +110 -0
- package/src/graphql/decorators.ts +358 -0
- package/src/graphql/execution-pipeline.ts +227 -0
- package/src/graphql/graphql-module.ts +563 -0
- package/src/graphql/index.ts +101 -0
- package/src/graphql/metadata.ts +237 -0
- package/src/graphql/schema-builder.ts +319 -0
- package/src/graphql/subscription-handler.ts +283 -0
- package/src/graphql/types.ts +324 -0
- package/src/index.ts +3 -0
- package/src/modules/index.ts +48 -1
- package/tests/integration/cli.test.ts +19 -19
- package/tests/unit/cli.test.ts +1 -1
- package/tests/unit/graphql.test.ts +991 -0
- package/tsconfig.declaration.json +14 -0
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GraphQL Subscription Handler
|
|
3
|
+
*
|
|
4
|
+
* Implements the graphql-transport-ws protocol over Bun's native WebSocket.
|
|
5
|
+
* Registered via app.setWebSocketHandler() so subscriptions run on the same
|
|
6
|
+
* port as the HTTP server — no separate port needed.
|
|
7
|
+
*
|
|
8
|
+
* Protocol spec: https://github.com/enisdenjo/graphql-ws/blob/master/PROTOCOL.md
|
|
9
|
+
*
|
|
10
|
+
* ## Usage
|
|
11
|
+
* Automatically configured by GraphQLModule.setup() when subscriptions: true.
|
|
12
|
+
* Requires an engine with supportsSubscriptions = true (e.g. GraphQLJsAdapter).
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import type { GraphQLEngine, GraphQLContext, ResolvedSchema } from "./types";
|
|
16
|
+
import type { Container } from "../container";
|
|
17
|
+
import type { Guard, Interceptor } from "./execution-pipeline";
|
|
18
|
+
import { buildGraphQLContext } from "./context-builder";
|
|
19
|
+
|
|
20
|
+
// ============= Protocol Message Types =============
|
|
21
|
+
|
|
22
|
+
interface ConnectionInitMessage {
|
|
23
|
+
type: "connection_init";
|
|
24
|
+
payload?: Record<string, unknown>;
|
|
25
|
+
}
|
|
26
|
+
interface ConnectionAckMessage {
|
|
27
|
+
type: "connection_ack";
|
|
28
|
+
}
|
|
29
|
+
interface SubscribeMessage {
|
|
30
|
+
type: "subscribe";
|
|
31
|
+
id: string;
|
|
32
|
+
payload: {
|
|
33
|
+
query: string;
|
|
34
|
+
variables?: Record<string, unknown>;
|
|
35
|
+
operationName?: string;
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
interface NextMessage {
|
|
39
|
+
type: "next";
|
|
40
|
+
id: string;
|
|
41
|
+
payload: { data?: unknown; errors?: unknown[] };
|
|
42
|
+
}
|
|
43
|
+
interface ErrorMessage {
|
|
44
|
+
type: "error";
|
|
45
|
+
id: string;
|
|
46
|
+
payload: Array<{ message: string }>;
|
|
47
|
+
}
|
|
48
|
+
interface CompleteMessage {
|
|
49
|
+
type: "complete";
|
|
50
|
+
id?: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
type ServerMessage = ConnectionAckMessage | NextMessage | ErrorMessage | CompleteMessage;
|
|
54
|
+
type ClientMessage = ConnectionInitMessage | SubscribeMessage | CompleteMessage;
|
|
55
|
+
|
|
56
|
+
// ============= Connection State =============
|
|
57
|
+
|
|
58
|
+
interface SubscriptionState {
|
|
59
|
+
generator: AsyncGenerator<unknown>;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
interface ConnectionState {
|
|
63
|
+
subscriptions: Map<string, SubscriptionState>;
|
|
64
|
+
initialized: boolean;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// ============= WebSocket Data =============
|
|
68
|
+
|
|
69
|
+
interface WsData {
|
|
70
|
+
connectionId: string;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// ============= Subscription Handler =============
|
|
74
|
+
|
|
75
|
+
export class SubscriptionHandler {
|
|
76
|
+
private connections = new Map<string, ConnectionState>();
|
|
77
|
+
|
|
78
|
+
constructor(
|
|
79
|
+
private engine: GraphQLEngine,
|
|
80
|
+
private engineSchema: unknown,
|
|
81
|
+
private graphqlPath: string,
|
|
82
|
+
private container: Container,
|
|
83
|
+
private globalGuards: Guard[],
|
|
84
|
+
private globalInterceptors: Interceptor[],
|
|
85
|
+
) {}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Returns a Bun WebSocketHandler to register on the server via app.setWebSocketHandler().
|
|
89
|
+
* Attaches an __upgradeHandler so Application.listen() can delegate WebSocket upgrades.
|
|
90
|
+
*/
|
|
91
|
+
getWebSocketConfig(): Bun.WebSocketHandler<WsData> & { __upgradeHandler: (req: Request, srv: Bun.Server) => Response | undefined } {
|
|
92
|
+
const upgradeHandler = this.handleUpgrade.bind(this);
|
|
93
|
+
return {
|
|
94
|
+
__upgradeHandler: upgradeHandler,
|
|
95
|
+
open: (ws) => {
|
|
96
|
+
this.connections.set(ws.data.connectionId, {
|
|
97
|
+
subscriptions: new Map(),
|
|
98
|
+
initialized: false,
|
|
99
|
+
});
|
|
100
|
+
},
|
|
101
|
+
|
|
102
|
+
message: async (ws, raw) => {
|
|
103
|
+
try {
|
|
104
|
+
const text =
|
|
105
|
+
typeof raw === "string" ? raw : new TextDecoder().decode(raw as ArrayBuffer);
|
|
106
|
+
const msg = JSON.parse(text) as ClientMessage;
|
|
107
|
+
await this.handleMessage(ws, msg);
|
|
108
|
+
} catch (err) {
|
|
109
|
+
this.send(ws, {
|
|
110
|
+
type: "error",
|
|
111
|
+
id: "",
|
|
112
|
+
payload: [{ message: `Protocol error: ${(err as Error).message}` }],
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
|
|
117
|
+
close: (ws) => {
|
|
118
|
+
const state = this.connections.get(ws.data.connectionId);
|
|
119
|
+
if (state) {
|
|
120
|
+
// Cancel all active subscriptions
|
|
121
|
+
for (const sub of state.subscriptions.values()) {
|
|
122
|
+
sub.generator.return?.(undefined);
|
|
123
|
+
}
|
|
124
|
+
this.connections.delete(ws.data.connectionId);
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
|
|
128
|
+
error: (ws, error) => {
|
|
129
|
+
console.error("[GraphQL WS] WebSocket error:", error);
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Middleware-like upgrade handler.
|
|
136
|
+
* Call this from the app's fetch function for WebSocket upgrade requests.
|
|
137
|
+
* Returns a Response if not a GraphQL WS path, undefined if upgraded.
|
|
138
|
+
*/
|
|
139
|
+
handleUpgrade(req: Request, server: Bun.Server): Response | undefined {
|
|
140
|
+
const url = new URL(req.url);
|
|
141
|
+
if (url.pathname !== this.graphqlPath) {
|
|
142
|
+
return undefined; // Not our path — let normal routing handle it
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const protocol = req.headers.get("sec-websocket-protocol") ?? "";
|
|
146
|
+
if (!protocol.includes("graphql-transport-ws")) {
|
|
147
|
+
return new Response("Unsupported WebSocket protocol", { status: 426 });
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const connectionId = crypto.randomUUID();
|
|
151
|
+
const upgraded = server.upgrade<WsData>(req, {
|
|
152
|
+
headers: {
|
|
153
|
+
"Sec-WebSocket-Protocol": "graphql-transport-ws",
|
|
154
|
+
},
|
|
155
|
+
data: { connectionId },
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
return upgraded ? undefined : new Response("WebSocket upgrade failed", { status: 400 });
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// ============= Message Handlers =============
|
|
162
|
+
|
|
163
|
+
private async handleMessage(
|
|
164
|
+
ws: Bun.ServerWebSocket<WsData>,
|
|
165
|
+
msg: ClientMessage,
|
|
166
|
+
): Promise<void> {
|
|
167
|
+
const state = this.connections.get(ws.data.connectionId);
|
|
168
|
+
if (!state) return;
|
|
169
|
+
|
|
170
|
+
switch (msg.type) {
|
|
171
|
+
case "connection_init":
|
|
172
|
+
if (state.initialized) {
|
|
173
|
+
// Already initialized — send error and close
|
|
174
|
+
ws.close(4429, "Too many initialisation requests");
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
state.initialized = true;
|
|
178
|
+
this.send(ws, { type: "connection_ack" });
|
|
179
|
+
break;
|
|
180
|
+
|
|
181
|
+
case "subscribe":
|
|
182
|
+
if (!state.initialized) {
|
|
183
|
+
ws.close(4401, "Unauthorized: connection not initialized");
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
if (state.subscriptions.has(msg.id)) {
|
|
187
|
+
ws.close(4409, `Subscriber for ${msg.id} already exists`);
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
await this.handleSubscribe(ws, state, msg);
|
|
191
|
+
break;
|
|
192
|
+
|
|
193
|
+
case "complete":
|
|
194
|
+
if (msg.id) {
|
|
195
|
+
const sub = state.subscriptions.get(msg.id);
|
|
196
|
+
if (sub) {
|
|
197
|
+
sub.generator.return?.(undefined);
|
|
198
|
+
state.subscriptions.delete(msg.id);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
break;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
private async handleSubscribe(
|
|
206
|
+
ws: Bun.ServerWebSocket<WsData>,
|
|
207
|
+
state: ConnectionState,
|
|
208
|
+
msg: SubscribeMessage,
|
|
209
|
+
): Promise<void> {
|
|
210
|
+
if (!this.engine.subscribe) {
|
|
211
|
+
this.send(ws, {
|
|
212
|
+
type: "error",
|
|
213
|
+
id: msg.id,
|
|
214
|
+
payload: [
|
|
215
|
+
{
|
|
216
|
+
message:
|
|
217
|
+
"Subscriptions are not supported by the configured GraphQL engine. " +
|
|
218
|
+
"Use GraphQLJsAdapter for subscription support.",
|
|
219
|
+
},
|
|
220
|
+
],
|
|
221
|
+
});
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const context: GraphQLContext = {
|
|
226
|
+
request: new Request(`ws://localhost${this.graphqlPath}`),
|
|
227
|
+
user: undefined,
|
|
228
|
+
httpContext: null,
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
let generator: AsyncGenerator<unknown>;
|
|
232
|
+
try {
|
|
233
|
+
generator = await this.engine.subscribe(
|
|
234
|
+
this.engineSchema,
|
|
235
|
+
msg.payload.query,
|
|
236
|
+
msg.payload.variables ?? {},
|
|
237
|
+
context,
|
|
238
|
+
msg.payload.operationName,
|
|
239
|
+
);
|
|
240
|
+
} catch (err) {
|
|
241
|
+
this.send(ws, {
|
|
242
|
+
type: "error",
|
|
243
|
+
id: msg.id,
|
|
244
|
+
payload: [{ message: (err as Error).message }],
|
|
245
|
+
});
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
state.subscriptions.set(msg.id, { generator });
|
|
250
|
+
|
|
251
|
+
// Stream results
|
|
252
|
+
(async () => {
|
|
253
|
+
try {
|
|
254
|
+
for await (const result of generator) {
|
|
255
|
+
if (!state.subscriptions.has(msg.id)) break; // Cancelled
|
|
256
|
+
this.send(ws, {
|
|
257
|
+
type: "next",
|
|
258
|
+
id: msg.id,
|
|
259
|
+
payload: result as { data?: unknown; errors?: unknown[] },
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
// Subscription completed
|
|
263
|
+
this.send(ws, { type: "complete", id: msg.id });
|
|
264
|
+
state.subscriptions.delete(msg.id);
|
|
265
|
+
} catch (err) {
|
|
266
|
+
this.send(ws, {
|
|
267
|
+
type: "error",
|
|
268
|
+
id: msg.id,
|
|
269
|
+
payload: [{ message: (err as Error).message }],
|
|
270
|
+
});
|
|
271
|
+
state.subscriptions.delete(msg.id);
|
|
272
|
+
}
|
|
273
|
+
})();
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
private send(ws: Bun.ServerWebSocket<WsData>, msg: ServerMessage): void {
|
|
277
|
+
try {
|
|
278
|
+
ws.send(JSON.stringify(msg));
|
|
279
|
+
} catch {
|
|
280
|
+
// Connection may have closed — ignore
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GraphQL Module Types
|
|
3
|
+
*
|
|
4
|
+
* Core type definitions for the Bueno GraphQL integration layer.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// ============= Constructor Type =============
|
|
8
|
+
|
|
9
|
+
export type Constructor<T = unknown> = new (...args: unknown[]) => T;
|
|
10
|
+
|
|
11
|
+
// ============= Scalar Types =============
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Represents a GraphQL scalar type via its JS constructor.
|
|
15
|
+
* Usage: () => String, () => Number, () => Boolean, () => GraphQLID
|
|
16
|
+
*/
|
|
17
|
+
export type GraphQLScalar =
|
|
18
|
+
| StringConstructor
|
|
19
|
+
| NumberConstructor
|
|
20
|
+
| BooleanConstructor
|
|
21
|
+
| typeof GraphQLID
|
|
22
|
+
| typeof GraphQLInt
|
|
23
|
+
| typeof GraphQLFloat;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* A thunk that returns a type constructor or array of constructors.
|
|
27
|
+
* Used to avoid circular reference issues in type definitions.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* () => String // GraphQL String scalar
|
|
31
|
+
* () => Number // GraphQL Float scalar
|
|
32
|
+
* () => [User] // [User!]! list
|
|
33
|
+
* () => User // User object type
|
|
34
|
+
*/
|
|
35
|
+
export type TypeFn = () => Constructor | Constructor[] | GraphQLScalar;
|
|
36
|
+
|
|
37
|
+
// ============= Scalar Sentinels =============
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Sentinel class representing GraphQL ID scalar.
|
|
41
|
+
* Usage: @Field(() => GraphQLID)
|
|
42
|
+
*/
|
|
43
|
+
export class GraphQLID {
|
|
44
|
+
static readonly __type = "ID";
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Sentinel class representing GraphQL Int scalar.
|
|
49
|
+
* Usage: @Field(() => GraphQLInt)
|
|
50
|
+
*/
|
|
51
|
+
export class GraphQLInt {
|
|
52
|
+
static readonly __type = "Int";
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Sentinel class representing GraphQL Float scalar.
|
|
57
|
+
* Usage: @Field(() => GraphQLFloat)
|
|
58
|
+
*/
|
|
59
|
+
export class GraphQLFloat {
|
|
60
|
+
static readonly __type = "Float";
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// ============= Metadata Shapes =============
|
|
64
|
+
|
|
65
|
+
/** Options for @Field decorator */
|
|
66
|
+
export interface FieldDecoratorOptions {
|
|
67
|
+
/** Whether the field can be null (default: false → non-null) */
|
|
68
|
+
nullable?: boolean;
|
|
69
|
+
/** Human-readable description */
|
|
70
|
+
description?: string;
|
|
71
|
+
/** Deprecation reason */
|
|
72
|
+
deprecationReason?: string;
|
|
73
|
+
/** Default value for input types */
|
|
74
|
+
defaultValue?: unknown;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Options for @Query/@Mutation/@Subscription decorators */
|
|
78
|
+
export interface FieldOptions extends FieldDecoratorOptions {
|
|
79
|
+
/** GraphQL field name override (default: method name) */
|
|
80
|
+
name?: string;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Metadata stored per @Field-decorated property */
|
|
84
|
+
export interface FieldMetadata {
|
|
85
|
+
propertyKey: string;
|
|
86
|
+
typeFn: TypeFn;
|
|
87
|
+
nullable: boolean;
|
|
88
|
+
description?: string;
|
|
89
|
+
deprecationReason?: string;
|
|
90
|
+
defaultValue?: unknown;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** Metadata for a single resolver method parameter */
|
|
94
|
+
export interface ParamMetadata {
|
|
95
|
+
index: number;
|
|
96
|
+
kind: "args" | "argsObject" | "context";
|
|
97
|
+
/** For @Args('name') — the specific argument name */
|
|
98
|
+
argName?: string;
|
|
99
|
+
/** For @Args('name', InputType) — the input object type */
|
|
100
|
+
inputTypeFn?: TypeFn;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** Metadata stored per @Query/@Mutation/@Subscription method */
|
|
104
|
+
export interface ResolverFieldMetadata {
|
|
105
|
+
/** Actual method name on the class */
|
|
106
|
+
methodName: string;
|
|
107
|
+
/** GraphQL field name (defaults to methodName) */
|
|
108
|
+
fieldName: string;
|
|
109
|
+
typeFn: TypeFn;
|
|
110
|
+
kind: "query" | "mutation" | "subscription";
|
|
111
|
+
nullable: boolean;
|
|
112
|
+
description?: string;
|
|
113
|
+
deprecationReason?: string;
|
|
114
|
+
paramMetadata: ParamMetadata[];
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Metadata stored per @Resolver-decorated class */
|
|
118
|
+
export interface ResolverClassMetadata {
|
|
119
|
+
/** GraphQL type name (default: class name) */
|
|
120
|
+
name: string;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Metadata stored per @ObjectType / @InputType class */
|
|
124
|
+
export interface TypeClassMetadata {
|
|
125
|
+
name: string;
|
|
126
|
+
kind: "object" | "input";
|
|
127
|
+
description?: string;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// ============= Schema Representation =============
|
|
131
|
+
|
|
132
|
+
/** A single resolvable field in the built schema */
|
|
133
|
+
export interface ResolvedField {
|
|
134
|
+
resolverInstance: unknown;
|
|
135
|
+
methodName: string;
|
|
136
|
+
paramMetadata: ParamMetadata[];
|
|
137
|
+
typeFn: TypeFn;
|
|
138
|
+
nullable: boolean;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** Internal schema representation consumed by the built-in engine */
|
|
142
|
+
export interface ResolvedSchema {
|
|
143
|
+
queryFields: Map<string, ResolvedField>;
|
|
144
|
+
mutationFields: Map<string, ResolvedField>;
|
|
145
|
+
subscriptionFields: Map<string, ResolvedField>;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// ============= GraphQL Context =============
|
|
149
|
+
|
|
150
|
+
/** Context available inside resolver methods */
|
|
151
|
+
export interface GraphQLContext {
|
|
152
|
+
/** The original HTTP Request */
|
|
153
|
+
request: Request;
|
|
154
|
+
/** Authenticated user (set by auth guards via context.set('user', ...)) */
|
|
155
|
+
user?: unknown;
|
|
156
|
+
/** Reference to the HTTP Context for advanced use */
|
|
157
|
+
httpContext: unknown;
|
|
158
|
+
/** Allow arbitrary values */
|
|
159
|
+
[key: string]: unknown;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// ============= Execution Results =============
|
|
163
|
+
|
|
164
|
+
/** A GraphQL error entry in the response */
|
|
165
|
+
export interface GraphQLError {
|
|
166
|
+
message: string;
|
|
167
|
+
locations?: Array<{ line: number; column: number }>;
|
|
168
|
+
path?: Array<string | number>;
|
|
169
|
+
extensions?: Record<string, unknown>;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** The GraphQL response envelope */
|
|
173
|
+
export interface GraphQLResult {
|
|
174
|
+
data?: Record<string, unknown> | null;
|
|
175
|
+
errors?: GraphQLError[];
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// ============= Engine Interface =============
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Pluggable GraphQL execution engine.
|
|
182
|
+
* Implement this interface to use graphql-js, GraphQL Yoga, Mercurius, etc.
|
|
183
|
+
*
|
|
184
|
+
* @example
|
|
185
|
+
* ```typescript
|
|
186
|
+
* import * as graphqlJs from 'graphql';
|
|
187
|
+
* import { GraphQLJsAdapter } from '@buenojs/bueno/graphql';
|
|
188
|
+
*
|
|
189
|
+
* GraphQLModule.setup(app, {
|
|
190
|
+
* engine: new GraphQLJsAdapter(graphqlJs),
|
|
191
|
+
* resolvers: [UserResolver],
|
|
192
|
+
* });
|
|
193
|
+
* ```
|
|
194
|
+
*/
|
|
195
|
+
export interface GraphQLEngine {
|
|
196
|
+
/**
|
|
197
|
+
* Build the internal schema representation from resolver and type metadata.
|
|
198
|
+
* Returns an opaque schema object that is passed back to execute().
|
|
199
|
+
*/
|
|
200
|
+
buildSchema(
|
|
201
|
+
resolvers: ResolverFieldsByType,
|
|
202
|
+
types: Map<string, FieldMetadata[]>,
|
|
203
|
+
sdl: string,
|
|
204
|
+
): unknown;
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Execute a GraphQL query or mutation.
|
|
208
|
+
*/
|
|
209
|
+
execute(
|
|
210
|
+
schema: unknown,
|
|
211
|
+
query: string,
|
|
212
|
+
variables: Record<string, unknown>,
|
|
213
|
+
context: GraphQLContext,
|
|
214
|
+
operationName?: string,
|
|
215
|
+
): Promise<GraphQLResult>;
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Execute a GraphQL subscription (optional).
|
|
219
|
+
* Returns an async generator that yields results.
|
|
220
|
+
*/
|
|
221
|
+
subscribe?(
|
|
222
|
+
schema: unknown,
|
|
223
|
+
query: string,
|
|
224
|
+
variables: Record<string, unknown>,
|
|
225
|
+
context: GraphQLContext,
|
|
226
|
+
operationName?: string,
|
|
227
|
+
): Promise<AsyncGenerator<GraphQLResult>>;
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Whether this engine supports introspection queries.
|
|
231
|
+
* Used to determine if the GraphQL Playground should be enabled.
|
|
232
|
+
*/
|
|
233
|
+
readonly supportsIntrospection: boolean;
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Whether this engine supports subscriptions.
|
|
237
|
+
*/
|
|
238
|
+
readonly supportsSubscriptions: boolean;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/** Resolver fields organized by root type */
|
|
242
|
+
export interface ResolverFieldsByType {
|
|
243
|
+
queries: Map<string, ResolvedField>;
|
|
244
|
+
mutations: Map<string, ResolvedField>;
|
|
245
|
+
subscriptions: Map<string, ResolvedField>;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// ============= Module Options =============
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Options passed to GraphQLModule.setup()
|
|
252
|
+
*/
|
|
253
|
+
export interface GraphQLModuleOptions {
|
|
254
|
+
/**
|
|
255
|
+
* Resolver classes to register.
|
|
256
|
+
* Dependencies are resolved from the DI container.
|
|
257
|
+
*/
|
|
258
|
+
resolvers: Constructor[];
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* GraphQL engine adapter (default: built-in lightweight engine).
|
|
262
|
+
* Use GraphQLJsAdapter for full spec compliance.
|
|
263
|
+
*/
|
|
264
|
+
engine?: GraphQLEngine;
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* HTTP path for the GraphQL endpoint (default: '/graphql').
|
|
268
|
+
*/
|
|
269
|
+
path?: string;
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Enable GraphiQL playground UI at GET <path>.
|
|
273
|
+
* Default: true when using an engine with supportsIntrospection,
|
|
274
|
+
* false when using the built-in engine.
|
|
275
|
+
* Set to true to force-enable even with the built-in engine (with warning).
|
|
276
|
+
*/
|
|
277
|
+
playground?: boolean;
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Serve the SDL at GET <path>/schema (default: true).
|
|
281
|
+
*/
|
|
282
|
+
introspection?: boolean;
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Enable WebSocket subscriptions (default: false).
|
|
286
|
+
* Requires an engine with supportsSubscriptions.
|
|
287
|
+
*/
|
|
288
|
+
subscriptions?: boolean;
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Sync @Field metadata to the OpenAPI property store (default: false).
|
|
292
|
+
* Enables unified types: one class works for both GraphQL and REST/OpenAPI.
|
|
293
|
+
* Requires @buenojs/bueno/openapi to be in scope.
|
|
294
|
+
*/
|
|
295
|
+
syncOpenAPI?: boolean;
|
|
296
|
+
|
|
297
|
+
/** Maximum query complexity score (default: 1000) */
|
|
298
|
+
complexityLimit?: number;
|
|
299
|
+
|
|
300
|
+
/** Maximum query depth (default: 10) */
|
|
301
|
+
maxDepth?: number;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// ============= Config Interface =============
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* GraphQL configuration added to BuenoConfig.
|
|
308
|
+
*/
|
|
309
|
+
export interface GraphQLConfig {
|
|
310
|
+
/** Enable GraphQL support (default: false) */
|
|
311
|
+
enabled?: boolean;
|
|
312
|
+
/** HTTP path (default: '/graphql') */
|
|
313
|
+
path?: string;
|
|
314
|
+
/** Enable playground (default: auto) */
|
|
315
|
+
playground?: boolean;
|
|
316
|
+
/** Enable introspection SDL endpoint (default: true) */
|
|
317
|
+
introspection?: boolean;
|
|
318
|
+
/** Maximum query complexity (default: 1000) */
|
|
319
|
+
complexityLimit?: number;
|
|
320
|
+
/** Maximum query depth (default: 10) */
|
|
321
|
+
maxDepth?: number;
|
|
322
|
+
/** Enable subscriptions (default: false) */
|
|
323
|
+
subscriptions?: boolean;
|
|
324
|
+
}
|
package/src/index.ts
CHANGED
package/src/modules/index.ts
CHANGED
|
@@ -356,6 +356,8 @@ export class Application {
|
|
|
356
356
|
new Map();
|
|
357
357
|
private moduleLoader: ModuleLoader;
|
|
358
358
|
private loadedLazyModules = new Set<Constructor>();
|
|
359
|
+
// biome-ignore lint/suspicious/noExplicitAny: WebSocket data type is user-defined
|
|
360
|
+
private websocketHandler: Bun.WebSocketHandler<any> | null = null;
|
|
359
361
|
|
|
360
362
|
constructor(moduleClass: Constructor) {
|
|
361
363
|
this.container = new Container();
|
|
@@ -385,6 +387,37 @@ export class Application {
|
|
|
385
387
|
return this;
|
|
386
388
|
}
|
|
387
389
|
|
|
390
|
+
/**
|
|
391
|
+
* Get the list of global guards (live reference).
|
|
392
|
+
* Used by the GraphQL module to run guards on resolver methods.
|
|
393
|
+
*/
|
|
394
|
+
getGlobalGuards(): Guard[] {
|
|
395
|
+
return this.globalGuards;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Get the list of global interceptors (live reference).
|
|
400
|
+
* Used by the GraphQL module to run interceptors on resolver methods.
|
|
401
|
+
*/
|
|
402
|
+
getGlobalInterceptors(): Interceptor[] {
|
|
403
|
+
return this.globalInterceptors;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* Register a WebSocket handler to be used when the server starts.
|
|
408
|
+
* Called by the GraphQL module to enable subscriptions on the same port.
|
|
409
|
+
*
|
|
410
|
+
* @example
|
|
411
|
+
* ```typescript
|
|
412
|
+
* app.setWebSocketHandler(subscriptionHandler.getWebSocketConfig());
|
|
413
|
+
* await app.listen(3000);
|
|
414
|
+
* ```
|
|
415
|
+
*/
|
|
416
|
+
// biome-ignore lint/suspicious/noExplicitAny: WebSocket data type is user-defined
|
|
417
|
+
setWebSocketHandler(handler: Bun.WebSocketHandler<any>): void {
|
|
418
|
+
this.websocketHandler = handler;
|
|
419
|
+
}
|
|
420
|
+
|
|
388
421
|
/**
|
|
389
422
|
* Add global pipes that apply to all parameters
|
|
390
423
|
* Global pipes run before parameter decorator pipes
|
|
@@ -793,7 +826,21 @@ export class Application {
|
|
|
793
826
|
this.server = Bun.serve({
|
|
794
827
|
port,
|
|
795
828
|
hostname,
|
|
796
|
-
|
|
829
|
+
...(this.websocketHandler ? { websocket: this.websocketHandler } : {}),
|
|
830
|
+
fetch: async (request: Request, server: Bun.Server) => {
|
|
831
|
+
// Handle WebSocket upgrade requests for subscriptions
|
|
832
|
+
if (
|
|
833
|
+
this.websocketHandler &&
|
|
834
|
+
request.headers.get("upgrade")?.toLowerCase() === "websocket"
|
|
835
|
+
) {
|
|
836
|
+
// The GraphQL subscription handler may have registered an upgrade callback
|
|
837
|
+
const wsUpgradeHandler = (this.websocketHandler as unknown as { __upgradeHandler?: (req: Request, srv: Bun.Server) => Response | undefined }).__upgradeHandler;
|
|
838
|
+
if (wsUpgradeHandler) {
|
|
839
|
+
const response = wsUpgradeHandler(request, server);
|
|
840
|
+
if (response) return response;
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
|
|
797
844
|
// Reject new requests during shutdown
|
|
798
845
|
if (this.isShuttingDown) {
|
|
799
846
|
return new Response("Service Unavailable", { status: 503 });
|