@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,152 +1,152 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Unit tests for `notFound()` / `isNotFoundResponse()` (Phase 6.3).
|
|
3
|
-
*
|
|
4
|
-
* Mirrors the shape of the redirect() tests (see packages/core/tests/server/
|
|
5
|
-
* redirect-loader.test.ts). These stay local to the helper — integration
|
|
6
|
-
* coverage that exercises the full SSR pipeline lives in the server test
|
|
7
|
-
* at packages/core/tests/server/not-found-page.test.ts.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import { describe, it, expect } from "bun:test";
|
|
11
|
-
import {
|
|
12
|
-
notFound,
|
|
13
|
-
isNotFoundResponse,
|
|
14
|
-
NOT_FOUND_BRAND,
|
|
15
|
-
} from "../not-found";
|
|
16
|
-
import { redirect } from "../redirect";
|
|
17
|
-
import { ManduFilling } from "../../filling/filling";
|
|
18
|
-
import { ManduContext } from "../../filling/context";
|
|
19
|
-
|
|
20
|
-
describe("notFound()", () => {
|
|
21
|
-
it("returns a Response with status 404", () => {
|
|
22
|
-
const res = notFound();
|
|
23
|
-
expect(res).toBeInstanceOf(Response);
|
|
24
|
-
expect(res.status).toBe(404);
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it("carries the provided message in its body", async () => {
|
|
28
|
-
const res = notFound({ message: "Post not found" });
|
|
29
|
-
const body = await res.text();
|
|
30
|
-
expect(body).toBe("Post not found");
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it("is recognized by isNotFoundResponse()", () => {
|
|
34
|
-
expect(isNotFoundResponse(notFound())).toBe(true);
|
|
35
|
-
expect(isNotFoundResponse(notFound({ message: "x" }))).toBe(true);
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it("rejects a bare `new Response(null, { status: 404 })` (brand check)", () => {
|
|
39
|
-
const bare = new Response(null, { status: 404 });
|
|
40
|
-
expect(isNotFoundResponse(bare)).toBe(false);
|
|
41
|
-
// Double-check: something we know is 404 but not ours (e.g. upstream proxy)
|
|
42
|
-
const proxied = new Response("upstream 404", {
|
|
43
|
-
status: 404,
|
|
44
|
-
headers: { "Content-Type": "text/plain" },
|
|
45
|
-
});
|
|
46
|
-
expect(isNotFoundResponse(proxied)).toBe(false);
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it("rejects a redirect() Response (notFound is a distinct brand)", () => {
|
|
50
|
-
expect(isNotFoundResponse(redirect("/foo"))).toBe(false);
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
it("rejects null / undefined / {} / primitives (type guard is strict)", () => {
|
|
54
|
-
expect(isNotFoundResponse(null)).toBe(false);
|
|
55
|
-
expect(isNotFoundResponse(undefined)).toBe(false);
|
|
56
|
-
expect(isNotFoundResponse({})).toBe(false);
|
|
57
|
-
expect(isNotFoundResponse("string")).toBe(false);
|
|
58
|
-
expect(isNotFoundResponse(404)).toBe(false);
|
|
59
|
-
expect(isNotFoundResponse(new Error("not a response"))).toBe(false);
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it("status is always 404 regardless of the message", () => {
|
|
63
|
-
expect(notFound().status).toBe(404);
|
|
64
|
-
expect(notFound({ message: "a" }).status).toBe(404);
|
|
65
|
-
expect(notFound({ message: "" }).status).toBe(404); // empty still 404
|
|
66
|
-
expect(notFound({ message: "x".repeat(1000) }).status).toBe(404);
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
it("body is text/plain (not JSON) — explicit content-type contract", async () => {
|
|
70
|
-
const res = notFound({ message: "missing" });
|
|
71
|
-
const ct = res.headers.get("content-type") ?? "";
|
|
72
|
-
expect(ct).toContain("text/plain");
|
|
73
|
-
expect(ct).toContain("charset=utf-8");
|
|
74
|
-
// Body must be the literal message, not a JSON envelope.
|
|
75
|
-
expect(await res.text()).toBe("missing");
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it("default body (no args) is the literal 'Not Found'", async () => {
|
|
79
|
-
const res = notFound();
|
|
80
|
-
expect(await res.text()).toBe("Not Found");
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it("loader returning notFound() is picked up by isNotFoundResponse", async () => {
|
|
84
|
-
const filling = new ManduFilling();
|
|
85
|
-
filling.loader(() => notFound({ message: "post gone" }));
|
|
86
|
-
|
|
87
|
-
const ctx = new ManduContext(new Request("http://test/post/1"), { id: "1" });
|
|
88
|
-
const returned = await filling.executeLoader(ctx);
|
|
89
|
-
expect(isNotFoundResponse(returned)).toBe(true);
|
|
90
|
-
// And the body survived.
|
|
91
|
-
const body = await (returned as Response).text();
|
|
92
|
-
expect(body).toBe("post gone");
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
it("loader throwing notFound() is picked up the same way (Remix idiom)", async () => {
|
|
96
|
-
const filling = new ManduFilling();
|
|
97
|
-
filling.loader(() => {
|
|
98
|
-
throw notFound({ message: "thrown" });
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
const ctx = new ManduContext(new Request("http://test/x"), {});
|
|
102
|
-
let caught: unknown = null;
|
|
103
|
-
try {
|
|
104
|
-
await filling.executeLoader(ctx);
|
|
105
|
-
} catch (e) {
|
|
106
|
-
caught = e;
|
|
107
|
-
}
|
|
108
|
-
expect(isNotFoundResponse(caught)).toBe(true);
|
|
109
|
-
expect(await (caught as Response).text()).toBe("thrown");
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
it("integration simulation: loadPageData-like branch distinguishes return vs throw", async () => {
|
|
113
|
-
// Emulates the exact shape of the SSR pipeline's try/catch: `returned`
|
|
114
|
-
// and `thrown` both route through `isNotFoundResponse`. This is the
|
|
115
|
-
// contract the server integration relies on.
|
|
116
|
-
const returnCase = notFound({ message: "A" });
|
|
117
|
-
const throwCase = notFound({ message: "B" });
|
|
118
|
-
|
|
119
|
-
// Both must be recognised.
|
|
120
|
-
expect(isNotFoundResponse(returnCase)).toBe(true);
|
|
121
|
-
expect(isNotFoundResponse(throwCase)).toBe(true);
|
|
122
|
-
|
|
123
|
-
// And each preserves its own message (no shared mutable state).
|
|
124
|
-
expect(await returnCase.text()).toBe("A");
|
|
125
|
-
expect(await throwCase.text()).toBe("B");
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
it("accepts a call with no arguments and still produces a valid sentinel", () => {
|
|
129
|
-
const res = notFound();
|
|
130
|
-
expect(res).toBeInstanceOf(Response);
|
|
131
|
-
expect(res.status).toBe(404);
|
|
132
|
-
expect(isNotFoundResponse(res)).toBe(true);
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
it("exports NOT_FOUND_BRAND as a unique symbol keyed to the package", () => {
|
|
136
|
-
// The brand must be a Symbol.for() so multiple copies of @mandujs/core
|
|
137
|
-
// (e.g. monorepo duplication) agree on identity. If this changes we've
|
|
138
|
-
// broken cross-package detection — regression guard.
|
|
139
|
-
expect(typeof NOT_FOUND_BRAND).toBe("symbol");
|
|
140
|
-
expect(Symbol.keyFor(NOT_FOUND_BRAND)).toBe("@mandujs/core/not-found");
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
it("two separate notFound() calls produce independently-branded Responses", () => {
|
|
144
|
-
// WeakSet membership is per-instance — the brand on one must not
|
|
145
|
-
// leak to another. Important for test isolation.
|
|
146
|
-
const a = notFound();
|
|
147
|
-
const b = notFound();
|
|
148
|
-
expect(a).not.toBe(b);
|
|
149
|
-
expect(isNotFoundResponse(a)).toBe(true);
|
|
150
|
-
expect(isNotFoundResponse(b)).toBe(true);
|
|
151
|
-
});
|
|
152
|
-
});
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for `notFound()` / `isNotFoundResponse()` (Phase 6.3).
|
|
3
|
+
*
|
|
4
|
+
* Mirrors the shape of the redirect() tests (see packages/core/tests/server/
|
|
5
|
+
* redirect-loader.test.ts). These stay local to the helper — integration
|
|
6
|
+
* coverage that exercises the full SSR pipeline lives in the server test
|
|
7
|
+
* at packages/core/tests/server/not-found-page.test.ts.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { describe, it, expect } from "bun:test";
|
|
11
|
+
import {
|
|
12
|
+
notFound,
|
|
13
|
+
isNotFoundResponse,
|
|
14
|
+
NOT_FOUND_BRAND,
|
|
15
|
+
} from "../not-found";
|
|
16
|
+
import { redirect } from "../redirect";
|
|
17
|
+
import { ManduFilling } from "../../filling/filling";
|
|
18
|
+
import { ManduContext } from "../../filling/context";
|
|
19
|
+
|
|
20
|
+
describe("notFound()", () => {
|
|
21
|
+
it("returns a Response with status 404", () => {
|
|
22
|
+
const res = notFound();
|
|
23
|
+
expect(res).toBeInstanceOf(Response);
|
|
24
|
+
expect(res.status).toBe(404);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("carries the provided message in its body", async () => {
|
|
28
|
+
const res = notFound({ message: "Post not found" });
|
|
29
|
+
const body = await res.text();
|
|
30
|
+
expect(body).toBe("Post not found");
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("is recognized by isNotFoundResponse()", () => {
|
|
34
|
+
expect(isNotFoundResponse(notFound())).toBe(true);
|
|
35
|
+
expect(isNotFoundResponse(notFound({ message: "x" }))).toBe(true);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("rejects a bare `new Response(null, { status: 404 })` (brand check)", () => {
|
|
39
|
+
const bare = new Response(null, { status: 404 });
|
|
40
|
+
expect(isNotFoundResponse(bare)).toBe(false);
|
|
41
|
+
// Double-check: something we know is 404 but not ours (e.g. upstream proxy)
|
|
42
|
+
const proxied = new Response("upstream 404", {
|
|
43
|
+
status: 404,
|
|
44
|
+
headers: { "Content-Type": "text/plain" },
|
|
45
|
+
});
|
|
46
|
+
expect(isNotFoundResponse(proxied)).toBe(false);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("rejects a redirect() Response (notFound is a distinct brand)", () => {
|
|
50
|
+
expect(isNotFoundResponse(redirect("/foo"))).toBe(false);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("rejects null / undefined / {} / primitives (type guard is strict)", () => {
|
|
54
|
+
expect(isNotFoundResponse(null)).toBe(false);
|
|
55
|
+
expect(isNotFoundResponse(undefined)).toBe(false);
|
|
56
|
+
expect(isNotFoundResponse({})).toBe(false);
|
|
57
|
+
expect(isNotFoundResponse("string")).toBe(false);
|
|
58
|
+
expect(isNotFoundResponse(404)).toBe(false);
|
|
59
|
+
expect(isNotFoundResponse(new Error("not a response"))).toBe(false);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("status is always 404 regardless of the message", () => {
|
|
63
|
+
expect(notFound().status).toBe(404);
|
|
64
|
+
expect(notFound({ message: "a" }).status).toBe(404);
|
|
65
|
+
expect(notFound({ message: "" }).status).toBe(404); // empty still 404
|
|
66
|
+
expect(notFound({ message: "x".repeat(1000) }).status).toBe(404);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it("body is text/plain (not JSON) — explicit content-type contract", async () => {
|
|
70
|
+
const res = notFound({ message: "missing" });
|
|
71
|
+
const ct = res.headers.get("content-type") ?? "";
|
|
72
|
+
expect(ct).toContain("text/plain");
|
|
73
|
+
expect(ct).toContain("charset=utf-8");
|
|
74
|
+
// Body must be the literal message, not a JSON envelope.
|
|
75
|
+
expect(await res.text()).toBe("missing");
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it("default body (no args) is the literal 'Not Found'", async () => {
|
|
79
|
+
const res = notFound();
|
|
80
|
+
expect(await res.text()).toBe("Not Found");
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("loader returning notFound() is picked up by isNotFoundResponse", async () => {
|
|
84
|
+
const filling = new ManduFilling();
|
|
85
|
+
filling.loader(() => notFound({ message: "post gone" }));
|
|
86
|
+
|
|
87
|
+
const ctx = new ManduContext(new Request("http://test/post/1"), { id: "1" });
|
|
88
|
+
const returned = await filling.executeLoader(ctx);
|
|
89
|
+
expect(isNotFoundResponse(returned)).toBe(true);
|
|
90
|
+
// And the body survived.
|
|
91
|
+
const body = await (returned as Response).text();
|
|
92
|
+
expect(body).toBe("post gone");
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it("loader throwing notFound() is picked up the same way (Remix idiom)", async () => {
|
|
96
|
+
const filling = new ManduFilling();
|
|
97
|
+
filling.loader(() => {
|
|
98
|
+
throw notFound({ message: "thrown" });
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
const ctx = new ManduContext(new Request("http://test/x"), {});
|
|
102
|
+
let caught: unknown = null;
|
|
103
|
+
try {
|
|
104
|
+
await filling.executeLoader(ctx);
|
|
105
|
+
} catch (e) {
|
|
106
|
+
caught = e;
|
|
107
|
+
}
|
|
108
|
+
expect(isNotFoundResponse(caught)).toBe(true);
|
|
109
|
+
expect(await (caught as Response).text()).toBe("thrown");
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it("integration simulation: loadPageData-like branch distinguishes return vs throw", async () => {
|
|
113
|
+
// Emulates the exact shape of the SSR pipeline's try/catch: `returned`
|
|
114
|
+
// and `thrown` both route through `isNotFoundResponse`. This is the
|
|
115
|
+
// contract the server integration relies on.
|
|
116
|
+
const returnCase = notFound({ message: "A" });
|
|
117
|
+
const throwCase = notFound({ message: "B" });
|
|
118
|
+
|
|
119
|
+
// Both must be recognised.
|
|
120
|
+
expect(isNotFoundResponse(returnCase)).toBe(true);
|
|
121
|
+
expect(isNotFoundResponse(throwCase)).toBe(true);
|
|
122
|
+
|
|
123
|
+
// And each preserves its own message (no shared mutable state).
|
|
124
|
+
expect(await returnCase.text()).toBe("A");
|
|
125
|
+
expect(await throwCase.text()).toBe("B");
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it("accepts a call with no arguments and still produces a valid sentinel", () => {
|
|
129
|
+
const res = notFound();
|
|
130
|
+
expect(res).toBeInstanceOf(Response);
|
|
131
|
+
expect(res.status).toBe(404);
|
|
132
|
+
expect(isNotFoundResponse(res)).toBe(true);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it("exports NOT_FOUND_BRAND as a unique symbol keyed to the package", () => {
|
|
136
|
+
// The brand must be a Symbol.for() so multiple copies of @mandujs/core
|
|
137
|
+
// (e.g. monorepo duplication) agree on identity. If this changes we've
|
|
138
|
+
// broken cross-package detection — regression guard.
|
|
139
|
+
expect(typeof NOT_FOUND_BRAND).toBe("symbol");
|
|
140
|
+
expect(Symbol.keyFor(NOT_FOUND_BRAND)).toBe("@mandujs/core/compat/not-found");
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it("two separate notFound() calls produce independently-branded Responses", () => {
|
|
144
|
+
// WeakSet membership is per-instance — the brand on one must not
|
|
145
|
+
// leak to another. Important for test isolation.
|
|
146
|
+
const a = notFound();
|
|
147
|
+
const b = notFound();
|
|
148
|
+
expect(a).not.toBe(b);
|
|
149
|
+
expect(isNotFoundResponse(a)).toBe(true);
|
|
150
|
+
expect(isNotFoundResponse(b)).toBe(true);
|
|
151
|
+
});
|
|
152
|
+
});
|
|
@@ -1,103 +1,103 @@
|
|
|
1
|
-
import { afterEach, describe, expect, test } from "bun:test";
|
|
2
|
-
import {
|
|
3
|
-
INTERNAL_EVENTS_ENDPOINT,
|
|
4
|
-
createRuntimeObservabilityLifecycle,
|
|
5
|
-
} from "../observability-lifecycle";
|
|
6
|
-
import { eventBus } from "../../observability/event-bus";
|
|
7
|
-
import {
|
|
8
|
-
resetTracer,
|
|
9
|
-
type Span,
|
|
10
|
-
type SpanExporter,
|
|
11
|
-
} from "../../observability/tracing";
|
|
12
|
-
|
|
13
|
-
class CaptureExporter implements SpanExporter {
|
|
14
|
-
readonly spans: Span[] = [];
|
|
15
|
-
export(spans: Span[]): void {
|
|
16
|
-
this.spans.push(...spans);
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
afterEach(() => {
|
|
21
|
-
resetTracer();
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
describe("runtime observability lifecycle", () => {
|
|
25
|
-
test("serves heap snapshots with perf data when exposed", async () => {
|
|
26
|
-
const lifecycle = createRuntimeObservabilityLifecycle({ isDev: true });
|
|
27
|
-
const response = lifecycle.handleEndpoint(
|
|
28
|
-
new Request("http://localhost/_mandu/heap"),
|
|
29
|
-
"/_mandu/heap"
|
|
30
|
-
);
|
|
31
|
-
|
|
32
|
-
expect(response?.status).toBe(200);
|
|
33
|
-
const body = await response!.json() as {
|
|
34
|
-
process: { heapUsed: number };
|
|
35
|
-
perf: unknown;
|
|
36
|
-
};
|
|
37
|
-
expect(body.process.heapUsed).toBeGreaterThan(0);
|
|
38
|
-
expect(body.perf).toBeDefined();
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
test("keeps metrics hidden in production unless explicitly enabled", () => {
|
|
42
|
-
const hidden = createRuntimeObservabilityLifecycle({ isDev: false });
|
|
43
|
-
const shown = createRuntimeObservabilityLifecycle({
|
|
44
|
-
isDev: false,
|
|
45
|
-
metricsEndpoint: true,
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
expect(hidden.handleEndpoint(new Request("http://localhost/_mandu/metrics"), "/_mandu/metrics")).toBeNull();
|
|
49
|
-
expect(shown.handleEndpoint(new Request("http://localhost/_mandu/metrics"), "/_mandu/metrics")?.status).toBe(200);
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
test("serves recent EventBus snapshots from the lifecycle endpoint", async () => {
|
|
53
|
-
const lifecycle = createRuntimeObservabilityLifecycle({ isDev: true });
|
|
54
|
-
const source = `observability-lifecycle-${Date.now()}`;
|
|
55
|
-
eventBus.emit({
|
|
56
|
-
type: "http",
|
|
57
|
-
severity: "info",
|
|
58
|
-
source,
|
|
59
|
-
message: "GET /observed 200",
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
const response = lifecycle.handleEndpoint(
|
|
63
|
-
new Request(`http://localhost${INTERNAL_EVENTS_ENDPOINT}/recent?source=${source}`),
|
|
64
|
-
`${INTERNAL_EVENTS_ENDPOINT}/recent`
|
|
65
|
-
);
|
|
66
|
-
|
|
67
|
-
expect(response?.status).toBe(200);
|
|
68
|
-
const body = await response!.json() as {
|
|
69
|
-
events: Array<{ source: string; message: string }>;
|
|
70
|
-
};
|
|
71
|
-
expect(body.events.some((event) => event.source === source)).toBe(true);
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
test("wraps requests in a root tracing span", async () => {
|
|
75
|
-
const exporter = new CaptureExporter();
|
|
76
|
-
const lifecycle = createRuntimeObservabilityLifecycle({
|
|
77
|
-
isDev: false,
|
|
78
|
-
tracing: {
|
|
79
|
-
enabled: true,
|
|
80
|
-
customExporter: exporter,
|
|
81
|
-
},
|
|
82
|
-
});
|
|
83
|
-
const incomingTraceId = "4bf92f3577b34da6a3ce929d0e0e4736";
|
|
84
|
-
const incomingSpanId = "00f067aa0ba902b7";
|
|
85
|
-
const response = await lifecycle.runRequest(
|
|
86
|
-
new Request("https://example.test/users", {
|
|
87
|
-
headers: {
|
|
88
|
-
traceparent: `00-${incomingTraceId}-${incomingSpanId}-01`,
|
|
89
|
-
},
|
|
90
|
-
}),
|
|
91
|
-
Date.now(),
|
|
92
|
-
"corr-1",
|
|
93
|
-
async () => new Response("created", { status: 201 })
|
|
94
|
-
);
|
|
95
|
-
|
|
96
|
-
expect(response.status).toBe(201);
|
|
97
|
-
expect(exporter.spans).toHaveLength(1);
|
|
98
|
-
expect(exporter.spans[0].traceId).toBe(incomingTraceId);
|
|
99
|
-
expect(exporter.spans[0].parentSpanId).toBe(incomingSpanId);
|
|
100
|
-
expect(exporter.spans[0].attributes["http.status_code"]).toBe(201);
|
|
101
|
-
expect(exporter.spans[0].status).toBe("ok");
|
|
102
|
-
});
|
|
103
|
-
});
|
|
1
|
+
import { afterEach, describe, expect, test } from "bun:test";
|
|
2
|
+
import {
|
|
3
|
+
INTERNAL_EVENTS_ENDPOINT,
|
|
4
|
+
createRuntimeObservabilityLifecycle,
|
|
5
|
+
} from "../observability-lifecycle";
|
|
6
|
+
import { eventBus } from "../../observability/event-bus";
|
|
7
|
+
import {
|
|
8
|
+
resetTracer,
|
|
9
|
+
type Span,
|
|
10
|
+
type SpanExporter,
|
|
11
|
+
} from "../../observability/tracing";
|
|
12
|
+
|
|
13
|
+
class CaptureExporter implements SpanExporter {
|
|
14
|
+
readonly spans: Span[] = [];
|
|
15
|
+
export(spans: Span[]): void {
|
|
16
|
+
this.spans.push(...spans);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
afterEach(() => {
|
|
21
|
+
resetTracer();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
describe("runtime observability lifecycle", () => {
|
|
25
|
+
test("serves heap snapshots with perf data when exposed", async () => {
|
|
26
|
+
const lifecycle = createRuntimeObservabilityLifecycle({ isDev: true });
|
|
27
|
+
const response = lifecycle.handleEndpoint(
|
|
28
|
+
new Request("http://localhost/_mandu/heap"),
|
|
29
|
+
"/_mandu/heap"
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
expect(response?.status).toBe(200);
|
|
33
|
+
const body = await response!.json() as {
|
|
34
|
+
process: { heapUsed: number };
|
|
35
|
+
perf: unknown;
|
|
36
|
+
};
|
|
37
|
+
expect(body.process.heapUsed).toBeGreaterThan(0);
|
|
38
|
+
expect(body.perf).toBeDefined();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test("keeps metrics hidden in production unless explicitly enabled", () => {
|
|
42
|
+
const hidden = createRuntimeObservabilityLifecycle({ isDev: false });
|
|
43
|
+
const shown = createRuntimeObservabilityLifecycle({
|
|
44
|
+
isDev: false,
|
|
45
|
+
metricsEndpoint: true,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
expect(hidden.handleEndpoint(new Request("http://localhost/_mandu/metrics"), "/_mandu/metrics")).toBeNull();
|
|
49
|
+
expect(shown.handleEndpoint(new Request("http://localhost/_mandu/metrics"), "/_mandu/metrics")?.status).toBe(200);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test("serves recent EventBus snapshots from the lifecycle endpoint", async () => {
|
|
53
|
+
const lifecycle = createRuntimeObservabilityLifecycle({ isDev: true });
|
|
54
|
+
const source = `observability-lifecycle-${Date.now()}`;
|
|
55
|
+
eventBus.emit({
|
|
56
|
+
type: "http",
|
|
57
|
+
severity: "info",
|
|
58
|
+
source,
|
|
59
|
+
message: "GET /observed 200",
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const response = lifecycle.handleEndpoint(
|
|
63
|
+
new Request(`http://localhost${INTERNAL_EVENTS_ENDPOINT}/recent?source=${source}`),
|
|
64
|
+
`${INTERNAL_EVENTS_ENDPOINT}/recent`
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
expect(response?.status).toBe(200);
|
|
68
|
+
const body = await response!.json() as {
|
|
69
|
+
events: Array<{ source: string; message: string }>;
|
|
70
|
+
};
|
|
71
|
+
expect(body.events.some((event) => event.source === source)).toBe(true);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test("wraps requests in a root tracing span", async () => {
|
|
75
|
+
const exporter = new CaptureExporter();
|
|
76
|
+
const lifecycle = createRuntimeObservabilityLifecycle({
|
|
77
|
+
isDev: false,
|
|
78
|
+
tracing: {
|
|
79
|
+
enabled: true,
|
|
80
|
+
customExporter: exporter,
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
const incomingTraceId = "4bf92f3577b34da6a3ce929d0e0e4736";
|
|
84
|
+
const incomingSpanId = "00f067aa0ba902b7";
|
|
85
|
+
const response = await lifecycle.runRequest(
|
|
86
|
+
new Request("https://example.test/users", {
|
|
87
|
+
headers: {
|
|
88
|
+
traceparent: `00-${incomingTraceId}-${incomingSpanId}-01`,
|
|
89
|
+
},
|
|
90
|
+
}),
|
|
91
|
+
Date.now(),
|
|
92
|
+
"corr-1",
|
|
93
|
+
async () => new Response("created", { status: 201 })
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
expect(response.status).toBe(201);
|
|
97
|
+
expect(exporter.spans).toHaveLength(1);
|
|
98
|
+
expect(exporter.spans[0].traceId).toBe(incomingTraceId);
|
|
99
|
+
expect(exporter.spans[0].parentSpanId).toBe(incomingSpanId);
|
|
100
|
+
expect(exporter.spans[0].attributes["http.status_code"]).toBe(201);
|
|
101
|
+
expect(exporter.spans[0].status).toBe("ok");
|
|
102
|
+
});
|
|
103
|
+
});
|