@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,2156 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __export = (target, all) => {
|
|
4
|
+
for (var name in all)
|
|
5
|
+
__defProp(target, name, {
|
|
6
|
+
get: all[name],
|
|
7
|
+
enumerable: true,
|
|
8
|
+
configurable: true,
|
|
9
|
+
set: (newValue) => all[name] = () => newValue
|
|
10
|
+
});
|
|
11
|
+
};
|
|
12
|
+
var __legacyDecorateClassTS = function(decorators, target, key, desc) {
|
|
13
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
14
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
|
|
15
|
+
r = Reflect.decorate(decorators, target, key, desc);
|
|
16
|
+
else
|
|
17
|
+
for (var i = decorators.length - 1;i >= 0; i--)
|
|
18
|
+
if (d = decorators[i])
|
|
19
|
+
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
20
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
21
|
+
};
|
|
22
|
+
var __legacyMetadataTS = (k, v) => {
|
|
23
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function")
|
|
24
|
+
return Reflect.metadata(k, v);
|
|
25
|
+
};
|
|
26
|
+
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
27
|
+
var __require = import.meta.require;
|
|
28
|
+
|
|
29
|
+
// src/container/forward-ref.ts
|
|
30
|
+
function forwardRef(fn) {
|
|
31
|
+
return {
|
|
32
|
+
__forwardRef: FORWARD_REF_SYMBOL,
|
|
33
|
+
forwardRef: fn
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function isForwardRef(value) {
|
|
37
|
+
return typeof value === "object" && value !== null && "__forwardRef" in value && "forwardRef" in value && typeof value.forwardRef === "function";
|
|
38
|
+
}
|
|
39
|
+
function resolveForwardRef(ref) {
|
|
40
|
+
if (isForwardRef(ref)) {
|
|
41
|
+
return ref.forwardRef();
|
|
42
|
+
}
|
|
43
|
+
return ref;
|
|
44
|
+
}
|
|
45
|
+
var FORWARD_REF_SYMBOL;
|
|
46
|
+
var init_forward_ref = __esm(() => {
|
|
47
|
+
FORWARD_REF_SYMBOL = Symbol.for("buno.forwardRef");
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// src/container/index.ts
|
|
51
|
+
var exports_container = {};
|
|
52
|
+
__export(exports_container, {
|
|
53
|
+
setContainerMetadata: () => setContainerMetadata,
|
|
54
|
+
resolveForwardRef: () => resolveForwardRef,
|
|
55
|
+
isForwardRef: () => isForwardRef,
|
|
56
|
+
getInjectTokens: () => getContainerMetadata,
|
|
57
|
+
getContainerMetadata: () => getContainerMetadata,
|
|
58
|
+
forwardRef: () => forwardRef,
|
|
59
|
+
createToken: () => createToken,
|
|
60
|
+
Token: () => Token,
|
|
61
|
+
Injectable: () => Injectable,
|
|
62
|
+
Inject: () => Inject,
|
|
63
|
+
Container: () => Container
|
|
64
|
+
});
|
|
65
|
+
function createToken(description) {
|
|
66
|
+
return Symbol(description);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
class ResolutionStack {
|
|
70
|
+
stack = new Set;
|
|
71
|
+
lazyResolutions = new Map;
|
|
72
|
+
push(token) {
|
|
73
|
+
if (this.stack.has(token)) {
|
|
74
|
+
throw new Error("Circular dependency detected: Token already in resolution stack");
|
|
75
|
+
}
|
|
76
|
+
this.stack.add(token);
|
|
77
|
+
}
|
|
78
|
+
pop(token) {
|
|
79
|
+
this.stack.delete(token);
|
|
80
|
+
}
|
|
81
|
+
has(token) {
|
|
82
|
+
return this.stack.has(token);
|
|
83
|
+
}
|
|
84
|
+
markLazy(token, placeholder) {
|
|
85
|
+
this.lazyResolutions.set(token, placeholder);
|
|
86
|
+
}
|
|
87
|
+
hasLazy(token) {
|
|
88
|
+
return this.lazyResolutions.has(token);
|
|
89
|
+
}
|
|
90
|
+
getLazy(token) {
|
|
91
|
+
return this.lazyResolutions.get(token);
|
|
92
|
+
}
|
|
93
|
+
clearLazy(token) {
|
|
94
|
+
this.lazyResolutions.delete(token);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
class Container {
|
|
99
|
+
providers = new Map;
|
|
100
|
+
resolutionStack = new ResolutionStack;
|
|
101
|
+
register(provider) {
|
|
102
|
+
if (this.providers.has(provider.token)) {
|
|
103
|
+
throw new Error(`Provider already registered for token: ${String(provider.token)}`);
|
|
104
|
+
}
|
|
105
|
+
this.providers.set(provider.token, {
|
|
106
|
+
provider: {
|
|
107
|
+
...provider,
|
|
108
|
+
scope: provider.scope ?? "singleton"
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
registerAll(providers) {
|
|
113
|
+
for (const provider of providers) {
|
|
114
|
+
this.register(provider);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
has(token) {
|
|
118
|
+
return this.providers.has(token);
|
|
119
|
+
}
|
|
120
|
+
resolve(token) {
|
|
121
|
+
const resolved = this.providers.get(token);
|
|
122
|
+
if (!resolved) {
|
|
123
|
+
throw new Error(`No provider registered for token: ${String(token)}`);
|
|
124
|
+
}
|
|
125
|
+
const { provider } = resolved;
|
|
126
|
+
if (this.resolutionStack.has(token)) {
|
|
127
|
+
return this.createLazyProxy(token, resolved);
|
|
128
|
+
}
|
|
129
|
+
if (provider.scope === "singleton" && resolved.instance !== undefined) {
|
|
130
|
+
return resolved.instance;
|
|
131
|
+
}
|
|
132
|
+
this.resolutionStack.push(token);
|
|
133
|
+
try {
|
|
134
|
+
const instance = this.createInstance(provider);
|
|
135
|
+
if (provider.scope === "singleton") {
|
|
136
|
+
resolved.instance = instance;
|
|
137
|
+
}
|
|
138
|
+
return instance;
|
|
139
|
+
} finally {
|
|
140
|
+
this.resolutionStack.pop(token);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
createLazyProxy(token, resolved) {
|
|
144
|
+
const existingLazy = this.resolutionStack.getLazy(token);
|
|
145
|
+
if (existingLazy && existingLazy.instance) {
|
|
146
|
+
return existingLazy.instance;
|
|
147
|
+
}
|
|
148
|
+
const placeholder = {
|
|
149
|
+
resolved: false
|
|
150
|
+
};
|
|
151
|
+
this.resolutionStack.markLazy(token, placeholder);
|
|
152
|
+
const proxy = new Proxy({}, {
|
|
153
|
+
get: (target, prop) => {
|
|
154
|
+
if (placeholder.resolved && placeholder.instance) {
|
|
155
|
+
const value = placeholder.instance[prop];
|
|
156
|
+
return typeof value === "function" ? value.bind(placeholder.instance) : value;
|
|
157
|
+
}
|
|
158
|
+
if (resolved.instance !== undefined) {
|
|
159
|
+
placeholder.instance = resolved.instance;
|
|
160
|
+
placeholder.resolved = true;
|
|
161
|
+
} else {
|
|
162
|
+
if (prop === "then") {
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
if (placeholder.instance) {
|
|
167
|
+
const value = placeholder.instance[prop];
|
|
168
|
+
return typeof value === "function" ? value.bind(placeholder.instance) : value;
|
|
169
|
+
}
|
|
170
|
+
return () => {
|
|
171
|
+
return;
|
|
172
|
+
};
|
|
173
|
+
},
|
|
174
|
+
set: (target, prop, value) => {
|
|
175
|
+
if (placeholder.instance) {
|
|
176
|
+
placeholder.instance[prop] = value;
|
|
177
|
+
return true;
|
|
178
|
+
}
|
|
179
|
+
return false;
|
|
180
|
+
},
|
|
181
|
+
has: (target, prop) => {
|
|
182
|
+
if (placeholder.instance) {
|
|
183
|
+
return prop in placeholder.instance;
|
|
184
|
+
}
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
placeholder.instance = proxy;
|
|
189
|
+
return proxy;
|
|
190
|
+
}
|
|
191
|
+
createInstance(provider) {
|
|
192
|
+
if (provider.useValue !== undefined) {
|
|
193
|
+
return provider.useValue;
|
|
194
|
+
}
|
|
195
|
+
if (provider.useFactory) {
|
|
196
|
+
const deps = this.resolveDeps(provider.inject ?? []);
|
|
197
|
+
return provider.useFactory(...deps);
|
|
198
|
+
}
|
|
199
|
+
if (provider.useClass) {
|
|
200
|
+
const deps = this.resolveDeps(provider.inject ?? []);
|
|
201
|
+
return new provider.useClass(...deps);
|
|
202
|
+
}
|
|
203
|
+
throw new Error(`Invalid provider configuration for token: ${String(provider.token)}`);
|
|
204
|
+
}
|
|
205
|
+
resolveDeps(tokens) {
|
|
206
|
+
return tokens.map((tokenOrRef) => {
|
|
207
|
+
const token = resolveForwardRef(tokenOrRef);
|
|
208
|
+
return this.resolve(token);
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
clear() {
|
|
212
|
+
this.providers.clear();
|
|
213
|
+
}
|
|
214
|
+
getTokens() {
|
|
215
|
+
return Array.from(this.providers.keys());
|
|
216
|
+
}
|
|
217
|
+
createChild() {
|
|
218
|
+
const child = new Container;
|
|
219
|
+
for (const [token, resolved] of this.providers) {
|
|
220
|
+
if (resolved.provider.scope === "singleton") {
|
|
221
|
+
child.providers.set(token, resolved);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return child;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
function setContainerMetadata(target, key, value) {
|
|
228
|
+
if (!containerMetadata.has(target)) {
|
|
229
|
+
containerMetadata.set(target, new Map);
|
|
230
|
+
}
|
|
231
|
+
containerMetadata.get(target)?.set(key, value);
|
|
232
|
+
}
|
|
233
|
+
function getContainerMetadata(target, key) {
|
|
234
|
+
return containerMetadata.get(target)?.get(key);
|
|
235
|
+
}
|
|
236
|
+
function Injectable(token) {
|
|
237
|
+
return (target) => {
|
|
238
|
+
setContainerMetadata(target, "injectable", true);
|
|
239
|
+
if (token) {
|
|
240
|
+
setContainerMetadata(target, "token", token);
|
|
241
|
+
}
|
|
242
|
+
return target;
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
function Inject(token) {
|
|
246
|
+
return (target, propertyKey, parameterIndex) => {
|
|
247
|
+
const targetObj = target;
|
|
248
|
+
const existingTokens = getContainerMetadata(targetObj, "inject:tokens") ?? [];
|
|
249
|
+
existingTokens[parameterIndex] = token;
|
|
250
|
+
setContainerMetadata(targetObj, "inject:tokens", existingTokens);
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
var Token, containerMetadata;
|
|
254
|
+
var init_container = __esm(() => {
|
|
255
|
+
init_forward_ref();
|
|
256
|
+
Token = createToken;
|
|
257
|
+
containerMetadata = new WeakMap;
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
// src/graphql/metadata.ts
|
|
261
|
+
var exports_metadata = {};
|
|
262
|
+
__export(exports_metadata, {
|
|
263
|
+
setResolverMetadata: () => setResolverMetadata,
|
|
264
|
+
setParamMetadata: () => setParamMetadata,
|
|
265
|
+
setObjectTypeMetadata: () => setObjectTypeMetadata,
|
|
266
|
+
setInputTypeMetadata: () => setInputTypeMetadata,
|
|
267
|
+
setGqlPrototypeMetadata: () => setGqlPrototypeMetadata,
|
|
268
|
+
setGqlPropertyMetadata: () => setGqlPropertyMetadata,
|
|
269
|
+
setGqlClassMetadata: () => setGqlClassMetadata,
|
|
270
|
+
getTypeMetadata: () => getTypeMetadata,
|
|
271
|
+
getSubscriptionFields: () => getSubscriptionFields,
|
|
272
|
+
getResolverMetadata: () => getResolverMetadata,
|
|
273
|
+
getQueryFields: () => getQueryFields,
|
|
274
|
+
getParamMetadata: () => getParamMetadata,
|
|
275
|
+
getMutationFields: () => getMutationFields,
|
|
276
|
+
getGqlPrototypeMetadata: () => getGqlPrototypeMetadata,
|
|
277
|
+
getGqlPropertyMetadata: () => getGqlPropertyMetadata,
|
|
278
|
+
getGqlPropertyKeys: () => getGqlPropertyKeys,
|
|
279
|
+
getGqlClassMetadata: () => getGqlClassMetadata,
|
|
280
|
+
getAllGqlPropertyMetadata: () => getAllGqlPropertyMetadata,
|
|
281
|
+
addSubscriptionField: () => addSubscriptionField,
|
|
282
|
+
addQueryField: () => addQueryField,
|
|
283
|
+
addMutationField: () => addMutationField,
|
|
284
|
+
GQL_SUBSCRIPTIONS_KEY: () => GQL_SUBSCRIPTIONS_KEY,
|
|
285
|
+
GQL_RESOLVER_KEY: () => GQL_RESOLVER_KEY,
|
|
286
|
+
GQL_QUERIES_KEY: () => GQL_QUERIES_KEY,
|
|
287
|
+
GQL_PARAMS_PREFIX: () => GQL_PARAMS_PREFIX,
|
|
288
|
+
GQL_OBJECTTYPE_KEY: () => GQL_OBJECTTYPE_KEY,
|
|
289
|
+
GQL_MUTATIONS_KEY: () => GQL_MUTATIONS_KEY,
|
|
290
|
+
GQL_INPUTTYPE_KEY: () => GQL_INPUTTYPE_KEY
|
|
291
|
+
});
|
|
292
|
+
function setGqlClassMetadata(target, key, value) {
|
|
293
|
+
if (!classMetadataStore.has(target)) {
|
|
294
|
+
classMetadataStore.set(target, new Map);
|
|
295
|
+
}
|
|
296
|
+
classMetadataStore.get(target)?.set(key, value);
|
|
297
|
+
}
|
|
298
|
+
function getGqlClassMetadata(target, key) {
|
|
299
|
+
return classMetadataStore.get(target)?.get(key);
|
|
300
|
+
}
|
|
301
|
+
function setGqlPrototypeMetadata(target, key, value) {
|
|
302
|
+
if (!prototypeMetadataStore.has(target)) {
|
|
303
|
+
prototypeMetadataStore.set(target, new Map);
|
|
304
|
+
}
|
|
305
|
+
prototypeMetadataStore.get(target)?.set(key, value);
|
|
306
|
+
}
|
|
307
|
+
function getGqlPrototypeMetadata(target, key) {
|
|
308
|
+
return prototypeMetadataStore.get(target)?.get(key);
|
|
309
|
+
}
|
|
310
|
+
function setGqlPropertyMetadata(target, propertyKey, value) {
|
|
311
|
+
if (!propertyMetadataStore.has(target)) {
|
|
312
|
+
propertyMetadataStore.set(target, new Map);
|
|
313
|
+
}
|
|
314
|
+
const key = typeof propertyKey === "symbol" ? propertyKey.toString() : propertyKey;
|
|
315
|
+
propertyMetadataStore.get(target)?.set(key, value);
|
|
316
|
+
}
|
|
317
|
+
function getGqlPropertyMetadata(target, propertyKey) {
|
|
318
|
+
const key = typeof propertyKey === "symbol" ? propertyKey.toString() : propertyKey;
|
|
319
|
+
return propertyMetadataStore.get(target)?.get(key);
|
|
320
|
+
}
|
|
321
|
+
function getGqlPropertyKeys(target) {
|
|
322
|
+
const map = propertyMetadataStore.get(target);
|
|
323
|
+
if (!map)
|
|
324
|
+
return [];
|
|
325
|
+
return Array.from(map.keys());
|
|
326
|
+
}
|
|
327
|
+
function getAllGqlPropertyMetadata(target) {
|
|
328
|
+
const map = propertyMetadataStore.get(target);
|
|
329
|
+
if (!map)
|
|
330
|
+
return [];
|
|
331
|
+
return Array.from(map.values());
|
|
332
|
+
}
|
|
333
|
+
function getResolverMetadata(target) {
|
|
334
|
+
return getGqlClassMetadata(target, GQL_RESOLVER_KEY);
|
|
335
|
+
}
|
|
336
|
+
function setResolverMetadata(target, metadata) {
|
|
337
|
+
setGqlClassMetadata(target, GQL_RESOLVER_KEY, metadata);
|
|
338
|
+
}
|
|
339
|
+
function getTypeMetadata(target) {
|
|
340
|
+
return getGqlClassMetadata(target, GQL_OBJECTTYPE_KEY) ?? getGqlClassMetadata(target, GQL_INPUTTYPE_KEY);
|
|
341
|
+
}
|
|
342
|
+
function setObjectTypeMetadata(target, metadata) {
|
|
343
|
+
setGqlClassMetadata(target, GQL_OBJECTTYPE_KEY, metadata);
|
|
344
|
+
}
|
|
345
|
+
function setInputTypeMetadata(target, metadata) {
|
|
346
|
+
setGqlClassMetadata(target, GQL_INPUTTYPE_KEY, metadata);
|
|
347
|
+
}
|
|
348
|
+
function getResolverFields(prototype, key) {
|
|
349
|
+
return getGqlPrototypeMetadata(prototype, key) ?? [];
|
|
350
|
+
}
|
|
351
|
+
function addResolverField(prototype, key, field) {
|
|
352
|
+
const existing = getResolverFields(prototype, key);
|
|
353
|
+
const idx = existing.findIndex((f) => f.methodName === field.methodName);
|
|
354
|
+
if (idx >= 0) {
|
|
355
|
+
existing[idx] = field;
|
|
356
|
+
} else {
|
|
357
|
+
existing.push(field);
|
|
358
|
+
}
|
|
359
|
+
setGqlPrototypeMetadata(prototype, key, existing);
|
|
360
|
+
}
|
|
361
|
+
function addQueryField(prototype, field) {
|
|
362
|
+
addResolverField(prototype, GQL_QUERIES_KEY, field);
|
|
363
|
+
}
|
|
364
|
+
function addMutationField(prototype, field) {
|
|
365
|
+
addResolverField(prototype, GQL_MUTATIONS_KEY, field);
|
|
366
|
+
}
|
|
367
|
+
function addSubscriptionField(prototype, field) {
|
|
368
|
+
addResolverField(prototype, GQL_SUBSCRIPTIONS_KEY, field);
|
|
369
|
+
}
|
|
370
|
+
function getQueryFields(prototype) {
|
|
371
|
+
return getResolverFields(prototype, GQL_QUERIES_KEY);
|
|
372
|
+
}
|
|
373
|
+
function getMutationFields(prototype) {
|
|
374
|
+
return getResolverFields(prototype, GQL_MUTATIONS_KEY);
|
|
375
|
+
}
|
|
376
|
+
function getSubscriptionFields(prototype) {
|
|
377
|
+
return getResolverFields(prototype, GQL_SUBSCRIPTIONS_KEY);
|
|
378
|
+
}
|
|
379
|
+
function setParamMetadata(prototype, methodName, paramIndex, meta) {
|
|
380
|
+
const key = `${GQL_PARAMS_PREFIX}${methodName}`;
|
|
381
|
+
const existing = getGqlPrototypeMetadata(prototype, key) ?? [];
|
|
382
|
+
existing[paramIndex] = meta;
|
|
383
|
+
setGqlPrototypeMetadata(prototype, key, existing);
|
|
384
|
+
}
|
|
385
|
+
function getParamMetadata(prototype, methodName) {
|
|
386
|
+
const key = `${GQL_PARAMS_PREFIX}${methodName}`;
|
|
387
|
+
return getGqlPrototypeMetadata(prototype, key) ?? [];
|
|
388
|
+
}
|
|
389
|
+
var classMetadataStore, prototypeMetadataStore, propertyMetadataStore, GQL_RESOLVER_KEY = "gql:resolver", GQL_OBJECTTYPE_KEY = "gql:objecttype", GQL_INPUTTYPE_KEY = "gql:inputtype", GQL_QUERIES_KEY = "gql:queries", GQL_MUTATIONS_KEY = "gql:mutations", GQL_SUBSCRIPTIONS_KEY = "gql:subscriptions", GQL_PARAMS_PREFIX = "gql:params:";
|
|
390
|
+
var init_metadata = __esm(() => {
|
|
391
|
+
classMetadataStore = new WeakMap;
|
|
392
|
+
prototypeMetadataStore = new WeakMap;
|
|
393
|
+
propertyMetadataStore = new WeakMap;
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
// src/graphql/context-builder.ts
|
|
397
|
+
var exports_context_builder = {};
|
|
398
|
+
__export(exports_context_builder, {
|
|
399
|
+
parseGraphQLRequest: () => parseGraphQLRequest,
|
|
400
|
+
enrichContextForGraphQL: () => enrichContextForGraphQL,
|
|
401
|
+
buildGraphQLContext: () => buildGraphQLContext
|
|
402
|
+
});
|
|
403
|
+
function buildGraphQLContext(httpContext) {
|
|
404
|
+
return {
|
|
405
|
+
request: httpContext.req,
|
|
406
|
+
user: httpContext.get("user"),
|
|
407
|
+
httpContext
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
function enrichContextForGraphQL(httpContext, operationName, operationType, resolverClass) {
|
|
411
|
+
httpContext.set("graphql:operation", operationName);
|
|
412
|
+
httpContext.set("graphql:type", operationType);
|
|
413
|
+
httpContext.set("graphql:resolverClass", resolverClass);
|
|
414
|
+
}
|
|
415
|
+
async function parseGraphQLRequest(request) {
|
|
416
|
+
if (request.method === "POST") {
|
|
417
|
+
try {
|
|
418
|
+
const body = await request.clone().json();
|
|
419
|
+
if (typeof body.query !== "string") {
|
|
420
|
+
return null;
|
|
421
|
+
}
|
|
422
|
+
return {
|
|
423
|
+
query: body.query,
|
|
424
|
+
variables: typeof body.variables === "object" && body.variables !== null ? body.variables : {},
|
|
425
|
+
operationName: typeof body.operationName === "string" ? body.operationName : undefined
|
|
426
|
+
};
|
|
427
|
+
} catch {
|
|
428
|
+
return null;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
if (request.method === "GET") {
|
|
432
|
+
const url = new URL(request.url);
|
|
433
|
+
const query = url.searchParams.get("query");
|
|
434
|
+
if (!query)
|
|
435
|
+
return null;
|
|
436
|
+
const varsStr = url.searchParams.get("variables");
|
|
437
|
+
let variables = {};
|
|
438
|
+
if (varsStr) {
|
|
439
|
+
try {
|
|
440
|
+
variables = JSON.parse(varsStr);
|
|
441
|
+
} catch {}
|
|
442
|
+
}
|
|
443
|
+
return {
|
|
444
|
+
query,
|
|
445
|
+
variables,
|
|
446
|
+
operationName: url.searchParams.get("operationName") ?? undefined
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
return null;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// src/openapi/metadata.ts
|
|
453
|
+
var exports_metadata2 = {};
|
|
454
|
+
__export(exports_metadata2, {
|
|
455
|
+
setApiPropertyMetadata: () => setApiPropertyMetadata,
|
|
456
|
+
setApiMethodMetadata: () => setApiMethodMetadata,
|
|
457
|
+
setApiMetadata: () => setApiMetadata,
|
|
458
|
+
getApiPropertyMetadata: () => getApiPropertyMetadata,
|
|
459
|
+
getApiPropertyKeys: () => getApiPropertyKeys,
|
|
460
|
+
getApiMethodMetadata: () => getApiMethodMetadata,
|
|
461
|
+
getApiMetadata: () => getApiMetadata
|
|
462
|
+
});
|
|
463
|
+
function setApiMetadata(target, key, value) {
|
|
464
|
+
if (!classMetadataStore2.has(target)) {
|
|
465
|
+
classMetadataStore2.set(target, new Map);
|
|
466
|
+
}
|
|
467
|
+
classMetadataStore2.get(target)?.set(key, value);
|
|
468
|
+
}
|
|
469
|
+
function getApiMetadata(target, key) {
|
|
470
|
+
return classMetadataStore2.get(target)?.get(key);
|
|
471
|
+
}
|
|
472
|
+
function setApiMethodMetadata(target, key, value) {
|
|
473
|
+
if (!methodMetadataStore.has(target)) {
|
|
474
|
+
methodMetadataStore.set(target, new Map);
|
|
475
|
+
}
|
|
476
|
+
methodMetadataStore.get(target)?.set(key, value);
|
|
477
|
+
}
|
|
478
|
+
function getApiMethodMetadata(target, key) {
|
|
479
|
+
return methodMetadataStore.get(target)?.get(key);
|
|
480
|
+
}
|
|
481
|
+
function setApiPropertyMetadata(target, propertyKey, value) {
|
|
482
|
+
if (!propertyMetadataStore2.has(target)) {
|
|
483
|
+
propertyMetadataStore2.set(target, new Map);
|
|
484
|
+
}
|
|
485
|
+
const key = typeof propertyKey === "symbol" ? propertyKey.toString() : propertyKey;
|
|
486
|
+
propertyMetadataStore2.get(target)?.set(key, value);
|
|
487
|
+
}
|
|
488
|
+
function getApiPropertyMetadata(target, propertyKey) {
|
|
489
|
+
const key = typeof propertyKey === "symbol" ? propertyKey.toString() : propertyKey;
|
|
490
|
+
return propertyMetadataStore2.get(target)?.get(key);
|
|
491
|
+
}
|
|
492
|
+
function getApiPropertyKeys(target) {
|
|
493
|
+
const metaMap = propertyMetadataStore2.get(target);
|
|
494
|
+
if (!metaMap)
|
|
495
|
+
return [];
|
|
496
|
+
return Array.from(metaMap.keys());
|
|
497
|
+
}
|
|
498
|
+
var classMetadataStore2, methodMetadataStore, propertyMetadataStore2;
|
|
499
|
+
var init_metadata2 = __esm(() => {
|
|
500
|
+
classMetadataStore2 = new WeakMap;
|
|
501
|
+
methodMetadataStore = new WeakMap;
|
|
502
|
+
propertyMetadataStore2 = new WeakMap;
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
// src/modules/guards.ts
|
|
506
|
+
var exports_guards = {};
|
|
507
|
+
__export(exports_guards, {
|
|
508
|
+
getMethodRoles: () => getMethodRoles,
|
|
509
|
+
getMethodGuards: () => getMethodGuards,
|
|
510
|
+
getClassGuards: () => getClassGuards,
|
|
511
|
+
executeGuards: () => executeGuards,
|
|
512
|
+
createForbiddenResponse: () => createForbiddenResponse,
|
|
513
|
+
UseGuards: () => UseGuards,
|
|
514
|
+
RolesGuard: () => RolesGuard,
|
|
515
|
+
Roles: () => Roles,
|
|
516
|
+
AuthGuard: () => AuthGuard
|
|
517
|
+
});
|
|
518
|
+
function setClassGuards(target, guards) {
|
|
519
|
+
guardsClassMetadata.set(target, guards);
|
|
520
|
+
}
|
|
521
|
+
function getClassGuards(target) {
|
|
522
|
+
return guardsClassMetadata.get(target);
|
|
523
|
+
}
|
|
524
|
+
function setMethodGuards(target, propertyKey, guards) {
|
|
525
|
+
if (!guardsMethodMetadata.has(target)) {
|
|
526
|
+
guardsMethodMetadata.set(target, new Map);
|
|
527
|
+
}
|
|
528
|
+
guardsMethodMetadata.get(target)?.set(propertyKey, guards);
|
|
529
|
+
}
|
|
530
|
+
function getMethodGuards(target, propertyKey) {
|
|
531
|
+
return guardsMethodMetadata.get(target)?.get(propertyKey);
|
|
532
|
+
}
|
|
533
|
+
function UseGuards(...guards) {
|
|
534
|
+
const decorator = (target, propertyKey, descriptor) => {
|
|
535
|
+
if (propertyKey !== undefined && descriptor !== undefined) {
|
|
536
|
+
const targetObj = target;
|
|
537
|
+
const existingGuards = getMethodGuards(targetObj, propertyKey) ?? [];
|
|
538
|
+
setMethodGuards(targetObj, propertyKey, [...existingGuards, ...guards]);
|
|
539
|
+
return descriptor;
|
|
540
|
+
} else {
|
|
541
|
+
const targetClass = target;
|
|
542
|
+
const existingGuards = getClassGuards(targetClass) ?? [];
|
|
543
|
+
setClassGuards(targetClass, [...existingGuards, ...guards]);
|
|
544
|
+
}
|
|
545
|
+
};
|
|
546
|
+
return decorator;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
class AuthGuard {
|
|
550
|
+
canActivate(context) {
|
|
551
|
+
const authHeader = context.req.headers.get("Authorization");
|
|
552
|
+
return authHeader !== null && authHeader.length > 0;
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
function setMethodRoles(target, propertyKey, roles) {
|
|
556
|
+
if (!rolesMethodMetadata.has(target)) {
|
|
557
|
+
rolesMethodMetadata.set(target, new Map);
|
|
558
|
+
}
|
|
559
|
+
rolesMethodMetadata.get(target)?.set(propertyKey, roles);
|
|
560
|
+
}
|
|
561
|
+
function getMethodRoles(target, propertyKey) {
|
|
562
|
+
return rolesMethodMetadata.get(target)?.get(propertyKey);
|
|
563
|
+
}
|
|
564
|
+
function Roles(...roles) {
|
|
565
|
+
return (target, propertyKey, descriptor) => {
|
|
566
|
+
const targetObj = target;
|
|
567
|
+
setMethodRoles(targetObj, propertyKey, roles);
|
|
568
|
+
return descriptor;
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
class RolesGuard {
|
|
573
|
+
canActivate(context) {
|
|
574
|
+
const requiredRoles = context.requiredRoles;
|
|
575
|
+
if (!requiredRoles || requiredRoles.length === 0) {
|
|
576
|
+
return true;
|
|
577
|
+
}
|
|
578
|
+
const user = context.user;
|
|
579
|
+
if (!user || !user.roles) {
|
|
580
|
+
return false;
|
|
581
|
+
}
|
|
582
|
+
return requiredRoles.some((role) => user.roles?.includes(role));
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
async function executeGuards(context, options) {
|
|
586
|
+
const {
|
|
587
|
+
globalGuards = [],
|
|
588
|
+
classGuards = [],
|
|
589
|
+
methodGuards = [],
|
|
590
|
+
resolveGuard
|
|
591
|
+
} = options;
|
|
592
|
+
const allGuards = [...globalGuards, ...classGuards, ...methodGuards];
|
|
593
|
+
for (const guard of allGuards) {
|
|
594
|
+
let guardInstance = null;
|
|
595
|
+
if (typeof guard === "function") {
|
|
596
|
+
const funcGuard = guard;
|
|
597
|
+
if (funcGuard.prototype && typeof funcGuard.prototype === "object" && "canActivate" in funcGuard.prototype) {
|
|
598
|
+
guardInstance = resolveGuard ? resolveGuard(guard) : null;
|
|
599
|
+
if (!guardInstance) {
|
|
600
|
+
const GuardClass = guard;
|
|
601
|
+
guardInstance = new GuardClass;
|
|
602
|
+
}
|
|
603
|
+
} else {
|
|
604
|
+
guardInstance = guard;
|
|
605
|
+
}
|
|
606
|
+
} else if (typeof guard === "object" && guard !== null) {
|
|
607
|
+
const objGuard = guard;
|
|
608
|
+
if ("canActivate" in objGuard && typeof objGuard.canActivate === "function") {
|
|
609
|
+
guardInstance = guard;
|
|
610
|
+
} else {
|
|
611
|
+
guardInstance = resolveGuard ? resolveGuard(guard) : null;
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
if (!guardInstance) {
|
|
615
|
+
console.warn("Guard could not be resolved:", guard);
|
|
616
|
+
continue;
|
|
617
|
+
}
|
|
618
|
+
let result;
|
|
619
|
+
if (typeof guardInstance === "function") {
|
|
620
|
+
result = await guardInstance(context);
|
|
621
|
+
} else {
|
|
622
|
+
result = await guardInstance.canActivate(context);
|
|
623
|
+
}
|
|
624
|
+
if (!result) {
|
|
625
|
+
return false;
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
return true;
|
|
629
|
+
}
|
|
630
|
+
function createForbiddenResponse() {
|
|
631
|
+
return new Response(JSON.stringify({
|
|
632
|
+
statusCode: 403,
|
|
633
|
+
error: "Forbidden",
|
|
634
|
+
message: "Access denied"
|
|
635
|
+
}), {
|
|
636
|
+
status: 403,
|
|
637
|
+
headers: {
|
|
638
|
+
"Content-Type": "application/json"
|
|
639
|
+
}
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
var guardsClassMetadata, guardsMethodMetadata, rolesMethodMetadata;
|
|
643
|
+
var init_guards = __esm(() => {
|
|
644
|
+
guardsClassMetadata = new WeakMap;
|
|
645
|
+
guardsMethodMetadata = new WeakMap;
|
|
646
|
+
rolesMethodMetadata = new WeakMap;
|
|
647
|
+
});
|
|
648
|
+
|
|
649
|
+
// src/graphql/graphql-module.ts
|
|
650
|
+
init_container();
|
|
651
|
+
|
|
652
|
+
// src/graphql/schema-builder.ts
|
|
653
|
+
init_metadata();
|
|
654
|
+
function typeFnToScalarName(typeFn) {
|
|
655
|
+
const type = typeFn();
|
|
656
|
+
const resolved = Array.isArray(type) ? type[0] : type;
|
|
657
|
+
if (resolved === String)
|
|
658
|
+
return "String";
|
|
659
|
+
if (resolved === Number)
|
|
660
|
+
return "Float";
|
|
661
|
+
if (resolved === Boolean)
|
|
662
|
+
return "Boolean";
|
|
663
|
+
const name = resolved.name;
|
|
664
|
+
if (name === "GraphQLID")
|
|
665
|
+
return "ID";
|
|
666
|
+
if (name === "GraphQLInt")
|
|
667
|
+
return "Int";
|
|
668
|
+
if (name === "GraphQLFloat")
|
|
669
|
+
return "Float";
|
|
670
|
+
return null;
|
|
671
|
+
}
|
|
672
|
+
function typeFnToSDL(typeFn, nullable) {
|
|
673
|
+
const type = typeFn();
|
|
674
|
+
const isArray = Array.isArray(type);
|
|
675
|
+
const inner = isArray ? type[0] : type;
|
|
676
|
+
let innerName;
|
|
677
|
+
const scalarName = typeFnToScalarName(() => inner);
|
|
678
|
+
if (scalarName) {
|
|
679
|
+
innerName = scalarName;
|
|
680
|
+
} else {
|
|
681
|
+
innerName = inner.name;
|
|
682
|
+
}
|
|
683
|
+
const innerSdl = isArray ? `[${innerName}!]` : innerName;
|
|
684
|
+
return nullable ? innerSdl : `${innerSdl}!`;
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
class SchemaBuilder {
|
|
688
|
+
resolverClasses;
|
|
689
|
+
resolverInstances;
|
|
690
|
+
typeClasses = new Set;
|
|
691
|
+
constructor(resolverClasses, resolverInstances) {
|
|
692
|
+
this.resolverClasses = resolverClasses;
|
|
693
|
+
this.resolverInstances = resolverInstances;
|
|
694
|
+
}
|
|
695
|
+
build() {
|
|
696
|
+
const queries = new Map;
|
|
697
|
+
const mutations = new Map;
|
|
698
|
+
const subscriptions = new Map;
|
|
699
|
+
for (const ResolverClass of this.resolverClasses) {
|
|
700
|
+
const instance = this.resolverInstances.get(ResolverClass);
|
|
701
|
+
const qFields = getQueryFields(ResolverClass.prototype);
|
|
702
|
+
for (const f of qFields) {
|
|
703
|
+
queries.set(f.fieldName, this.toResolvedField(f, instance));
|
|
704
|
+
this.collectReturnType(f.typeFn);
|
|
705
|
+
}
|
|
706
|
+
const mFields = getMutationFields(ResolverClass.prototype);
|
|
707
|
+
for (const f of mFields) {
|
|
708
|
+
mutations.set(f.fieldName, this.toResolvedField(f, instance));
|
|
709
|
+
this.collectReturnType(f.typeFn);
|
|
710
|
+
}
|
|
711
|
+
const sFields = getSubscriptionFields(ResolverClass.prototype);
|
|
712
|
+
for (const f of sFields) {
|
|
713
|
+
subscriptions.set(f.fieldName, this.toResolvedField(f, instance));
|
|
714
|
+
this.collectReturnType(f.typeFn);
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
this.collectNestedTypes();
|
|
718
|
+
const typeFields = this.buildTypeFields();
|
|
719
|
+
const sdl = this.generateSDL(queries, mutations, subscriptions, typeFields);
|
|
720
|
+
const resolverFields = {
|
|
721
|
+
queries,
|
|
722
|
+
mutations,
|
|
723
|
+
subscriptions
|
|
724
|
+
};
|
|
725
|
+
const resolvedSchema = {
|
|
726
|
+
queryFields: queries,
|
|
727
|
+
mutationFields: mutations,
|
|
728
|
+
subscriptionFields: subscriptions
|
|
729
|
+
};
|
|
730
|
+
return { sdl, resolvedSchema, resolverFields, typeFields };
|
|
731
|
+
}
|
|
732
|
+
toResolvedField(field, instance) {
|
|
733
|
+
return {
|
|
734
|
+
resolverInstance: instance,
|
|
735
|
+
methodName: field.methodName,
|
|
736
|
+
paramMetadata: field.paramMetadata,
|
|
737
|
+
typeFn: field.typeFn,
|
|
738
|
+
nullable: field.nullable
|
|
739
|
+
};
|
|
740
|
+
}
|
|
741
|
+
collectReturnType(typeFn) {
|
|
742
|
+
const type = typeFn();
|
|
743
|
+
const resolved = Array.isArray(type) ? type[0] : type;
|
|
744
|
+
if (typeof resolved === "function") {
|
|
745
|
+
const meta = getTypeMetadata(resolved);
|
|
746
|
+
if (meta) {
|
|
747
|
+
this.typeClasses.add(resolved);
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
collectNestedTypes() {
|
|
752
|
+
let changed = true;
|
|
753
|
+
while (changed) {
|
|
754
|
+
changed = false;
|
|
755
|
+
for (const TypeClass of Array.from(this.typeClasses)) {
|
|
756
|
+
const fields = getAllGqlPropertyMetadata(TypeClass.prototype);
|
|
757
|
+
for (const field of fields) {
|
|
758
|
+
const type = field.typeFn();
|
|
759
|
+
const inner = Array.isArray(type) ? type[0] : type;
|
|
760
|
+
if (typeof inner === "function") {
|
|
761
|
+
const meta = getTypeMetadata(inner);
|
|
762
|
+
if (meta && !this.typeClasses.has(inner)) {
|
|
763
|
+
this.typeClasses.add(inner);
|
|
764
|
+
changed = true;
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
buildTypeFields() {
|
|
772
|
+
const result = new Map;
|
|
773
|
+
for (const TypeClass of this.typeClasses) {
|
|
774
|
+
const meta = getTypeMetadata(TypeClass);
|
|
775
|
+
if (!meta)
|
|
776
|
+
continue;
|
|
777
|
+
const fields = getAllGqlPropertyMetadata(TypeClass.prototype);
|
|
778
|
+
result.set(meta.name, fields);
|
|
779
|
+
}
|
|
780
|
+
return result;
|
|
781
|
+
}
|
|
782
|
+
generateSDL(queries, mutations, subscriptions, typeFields) {
|
|
783
|
+
const lines = [];
|
|
784
|
+
for (const TypeClass of this.typeClasses) {
|
|
785
|
+
const meta = getTypeMetadata(TypeClass);
|
|
786
|
+
if (!meta)
|
|
787
|
+
continue;
|
|
788
|
+
const fields = getAllGqlPropertyMetadata(TypeClass.prototype);
|
|
789
|
+
if (fields.length === 0)
|
|
790
|
+
continue;
|
|
791
|
+
const keyword = meta.kind === "input" ? "input" : "type";
|
|
792
|
+
if (meta.description) {
|
|
793
|
+
lines.push(`"""${meta.description}"""`);
|
|
794
|
+
}
|
|
795
|
+
lines.push(`${keyword} ${meta.name} {`);
|
|
796
|
+
for (const f of fields) {
|
|
797
|
+
if (f.description) {
|
|
798
|
+
lines.push(` """${f.description}"""`);
|
|
799
|
+
}
|
|
800
|
+
const typeSdl = typeFnToSDL(f.typeFn, f.nullable);
|
|
801
|
+
let fieldLine = ` ${f.propertyKey}: ${typeSdl}`;
|
|
802
|
+
if (f.defaultValue !== undefined) {
|
|
803
|
+
fieldLine += ` = ${JSON.stringify(f.defaultValue)}`;
|
|
804
|
+
}
|
|
805
|
+
lines.push(fieldLine);
|
|
806
|
+
if (f.deprecationReason) {
|
|
807
|
+
lines[lines.length - 1] += ` @deprecated(reason: "${f.deprecationReason}")`;
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
lines.push("}");
|
|
811
|
+
lines.push("");
|
|
812
|
+
}
|
|
813
|
+
if (queries.size > 0) {
|
|
814
|
+
lines.push("type Query {");
|
|
815
|
+
for (const [fieldName, field] of queries) {
|
|
816
|
+
const args = this.buildArgsSDL(fieldName, queries);
|
|
817
|
+
const typeSdl = typeFnToSDL(field.typeFn, field.nullable);
|
|
818
|
+
lines.push(` ${fieldName}${args}: ${typeSdl}`);
|
|
819
|
+
}
|
|
820
|
+
lines.push("}");
|
|
821
|
+
lines.push("");
|
|
822
|
+
}
|
|
823
|
+
if (mutations.size > 0) {
|
|
824
|
+
lines.push("type Mutation {");
|
|
825
|
+
for (const [fieldName, field] of mutations) {
|
|
826
|
+
const args = this.buildArgsSDL(fieldName, mutations);
|
|
827
|
+
const typeSdl = typeFnToSDL(field.typeFn, field.nullable);
|
|
828
|
+
lines.push(` ${fieldName}${args}: ${typeSdl}`);
|
|
829
|
+
}
|
|
830
|
+
lines.push("}");
|
|
831
|
+
lines.push("");
|
|
832
|
+
}
|
|
833
|
+
if (subscriptions.size > 0) {
|
|
834
|
+
lines.push("type Subscription {");
|
|
835
|
+
for (const [fieldName, field] of subscriptions) {
|
|
836
|
+
const typeSdl = typeFnToSDL(field.typeFn, field.nullable);
|
|
837
|
+
lines.push(` ${fieldName}: ${typeSdl}`);
|
|
838
|
+
}
|
|
839
|
+
lines.push("}");
|
|
840
|
+
lines.push("");
|
|
841
|
+
}
|
|
842
|
+
return lines.join(`
|
|
843
|
+
`).trim();
|
|
844
|
+
}
|
|
845
|
+
buildArgsSDL(fieldName, fields) {
|
|
846
|
+
const field = fields.get(fieldName);
|
|
847
|
+
if (!field || field.paramMetadata.length === 0)
|
|
848
|
+
return "";
|
|
849
|
+
const args = [];
|
|
850
|
+
for (const param of field.paramMetadata) {
|
|
851
|
+
if (param.kind === "context")
|
|
852
|
+
continue;
|
|
853
|
+
if (param.kind === "args" && param.argName) {
|
|
854
|
+
continue;
|
|
855
|
+
}
|
|
856
|
+
if (param.kind === "argsObject" && param.inputTypeFn) {
|
|
857
|
+
const inputType = param.inputTypeFn();
|
|
858
|
+
const typeName = Array.isArray(inputType) ? inputType[0].name : inputType.name;
|
|
859
|
+
if (param.argName) {
|
|
860
|
+
args.push(`${param.argName}: ${typeName}!`);
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
return args.length > 0 ? `(${args.join(", ")})` : "";
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
var registeredTypeClasses = new Set;
|
|
868
|
+
function registerTypeClass(ctor) {
|
|
869
|
+
registeredTypeClasses.add(ctor);
|
|
870
|
+
}
|
|
871
|
+
function getRegisteredTypeClasses() {
|
|
872
|
+
return Array.from(registeredTypeClasses);
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
// src/graphql/built-in-engine.ts
|
|
876
|
+
class GraphQLParser {
|
|
877
|
+
input;
|
|
878
|
+
pos = 0;
|
|
879
|
+
constructor(input) {
|
|
880
|
+
this.input = input;
|
|
881
|
+
}
|
|
882
|
+
parse() {
|
|
883
|
+
this.skipWhitespaceAndComments();
|
|
884
|
+
let operation = "query";
|
|
885
|
+
if (this.peek("mutation")) {
|
|
886
|
+
this.pos += 8;
|
|
887
|
+
operation = "mutation";
|
|
888
|
+
this.skipWhitespaceAndComments();
|
|
889
|
+
if (this.input[this.pos] !== "{") {
|
|
890
|
+
this.skipName();
|
|
891
|
+
this.skipWhitespaceAndComments();
|
|
892
|
+
}
|
|
893
|
+
} else if (this.peek("query")) {
|
|
894
|
+
this.pos += 5;
|
|
895
|
+
operation = "query";
|
|
896
|
+
this.skipWhitespaceAndComments();
|
|
897
|
+
if (this.input[this.pos] !== "{") {
|
|
898
|
+
this.skipName();
|
|
899
|
+
this.skipWhitespaceAndComments();
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
if (this.input[this.pos] === "(") {
|
|
903
|
+
this.skipBalanced("(", ")");
|
|
904
|
+
this.skipWhitespaceAndComments();
|
|
905
|
+
}
|
|
906
|
+
const selections = this.parseSelectionSet();
|
|
907
|
+
return { operation, selections };
|
|
908
|
+
}
|
|
909
|
+
parseSelectionSet() {
|
|
910
|
+
this.expect("{");
|
|
911
|
+
this.skipWhitespaceAndComments();
|
|
912
|
+
const selections = [];
|
|
913
|
+
while (this.pos < this.input.length && this.input[this.pos] !== "}") {
|
|
914
|
+
if (this.input[this.pos] === "." && this.input[this.pos + 1] === "." && this.input[this.pos + 2] === ".") {
|
|
915
|
+
throw new Error("Fragment spreads are not supported by the built-in GraphQL engine. " + "Install the 'graphql' package and use GraphQLJsAdapter for full spec support.");
|
|
916
|
+
}
|
|
917
|
+
if (this.peek("on ") || this.peek("on\t") || this.peek("on{")) {
|
|
918
|
+
throw new Error("Inline fragments are not supported by the built-in GraphQL engine. " + "Install the 'graphql' package and use GraphQLJsAdapter for full spec support.");
|
|
919
|
+
}
|
|
920
|
+
const sel = this.parseSelection();
|
|
921
|
+
selections.push(sel);
|
|
922
|
+
this.skipWhitespaceAndComments();
|
|
923
|
+
if (this.input[this.pos] === ",") {
|
|
924
|
+
this.pos++;
|
|
925
|
+
this.skipWhitespaceAndComments();
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
this.expect("}");
|
|
929
|
+
return selections;
|
|
930
|
+
}
|
|
931
|
+
parseSelection() {
|
|
932
|
+
const firstName = this.parseName();
|
|
933
|
+
this.skipWhitespaceAndComments();
|
|
934
|
+
let name = firstName;
|
|
935
|
+
let alias;
|
|
936
|
+
if (this.input[this.pos] === ":") {
|
|
937
|
+
alias = firstName;
|
|
938
|
+
this.pos++;
|
|
939
|
+
this.skipWhitespaceAndComments();
|
|
940
|
+
name = this.parseName();
|
|
941
|
+
this.skipWhitespaceAndComments();
|
|
942
|
+
}
|
|
943
|
+
if (name.startsWith("__")) {
|
|
944
|
+
throw new Error(`Introspection field '${name}' is not supported by the built-in GraphQL engine. ` + "Install the 'graphql' package and use GraphQLJsAdapter for introspection support.");
|
|
945
|
+
}
|
|
946
|
+
const args = [];
|
|
947
|
+
if (this.input[this.pos] === "(") {
|
|
948
|
+
args.push(...this.parseArguments());
|
|
949
|
+
this.skipWhitespaceAndComments();
|
|
950
|
+
}
|
|
951
|
+
let selections = [];
|
|
952
|
+
if (this.input[this.pos] === "{") {
|
|
953
|
+
selections = this.parseSelectionSet();
|
|
954
|
+
this.skipWhitespaceAndComments();
|
|
955
|
+
}
|
|
956
|
+
return { name, alias, arguments: args, selections };
|
|
957
|
+
}
|
|
958
|
+
parseArguments() {
|
|
959
|
+
this.expect("(");
|
|
960
|
+
this.skipWhitespaceAndComments();
|
|
961
|
+
const args = [];
|
|
962
|
+
while (this.pos < this.input.length && this.input[this.pos] !== ")") {
|
|
963
|
+
const argName = this.parseName();
|
|
964
|
+
this.skipWhitespaceAndComments();
|
|
965
|
+
this.expect(":");
|
|
966
|
+
this.skipWhitespaceAndComments();
|
|
967
|
+
const value = this.parseValue();
|
|
968
|
+
args.push({ name: argName, value });
|
|
969
|
+
this.skipWhitespaceAndComments();
|
|
970
|
+
if (this.input[this.pos] === ",") {
|
|
971
|
+
this.pos++;
|
|
972
|
+
this.skipWhitespaceAndComments();
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
this.expect(")");
|
|
976
|
+
return args;
|
|
977
|
+
}
|
|
978
|
+
parseValue() {
|
|
979
|
+
const ch = this.input[this.pos];
|
|
980
|
+
if (ch === '"') {
|
|
981
|
+
return this.parseString();
|
|
982
|
+
}
|
|
983
|
+
if (ch === "-" || ch >= "0" && ch <= "9") {
|
|
984
|
+
return this.parseNumber();
|
|
985
|
+
}
|
|
986
|
+
if (this.peek("true")) {
|
|
987
|
+
this.pos += 4;
|
|
988
|
+
return true;
|
|
989
|
+
}
|
|
990
|
+
if (this.peek("false")) {
|
|
991
|
+
this.pos += 5;
|
|
992
|
+
return false;
|
|
993
|
+
}
|
|
994
|
+
if (this.peek("null")) {
|
|
995
|
+
this.pos += 4;
|
|
996
|
+
return null;
|
|
997
|
+
}
|
|
998
|
+
if (ch === "$") {
|
|
999
|
+
this.pos++;
|
|
1000
|
+
const varName = this.parseName();
|
|
1001
|
+
return { __variable: varName };
|
|
1002
|
+
}
|
|
1003
|
+
if (ch === "{") {
|
|
1004
|
+
return this.parseObjectLiteral();
|
|
1005
|
+
}
|
|
1006
|
+
if (ch === "[") {
|
|
1007
|
+
return this.parseListLiteral();
|
|
1008
|
+
}
|
|
1009
|
+
if (ch && /[A-Z_a-z]/.test(ch)) {
|
|
1010
|
+
return this.parseName();
|
|
1011
|
+
}
|
|
1012
|
+
throw new Error(`Unexpected character '${ch}' at position ${this.pos}`);
|
|
1013
|
+
}
|
|
1014
|
+
parseString() {
|
|
1015
|
+
this.expect('"');
|
|
1016
|
+
let result = "";
|
|
1017
|
+
while (this.pos < this.input.length && this.input[this.pos] !== '"') {
|
|
1018
|
+
if (this.input[this.pos] === "\\") {
|
|
1019
|
+
this.pos++;
|
|
1020
|
+
const esc = this.input[this.pos];
|
|
1021
|
+
const escapes = {
|
|
1022
|
+
'"': '"',
|
|
1023
|
+
"\\": "\\",
|
|
1024
|
+
"/": "/",
|
|
1025
|
+
n: `
|
|
1026
|
+
`,
|
|
1027
|
+
r: "\r",
|
|
1028
|
+
t: "\t",
|
|
1029
|
+
b: "\b",
|
|
1030
|
+
f: "\f"
|
|
1031
|
+
};
|
|
1032
|
+
result += escapes[esc] ?? esc;
|
|
1033
|
+
} else {
|
|
1034
|
+
result += this.input[this.pos];
|
|
1035
|
+
}
|
|
1036
|
+
this.pos++;
|
|
1037
|
+
}
|
|
1038
|
+
this.expect('"');
|
|
1039
|
+
return result;
|
|
1040
|
+
}
|
|
1041
|
+
parseNumber() {
|
|
1042
|
+
let numStr = "";
|
|
1043
|
+
if (this.input[this.pos] === "-") {
|
|
1044
|
+
numStr += "-";
|
|
1045
|
+
this.pos++;
|
|
1046
|
+
}
|
|
1047
|
+
while (this.pos < this.input.length && /[\d.]/.test(this.input[this.pos])) {
|
|
1048
|
+
numStr += this.input[this.pos++];
|
|
1049
|
+
}
|
|
1050
|
+
return Number(numStr);
|
|
1051
|
+
}
|
|
1052
|
+
parseObjectLiteral() {
|
|
1053
|
+
this.expect("{");
|
|
1054
|
+
this.skipWhitespaceAndComments();
|
|
1055
|
+
const obj = {};
|
|
1056
|
+
while (this.pos < this.input.length && this.input[this.pos] !== "}") {
|
|
1057
|
+
const key = this.parseName();
|
|
1058
|
+
this.skipWhitespaceAndComments();
|
|
1059
|
+
this.expect(":");
|
|
1060
|
+
this.skipWhitespaceAndComments();
|
|
1061
|
+
obj[key] = this.parseValue();
|
|
1062
|
+
this.skipWhitespaceAndComments();
|
|
1063
|
+
if (this.input[this.pos] === ",") {
|
|
1064
|
+
this.pos++;
|
|
1065
|
+
this.skipWhitespaceAndComments();
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
this.expect("}");
|
|
1069
|
+
return obj;
|
|
1070
|
+
}
|
|
1071
|
+
parseListLiteral() {
|
|
1072
|
+
this.expect("[");
|
|
1073
|
+
this.skipWhitespaceAndComments();
|
|
1074
|
+
const items = [];
|
|
1075
|
+
while (this.pos < this.input.length && this.input[this.pos] !== "]") {
|
|
1076
|
+
items.push(this.parseValue());
|
|
1077
|
+
this.skipWhitespaceAndComments();
|
|
1078
|
+
if (this.input[this.pos] === ",") {
|
|
1079
|
+
this.pos++;
|
|
1080
|
+
this.skipWhitespaceAndComments();
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
this.expect("]");
|
|
1084
|
+
return items;
|
|
1085
|
+
}
|
|
1086
|
+
parseName() {
|
|
1087
|
+
const start = this.pos;
|
|
1088
|
+
while (this.pos < this.input.length && /[\w]/.test(this.input[this.pos])) {
|
|
1089
|
+
this.pos++;
|
|
1090
|
+
}
|
|
1091
|
+
if (this.pos === start) {
|
|
1092
|
+
throw new Error(`Expected name at position ${this.pos}, got '${this.input[this.pos]}'`);
|
|
1093
|
+
}
|
|
1094
|
+
return this.input.slice(start, this.pos);
|
|
1095
|
+
}
|
|
1096
|
+
skipName() {
|
|
1097
|
+
while (this.pos < this.input.length && /[\w]/.test(this.input[this.pos])) {
|
|
1098
|
+
this.pos++;
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
skipBalanced(open, close) {
|
|
1102
|
+
this.expect(open);
|
|
1103
|
+
let depth = 1;
|
|
1104
|
+
while (this.pos < this.input.length && depth > 0) {
|
|
1105
|
+
if (this.input[this.pos] === open)
|
|
1106
|
+
depth++;
|
|
1107
|
+
else if (this.input[this.pos] === close)
|
|
1108
|
+
depth--;
|
|
1109
|
+
this.pos++;
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
skipWhitespaceAndComments() {
|
|
1113
|
+
while (this.pos < this.input.length) {
|
|
1114
|
+
if (/\s/.test(this.input[this.pos])) {
|
|
1115
|
+
this.pos++;
|
|
1116
|
+
continue;
|
|
1117
|
+
}
|
|
1118
|
+
if (this.input[this.pos] === "#") {
|
|
1119
|
+
while (this.pos < this.input.length && this.input[this.pos] !== `
|
|
1120
|
+
`) {
|
|
1121
|
+
this.pos++;
|
|
1122
|
+
}
|
|
1123
|
+
continue;
|
|
1124
|
+
}
|
|
1125
|
+
break;
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
expect(char) {
|
|
1129
|
+
if (this.input[this.pos] !== char) {
|
|
1130
|
+
throw new Error(`Expected '${char}' at position ${this.pos}, got '${this.input[this.pos] ?? "EOF"}'`);
|
|
1131
|
+
}
|
|
1132
|
+
this.pos++;
|
|
1133
|
+
}
|
|
1134
|
+
peek(str) {
|
|
1135
|
+
return this.input.startsWith(str, this.pos);
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
class BuiltinGraphQLEngine {
|
|
1140
|
+
supportsIntrospection = false;
|
|
1141
|
+
supportsSubscriptions = false;
|
|
1142
|
+
buildSchema(resolvers, _types, sdl) {
|
|
1143
|
+
const schema = {
|
|
1144
|
+
queries: resolvers.queries,
|
|
1145
|
+
mutations: resolvers.mutations,
|
|
1146
|
+
sdl
|
|
1147
|
+
};
|
|
1148
|
+
return schema;
|
|
1149
|
+
}
|
|
1150
|
+
async execute(schema, query, variables, context, _operationName) {
|
|
1151
|
+
const s = schema;
|
|
1152
|
+
let doc;
|
|
1153
|
+
try {
|
|
1154
|
+
doc = new GraphQLParser(query).parse();
|
|
1155
|
+
} catch (err) {
|
|
1156
|
+
return {
|
|
1157
|
+
data: null,
|
|
1158
|
+
errors: [{ message: String(err.message) }]
|
|
1159
|
+
};
|
|
1160
|
+
}
|
|
1161
|
+
const fields = doc.operation === "mutation" ? s.mutations : s.queries;
|
|
1162
|
+
const data = {};
|
|
1163
|
+
const errors = [];
|
|
1164
|
+
for (const sel of doc.selections) {
|
|
1165
|
+
try {
|
|
1166
|
+
const resolvedValue = await this.resolveSelection(sel, fields, variables, context);
|
|
1167
|
+
const resultKey = sel.alias ?? sel.name;
|
|
1168
|
+
data[resultKey] = resolvedValue;
|
|
1169
|
+
} catch (err) {
|
|
1170
|
+
const resultKey = sel.alias ?? sel.name;
|
|
1171
|
+
data[resultKey] = null;
|
|
1172
|
+
errors.push({
|
|
1173
|
+
message: String(err.message),
|
|
1174
|
+
path: [resultKey]
|
|
1175
|
+
});
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
const result = { data };
|
|
1179
|
+
if (errors.length > 0) {
|
|
1180
|
+
result.errors = errors;
|
|
1181
|
+
}
|
|
1182
|
+
return result;
|
|
1183
|
+
}
|
|
1184
|
+
async resolveSelection(sel, fields, variables, context) {
|
|
1185
|
+
const field = fields.get(sel.name);
|
|
1186
|
+
if (!field) {
|
|
1187
|
+
throw new Error(`Field '${sel.name}' does not exist on type`);
|
|
1188
|
+
}
|
|
1189
|
+
const args = this.buildArgsFromSelection(sel, variables);
|
|
1190
|
+
const instance = field.resolverInstance;
|
|
1191
|
+
const methodArgs = this.buildMethodArgs(field, args, context);
|
|
1192
|
+
const rawResult = await instance[field.methodName](...methodArgs);
|
|
1193
|
+
if (sel.selections.length > 0 && rawResult !== null && rawResult !== undefined) {
|
|
1194
|
+
if (Array.isArray(rawResult)) {
|
|
1195
|
+
return Promise.all(rawResult.map((item) => this.resolveObject(item, sel.selections, variables, context)));
|
|
1196
|
+
}
|
|
1197
|
+
return this.resolveObject(rawResult, sel.selections, variables, context);
|
|
1198
|
+
}
|
|
1199
|
+
return rawResult;
|
|
1200
|
+
}
|
|
1201
|
+
async resolveObject(obj, selections, variables, context) {
|
|
1202
|
+
const result = {};
|
|
1203
|
+
const record = obj;
|
|
1204
|
+
for (const sel of selections) {
|
|
1205
|
+
if (sel.name.startsWith("__")) {
|
|
1206
|
+
throw new Error(`Introspection field '${sel.name}' is not supported by the built-in engine.`);
|
|
1207
|
+
}
|
|
1208
|
+
const resultKey = sel.alias ?? sel.name;
|
|
1209
|
+
const fieldValue = record[sel.name];
|
|
1210
|
+
if (sel.selections.length > 0 && fieldValue !== null && fieldValue !== undefined) {
|
|
1211
|
+
if (Array.isArray(fieldValue)) {
|
|
1212
|
+
result[resultKey] = await Promise.all(fieldValue.map((item) => this.resolveObject(item, sel.selections, variables, context)));
|
|
1213
|
+
} else {
|
|
1214
|
+
result[resultKey] = await this.resolveObject(fieldValue, sel.selections, variables, context);
|
|
1215
|
+
}
|
|
1216
|
+
} else {
|
|
1217
|
+
result[resultKey] = fieldValue ?? null;
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
return result;
|
|
1221
|
+
}
|
|
1222
|
+
buildArgsFromSelection(sel, variables) {
|
|
1223
|
+
const args = {};
|
|
1224
|
+
for (const arg of sel.arguments) {
|
|
1225
|
+
args[arg.name] = this.resolveValue(arg.value, variables);
|
|
1226
|
+
}
|
|
1227
|
+
return args;
|
|
1228
|
+
}
|
|
1229
|
+
resolveValue(value, variables) {
|
|
1230
|
+
if (value !== null && typeof value === "object" && "__variable" in value) {
|
|
1231
|
+
const varName = value.__variable;
|
|
1232
|
+
return variables[varName];
|
|
1233
|
+
}
|
|
1234
|
+
if (Array.isArray(value)) {
|
|
1235
|
+
return value.map((v) => this.resolveValue(v, variables));
|
|
1236
|
+
}
|
|
1237
|
+
if (value !== null && typeof value === "object") {
|
|
1238
|
+
const result = {};
|
|
1239
|
+
for (const [k, v] of Object.entries(value)) {
|
|
1240
|
+
result[k] = this.resolveValue(v, variables);
|
|
1241
|
+
}
|
|
1242
|
+
return result;
|
|
1243
|
+
}
|
|
1244
|
+
return value;
|
|
1245
|
+
}
|
|
1246
|
+
buildMethodArgs(field, args, context) {
|
|
1247
|
+
const methodArgs = [];
|
|
1248
|
+
for (const param of field.paramMetadata) {
|
|
1249
|
+
if (param.kind === "context") {
|
|
1250
|
+
methodArgs[param.index] = context;
|
|
1251
|
+
} else if (param.kind === "args" && param.argName) {
|
|
1252
|
+
methodArgs[param.index] = args[param.argName];
|
|
1253
|
+
} else if (param.kind === "argsObject" && param.argName) {
|
|
1254
|
+
methodArgs[param.index] = args[param.argName];
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
return methodArgs;
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
// src/graphql/subscription-handler.ts
|
|
1262
|
+
class SubscriptionHandler {
|
|
1263
|
+
engine;
|
|
1264
|
+
engineSchema;
|
|
1265
|
+
graphqlPath;
|
|
1266
|
+
container;
|
|
1267
|
+
globalGuards;
|
|
1268
|
+
globalInterceptors;
|
|
1269
|
+
connections = new Map;
|
|
1270
|
+
constructor(engine, engineSchema, graphqlPath, container, globalGuards, globalInterceptors) {
|
|
1271
|
+
this.engine = engine;
|
|
1272
|
+
this.engineSchema = engineSchema;
|
|
1273
|
+
this.graphqlPath = graphqlPath;
|
|
1274
|
+
this.container = container;
|
|
1275
|
+
this.globalGuards = globalGuards;
|
|
1276
|
+
this.globalInterceptors = globalInterceptors;
|
|
1277
|
+
}
|
|
1278
|
+
getWebSocketConfig() {
|
|
1279
|
+
const upgradeHandler = this.handleUpgrade.bind(this);
|
|
1280
|
+
return {
|
|
1281
|
+
__upgradeHandler: upgradeHandler,
|
|
1282
|
+
open: (ws) => {
|
|
1283
|
+
this.connections.set(ws.data.connectionId, {
|
|
1284
|
+
subscriptions: new Map,
|
|
1285
|
+
initialized: false
|
|
1286
|
+
});
|
|
1287
|
+
},
|
|
1288
|
+
message: async (ws, raw) => {
|
|
1289
|
+
try {
|
|
1290
|
+
const text = typeof raw === "string" ? raw : new TextDecoder().decode(raw);
|
|
1291
|
+
const msg = JSON.parse(text);
|
|
1292
|
+
await this.handleMessage(ws, msg);
|
|
1293
|
+
} catch (err) {
|
|
1294
|
+
this.send(ws, {
|
|
1295
|
+
type: "error",
|
|
1296
|
+
id: "",
|
|
1297
|
+
payload: [{ message: `Protocol error: ${err.message}` }]
|
|
1298
|
+
});
|
|
1299
|
+
}
|
|
1300
|
+
},
|
|
1301
|
+
close: (ws) => {
|
|
1302
|
+
const state = this.connections.get(ws.data.connectionId);
|
|
1303
|
+
if (state) {
|
|
1304
|
+
for (const sub of state.subscriptions.values()) {
|
|
1305
|
+
sub.generator.return?.(undefined);
|
|
1306
|
+
}
|
|
1307
|
+
this.connections.delete(ws.data.connectionId);
|
|
1308
|
+
}
|
|
1309
|
+
},
|
|
1310
|
+
error: (ws, error) => {
|
|
1311
|
+
console.error("[GraphQL WS] WebSocket error:", error);
|
|
1312
|
+
}
|
|
1313
|
+
};
|
|
1314
|
+
}
|
|
1315
|
+
handleUpgrade(req, server) {
|
|
1316
|
+
const url = new URL(req.url);
|
|
1317
|
+
if (url.pathname !== this.graphqlPath) {
|
|
1318
|
+
return;
|
|
1319
|
+
}
|
|
1320
|
+
const protocol = req.headers.get("sec-websocket-protocol") ?? "";
|
|
1321
|
+
if (!protocol.includes("graphql-transport-ws")) {
|
|
1322
|
+
return new Response("Unsupported WebSocket protocol", { status: 426 });
|
|
1323
|
+
}
|
|
1324
|
+
const connectionId = crypto.randomUUID();
|
|
1325
|
+
const upgraded = server.upgrade(req, {
|
|
1326
|
+
headers: {
|
|
1327
|
+
"Sec-WebSocket-Protocol": "graphql-transport-ws"
|
|
1328
|
+
},
|
|
1329
|
+
data: { connectionId }
|
|
1330
|
+
});
|
|
1331
|
+
return upgraded ? undefined : new Response("WebSocket upgrade failed", { status: 400 });
|
|
1332
|
+
}
|
|
1333
|
+
async handleMessage(ws, msg) {
|
|
1334
|
+
const state = this.connections.get(ws.data.connectionId);
|
|
1335
|
+
if (!state)
|
|
1336
|
+
return;
|
|
1337
|
+
switch (msg.type) {
|
|
1338
|
+
case "connection_init":
|
|
1339
|
+
if (state.initialized) {
|
|
1340
|
+
ws.close(4429, "Too many initialisation requests");
|
|
1341
|
+
return;
|
|
1342
|
+
}
|
|
1343
|
+
state.initialized = true;
|
|
1344
|
+
this.send(ws, { type: "connection_ack" });
|
|
1345
|
+
break;
|
|
1346
|
+
case "subscribe":
|
|
1347
|
+
if (!state.initialized) {
|
|
1348
|
+
ws.close(4401, "Unauthorized: connection not initialized");
|
|
1349
|
+
return;
|
|
1350
|
+
}
|
|
1351
|
+
if (state.subscriptions.has(msg.id)) {
|
|
1352
|
+
ws.close(4409, `Subscriber for ${msg.id} already exists`);
|
|
1353
|
+
return;
|
|
1354
|
+
}
|
|
1355
|
+
await this.handleSubscribe(ws, state, msg);
|
|
1356
|
+
break;
|
|
1357
|
+
case "complete":
|
|
1358
|
+
if (msg.id) {
|
|
1359
|
+
const sub = state.subscriptions.get(msg.id);
|
|
1360
|
+
if (sub) {
|
|
1361
|
+
sub.generator.return?.(undefined);
|
|
1362
|
+
state.subscriptions.delete(msg.id);
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
break;
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
async handleSubscribe(ws, state, msg) {
|
|
1369
|
+
if (!this.engine.subscribe) {
|
|
1370
|
+
this.send(ws, {
|
|
1371
|
+
type: "error",
|
|
1372
|
+
id: msg.id,
|
|
1373
|
+
payload: [
|
|
1374
|
+
{
|
|
1375
|
+
message: "Subscriptions are not supported by the configured GraphQL engine. " + "Use GraphQLJsAdapter for subscription support."
|
|
1376
|
+
}
|
|
1377
|
+
]
|
|
1378
|
+
});
|
|
1379
|
+
return;
|
|
1380
|
+
}
|
|
1381
|
+
const context = {
|
|
1382
|
+
request: new Request(`ws://localhost${this.graphqlPath}`),
|
|
1383
|
+
user: undefined,
|
|
1384
|
+
httpContext: null
|
|
1385
|
+
};
|
|
1386
|
+
let generator;
|
|
1387
|
+
try {
|
|
1388
|
+
generator = await this.engine.subscribe(this.engineSchema, msg.payload.query, msg.payload.variables ?? {}, context, msg.payload.operationName);
|
|
1389
|
+
} catch (err) {
|
|
1390
|
+
this.send(ws, {
|
|
1391
|
+
type: "error",
|
|
1392
|
+
id: msg.id,
|
|
1393
|
+
payload: [{ message: err.message }]
|
|
1394
|
+
});
|
|
1395
|
+
return;
|
|
1396
|
+
}
|
|
1397
|
+
state.subscriptions.set(msg.id, { generator });
|
|
1398
|
+
(async () => {
|
|
1399
|
+
try {
|
|
1400
|
+
for await (const result of generator) {
|
|
1401
|
+
if (!state.subscriptions.has(msg.id))
|
|
1402
|
+
break;
|
|
1403
|
+
this.send(ws, {
|
|
1404
|
+
type: "next",
|
|
1405
|
+
id: msg.id,
|
|
1406
|
+
payload: result
|
|
1407
|
+
});
|
|
1408
|
+
}
|
|
1409
|
+
this.send(ws, { type: "complete", id: msg.id });
|
|
1410
|
+
state.subscriptions.delete(msg.id);
|
|
1411
|
+
} catch (err) {
|
|
1412
|
+
this.send(ws, {
|
|
1413
|
+
type: "error",
|
|
1414
|
+
id: msg.id,
|
|
1415
|
+
payload: [{ message: err.message }]
|
|
1416
|
+
});
|
|
1417
|
+
state.subscriptions.delete(msg.id);
|
|
1418
|
+
}
|
|
1419
|
+
})();
|
|
1420
|
+
}
|
|
1421
|
+
send(ws, msg) {
|
|
1422
|
+
try {
|
|
1423
|
+
ws.send(JSON.stringify(msg));
|
|
1424
|
+
} catch {}
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1427
|
+
|
|
1428
|
+
// src/graphql/graphql-module.ts
|
|
1429
|
+
init_metadata();
|
|
1430
|
+
function generatePlaygroundHTML(path) {
|
|
1431
|
+
return `<!DOCTYPE html>
|
|
1432
|
+
<html>
|
|
1433
|
+
<head>
|
|
1434
|
+
<meta charset="utf-8" />
|
|
1435
|
+
<title>GraphQL Playground</title>
|
|
1436
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
1437
|
+
<link rel="stylesheet" href="https://unpkg.com/graphiql@3/graphiql.min.css" />
|
|
1438
|
+
<style>body { margin: 0; }</style>
|
|
1439
|
+
</head>
|
|
1440
|
+
<body>
|
|
1441
|
+
<div id="graphiql" style="height:100vh;"></div>
|
|
1442
|
+
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
|
|
1443
|
+
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
|
|
1444
|
+
<script crossorigin src="https://unpkg.com/graphiql@3/graphiql.min.js"></script>
|
|
1445
|
+
<script>
|
|
1446
|
+
const root = ReactDOM.createRoot(document.getElementById('graphiql'));
|
|
1447
|
+
root.render(React.createElement(GraphiQL, {
|
|
1448
|
+
fetcher: GraphiQL.createFetcher({ url: '${path}' }),
|
|
1449
|
+
}));
|
|
1450
|
+
</script>
|
|
1451
|
+
</body>
|
|
1452
|
+
</html>`;
|
|
1453
|
+
}
|
|
1454
|
+
|
|
1455
|
+
class GraphQLModuleInstance {
|
|
1456
|
+
sdl;
|
|
1457
|
+
resolvedSchema;
|
|
1458
|
+
path;
|
|
1459
|
+
constructor(sdl, resolvedSchema, path) {
|
|
1460
|
+
this.sdl = sdl;
|
|
1461
|
+
this.resolvedSchema = resolvedSchema;
|
|
1462
|
+
this.path = path;
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
class DeferredInitializer {
|
|
1467
|
+
resolverClasses;
|
|
1468
|
+
app;
|
|
1469
|
+
engine;
|
|
1470
|
+
initialized = false;
|
|
1471
|
+
initPromise = null;
|
|
1472
|
+
initError = null;
|
|
1473
|
+
resolverInstances = new Map;
|
|
1474
|
+
resolverFields = null;
|
|
1475
|
+
resolvedSchema = null;
|
|
1476
|
+
sdl = "";
|
|
1477
|
+
engineSchema = null;
|
|
1478
|
+
constructor(resolverClasses, app, engine) {
|
|
1479
|
+
this.resolverClasses = resolverClasses;
|
|
1480
|
+
this.app = app;
|
|
1481
|
+
this.engine = engine;
|
|
1482
|
+
}
|
|
1483
|
+
async ensureInitialized() {
|
|
1484
|
+
if (this.initialized) {
|
|
1485
|
+
if (this.initError)
|
|
1486
|
+
throw this.initError;
|
|
1487
|
+
return;
|
|
1488
|
+
}
|
|
1489
|
+
if (this.initPromise) {
|
|
1490
|
+
await this.initPromise;
|
|
1491
|
+
if (this.initError)
|
|
1492
|
+
throw this.initError;
|
|
1493
|
+
return;
|
|
1494
|
+
}
|
|
1495
|
+
this.initPromise = this.initialize();
|
|
1496
|
+
await this.initPromise;
|
|
1497
|
+
if (this.initError)
|
|
1498
|
+
throw this.initError;
|
|
1499
|
+
}
|
|
1500
|
+
async initialize() {
|
|
1501
|
+
try {
|
|
1502
|
+
for (const ResolverClass of this.resolverClasses) {
|
|
1503
|
+
if (this.resolverInstances.has(ResolverClass))
|
|
1504
|
+
continue;
|
|
1505
|
+
const token = ResolverClass;
|
|
1506
|
+
if (!this.app.container.has(token)) {
|
|
1507
|
+
this.app.container.register({
|
|
1508
|
+
token,
|
|
1509
|
+
useClass: ResolverClass
|
|
1510
|
+
});
|
|
1511
|
+
}
|
|
1512
|
+
const injectTokens = getContainerMetadata(ResolverClass, "inject:tokens") ?? [];
|
|
1513
|
+
const deps = injectTokens.map((t) => {
|
|
1514
|
+
const resolved = resolveForwardRef(t);
|
|
1515
|
+
return this.app.container.resolve(resolved);
|
|
1516
|
+
});
|
|
1517
|
+
const instance = new ResolverClass(...deps);
|
|
1518
|
+
this.resolverInstances.set(ResolverClass, instance);
|
|
1519
|
+
}
|
|
1520
|
+
const builder = new SchemaBuilder(this.resolverClasses, this.resolverInstances);
|
|
1521
|
+
const { sdl, resolvedSchema, resolverFields, typeFields } = builder.build();
|
|
1522
|
+
this.sdl = sdl;
|
|
1523
|
+
this.resolvedSchema = resolvedSchema;
|
|
1524
|
+
this.resolverFields = resolverFields;
|
|
1525
|
+
this.engineSchema = this.engine.buildSchema(resolverFields, typeFields, sdl);
|
|
1526
|
+
this.initialized = true;
|
|
1527
|
+
} catch (err) {
|
|
1528
|
+
this.initError = err instanceof Error ? err : new Error(String(err));
|
|
1529
|
+
throw this.initError;
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
class GraphQLModule {
|
|
1535
|
+
static setup(app, options) {
|
|
1536
|
+
const engine = options.engine ?? new BuiltinGraphQLEngine;
|
|
1537
|
+
const path = options.path ?? "/graphql";
|
|
1538
|
+
const introspection = options.introspection !== false;
|
|
1539
|
+
const playgroundEnabled = options.playground !== undefined ? options.playground : engine.supportsIntrospection;
|
|
1540
|
+
if (options.playground === true && !engine.supportsIntrospection) {
|
|
1541
|
+
console.warn("[GraphQL] Playground enabled but the built-in engine does not support " + "introspection. GraphiQL will not work correctly. " + "Use GraphQLJsAdapter for full playground support.");
|
|
1542
|
+
}
|
|
1543
|
+
const initializer = new DeferredInitializer(options.resolvers, app, engine);
|
|
1544
|
+
app.router.post(path, async (httpContext) => {
|
|
1545
|
+
const parsed = await parseGraphQLRequest(httpContext.req);
|
|
1546
|
+
if (!parsed) {
|
|
1547
|
+
return httpContext.json({
|
|
1548
|
+
errors: [{ message: "Invalid GraphQL request: expected JSON body with 'query' field" }]
|
|
1549
|
+
}, { status: 400 });
|
|
1550
|
+
}
|
|
1551
|
+
try {
|
|
1552
|
+
await initializer.ensureInitialized();
|
|
1553
|
+
} catch (err) {
|
|
1554
|
+
return httpContext.json({
|
|
1555
|
+
errors: [{ message: `GraphQL initialization failed: ${err.message}` }]
|
|
1556
|
+
});
|
|
1557
|
+
}
|
|
1558
|
+
const gqlContext = buildGraphQLContext(httpContext);
|
|
1559
|
+
const schema = initializer.engineSchema;
|
|
1560
|
+
const fields = initializer.resolverFields;
|
|
1561
|
+
const trimmed = parsed.query.trim();
|
|
1562
|
+
const isMutation = trimmed.startsWith("mutation");
|
|
1563
|
+
const operationType = isMutation ? "mutation" : "query";
|
|
1564
|
+
const operationFields = isMutation ? fields.mutations : fields.queries;
|
|
1565
|
+
let result;
|
|
1566
|
+
try {
|
|
1567
|
+
result = await GraphQLModule.executeWithPipeline(engine, schema, parsed, gqlContext, httpContext, operationFields, operationType, isMutation ? "mutation" : "query", options, app);
|
|
1568
|
+
} catch (err) {
|
|
1569
|
+
result = {
|
|
1570
|
+
errors: [{ message: err.message }]
|
|
1571
|
+
};
|
|
1572
|
+
}
|
|
1573
|
+
return httpContext.json(result);
|
|
1574
|
+
});
|
|
1575
|
+
if (introspection) {
|
|
1576
|
+
app.router.get(`${path}/schema`, async (httpContext) => {
|
|
1577
|
+
await initializer.ensureInitialized().catch(() => {});
|
|
1578
|
+
return httpContext.text(initializer.sdl || "# Schema not yet initialized");
|
|
1579
|
+
});
|
|
1580
|
+
}
|
|
1581
|
+
if (playgroundEnabled) {
|
|
1582
|
+
app.router.get(path, (httpContext) => {
|
|
1583
|
+
return httpContext.html(generatePlaygroundHTML(path));
|
|
1584
|
+
});
|
|
1585
|
+
}
|
|
1586
|
+
if (options.subscriptions) {
|
|
1587
|
+
if (!engine.supportsSubscriptions) {
|
|
1588
|
+
console.warn("[GraphQL] subscriptions: true but the configured engine does not support " + "subscriptions. Use GraphQLJsAdapter for subscription support.");
|
|
1589
|
+
} else {
|
|
1590
|
+
const subHandler = new SubscriptionHandler(engine, null, path, app.container, app.getGlobalGuards(), app.getGlobalInterceptors());
|
|
1591
|
+
app.setWebSocketHandler(subHandler.getWebSocketConfig());
|
|
1592
|
+
}
|
|
1593
|
+
}
|
|
1594
|
+
if (options.syncOpenAPI) {
|
|
1595
|
+
queueMicrotask(async () => {
|
|
1596
|
+
try {
|
|
1597
|
+
await initializer.ensureInitialized().catch(() => {});
|
|
1598
|
+
const typeClasses = getRegisteredTypeClasses();
|
|
1599
|
+
const { setApiPropertyMetadata: setApiPropertyMetadata2 } = await Promise.resolve().then(() => (init_metadata2(), exports_metadata2));
|
|
1600
|
+
const { getGqlPropertyKeys: getGqlPropertyKeys2, getGqlPropertyMetadata: getGqlPropertyMetadata2 } = await Promise.resolve().then(() => (init_metadata(), exports_metadata));
|
|
1601
|
+
for (const TypeClass of typeClasses) {
|
|
1602
|
+
const keys = getGqlPropertyKeys2(TypeClass.prototype);
|
|
1603
|
+
for (const key of keys) {
|
|
1604
|
+
const fieldMeta = getGqlPropertyMetadata2(TypeClass.prototype, key);
|
|
1605
|
+
if (!fieldMeta)
|
|
1606
|
+
continue;
|
|
1607
|
+
const apiOptions = GraphQLModule.fieldToApiProperty(fieldMeta);
|
|
1608
|
+
setApiPropertyMetadata2(TypeClass.prototype, key, apiOptions);
|
|
1609
|
+
}
|
|
1610
|
+
}
|
|
1611
|
+
} catch {
|
|
1612
|
+
console.warn("[GraphQL] syncOpenAPI: failed to sync field metadata to OpenAPI store. Ensure @buenojs/bueno/openapi is available.");
|
|
1613
|
+
}
|
|
1614
|
+
});
|
|
1615
|
+
}
|
|
1616
|
+
return new GraphQLModuleInstance("", {}, path);
|
|
1617
|
+
}
|
|
1618
|
+
static async executeWithPipeline(engine, schema, parsed, gqlContext, httpContext, operationFields, operationType, _opTypeStr, options, app) {
|
|
1619
|
+
const topLevelFields = extractTopLevelFields(parsed.query);
|
|
1620
|
+
for (const fieldName of topLevelFields) {
|
|
1621
|
+
const resolvedField = operationFields.get(fieldName);
|
|
1622
|
+
if (!resolvedField)
|
|
1623
|
+
continue;
|
|
1624
|
+
const resolverClass = findResolverClass(options.resolvers, fieldName, operationType);
|
|
1625
|
+
if (!resolverClass)
|
|
1626
|
+
continue;
|
|
1627
|
+
const guardsPassed = await runGuardsForField(resolverClass, fieldName, httpContext, gqlContext, operationType, app);
|
|
1628
|
+
if (!guardsPassed) {
|
|
1629
|
+
return {
|
|
1630
|
+
errors: [
|
|
1631
|
+
{
|
|
1632
|
+
message: `Access denied to field '${fieldName}'`,
|
|
1633
|
+
path: [fieldName],
|
|
1634
|
+
extensions: { code: "FORBIDDEN" }
|
|
1635
|
+
}
|
|
1636
|
+
]
|
|
1637
|
+
};
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1640
|
+
return engine.execute(schema, parsed.query, parsed.variables, gqlContext, parsed.operationName);
|
|
1641
|
+
}
|
|
1642
|
+
static fieldToApiProperty(fieldMeta) {
|
|
1643
|
+
const type = fieldMeta.typeFn();
|
|
1644
|
+
const isArray = Array.isArray(type);
|
|
1645
|
+
const inner = isArray ? type[0] : type;
|
|
1646
|
+
let apiType;
|
|
1647
|
+
if (inner === String)
|
|
1648
|
+
apiType = "string";
|
|
1649
|
+
else if (inner === Number)
|
|
1650
|
+
apiType = "number";
|
|
1651
|
+
else if (inner === Boolean)
|
|
1652
|
+
apiType = "boolean";
|
|
1653
|
+
else if (inner.name === "GraphQLID")
|
|
1654
|
+
apiType = "string";
|
|
1655
|
+
else if (inner.name === "GraphQLInt")
|
|
1656
|
+
apiType = "integer";
|
|
1657
|
+
else if (inner.name === "GraphQLFloat")
|
|
1658
|
+
apiType = "number";
|
|
1659
|
+
else
|
|
1660
|
+
apiType = inner;
|
|
1661
|
+
const options = {
|
|
1662
|
+
required: !fieldMeta.nullable
|
|
1663
|
+
};
|
|
1664
|
+
if (isArray) {
|
|
1665
|
+
options.type = "array";
|
|
1666
|
+
options.items = typeof apiType === "string" ? { type: apiType } : { $ref: `#/components/schemas/${inner.name}` };
|
|
1667
|
+
} else {
|
|
1668
|
+
options.type = apiType;
|
|
1669
|
+
}
|
|
1670
|
+
if (fieldMeta.description) {
|
|
1671
|
+
options.description = fieldMeta.description;
|
|
1672
|
+
}
|
|
1673
|
+
return options;
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
function extractTopLevelFields(query) {
|
|
1677
|
+
const stripped = query.trim().replace(/^(query|mutation)\s*\w*\s*(\([^)]*\))?\s*/, "");
|
|
1678
|
+
const match = stripped.match(/^\{([\s\S]*)\}$/);
|
|
1679
|
+
if (!match)
|
|
1680
|
+
return [];
|
|
1681
|
+
const body = match[1];
|
|
1682
|
+
const fields = [];
|
|
1683
|
+
let depth = 0;
|
|
1684
|
+
let nameStart = -1;
|
|
1685
|
+
for (let i = 0;i < body.length; i++) {
|
|
1686
|
+
const ch = body[i];
|
|
1687
|
+
if (ch === "{") {
|
|
1688
|
+
depth++;
|
|
1689
|
+
} else if (ch === "}") {
|
|
1690
|
+
depth--;
|
|
1691
|
+
} else if (depth === 0 && /[A-Za-z_]/.test(ch) && nameStart < 0) {
|
|
1692
|
+
nameStart = i;
|
|
1693
|
+
} else if (depth === 0 && nameStart >= 0 && !/[\w]/.test(ch)) {
|
|
1694
|
+
const name = body.slice(nameStart, i);
|
|
1695
|
+
if (body[i] === ":") {
|
|
1696
|
+
nameStart = -1;
|
|
1697
|
+
continue;
|
|
1698
|
+
}
|
|
1699
|
+
if (name !== "on")
|
|
1700
|
+
fields.push(name);
|
|
1701
|
+
nameStart = -1;
|
|
1702
|
+
}
|
|
1703
|
+
}
|
|
1704
|
+
if (nameStart >= 0) {
|
|
1705
|
+
fields.push(body.slice(nameStart));
|
|
1706
|
+
}
|
|
1707
|
+
return [...new Set(fields)];
|
|
1708
|
+
}
|
|
1709
|
+
function findResolverClass(resolverClasses, fieldName, operationType) {
|
|
1710
|
+
for (const ResolverClass of resolverClasses) {
|
|
1711
|
+
const fields = operationType === "mutation" ? getMutationFields(ResolverClass.prototype) : getQueryFields(ResolverClass.prototype);
|
|
1712
|
+
if (fields.some((f) => f.fieldName === fieldName)) {
|
|
1713
|
+
return ResolverClass;
|
|
1714
|
+
}
|
|
1715
|
+
}
|
|
1716
|
+
return;
|
|
1717
|
+
}
|
|
1718
|
+
async function runGuardsForField(resolverClass, fieldName, httpContext, gqlContext, operationType, app) {
|
|
1719
|
+
const { executeGuards: executeGuards2, getClassGuards: getClassGuards2, getMethodGuards: getMethodGuards2 } = await Promise.resolve().then(() => (init_guards(), exports_guards));
|
|
1720
|
+
const { resolveForwardRef: resolveForwardRef2 } = await Promise.resolve().then(() => (init_container(), exports_container));
|
|
1721
|
+
const classGuards = getClassGuards2(resolverClass) ?? [];
|
|
1722
|
+
const methodGuards = getMethodGuards2(resolverClass.prototype, fieldName) ?? [];
|
|
1723
|
+
const globalGuards = app.getGlobalGuards();
|
|
1724
|
+
const { enrichContextForGraphQL: enrichContextForGraphQL2 } = await Promise.resolve().then(() => exports_context_builder);
|
|
1725
|
+
enrichContextForGraphQL2(httpContext, fieldName, operationType, resolverClass);
|
|
1726
|
+
return executeGuards2(httpContext, {
|
|
1727
|
+
globalGuards,
|
|
1728
|
+
classGuards,
|
|
1729
|
+
methodGuards,
|
|
1730
|
+
resolveGuard: (guard) => {
|
|
1731
|
+
if (typeof guard === "object" && guard !== null && !("canActivate" in guard)) {
|
|
1732
|
+
try {
|
|
1733
|
+
return app.container.resolve(guard);
|
|
1734
|
+
} catch {
|
|
1735
|
+
return null;
|
|
1736
|
+
}
|
|
1737
|
+
}
|
|
1738
|
+
return null;
|
|
1739
|
+
}
|
|
1740
|
+
});
|
|
1741
|
+
}
|
|
1742
|
+
// src/graphql/decorators.ts
|
|
1743
|
+
init_metadata();
|
|
1744
|
+
function Resolver(name) {
|
|
1745
|
+
return (target) => {
|
|
1746
|
+
const ctor = target;
|
|
1747
|
+
setResolverMetadata(ctor, {
|
|
1748
|
+
name: name ?? ctor.name
|
|
1749
|
+
});
|
|
1750
|
+
};
|
|
1751
|
+
}
|
|
1752
|
+
function ObjectType(name, options) {
|
|
1753
|
+
return (target) => {
|
|
1754
|
+
const ctor = target;
|
|
1755
|
+
setObjectTypeMetadata(ctor, {
|
|
1756
|
+
name: name ?? ctor.name,
|
|
1757
|
+
kind: "object",
|
|
1758
|
+
description: options?.description
|
|
1759
|
+
});
|
|
1760
|
+
registerTypeClass(ctor);
|
|
1761
|
+
};
|
|
1762
|
+
}
|
|
1763
|
+
function InputType(name, options) {
|
|
1764
|
+
return (target) => {
|
|
1765
|
+
const ctor = target;
|
|
1766
|
+
setInputTypeMetadata(ctor, {
|
|
1767
|
+
name: name ?? ctor.name,
|
|
1768
|
+
kind: "input",
|
|
1769
|
+
description: options?.description
|
|
1770
|
+
});
|
|
1771
|
+
registerTypeClass(ctor);
|
|
1772
|
+
};
|
|
1773
|
+
}
|
|
1774
|
+
function Query(typeFn, options = {}) {
|
|
1775
|
+
return (target, propertyKey, descriptor) => {
|
|
1776
|
+
const methodName = String(propertyKey);
|
|
1777
|
+
addQueryField(target, {
|
|
1778
|
+
methodName,
|
|
1779
|
+
fieldName: options.name ?? methodName,
|
|
1780
|
+
typeFn,
|
|
1781
|
+
kind: "query",
|
|
1782
|
+
nullable: options.nullable ?? false,
|
|
1783
|
+
description: options.description,
|
|
1784
|
+
deprecationReason: options.deprecationReason,
|
|
1785
|
+
paramMetadata: getParamMetadata(target, methodName)
|
|
1786
|
+
});
|
|
1787
|
+
return descriptor;
|
|
1788
|
+
};
|
|
1789
|
+
}
|
|
1790
|
+
function Mutation(typeFn, options = {}) {
|
|
1791
|
+
return (target, propertyKey, descriptor) => {
|
|
1792
|
+
const methodName = String(propertyKey);
|
|
1793
|
+
addMutationField(target, {
|
|
1794
|
+
methodName,
|
|
1795
|
+
fieldName: options.name ?? methodName,
|
|
1796
|
+
typeFn,
|
|
1797
|
+
kind: "mutation",
|
|
1798
|
+
nullable: options.nullable ?? false,
|
|
1799
|
+
description: options.description,
|
|
1800
|
+
deprecationReason: options.deprecationReason,
|
|
1801
|
+
paramMetadata: getParamMetadata(target, methodName)
|
|
1802
|
+
});
|
|
1803
|
+
return descriptor;
|
|
1804
|
+
};
|
|
1805
|
+
}
|
|
1806
|
+
function Subscription(typeFn, options = {}) {
|
|
1807
|
+
return (target, propertyKey, descriptor) => {
|
|
1808
|
+
const methodName = String(propertyKey);
|
|
1809
|
+
addSubscriptionField(target, {
|
|
1810
|
+
methodName,
|
|
1811
|
+
fieldName: options.name ?? methodName,
|
|
1812
|
+
typeFn,
|
|
1813
|
+
kind: "subscription",
|
|
1814
|
+
nullable: options.nullable ?? false,
|
|
1815
|
+
description: options.description,
|
|
1816
|
+
deprecationReason: options.deprecationReason,
|
|
1817
|
+
paramMetadata: getParamMetadata(target, methodName)
|
|
1818
|
+
});
|
|
1819
|
+
return descriptor;
|
|
1820
|
+
};
|
|
1821
|
+
}
|
|
1822
|
+
function Field(typeFn, options = {}) {
|
|
1823
|
+
return (target, propertyKey) => {
|
|
1824
|
+
const key = typeof propertyKey === "symbol" ? propertyKey.toString() : propertyKey;
|
|
1825
|
+
setGqlPropertyMetadata(target, propertyKey, {
|
|
1826
|
+
propertyKey: key,
|
|
1827
|
+
typeFn,
|
|
1828
|
+
nullable: options.nullable ?? false,
|
|
1829
|
+
description: options.description,
|
|
1830
|
+
deprecationReason: options.deprecationReason,
|
|
1831
|
+
defaultValue: options.defaultValue
|
|
1832
|
+
});
|
|
1833
|
+
};
|
|
1834
|
+
}
|
|
1835
|
+
function Args(name, inputType) {
|
|
1836
|
+
return (target, propertyKey, parameterIndex) => {
|
|
1837
|
+
if (!propertyKey)
|
|
1838
|
+
return;
|
|
1839
|
+
const methodName = String(propertyKey);
|
|
1840
|
+
setParamMetadata(target, methodName, parameterIndex, {
|
|
1841
|
+
index: parameterIndex,
|
|
1842
|
+
kind: inputType ? "argsObject" : "args",
|
|
1843
|
+
argName: name,
|
|
1844
|
+
inputTypeFn: inputType ? () => inputType : undefined
|
|
1845
|
+
});
|
|
1846
|
+
syncParamMetadata(target, methodName);
|
|
1847
|
+
};
|
|
1848
|
+
}
|
|
1849
|
+
function GqlContext() {
|
|
1850
|
+
return (target, propertyKey, parameterIndex) => {
|
|
1851
|
+
if (!propertyKey)
|
|
1852
|
+
return;
|
|
1853
|
+
const methodName = String(propertyKey);
|
|
1854
|
+
setParamMetadata(target, methodName, parameterIndex, {
|
|
1855
|
+
index: parameterIndex,
|
|
1856
|
+
kind: "context"
|
|
1857
|
+
});
|
|
1858
|
+
syncParamMetadata(target, methodName);
|
|
1859
|
+
};
|
|
1860
|
+
}
|
|
1861
|
+
function syncParamMetadata(prototype, methodName) {
|
|
1862
|
+
const params = getParamMetadata(prototype, methodName);
|
|
1863
|
+
const syncFields = (fields, addFn) => {
|
|
1864
|
+
const field = fields.find((f) => f.methodName === methodName);
|
|
1865
|
+
if (field) {
|
|
1866
|
+
addFn(prototype, { ...field, paramMetadata: params });
|
|
1867
|
+
}
|
|
1868
|
+
};
|
|
1869
|
+
syncFields(getQueryFields(prototype), addQueryField);
|
|
1870
|
+
syncFields(getMutationFields(prototype), addMutationField);
|
|
1871
|
+
syncFields(getSubscriptionFields(prototype), addSubscriptionField);
|
|
1872
|
+
}
|
|
1873
|
+
// src/graphql/types.ts
|
|
1874
|
+
class GraphQLID {
|
|
1875
|
+
static __type = "ID";
|
|
1876
|
+
}
|
|
1877
|
+
|
|
1878
|
+
class GraphQLInt {
|
|
1879
|
+
static __type = "Int";
|
|
1880
|
+
}
|
|
1881
|
+
|
|
1882
|
+
class GraphQLFloat {
|
|
1883
|
+
static __type = "Float";
|
|
1884
|
+
}
|
|
1885
|
+
// src/graphql/execution-pipeline.ts
|
|
1886
|
+
init_guards();
|
|
1887
|
+
|
|
1888
|
+
// src/modules/interceptors.ts
|
|
1889
|
+
var interceptorsClassMetadata = new WeakMap;
|
|
1890
|
+
var interceptorsMethodMetadata = new WeakMap;
|
|
1891
|
+
function setClassInterceptors(target, interceptors) {
|
|
1892
|
+
interceptorsClassMetadata.set(target, interceptors);
|
|
1893
|
+
}
|
|
1894
|
+
function getClassInterceptors(target) {
|
|
1895
|
+
return interceptorsClassMetadata.get(target);
|
|
1896
|
+
}
|
|
1897
|
+
function setMethodInterceptors(target, propertyKey, interceptors) {
|
|
1898
|
+
if (!interceptorsMethodMetadata.has(target)) {
|
|
1899
|
+
interceptorsMethodMetadata.set(target, new Map);
|
|
1900
|
+
}
|
|
1901
|
+
interceptorsMethodMetadata.get(target)?.set(propertyKey, interceptors);
|
|
1902
|
+
}
|
|
1903
|
+
function getMethodInterceptors(target, propertyKey) {
|
|
1904
|
+
return interceptorsMethodMetadata.get(target)?.get(propertyKey);
|
|
1905
|
+
}
|
|
1906
|
+
function UseInterceptors(...interceptors) {
|
|
1907
|
+
const decorator = (target, propertyKey, descriptor) => {
|
|
1908
|
+
if (propertyKey !== undefined && descriptor !== undefined) {
|
|
1909
|
+
const targetObj = target;
|
|
1910
|
+
const existingInterceptors = getMethodInterceptors(targetObj, propertyKey) ?? [];
|
|
1911
|
+
setMethodInterceptors(targetObj, propertyKey, [
|
|
1912
|
+
...existingInterceptors,
|
|
1913
|
+
...interceptors
|
|
1914
|
+
]);
|
|
1915
|
+
return descriptor;
|
|
1916
|
+
} else {
|
|
1917
|
+
const targetClass = target;
|
|
1918
|
+
const existingInterceptors = getClassInterceptors(targetClass) ?? [];
|
|
1919
|
+
setClassInterceptors(targetClass, [
|
|
1920
|
+
...existingInterceptors,
|
|
1921
|
+
...interceptors
|
|
1922
|
+
]);
|
|
1923
|
+
}
|
|
1924
|
+
};
|
|
1925
|
+
return decorator;
|
|
1926
|
+
}
|
|
1927
|
+
|
|
1928
|
+
class LoggingInterceptor {
|
|
1929
|
+
async intercept(context, next) {
|
|
1930
|
+
const method = context.method;
|
|
1931
|
+
const path = context.path;
|
|
1932
|
+
const startTime = Date.now();
|
|
1933
|
+
console.log(`[${method}] ${path} - Started`);
|
|
1934
|
+
try {
|
|
1935
|
+
const result = await next.handle();
|
|
1936
|
+
const duration = Date.now() - startTime;
|
|
1937
|
+
console.log(`[${method}] ${path} - Completed in ${duration}ms`);
|
|
1938
|
+
return result;
|
|
1939
|
+
} catch (error) {
|
|
1940
|
+
const duration = Date.now() - startTime;
|
|
1941
|
+
console.error(`[${method}] ${path} - Failed in ${duration}ms`, error);
|
|
1942
|
+
throw error;
|
|
1943
|
+
}
|
|
1944
|
+
}
|
|
1945
|
+
}
|
|
1946
|
+
|
|
1947
|
+
class TransformInterceptor {
|
|
1948
|
+
async intercept(context, next) {
|
|
1949
|
+
const result = await next.handle();
|
|
1950
|
+
return {
|
|
1951
|
+
data: result,
|
|
1952
|
+
timestamp: new Date().toISOString()
|
|
1953
|
+
};
|
|
1954
|
+
}
|
|
1955
|
+
}
|
|
1956
|
+
|
|
1957
|
+
class TimeoutInterceptor {
|
|
1958
|
+
timeoutMs;
|
|
1959
|
+
constructor(timeoutMs) {
|
|
1960
|
+
this.timeoutMs = timeoutMs;
|
|
1961
|
+
}
|
|
1962
|
+
async intercept(context, next) {
|
|
1963
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
1964
|
+
setTimeout(() => {
|
|
1965
|
+
reject(new Error(`Request timeout after ${this.timeoutMs}ms`));
|
|
1966
|
+
}, this.timeoutMs);
|
|
1967
|
+
});
|
|
1968
|
+
return Promise.race([next.handle(), timeoutPromise]);
|
|
1969
|
+
}
|
|
1970
|
+
}
|
|
1971
|
+
|
|
1972
|
+
class CacheInterceptor {
|
|
1973
|
+
ttlMs;
|
|
1974
|
+
static cache = new Map;
|
|
1975
|
+
static cleanupInterval = null;
|
|
1976
|
+
constructor(ttlMs = 60000) {
|
|
1977
|
+
this.ttlMs = ttlMs;
|
|
1978
|
+
CacheInterceptor.setupCleanup();
|
|
1979
|
+
}
|
|
1980
|
+
static setupCleanup() {
|
|
1981
|
+
if (CacheInterceptor.cleanupInterval === null) {
|
|
1982
|
+
CacheInterceptor.cleanupInterval = setInterval(() => {
|
|
1983
|
+
const now = Date.now();
|
|
1984
|
+
for (const [key, entry] of CacheInterceptor.cache.entries()) {
|
|
1985
|
+
if (entry.expiresAt < now) {
|
|
1986
|
+
CacheInterceptor.cache.delete(key);
|
|
1987
|
+
}
|
|
1988
|
+
}
|
|
1989
|
+
}, 60000);
|
|
1990
|
+
}
|
|
1991
|
+
}
|
|
1992
|
+
getCacheKey(context) {
|
|
1993
|
+
return `${context.method}:${context.path}:${context.url.search}`;
|
|
1994
|
+
}
|
|
1995
|
+
static clearCache() {
|
|
1996
|
+
CacheInterceptor.cache.clear();
|
|
1997
|
+
}
|
|
1998
|
+
static clearCachePattern(pattern) {
|
|
1999
|
+
for (const key of CacheInterceptor.cache.keys()) {
|
|
2000
|
+
if (key.includes(pattern)) {
|
|
2001
|
+
CacheInterceptor.cache.delete(key);
|
|
2002
|
+
}
|
|
2003
|
+
}
|
|
2004
|
+
}
|
|
2005
|
+
async intercept(context, next) {
|
|
2006
|
+
const cacheKey = this.getCacheKey(context);
|
|
2007
|
+
const now = Date.now();
|
|
2008
|
+
const cached = CacheInterceptor.cache.get(cacheKey);
|
|
2009
|
+
if (cached && cached.expiresAt > now) {
|
|
2010
|
+
return cached.value;
|
|
2011
|
+
}
|
|
2012
|
+
const result = await next.handle();
|
|
2013
|
+
CacheInterceptor.cache.set(cacheKey, {
|
|
2014
|
+
value: result,
|
|
2015
|
+
expiresAt: now + this.ttlMs
|
|
2016
|
+
});
|
|
2017
|
+
return result;
|
|
2018
|
+
}
|
|
2019
|
+
}
|
|
2020
|
+
|
|
2021
|
+
class HeaderInterceptor {
|
|
2022
|
+
headers;
|
|
2023
|
+
constructor(headers) {
|
|
2024
|
+
this.headers = headers;
|
|
2025
|
+
}
|
|
2026
|
+
async intercept(context, next) {
|
|
2027
|
+
const result = await next.handle();
|
|
2028
|
+
if (result instanceof Response) {
|
|
2029
|
+
const newHeaders = new Headers(result.headers);
|
|
2030
|
+
for (const [key, value] of Object.entries(this.headers)) {
|
|
2031
|
+
newHeaders.set(key, value);
|
|
2032
|
+
}
|
|
2033
|
+
return new Response(result.body, {
|
|
2034
|
+
status: result.status,
|
|
2035
|
+
statusText: result.statusText,
|
|
2036
|
+
headers: newHeaders
|
|
2037
|
+
});
|
|
2038
|
+
}
|
|
2039
|
+
return result;
|
|
2040
|
+
}
|
|
2041
|
+
}
|
|
2042
|
+
function createCallHandler(interceptors, context, finalHandler, resolveInterceptor) {
|
|
2043
|
+
let index = 0;
|
|
2044
|
+
const execute = async () => {
|
|
2045
|
+
if (index >= interceptors.length) {
|
|
2046
|
+
return finalHandler();
|
|
2047
|
+
}
|
|
2048
|
+
const interceptor = interceptors[index++];
|
|
2049
|
+
let interceptorInstance = interceptor;
|
|
2050
|
+
if (resolveInterceptor && !isNestInterceptor(interceptor) && !isInterceptorFn(interceptor)) {
|
|
2051
|
+
interceptorInstance = resolveInterceptor(interceptor);
|
|
2052
|
+
}
|
|
2053
|
+
if (!interceptorInstance) {
|
|
2054
|
+
console.warn("Interceptor could not be resolved:", interceptor);
|
|
2055
|
+
return execute();
|
|
2056
|
+
}
|
|
2057
|
+
const next = {
|
|
2058
|
+
handle: () => execute()
|
|
2059
|
+
};
|
|
2060
|
+
if (isInterceptorFn(interceptorInstance)) {
|
|
2061
|
+
return interceptorInstance(context, () => execute());
|
|
2062
|
+
} else {
|
|
2063
|
+
return interceptorInstance.intercept(context, next);
|
|
2064
|
+
}
|
|
2065
|
+
};
|
|
2066
|
+
return {
|
|
2067
|
+
handle: execute
|
|
2068
|
+
};
|
|
2069
|
+
}
|
|
2070
|
+
async function executeInterceptors(context, handler, options) {
|
|
2071
|
+
const {
|
|
2072
|
+
globalInterceptors = [],
|
|
2073
|
+
classInterceptors = [],
|
|
2074
|
+
methodInterceptors = [],
|
|
2075
|
+
resolveInterceptor
|
|
2076
|
+
} = options;
|
|
2077
|
+
const allInterceptors = [
|
|
2078
|
+
...globalInterceptors,
|
|
2079
|
+
...classInterceptors,
|
|
2080
|
+
...methodInterceptors
|
|
2081
|
+
];
|
|
2082
|
+
if (allInterceptors.length === 0) {
|
|
2083
|
+
return handler();
|
|
2084
|
+
}
|
|
2085
|
+
const resolvedInterceptors = [];
|
|
2086
|
+
for (const interceptor of allInterceptors) {
|
|
2087
|
+
let instance = null;
|
|
2088
|
+
if (typeof interceptor === "function") {
|
|
2089
|
+
const funcInterceptor = interceptor;
|
|
2090
|
+
if (funcInterceptor.prototype && typeof funcInterceptor.prototype === "object" && "intercept" in funcInterceptor.prototype) {
|
|
2091
|
+
instance = resolveInterceptor ? resolveInterceptor(interceptor) : null;
|
|
2092
|
+
if (!instance) {
|
|
2093
|
+
const InterceptorClass = interceptor;
|
|
2094
|
+
instance = new InterceptorClass;
|
|
2095
|
+
}
|
|
2096
|
+
} else {
|
|
2097
|
+
instance = interceptor;
|
|
2098
|
+
}
|
|
2099
|
+
} else if (typeof interceptor === "object" && interceptor !== null) {
|
|
2100
|
+
const objInterceptor = interceptor;
|
|
2101
|
+
if ("intercept" in objInterceptor && typeof objInterceptor.intercept === "function") {
|
|
2102
|
+
instance = interceptor;
|
|
2103
|
+
} else {
|
|
2104
|
+
instance = resolveInterceptor ? resolveInterceptor(interceptor) : null;
|
|
2105
|
+
}
|
|
2106
|
+
}
|
|
2107
|
+
if (instance) {
|
|
2108
|
+
resolvedInterceptors.push(instance);
|
|
2109
|
+
} else {
|
|
2110
|
+
console.warn("Interceptor could not be resolved:", interceptor);
|
|
2111
|
+
}
|
|
2112
|
+
}
|
|
2113
|
+
const callHandler = createCallHandler(resolvedInterceptors, context, handler, resolveInterceptor);
|
|
2114
|
+
return callHandler.handle();
|
|
2115
|
+
}
|
|
2116
|
+
function isNestInterceptor(value) {
|
|
2117
|
+
return typeof value === "object" && value !== null && "intercept" in value && typeof value.intercept === "function";
|
|
2118
|
+
}
|
|
2119
|
+
function isInterceptorFn(value) {
|
|
2120
|
+
return typeof value === "function" && !isNestInterceptor(value);
|
|
2121
|
+
}
|
|
2122
|
+
// src/graphql/execution-pipeline.ts
|
|
2123
|
+
class GraphQLForbiddenError extends Error {
|
|
2124
|
+
extensions = { code: "FORBIDDEN" };
|
|
2125
|
+
constructor(message = "Forbidden") {
|
|
2126
|
+
super(message);
|
|
2127
|
+
this.name = "GraphQLForbiddenError";
|
|
2128
|
+
}
|
|
2129
|
+
toGraphQLError() {
|
|
2130
|
+
return {
|
|
2131
|
+
message: this.message,
|
|
2132
|
+
extensions: this.extensions
|
|
2133
|
+
};
|
|
2134
|
+
}
|
|
2135
|
+
}
|
|
2136
|
+
export {
|
|
2137
|
+
buildGraphQLContext,
|
|
2138
|
+
SubscriptionHandler,
|
|
2139
|
+
Subscription,
|
|
2140
|
+
SchemaBuilder,
|
|
2141
|
+
Resolver,
|
|
2142
|
+
Query,
|
|
2143
|
+
ObjectType,
|
|
2144
|
+
Mutation,
|
|
2145
|
+
InputType,
|
|
2146
|
+
GraphQLModuleInstance,
|
|
2147
|
+
GraphQLModule,
|
|
2148
|
+
GraphQLInt,
|
|
2149
|
+
GraphQLID,
|
|
2150
|
+
GraphQLForbiddenError,
|
|
2151
|
+
GraphQLFloat,
|
|
2152
|
+
GqlContext,
|
|
2153
|
+
Field,
|
|
2154
|
+
BuiltinGraphQLEngine,
|
|
2155
|
+
Args
|
|
2156
|
+
};
|