@mandujs/core 0.54.32 → 0.55.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +654 -654
- package/package.json +106 -210
- package/scripts/postinstall-lock.ts +153 -153
- package/src/a11y/__tests__/run-audit.test.ts +333 -333
- package/src/a11y/fix-hints.ts +76 -76
- package/src/a11y/index.ts +18 -18
- package/src/a11y/run-audit.ts +15 -15
- package/src/a11y/types.ts +125 -125
- package/src/agent/__tests__/apply.test.ts +224 -0
- package/src/agent/__tests__/context.test.ts +97 -98
- package/src/agent/apply.ts +1031 -0
- package/src/agent/context.ts +552 -552
- package/src/agent/index.ts +4 -3
- package/src/agent/plan.ts +161 -243
- package/src/agent/repair.ts +239 -162
- package/src/agent/sync.ts +197 -198
- package/src/agent/types.ts +242 -94
- package/src/agent/verify.ts +55 -55
- package/src/auth/__tests__/login.test.ts +1 -1
- package/src/auth/__tests__/password.test.ts +15 -5
- package/src/auth/__tests__/reset.test.ts +2 -2
- package/src/auth/__tests__/tokens.test.ts +274 -274
- package/src/auth/__tests__/verification.test.ts +274 -274
- package/src/auth/index.ts +76 -76
- package/src/auth/login.ts +225 -225
- package/src/auth/password.ts +14 -2
- package/src/auth/reset.ts +243 -243
- package/src/auth/tokens.ts +612 -612
- package/src/auth/verification.ts +253 -253
- package/src/brain/__tests__/redactor.test.ts +94 -94
- package/src/brain/adapters/__tests__/_helpers.ts +83 -83
- package/src/brain/adapters/__tests__/anthropic-oauth.test.ts +196 -196
- package/src/brain/adapters/__tests__/chatgpt-auth.test.ts +193 -193
- package/src/brain/adapters/__tests__/openai-oauth.test.ts +209 -209
- package/src/brain/adapters/__tests__/resolver.test.ts +143 -143
- package/src/brain/adapters/anthropic-oauth.ts +1 -1
- package/src/brain/adapters/chatgpt-auth.ts +300 -300
- package/src/brain/adapters/index.ts +319 -319
- package/src/brain/adapters/oauth-flow.ts +439 -439
- package/src/brain/consent.ts +240 -240
- package/src/brain/credentials.ts +396 -396
- package/src/brain/doctor/analyzer.ts +7 -7
- package/src/bundler/__snapshots__/build.test.ts.snap +5 -5
- package/src/bundler/__tests__/build-runner.ts +166 -166
- package/src/bundler/__tests__/client-boundary-transform.test.ts +524 -524
- package/src/bundler/__tests__/cold-start.test.ts +60 -60
- package/src/bundler/__tests__/css.test.ts +49 -20
- package/src/bundler/__tests__/dev-reliability.test.ts +619 -619
- package/src/bundler/__tests__/extended-watch.test.ts +711 -711
- package/src/bundler/__tests__/fast-refresh.test.ts +24 -24
- package/src/bundler/__tests__/generation.test.ts +447 -0
- package/src/bundler/__tests__/hdr.test.ts +24 -18
- package/src/bundler/__tests__/hmr-client.test.ts +62 -24
- package/src/bundler/__tests__/jsx-runtime-shim.test.ts +179 -140
- package/src/bundler/__tests__/manifest-schema.test.ts +305 -266
- package/src/bundler/__tests__/prod-smoke.test.ts +138 -138
- package/src/bundler/__tests__/reverse-import-graph.test.ts +42 -42
- package/src/bundler/__tests__/slot-dispatch.test.ts +573 -573
- package/src/bundler/__tests__/url-cap-and-slot-regex.test.ts +286 -286
- package/src/bundler/__tests__/vendor-cache.test.ts +455 -455
- package/src/bundler/analyzer.ts +15 -15
- package/src/bundler/budget.ts +404 -404
- package/src/bundler/build.test.ts +898 -915
- package/src/bundler/build.ts +113 -24
- package/src/bundler/client-boundary-transform.ts +977 -977
- package/src/bundler/css.ts +65 -44
- package/src/bundler/dev.ts +105 -73
- package/src/bundler/fast-refresh-preamble.ts +47 -47
- package/src/bundler/generation.ts +602 -0
- package/src/bundler/index.ts +3 -3
- package/src/bundler/manifest-schema.ts +55 -40
- package/src/bundler/plugins/__tests__/block-generated-imports.test.ts +13 -13
- package/src/bundler/plugins/__tests__/react-compiler-config.test.ts +83 -83
- package/src/bundler/plugins/__tests__/react-compiler-lint.test.ts +110 -110
- package/src/bundler/plugins/block-generated-imports.ts +16 -15
- package/src/bundler/plugins/index.ts +83 -83
- package/src/bundler/plugins/react-compiler-config.ts +108 -108
- package/src/bundler/plugins/react-compiler-lint.ts +253 -253
- package/src/bundler/plugins/react-compiler.ts +162 -162
- package/src/bundler/prerender.ts +29 -9
- package/src/bundler/reverse-import-graph.ts +339 -339
- package/src/bundler/safe-build.test.ts +1 -1
- package/src/bundler/safe-build.ts +103 -103
- package/src/bundler/scenario-matrix.ts +229 -229
- package/src/bundler/types.ts +58 -56
- package/src/bundler/vendor-cache-types.ts +130 -130
- package/src/bundler/vendor-cache.ts +526 -526
- package/src/change/snapshot.ts +18 -18
- package/src/client/Form.tsx +105 -105
- package/src/client/Link.tsx +9 -9
- package/src/client/__tests__/props-serialization.test.ts +37 -37
- package/src/client/__tests__/use-sse.test.ts +153 -153
- package/src/client/globals.ts +1 -1
- package/src/client/hooks.ts +362 -362
- package/src/client/hydrate.ts +2 -2
- package/src/client/index.ts +1 -1
- package/src/client/island.ts +79 -79
- package/src/client/prefetch-helper.ts +55 -55
- package/src/client/props-serialization.ts +233 -233
- package/src/client/runtime-entry.ts +598 -598
- package/src/client/runtime.ts +1 -1
- package/src/client/serialize.ts +50 -50
- package/src/client/use-fetch.ts +6 -6
- package/src/client/use-head.ts +197 -197
- package/src/client/use-sse.ts +378 -378
- package/src/client/window-state.ts +101 -101
- package/src/components/Image-compat.ts +3 -0
- package/src/components/Image.tsx +162 -162
- package/src/config/validate.ts +1 -1
- package/src/config/watcher.ts +311 -311
- package/src/constants.ts +40 -40
- package/src/content/collection.ts +9 -9
- package/src/content/content-layer.ts +7 -7
- package/src/content/data-store.ts +245 -245
- package/src/content/frontmatter.ts +189 -189
- package/src/content/generate-types.ts +1 -1
- package/src/content/index.ts +2 -2
- package/src/content/loader-context.ts +171 -171
- package/src/content/loaders/api.ts +216 -216
- package/src/content/loaders/file.ts +172 -172
- package/src/content/loaders/glob.ts +253 -253
- package/src/content/loaders/index.ts +34 -34
- package/src/content/loaders/types.ts +137 -137
- package/src/content/meta-store.ts +209 -209
- package/src/content/prebuild.test.ts +571 -571
- package/src/content/prebuild.ts +636 -636
- package/src/content/schema.ts +20 -20
- package/src/content/sidebar.ts +630 -630
- package/src/content/slug.ts +110 -110
- package/src/content/types.ts +282 -282
- package/src/content/watcher.ts +135 -135
- package/src/contract/client-safe.test.ts +42 -42
- package/src/contract/client-safe.ts +114 -114
- package/src/contract/define.ts +11 -11
- package/src/contract/index.ts +1 -1
- package/src/contract/normalize.test.ts +276 -276
- package/src/contract/normalize.ts +410 -410
- package/src/contract/registry.test.ts +206 -206
- package/src/contract/route-helpers.ts +1 -1
- package/src/contract/rpc.ts +443 -443
- package/src/contract/types.ts +58 -58
- package/src/db/__tests__/db.test.ts +9 -9
- package/src/db/index.ts +2 -2
- package/src/db/migrations/history-table.ts +345 -345
- package/src/db/migrations/index.ts +3 -3
- package/src/db/migrations/lock.ts +324 -324
- package/src/db/migrations/runner.ts +650 -650
- package/src/deploy/cache.ts +140 -140
- package/src/deploy/compile/vercel.ts +344 -344
- package/src/deploy/index.ts +87 -87
- package/src/deploy/inference/brain.ts +268 -268
- package/src/deploy/inference/context.ts +82 -82
- package/src/deploy/inference/filling-extract.ts +245 -245
- package/src/deploy/inference/heuristic.ts +182 -182
- package/src/deploy/intent.ts +173 -173
- package/src/deploy/plan.ts +178 -178
- package/src/design/__tests__/agents-link.test.ts +109 -109
- package/src/design/__tests__/extract-patch-diff.test.ts +265 -265
- package/src/design/__tests__/lint.test.ts +110 -110
- package/src/design/__tests__/parser.test.ts +195 -195
- package/src/design/__tests__/tailwind-theme.test.ts +229 -229
- package/src/design/agents-link.ts +165 -165
- package/src/design/diff.ts +138 -138
- package/src/design/extract.ts +285 -285
- package/src/design/index.ts +102 -102
- package/src/design/lint.ts +209 -209
- package/src/design/parser.ts +555 -555
- package/src/design/patch.ts +241 -241
- package/src/design/scaffold.ts +147 -147
- package/src/design/tailwind-theme.ts +441 -441
- package/src/design/types.ts +210 -210
- package/src/desktop/__tests__/smoke.test.ts +2 -2
- package/src/desktop/__tests__/webview-fallback.test.ts +12 -32
- package/src/desktop/__tests__/window.test.ts +20 -99
- package/src/desktop/__tests__/worker.test.ts +266 -266
- package/src/desktop/index.ts +43 -43
- package/src/desktop/types.ts +158 -158
- package/src/desktop/webview-fallback.ts +17 -4
- package/src/desktop/window.ts +17 -8
- package/src/desktop/worker.ts +180 -180
- package/src/dev-error-overlay/__tests__/overlay-injector.test.ts +241 -241
- package/src/dev-error-overlay/index.ts +30 -30
- package/src/dev-error-overlay/overlay-injector.ts +243 -243
- package/src/dev-error-overlay/overlay-styles.ts +52 -52
- package/src/dev-error-overlay/types.ts +66 -66
- package/src/devtools/ai/context-builder.ts +375 -375
- package/src/devtools/ai/index.ts +25 -25
- package/src/devtools/ai/mcp-connector.ts +25 -25
- package/src/devtools/client/catchers/error-catcher.ts +344 -344
- package/src/devtools/client/catchers/index.ts +18 -18
- package/src/devtools/client/components/index.ts +39 -39
- package/src/devtools/client/components/mandu-character.tsx +331 -331
- package/src/devtools/client/components/panel/errors-panel.tsx +259 -259
- package/src/devtools/client/components/panel/guard-panel.tsx +30 -30
- package/src/devtools/client/components/panel/islands-panel.tsx +16 -16
- package/src/devtools/client/components/panel/network-panel.tsx +291 -291
- package/src/devtools/client/components/panel/panel-container.tsx +1 -1
- package/src/devtools/client/components/panel/preview-panel.tsx +46 -46
- package/src/devtools/client/filters/context-filters.ts +282 -282
- package/src/devtools/client/filters/index.ts +16 -16
- package/src/devtools/client/index.ts +63 -63
- package/src/devtools/hook/create-hook.ts +207 -207
- package/src/devtools/hook/index.ts +13 -13
- package/src/devtools/index.ts +439 -439
- package/src/devtools/init.ts +265 -265
- package/src/devtools/protocol.ts +237 -237
- package/src/devtools/server/index.ts +17 -17
- package/src/devtools/types.ts +35 -35
- package/src/devtools/worker/index.ts +25 -25
- package/src/devtools/worker/redaction-worker.ts +233 -233
- package/src/diagnose/__tests__/checks.test.ts +136 -136
- package/src/diagnose/checks.ts +185 -185
- package/src/diagnose/index.ts +17 -17
- package/src/diagnose/run.ts +10 -10
- package/src/diagnose/types.ts +53 -53
- package/src/email/__tests__/email.test.ts +355 -355
- package/src/email/index.ts +282 -282
- package/src/email/smtp.ts +64 -64
- package/src/error/domains.ts +265 -265
- package/src/error/types.ts +6 -6
- package/src/errors/extractor.ts +409 -409
- package/src/errors/index.ts +19 -19
- package/src/filling/__tests__/session-sqlite.test.ts +5 -1
- package/src/filling/auth.ts +308 -308
- package/src/filling/body-parse.test.ts +60 -60
- package/src/filling/cookie-codec.ts +5 -3
- package/src/filling/deps.ts +265 -265
- package/src/filling/head-method.test.ts +154 -154
- package/src/filling/session-sqlite.ts +617 -617
- package/src/filling/sse.ts +5 -5
- package/src/filling/ws.ts +78 -78
- package/src/generator/generate.ts +30 -30
- package/src/generator/index.ts +3 -3
- package/src/generator/templates.test.ts +48 -48
- package/src/generator/templates.ts +219 -219
- package/src/guard/__tests__/design-inline-class.test.ts +219 -219
- package/src/guard/__tests__/tsgolint-bridge.test.ts +347 -347
- package/src/guard/analyzer.ts +360 -360
- package/src/guard/auto-correct.ts +1 -1
- package/src/guard/check.ts +39 -24
- package/src/guard/config-guard.ts +13 -13
- package/src/guard/contract-guard.ts +9 -9
- package/src/guard/define-rule.ts +243 -243
- package/src/guard/design-inline-class.ts +393 -393
- package/src/guard/file-type.test.ts +24 -24
- package/src/guard/fs-routes-policy.ts +51 -51
- package/src/guard/graph.ts +1 -1
- package/src/guard/healing.ts +36 -36
- package/src/guard/index.ts +11 -11
- package/src/guard/presets/atomic.ts +70 -70
- package/src/guard/presets/clean.ts +77 -77
- package/src/guard/presets/fsd.ts +79 -79
- package/src/guard/presets/hexagonal.ts +68 -68
- package/src/guard/reporter.ts +442 -442
- package/src/guard/rule-presets.ts +379 -379
- package/src/guard/semantic-slots.ts +1 -1
- package/src/guard/suggestions.ts +358 -358
- package/src/guard/tsgolint-bridge.ts +512 -512
- package/src/guard/types.ts +348 -348
- package/src/guard/watcher.ts +405 -405
- package/src/i18n/define.ts +126 -126
- package/src/i18n/index.ts +52 -52
- package/src/i18n/message-registry.ts +173 -173
- package/src/i18n/types.ts +112 -112
- package/src/id/__tests__/id.test.ts +3 -3
- package/src/id/index.ts +105 -105
- package/src/index.ts +9 -48
- package/src/internal/client-boundary.ts +266 -266
- package/src/internal/index.ts +2 -2
- package/src/kitchen/api/agent-devtools-api.ts +779 -779
- package/src/kitchen/api/bundle-inspector.ts +179 -0
- package/src/kitchen/api/errors-grouping.ts +126 -126
- package/src/kitchen/api/file-api.ts +11 -11
- package/src/kitchen/kitchen-handler.ts +36 -0
- package/src/kitchen/kitchen-ui.ts +456 -0
- package/src/logging/index.ts +22 -22
- package/src/logging/transports.ts +365 -365
- package/src/middleware/bridge.ts +147 -147
- package/src/middleware/compose.ts +134 -134
- package/src/middleware/compress.ts +62 -62
- package/src/middleware/cors.ts +47 -47
- package/src/middleware/csrf.ts +328 -328
- package/src/middleware/define.ts +132 -132
- package/src/middleware/jwt.ts +134 -134
- package/src/middleware/logger.ts +58 -58
- package/src/middleware/oauth/__tests__/oauth.test.ts +1 -1
- package/src/middleware/oauth/index.ts +505 -505
- package/src/middleware/oauth/providers.ts +115 -115
- package/src/middleware/rate-limit/index.ts +522 -522
- package/src/middleware/rate-limit/sqlite-store.ts +382 -382
- package/src/middleware/scheduler-cron.ts +96 -96
- package/src/middleware/secure/__tests__/secure.test.ts +360 -360
- package/src/middleware/secure/csp.ts +193 -193
- package/src/middleware/session.ts +174 -174
- package/src/middleware/timeout.ts +55 -55
- package/src/observability/logger-adapter.ts +36 -36
- package/src/observability/sqlite-store.ts +254 -254
- package/src/openapi/generator.ts +1 -1
- package/src/openapi/openapi.test.ts +43 -43
- package/src/perf/__tests__/user-marks.test.ts +354 -354
- package/src/perf/index.ts +133 -133
- package/src/perf/user-marks.ts +1 -1
- package/src/plugins/__tests__/lifecycle-integration.test.ts +272 -272
- package/src/plugins/__tests__/runner.test.ts +409 -409
- package/src/plugins/define.ts +124 -124
- package/src/plugins/examples/dep-check-plugin.ts +80 -80
- package/src/plugins/examples/prerender-cache-plugin.ts +111 -111
- package/src/plugins/examples/sitemap-plugin.ts +65 -65
- package/src/plugins/runner.ts +361 -361
- package/src/plugins/types.ts +368 -368
- package/src/report/index.ts +1 -1
- package/src/resource/__tests__/generator.test.ts +7 -7
- package/src/resource/__tests__/schema.test.ts +14 -14
- package/src/resource/ddl/__tests__/diff.test.ts +639 -639
- package/src/resource/ddl/__tests__/emit.test.ts +165 -165
- package/src/resource/ddl/__tests__/snapshot.test.ts +499 -499
- package/src/resource/ddl/emit.ts +146 -146
- package/src/resource/ddl/persistence-types.ts +218 -218
- package/src/resource/ddl/type-map.ts +223 -223
- package/src/resource/ddl/types.ts +232 -232
- package/src/resource/generator-repo.ts +630 -630
- package/src/resource/generator-schema.ts +11 -11
- package/src/resource/generators/slot.ts +72 -72
- package/src/resource/schema.ts +21 -21
- package/src/router/client-entry.test.ts +227 -227
- package/src/router/client-entry.ts +218 -218
- package/src/router/fs-patterns.test.ts +96 -96
- package/src/router/fs-routes.test.ts +532 -532
- package/src/router/fs-routes.ts +53 -53
- package/src/router/fs-scanner.ts +216 -216
- package/src/router/fs-types.ts +19 -19
- package/src/router/route-source-analyzer.ts +521 -521
- package/src/routes/index.ts +74 -74
- package/src/routes/metadata-routes.ts +427 -427
- package/src/routes/types.ts +341 -341
- package/src/runtime/__tests__/devtools-adapter.test.ts +68 -68
- package/src/runtime/__tests__/error-boundary-redaction.test.ts +141 -141
- package/src/runtime/__tests__/hdr-client.test.ts +223 -223
- package/src/runtime/__tests__/http-errors.test.ts +117 -117
- package/src/runtime/__tests__/inline-client-hydration.test.ts +237 -237
- package/src/runtime/__tests__/not-found.test.ts +152 -152
- package/src/runtime/__tests__/observability-lifecycle.test.ts +103 -103
- package/src/runtime/__tests__/page-render-response.test.ts +517 -517
- package/src/runtime/__tests__/request-middleware.test.ts +70 -70
- package/src/runtime/__tests__/searchparams-page-props.test.ts +81 -81
- package/src/runtime/adapter.ts +47 -47
- package/src/runtime/boundary.tsx +252 -252
- package/src/runtime/cache.ts +494 -494
- package/src/runtime/compose.ts +222 -222
- package/src/runtime/devtools-adapter.ts +68 -68
- package/src/runtime/escape.ts +34 -34
- package/src/runtime/fast-refresh-runtime.ts +322 -322
- package/src/runtime/fast-refresh-types.ts +1 -1
- package/src/runtime/handler.ts +65 -65
- package/src/runtime/handlers.ts +30 -11
- package/src/runtime/http-errors.ts +113 -113
- package/src/runtime/image-handler.ts +1 -1
- package/src/runtime/lifecycle.ts +381 -381
- package/src/runtime/logger.test.ts +345 -345
- package/src/runtime/middleware.ts +264 -264
- package/src/runtime/not-found.ts +93 -93
- package/src/runtime/observability-lifecycle.ts +290 -290
- package/src/runtime/openapi-endpoint.ts +236 -236
- package/src/runtime/page-render-response.ts +287 -274
- package/src/runtime/ppr.ts +74 -74
- package/src/runtime/rate-limit.ts +1 -1
- package/src/runtime/redirect.ts +1 -1
- package/src/runtime/registry.ts +171 -171
- package/src/runtime/request-middleware.ts +31 -31
- package/src/runtime/router.test.ts +4 -4
- package/src/runtime/router.ts +10 -10
- package/src/runtime/server.ts +127 -211
- package/src/runtime/shims.ts +48 -48
- package/src/runtime/ssr.ts +43 -17
- package/src/runtime/static-files.ts +369 -289
- package/src/runtime/streaming-ssr.ts +219 -180
- package/src/runtime/trace.ts +144 -144
- package/src/scheduler/__tests__/scheduler.test.ts +12 -11
- package/src/scheduler/index.ts +11 -5
- package/src/scheduler/validate.ts +169 -169
- package/src/seo/index.ts +219 -219
- package/src/seo/integration/ssr.ts +306 -306
- package/src/seo/render/basic.ts +435 -435
- package/src/seo/render/index.ts +143 -143
- package/src/seo/render/jsonld.ts +539 -539
- package/src/seo/render/opengraph.ts +197 -197
- package/src/seo/render/robots.ts +116 -116
- package/src/seo/render/sitemap.ts +137 -137
- package/src/seo/render/twitter.ts +127 -127
- package/src/seo/resolve/opengraph.ts +143 -143
- package/src/seo/resolve/robots.ts +73 -73
- package/src/seo/resolve/title.ts +94 -94
- package/src/seo/resolve/twitter.ts +73 -73
- package/src/seo/resolve/url.ts +104 -104
- package/src/seo/routes/index.ts +290 -290
- package/src/seo/types.ts +588 -588
- package/src/slot/validator.ts +39 -39
- package/src/spec/schema.ts +35 -35
- package/src/storage/s3/__tests__/s3.test.ts +479 -479
- package/src/storage/s3/index.ts +412 -412
- package/src/testing/__tests__/assertions.test.ts +632 -632
- package/src/testing/__tests__/reporter.test.ts +454 -454
- package/src/testing/assertions.ts +986 -986
- package/src/testing/db.ts +157 -157
- package/src/testing/index.ts +9 -3
- package/src/testing/lcov.ts +192 -0
- package/src/testing/mocks.ts +203 -203
- package/src/testing/session.ts +190 -190
- package/src/types/branded.ts +56 -56
- package/src/types/index.ts +1 -1
- package/src/utils/safe-io.ts +188 -188
- package/src/utils/string-safe.ts +298 -298
- package/src/watcher/__tests__/watcher.test.ts +59 -59
- package/src/watcher/watcher.ts +61 -61
|
@@ -1,236 +1,236 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Runtime OpenAPI endpoint.
|
|
3
|
-
*
|
|
4
|
-
* Serves the build-time `.mandu/openapi.json` / `.mandu/openapi.yaml`
|
|
5
|
-
* artifacts at a stable URL (default `/__mandu/openapi.json` and
|
|
6
|
-
* `.yaml`) so API consumers (Postman, codegen, Swagger UI proxies) can
|
|
7
|
-
* fetch a canonical spec without reaching into the framework's dev
|
|
8
|
-
* Kitchen dashboard.
|
|
9
|
-
*
|
|
10
|
-
* Contract:
|
|
11
|
-
* - Disabled by default — the server dispatcher gates this handler
|
|
12
|
-
* behind `ManduConfig.openapi.enabled` or the
|
|
13
|
-
* `MANDU_OPENAPI_ENABLED=1` env var.
|
|
14
|
-
* - Lazy-load artifacts on first request; in-memory cache survives
|
|
15
|
-
* for the lifetime of the server instance. Re-deploy to invalidate.
|
|
16
|
-
* - If artifacts are missing on disk, fall back to live generation
|
|
17
|
-
* from the registered manifest so `mandu dev` users still get a
|
|
18
|
-
* valid response without running `mandu build` first.
|
|
19
|
-
* - ETag = SHA-256 of the JSON body. Supports `If-None-Match` 304
|
|
20
|
-
* short-circuiting so downstream caches (CDN, reverse proxy) behave
|
|
21
|
-
* correctly.
|
|
22
|
-
*/
|
|
23
|
-
|
|
24
|
-
import type { RoutesManifest } from "../spec/schema";
|
|
25
|
-
import {
|
|
26
|
-
generateOpenAPIDocument,
|
|
27
|
-
hashOpenAPIJSON,
|
|
28
|
-
openAPIToJSON,
|
|
29
|
-
openAPIToYAML,
|
|
30
|
-
readOpenAPIArtifacts,
|
|
31
|
-
} from "../openapi/generator";
|
|
32
|
-
|
|
33
|
-
export const DEFAULT_OPENAPI_BASE_PATH = "/__mandu/openapi";
|
|
34
|
-
const DEFAULT_ARTIFACT_DIR = ".mandu";
|
|
35
|
-
const CACHE_CONTROL = "public, max-age=0, must-revalidate";
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Runtime-resolved OpenAPI endpoint configuration. Mirrors the shape
|
|
39
|
-
* the server threads through `ServerRegistrySettings` so the hot-path
|
|
40
|
-
* dispatch can stay allocation-free.
|
|
41
|
-
*/
|
|
42
|
-
export interface OpenAPIEndpointSettings {
|
|
43
|
-
/** Base path without the trailing `.json`/`.yaml`. Default `/__mandu/openapi`. */
|
|
44
|
-
basePath: string;
|
|
45
|
-
/** Absolute directory containing `openapi.json` / `openapi.yaml` artifacts. */
|
|
46
|
-
artifactDir: string;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
interface CacheEntry {
|
|
50
|
-
json: string;
|
|
51
|
-
yaml: string;
|
|
52
|
-
hash: string;
|
|
53
|
-
etag: string;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/** Module-scoped cache — invalidated by `invalidateOpenAPIEndpointCache()`. */
|
|
57
|
-
let cache: CacheEntry | null = null;
|
|
58
|
-
let pending: Promise<CacheEntry | null> | null = null;
|
|
59
|
-
|
|
60
|
-
/** Test / HMR hook: drop the cached spec so the next request recomputes. */
|
|
61
|
-
export function invalidateOpenAPIEndpointCache(): void {
|
|
62
|
-
cache = null;
|
|
63
|
-
pending = null;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Resolve the OpenAPI body, either from disk artifacts (preferred) or
|
|
68
|
-
* by generating live from the manifest. Concurrent callers share one
|
|
69
|
-
* in-flight load so we never rebuild the spec twice on a thundering
|
|
70
|
-
* herd.
|
|
71
|
-
*/
|
|
72
|
-
async function loadSpec(
|
|
73
|
-
manifest: RoutesManifest,
|
|
74
|
-
rootDir: string,
|
|
75
|
-
settings: OpenAPIEndpointSettings
|
|
76
|
-
): Promise<CacheEntry | null> {
|
|
77
|
-
if (cache) return cache;
|
|
78
|
-
if (pending) return pending;
|
|
79
|
-
|
|
80
|
-
pending = (async (): Promise<CacheEntry | null> => {
|
|
81
|
-
try {
|
|
82
|
-
// 1. Prefer on-disk artifacts (produced by `mandu build`). Keeps
|
|
83
|
-
// request-time cost at a single file read instead of walking
|
|
84
|
-
// every contract module again.
|
|
85
|
-
const fromDisk = await readOpenAPIArtifacts(settings.artifactDir, rootDir);
|
|
86
|
-
if (fromDisk) {
|
|
87
|
-
const entry: CacheEntry = {
|
|
88
|
-
json: fromDisk.json,
|
|
89
|
-
yaml: fromDisk.yaml,
|
|
90
|
-
hash: fromDisk.hash,
|
|
91
|
-
etag: `"${fromDisk.hash}"`,
|
|
92
|
-
};
|
|
93
|
-
cache = entry;
|
|
94
|
-
return entry;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// 2. Fallback: generate live. Useful in `mandu dev` before the
|
|
98
|
-
// user has run `mandu build`, or in test harnesses that boot a
|
|
99
|
-
// server directly from a manifest fixture.
|
|
100
|
-
const doc = await generateOpenAPIDocument(manifest, rootDir);
|
|
101
|
-
const json = openAPIToJSON(doc);
|
|
102
|
-
const yaml = openAPIToYAML(doc);
|
|
103
|
-
const hash = await hashOpenAPIJSON(json);
|
|
104
|
-
const entry: CacheEntry = {
|
|
105
|
-
json,
|
|
106
|
-
yaml,
|
|
107
|
-
hash,
|
|
108
|
-
etag: `"${hash}"`,
|
|
109
|
-
};
|
|
110
|
-
cache = entry;
|
|
111
|
-
return entry;
|
|
112
|
-
} catch {
|
|
113
|
-
// Swallow the error — a 500 here would be worse DX than a 404.
|
|
114
|
-
// Invalidate so the next request retries.
|
|
115
|
-
cache = null;
|
|
116
|
-
return null;
|
|
117
|
-
} finally {
|
|
118
|
-
pending = null;
|
|
119
|
-
}
|
|
120
|
-
})();
|
|
121
|
-
|
|
122
|
-
return pending;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Handle a GET request for the OpenAPI endpoint.
|
|
127
|
-
*
|
|
128
|
-
* Returns `null` when the pathname does not match — the dispatcher
|
|
129
|
-
* should fall through to the normal route resolution pipeline. Returns
|
|
130
|
-
* a `Response` for both hit (200 + spec) and miss (404 when the spec
|
|
131
|
-
* cannot be materialized). Only `GET` and `HEAD` are accepted; every
|
|
132
|
-
* other method gets a 405 with `Allow: GET, HEAD`.
|
|
133
|
-
*/
|
|
134
|
-
export async function handleOpenAPIRequest(
|
|
135
|
-
req: Request,
|
|
136
|
-
pathname: string,
|
|
137
|
-
manifest: RoutesManifest,
|
|
138
|
-
rootDir: string,
|
|
139
|
-
settings: OpenAPIEndpointSettings
|
|
140
|
-
): Promise<Response | null> {
|
|
141
|
-
const jsonPath = `${settings.basePath}.json`;
|
|
142
|
-
const yamlPath = `${settings.basePath}.yaml`;
|
|
143
|
-
|
|
144
|
-
let variant: "json" | "yaml";
|
|
145
|
-
if (pathname === jsonPath) variant = "json";
|
|
146
|
-
else if (pathname === yamlPath) variant = "yaml";
|
|
147
|
-
else return null;
|
|
148
|
-
|
|
149
|
-
if (req.method !== "GET" && req.method !== "HEAD") {
|
|
150
|
-
return new Response("Method Not Allowed", {
|
|
151
|
-
status: 405,
|
|
152
|
-
headers: {
|
|
153
|
-
Allow: "GET, HEAD",
|
|
154
|
-
"Cache-Control": "no-store",
|
|
155
|
-
},
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
const entry = await loadSpec(manifest, rootDir, settings);
|
|
160
|
-
if (!entry) {
|
|
161
|
-
return new Response("Not Found", { status: 404 });
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
// Conditional-GET: honour `If-None-Match` for CDN / browser caches.
|
|
165
|
-
const ifNoneMatch = req.headers.get("if-none-match");
|
|
166
|
-
if (ifNoneMatch && ifNoneMatch === entry.etag) {
|
|
167
|
-
return new Response(null, {
|
|
168
|
-
status: 304,
|
|
169
|
-
headers: {
|
|
170
|
-
ETag: entry.etag,
|
|
171
|
-
"Cache-Control": CACHE_CONTROL,
|
|
172
|
-
},
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
const body = variant === "json" ? entry.json : entry.yaml;
|
|
177
|
-
const contentType =
|
|
178
|
-
variant === "json"
|
|
179
|
-
? "application/json; charset=utf-8"
|
|
180
|
-
: "application/yaml; charset=utf-8";
|
|
181
|
-
|
|
182
|
-
// HEAD responses carry the headers but drop the body.
|
|
183
|
-
const responseBody = req.method === "HEAD" ? null : body;
|
|
184
|
-
return new Response(responseBody, {
|
|
185
|
-
status: 200,
|
|
186
|
-
headers: {
|
|
187
|
-
"Content-Type": contentType,
|
|
188
|
-
"Cache-Control": CACHE_CONTROL,
|
|
189
|
-
ETag: entry.etag,
|
|
190
|
-
// Expose ETag to browser JS so API explorer UIs can display the
|
|
191
|
-
// deploy identifier without a round-trip.
|
|
192
|
-
"Access-Control-Expose-Headers": "ETag",
|
|
193
|
-
},
|
|
194
|
-
});
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* Resolve the effective endpoint settings for a server boot. Normalizes
|
|
199
|
-
* the `path` option (users may pass with or without leading slash, and
|
|
200
|
-
* with or without the `.json` suffix) and chooses the artifact
|
|
201
|
-
* directory default.
|
|
202
|
-
*/
|
|
203
|
-
export function resolveOpenAPIEndpointSettings(
|
|
204
|
-
rootDir: string,
|
|
205
|
-
path?: string
|
|
206
|
-
): OpenAPIEndpointSettings {
|
|
207
|
-
let basePath = path ?? DEFAULT_OPENAPI_BASE_PATH;
|
|
208
|
-
if (!basePath.startsWith("/")) basePath = `/${basePath}`;
|
|
209
|
-
// Strip trailing `.json` / `.yaml` / trailing slash so the handler can
|
|
210
|
-
// append suffixes uniformly.
|
|
211
|
-
basePath = basePath.replace(/\.(json|yaml|yml)$/i, "").replace(/\/+$/, "");
|
|
212
|
-
if (basePath === "") basePath = DEFAULT_OPENAPI_BASE_PATH;
|
|
213
|
-
|
|
214
|
-
// POSIX-style join — artifact paths are treated as absolute by
|
|
215
|
-
// `readOpenAPIArtifacts`, which itself uses `node:path` for portability.
|
|
216
|
-
const normalizedRoot = rootDir.replace(/[\\/]+$/, "");
|
|
217
|
-
const separator = normalizedRoot.includes("\\") ? "\\" : "/";
|
|
218
|
-
const artifactDir = `${normalizedRoot}${separator}${DEFAULT_ARTIFACT_DIR}`;
|
|
219
|
-
return { basePath, artifactDir };
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
/**
|
|
223
|
-
* Decide whether the OpenAPI endpoint should be active for this
|
|
224
|
-
* server instance. The config flag wins; an explicit `false` still
|
|
225
|
-
* disables the endpoint even when the env var is set (explicit > env).
|
|
226
|
-
* Absent config + truthy env var (`MANDU_OPENAPI_ENABLED=1`) opts in.
|
|
227
|
-
*/
|
|
228
|
-
export function isOpenAPIEndpointEnabled(
|
|
229
|
-
enabled: boolean | undefined,
|
|
230
|
-
env: NodeJS.ProcessEnv | Record<string, string | undefined> = process.env
|
|
231
|
-
): boolean {
|
|
232
|
-
if (enabled === true) return true;
|
|
233
|
-
if (enabled === false) return false;
|
|
234
|
-
const raw = env.MANDU_OPENAPI_ENABLED;
|
|
235
|
-
return raw === "1" || raw === "true";
|
|
236
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Runtime OpenAPI endpoint.
|
|
3
|
+
*
|
|
4
|
+
* Serves the build-time `.mandu/openapi.json` / `.mandu/openapi.yaml`
|
|
5
|
+
* artifacts at a stable URL (default `/__mandu/openapi.json` and
|
|
6
|
+
* `.yaml`) so API consumers (Postman, codegen, Swagger UI proxies) can
|
|
7
|
+
* fetch a canonical spec without reaching into the framework's dev
|
|
8
|
+
* Kitchen dashboard.
|
|
9
|
+
*
|
|
10
|
+
* Contract:
|
|
11
|
+
* - Disabled by default — the server dispatcher gates this handler
|
|
12
|
+
* behind `ManduConfig.openapi.enabled` or the
|
|
13
|
+
* `MANDU_OPENAPI_ENABLED=1` env var.
|
|
14
|
+
* - Lazy-load artifacts on first request; in-memory cache survives
|
|
15
|
+
* for the lifetime of the server instance. Re-deploy to invalidate.
|
|
16
|
+
* - If artifacts are missing on disk, fall back to live generation
|
|
17
|
+
* from the registered manifest so `mandu dev` users still get a
|
|
18
|
+
* valid response without running `mandu build` first.
|
|
19
|
+
* - ETag = SHA-256 of the JSON body. Supports `If-None-Match` 304
|
|
20
|
+
* short-circuiting so downstream caches (CDN, reverse proxy) behave
|
|
21
|
+
* correctly.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import type { RoutesManifest } from "../spec/schema";
|
|
25
|
+
import {
|
|
26
|
+
generateOpenAPIDocument,
|
|
27
|
+
hashOpenAPIJSON,
|
|
28
|
+
openAPIToJSON,
|
|
29
|
+
openAPIToYAML,
|
|
30
|
+
readOpenAPIArtifacts,
|
|
31
|
+
} from "../openapi/generator";
|
|
32
|
+
|
|
33
|
+
export const DEFAULT_OPENAPI_BASE_PATH = "/__mandu/openapi";
|
|
34
|
+
const DEFAULT_ARTIFACT_DIR = ".mandu";
|
|
35
|
+
const CACHE_CONTROL = "public, max-age=0, must-revalidate";
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Runtime-resolved OpenAPI endpoint configuration. Mirrors the shape
|
|
39
|
+
* the server threads through `ServerRegistrySettings` so the hot-path
|
|
40
|
+
* dispatch can stay allocation-free.
|
|
41
|
+
*/
|
|
42
|
+
export interface OpenAPIEndpointSettings {
|
|
43
|
+
/** Base path without the trailing `.json`/`.yaml`. Default `/__mandu/openapi`. */
|
|
44
|
+
basePath: string;
|
|
45
|
+
/** Absolute directory containing `openapi.json` / `openapi.yaml` artifacts. */
|
|
46
|
+
artifactDir: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
interface CacheEntry {
|
|
50
|
+
json: string;
|
|
51
|
+
yaml: string;
|
|
52
|
+
hash: string;
|
|
53
|
+
etag: string;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Module-scoped cache — invalidated by `invalidateOpenAPIEndpointCache()`. */
|
|
57
|
+
let cache: CacheEntry | null = null;
|
|
58
|
+
let pending: Promise<CacheEntry | null> | null = null;
|
|
59
|
+
|
|
60
|
+
/** Test / HMR hook: drop the cached spec so the next request recomputes. */
|
|
61
|
+
export function invalidateOpenAPIEndpointCache(): void {
|
|
62
|
+
cache = null;
|
|
63
|
+
pending = null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Resolve the OpenAPI body, either from disk artifacts (preferred) or
|
|
68
|
+
* by generating live from the manifest. Concurrent callers share one
|
|
69
|
+
* in-flight load so we never rebuild the spec twice on a thundering
|
|
70
|
+
* herd.
|
|
71
|
+
*/
|
|
72
|
+
async function loadSpec(
|
|
73
|
+
manifest: RoutesManifest,
|
|
74
|
+
rootDir: string,
|
|
75
|
+
settings: OpenAPIEndpointSettings
|
|
76
|
+
): Promise<CacheEntry | null> {
|
|
77
|
+
if (cache) return cache;
|
|
78
|
+
if (pending) return pending;
|
|
79
|
+
|
|
80
|
+
pending = (async (): Promise<CacheEntry | null> => {
|
|
81
|
+
try {
|
|
82
|
+
// 1. Prefer on-disk artifacts (produced by `mandu build`). Keeps
|
|
83
|
+
// request-time cost at a single file read instead of walking
|
|
84
|
+
// every contract module again.
|
|
85
|
+
const fromDisk = await readOpenAPIArtifacts(settings.artifactDir, rootDir);
|
|
86
|
+
if (fromDisk) {
|
|
87
|
+
const entry: CacheEntry = {
|
|
88
|
+
json: fromDisk.json,
|
|
89
|
+
yaml: fromDisk.yaml,
|
|
90
|
+
hash: fromDisk.hash,
|
|
91
|
+
etag: `"${fromDisk.hash}"`,
|
|
92
|
+
};
|
|
93
|
+
cache = entry;
|
|
94
|
+
return entry;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// 2. Fallback: generate live. Useful in `mandu dev` before the
|
|
98
|
+
// user has run `mandu build`, or in test harnesses that boot a
|
|
99
|
+
// server directly from a manifest fixture.
|
|
100
|
+
const doc = await generateOpenAPIDocument(manifest, rootDir);
|
|
101
|
+
const json = openAPIToJSON(doc);
|
|
102
|
+
const yaml = openAPIToYAML(doc);
|
|
103
|
+
const hash = await hashOpenAPIJSON(json);
|
|
104
|
+
const entry: CacheEntry = {
|
|
105
|
+
json,
|
|
106
|
+
yaml,
|
|
107
|
+
hash,
|
|
108
|
+
etag: `"${hash}"`,
|
|
109
|
+
};
|
|
110
|
+
cache = entry;
|
|
111
|
+
return entry;
|
|
112
|
+
} catch {
|
|
113
|
+
// Swallow the error — a 500 here would be worse DX than a 404.
|
|
114
|
+
// Invalidate so the next request retries.
|
|
115
|
+
cache = null;
|
|
116
|
+
return null;
|
|
117
|
+
} finally {
|
|
118
|
+
pending = null;
|
|
119
|
+
}
|
|
120
|
+
})();
|
|
121
|
+
|
|
122
|
+
return pending;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Handle a GET request for the OpenAPI endpoint.
|
|
127
|
+
*
|
|
128
|
+
* Returns `null` when the pathname does not match — the dispatcher
|
|
129
|
+
* should fall through to the normal route resolution pipeline. Returns
|
|
130
|
+
* a `Response` for both hit (200 + spec) and miss (404 when the spec
|
|
131
|
+
* cannot be materialized). Only `GET` and `HEAD` are accepted; every
|
|
132
|
+
* other method gets a 405 with `Allow: GET, HEAD`.
|
|
133
|
+
*/
|
|
134
|
+
export async function handleOpenAPIRequest(
|
|
135
|
+
req: Request,
|
|
136
|
+
pathname: string,
|
|
137
|
+
manifest: RoutesManifest,
|
|
138
|
+
rootDir: string,
|
|
139
|
+
settings: OpenAPIEndpointSettings
|
|
140
|
+
): Promise<Response | null> {
|
|
141
|
+
const jsonPath = `${settings.basePath}.json`;
|
|
142
|
+
const yamlPath = `${settings.basePath}.yaml`;
|
|
143
|
+
|
|
144
|
+
let variant: "json" | "yaml";
|
|
145
|
+
if (pathname === jsonPath) variant = "json";
|
|
146
|
+
else if (pathname === yamlPath) variant = "yaml";
|
|
147
|
+
else return null;
|
|
148
|
+
|
|
149
|
+
if (req.method !== "GET" && req.method !== "HEAD") {
|
|
150
|
+
return new Response("Method Not Allowed", {
|
|
151
|
+
status: 405,
|
|
152
|
+
headers: {
|
|
153
|
+
Allow: "GET, HEAD",
|
|
154
|
+
"Cache-Control": "no-store",
|
|
155
|
+
},
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const entry = await loadSpec(manifest, rootDir, settings);
|
|
160
|
+
if (!entry) {
|
|
161
|
+
return new Response("Not Found", { status: 404 });
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Conditional-GET: honour `If-None-Match` for CDN / browser caches.
|
|
165
|
+
const ifNoneMatch = req.headers.get("if-none-match");
|
|
166
|
+
if (ifNoneMatch && ifNoneMatch === entry.etag) {
|
|
167
|
+
return new Response(null, {
|
|
168
|
+
status: 304,
|
|
169
|
+
headers: {
|
|
170
|
+
ETag: entry.etag,
|
|
171
|
+
"Cache-Control": CACHE_CONTROL,
|
|
172
|
+
},
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const body = variant === "json" ? entry.json : entry.yaml;
|
|
177
|
+
const contentType =
|
|
178
|
+
variant === "json"
|
|
179
|
+
? "application/json; charset=utf-8"
|
|
180
|
+
: "application/yaml; charset=utf-8";
|
|
181
|
+
|
|
182
|
+
// HEAD responses carry the headers but drop the body.
|
|
183
|
+
const responseBody = req.method === "HEAD" ? null : body;
|
|
184
|
+
return new Response(responseBody, {
|
|
185
|
+
status: 200,
|
|
186
|
+
headers: {
|
|
187
|
+
"Content-Type": contentType,
|
|
188
|
+
"Cache-Control": CACHE_CONTROL,
|
|
189
|
+
ETag: entry.etag,
|
|
190
|
+
// Expose ETag to browser JS so API explorer UIs can display the
|
|
191
|
+
// deploy identifier without a round-trip.
|
|
192
|
+
"Access-Control-Expose-Headers": "ETag",
|
|
193
|
+
},
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Resolve the effective endpoint settings for a server boot. Normalizes
|
|
199
|
+
* the `path` option (users may pass with or without leading slash, and
|
|
200
|
+
* with or without the `.json` suffix) and chooses the artifact
|
|
201
|
+
* directory default.
|
|
202
|
+
*/
|
|
203
|
+
export function resolveOpenAPIEndpointSettings(
|
|
204
|
+
rootDir: string,
|
|
205
|
+
path?: string
|
|
206
|
+
): OpenAPIEndpointSettings {
|
|
207
|
+
let basePath = path ?? DEFAULT_OPENAPI_BASE_PATH;
|
|
208
|
+
if (!basePath.startsWith("/")) basePath = `/${basePath}`;
|
|
209
|
+
// Strip trailing `.json` / `.yaml` / trailing slash so the handler can
|
|
210
|
+
// append suffixes uniformly.
|
|
211
|
+
basePath = basePath.replace(/\.(json|yaml|yml)$/i, "").replace(/\/+$/, "");
|
|
212
|
+
if (basePath === "") basePath = DEFAULT_OPENAPI_BASE_PATH;
|
|
213
|
+
|
|
214
|
+
// POSIX-style join — artifact paths are treated as absolute by
|
|
215
|
+
// `readOpenAPIArtifacts`, which itself uses `node:path` for portability.
|
|
216
|
+
const normalizedRoot = rootDir.replace(/[\\/]+$/, "");
|
|
217
|
+
const separator = normalizedRoot.includes("\\") ? "\\" : "/";
|
|
218
|
+
const artifactDir = `${normalizedRoot}${separator}${DEFAULT_ARTIFACT_DIR}`;
|
|
219
|
+
return { basePath, artifactDir };
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Decide whether the OpenAPI endpoint should be active for this
|
|
224
|
+
* server instance. The config flag wins; an explicit `false` still
|
|
225
|
+
* disables the endpoint even when the env var is set (explicit > env).
|
|
226
|
+
* Absent config + truthy env var (`MANDU_OPENAPI_ENABLED=1`) opts in.
|
|
227
|
+
*/
|
|
228
|
+
export function isOpenAPIEndpointEnabled(
|
|
229
|
+
enabled: boolean | undefined,
|
|
230
|
+
env: NodeJS.ProcessEnv | Record<string, string | undefined> = process.env
|
|
231
|
+
): boolean {
|
|
232
|
+
if (enabled === true) return true;
|
|
233
|
+
if (enabled === false) return false;
|
|
234
|
+
const raw = env.MANDU_OPENAPI_ENABLED;
|
|
235
|
+
return raw === "1" || raw === "true";
|
|
236
|
+
}
|