@mandujs/core 0.53.2 → 0.54.0
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 +2 -2
- 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 +394 -394
- package/src/a11y/types.ts +125 -125
- package/src/auth/__tests__/login.test.ts +1 -1
- package/src/auth/__tests__/password.test.ts +122 -122
- 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 +120 -120
- 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/bundler/__tests__/build-runner.ts +113 -113
- 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 +10 -10
- package/src/bundler/__tests__/hdr.test.ts +353 -353
- package/src/bundler/__tests__/hmr-client.test.ts +532 -532
- package/src/bundler/__tests__/manifest-schema.test.ts +266 -266
- package/src/bundler/__tests__/prod-smoke.test.ts +138 -138
- package/src/bundler/__tests__/reverse-import-graph.test.ts +519 -519
- 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/budget.ts +404 -404
- package/src/bundler/build.test.ts +179 -179
- package/src/bundler/build.ts +55 -55
- package/src/bundler/dev.ts +42 -42
- package/src/bundler/manifest-schema.ts +301 -301
- package/src/bundler/plugins/__tests__/block-generated-imports.test.ts +263 -263
- 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 +155 -155
- 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/reverse-import-graph.ts +339 -339
- package/src/bundler/safe-build.test.ts +201 -201
- package/src/bundler/safe-build.ts +103 -103
- package/src/bundler/scenario-matrix.ts +229 -229
- package/src/bundler/types.ts +10 -10
- 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__/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 +340 -340
- package/src/client/prefetch-helper.ts +55 -55
- package/src/client/router.ts +11 -11
- package/src/client/runtime.ts +47 -47
- package/src/client/serialize.ts +404 -404
- 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.tsx +162 -162
- package/src/config/validate.ts +3 -3
- package/src/config/watcher.ts +311 -311
- package/src/constants.ts +40 -40
- package/src/content/collection.ts +8 -8
- 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/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/schema.ts +48 -48
- package/src/contract/types.ts +58 -58
- package/src/db/__tests__/db.test.ts +482 -482
- package/src/db/index.ts +138 -138
- 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 +182 -182
- 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__/webview-fallback.test.ts +254 -254
- package/src/desktop/__tests__/window.test.ts +248 -248
- 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/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/overlay.tsx +368 -368
- 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 +320 -320
- package/src/devtools/client/components/panel/network-panel.tsx +291 -291
- package/src/devtools/client/components/panel/panel-container.tsx +500 -500
- 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/client/persistence.ts +335 -335
- 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/server/source-context.ts +450 -450
- 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/devtools/worker/worker-manager.ts +410 -410
- package/src/diagnose/__tests__/checks.test.ts +451 -380
- package/src/diagnose/checks.ts +832 -720
- package/src/diagnose/index.ts +17 -16
- package/src/diagnose/run.ts +93 -91
- 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/result.ts +60 -60
- 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 +454 -454
- package/src/filling/auth.ts +308 -308
- package/src/filling/cookie-codec.ts +299 -299
- package/src/filling/deps.ts +265 -265
- 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/index.ts +3 -3
- 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/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/graph.ts +1 -1
- package/src/guard/healing.ts +36 -36
- 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/index.ts +105 -105
- package/src/index.ts +2 -2
- package/src/kitchen/api/agent-devtools-api.ts +779 -544
- package/src/kitchen/api/errors-grouping.ts +126 -0
- package/src/kitchen/kitchen-handler.ts +192 -63
- package/src/kitchen/kitchen-ui.ts +842 -464
- 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/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 +32 -32
- package/src/resource/ddl/__tests__/diff.test.ts +639 -639
- package/src/resource/ddl/__tests__/emit.test.ts +823 -823
- package/src/resource/ddl/__tests__/snapshot.test.ts +499 -499
- package/src/resource/ddl/emit.ts +559 -559
- 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/schema.ts +1 -1
- package/src/router/fs-patterns.test.ts +96 -96
- package/src/router/fs-routes.ts +6 -6
- 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__/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__/not-found.test.ts +152 -152
- 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/fast-refresh-runtime.ts +322 -322
- package/src/runtime/handler.ts +65 -65
- package/src/runtime/handlers.ts +300 -300
- 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/openapi-endpoint.ts +236 -236
- package/src/runtime/ppr.ts +74 -74
- package/src/runtime/registry.ts +171 -171
- package/src/runtime/router.ts +105 -105
- package/src/runtime/server.ts +67 -67
- package/src/runtime/shims.ts +48 -48
- package/src/runtime/ssr.ts +24 -31
- package/src/runtime/streaming-ssr.ts +32 -37
- package/src/runtime/trace.ts +144 -144
- 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/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/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/watcher.ts +18 -18
|
@@ -1,380 +1,451 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for the Issue #215 extended diagnose checks.
|
|
3
|
-
*
|
|
4
|
-
* Every test uses an isolated `mkdtemp` fixture root so we never touch
|
|
5
|
-
* the real project tree. Checks are pure I/O functions; seeding the
|
|
6
|
-
* fixture directly (no build pipeline required) keeps these tests fast
|
|
7
|
-
* and deterministic.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import { describe, it, expect, beforeEach, afterEach } from "bun:test";
|
|
11
|
-
import fs from "fs/promises";
|
|
12
|
-
import path from "path";
|
|
13
|
-
import os from "os";
|
|
14
|
-
import {
|
|
15
|
-
checkManifestFreshness,
|
|
16
|
-
checkPrerenderPollution,
|
|
17
|
-
checkCloneElementWarnings,
|
|
18
|
-
checkDevArtifactsInProd,
|
|
19
|
-
checkPackageExportGaps,
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
await fs.
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
//
|
|
35
|
-
//
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
expect(result.
|
|
46
|
-
expect(result.
|
|
47
|
-
expect(result.
|
|
48
|
-
expect(result.
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
expect(result.
|
|
62
|
-
expect(result.
|
|
63
|
-
expect(result.
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
expect(result.
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
expect(result.
|
|
87
|
-
expect(result.
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
expect(result.
|
|
104
|
-
expect(result.
|
|
105
|
-
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
//
|
|
110
|
-
//
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
await writeFile(rootDir, ".mandu/prerendered/
|
|
125
|
-
await writeFile(rootDir, ".mandu/prerendered/
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
expect(result.
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
expect(result.
|
|
136
|
-
expect(result.
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
//
|
|
142
|
-
//
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
expect(result.
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
expect(result.
|
|
160
|
-
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
//
|
|
165
|
-
//
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
expect(result.
|
|
189
|
-
expect(result.
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
expect(result.
|
|
198
|
-
expect(result.
|
|
199
|
-
expect(result.
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
expect(result.
|
|
208
|
-
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
//
|
|
213
|
-
//
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
expect(result.
|
|
234
|
-
expect(result.
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
await writeFile(rootDir, "
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
expect(result.
|
|
243
|
-
expect(result.
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
expect(result.
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
});
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
//
|
|
268
|
-
//
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
expect(result.
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
await
|
|
291
|
-
await writeFile(rootDir, "src/
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
expect(result.
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
await
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
expect(result.
|
|
303
|
-
expect(result.
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
await
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
await
|
|
316
|
-
const
|
|
317
|
-
|
|
318
|
-
expect(result.
|
|
319
|
-
|
|
320
|
-
});
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
//
|
|
324
|
-
//
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
});
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
expect(
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
const
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
expect(
|
|
378
|
-
expect(
|
|
379
|
-
|
|
380
|
-
});
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the Issue #215 extended diagnose checks.
|
|
3
|
+
*
|
|
4
|
+
* Every test uses an isolated `mkdtemp` fixture root so we never touch
|
|
5
|
+
* the real project tree. Checks are pure I/O functions; seeding the
|
|
6
|
+
* fixture directly (no build pipeline required) keeps these tests fast
|
|
7
|
+
* and deterministic.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { describe, it, expect, beforeEach, afterEach } from "bun:test";
|
|
11
|
+
import fs from "fs/promises";
|
|
12
|
+
import path from "path";
|
|
13
|
+
import os from "os";
|
|
14
|
+
import {
|
|
15
|
+
checkManifestFreshness,
|
|
16
|
+
checkPrerenderPollution,
|
|
17
|
+
checkCloneElementWarnings,
|
|
18
|
+
checkDevArtifactsInProd,
|
|
19
|
+
checkPackageExportGaps,
|
|
20
|
+
checkNestedInternalCore,
|
|
21
|
+
} from "../checks";
|
|
22
|
+
import { runExtendedDiagnose, buildReport } from "../run";
|
|
23
|
+
|
|
24
|
+
async function mkTmpRoot(): Promise<string> {
|
|
25
|
+
return fs.mkdtemp(path.join(os.tmpdir(), "mandu-diagnose-"));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async function writeFile(rootDir: string, rel: string, content: string): Promise<void> {
|
|
29
|
+
const abs = path.join(rootDir, rel);
|
|
30
|
+
await fs.mkdir(path.dirname(abs), { recursive: true });
|
|
31
|
+
await fs.writeFile(abs, content, "utf-8");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// ──────────────────────────────────────────────────────────────────
|
|
35
|
+
// manifest_freshness
|
|
36
|
+
// ──────────────────────────────────────────────────────────────────
|
|
37
|
+
|
|
38
|
+
describe("checkManifestFreshness", () => {
|
|
39
|
+
let rootDir: string;
|
|
40
|
+
beforeEach(async () => { rootDir = await mkTmpRoot(); });
|
|
41
|
+
afterEach(async () => { await fs.rm(rootDir, { recursive: true, force: true }); });
|
|
42
|
+
|
|
43
|
+
it("returns error when .mandu/manifest.json is missing", async () => {
|
|
44
|
+
const result = await checkManifestFreshness(rootDir);
|
|
45
|
+
expect(result.ok).toBe(false);
|
|
46
|
+
expect(result.rule).toBe("manifest_freshness");
|
|
47
|
+
expect(result.severity).toBe("error");
|
|
48
|
+
expect(result.message).toMatch(/missing/);
|
|
49
|
+
expect(result.suggestion).toMatch(/mandu build/);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("returns error when env=development (dev manifest shipped to prod)", async () => {
|
|
53
|
+
await writeFile(rootDir, ".mandu/manifest.json", JSON.stringify({
|
|
54
|
+
version: 1,
|
|
55
|
+
buildTime: "2026-01-01T00:00:00.000Z",
|
|
56
|
+
env: "development",
|
|
57
|
+
bundles: { "page-home": { js: "/x.js", dependencies: [], priority: "immediate" } },
|
|
58
|
+
shared: { runtime: "", vendor: "" },
|
|
59
|
+
}));
|
|
60
|
+
const result = await checkManifestFreshness(rootDir);
|
|
61
|
+
expect(result.ok).toBe(false);
|
|
62
|
+
expect(result.severity).toBe("error");
|
|
63
|
+
expect(result.message).toMatch(/dev-mode/);
|
|
64
|
+
expect(result.details?.env).toBe("development");
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("returns ok for production manifest with populated bundles", async () => {
|
|
68
|
+
await writeFile(rootDir, ".mandu/manifest.json", JSON.stringify({
|
|
69
|
+
version: 1, buildTime: "2026-01-01T00:00:00.000Z", env: "production",
|
|
70
|
+
bundles: { "page-home": { js: "/x.js", dependencies: [], priority: "immediate" } },
|
|
71
|
+
shared: { runtime: "/r.js", vendor: "/v.js" },
|
|
72
|
+
}));
|
|
73
|
+
const result = await checkManifestFreshness(rootDir);
|
|
74
|
+
expect(result.ok).toBe(true);
|
|
75
|
+
expect(result.rule).toBe("manifest_freshness");
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it("returns warning when production but islands present with no bundles", async () => {
|
|
79
|
+
await writeFile(rootDir, ".mandu/manifest.json", JSON.stringify({
|
|
80
|
+
version: 1, buildTime: "x", env: "production",
|
|
81
|
+
bundles: {},
|
|
82
|
+
islands: { "Foo": { js: "/f.js", route: "/x", priority: "visible" } },
|
|
83
|
+
shared: { runtime: "", vendor: "" },
|
|
84
|
+
}));
|
|
85
|
+
const result = await checkManifestFreshness(rootDir);
|
|
86
|
+
expect(result.ok).toBe(false);
|
|
87
|
+
expect(result.severity).toBe("warning");
|
|
88
|
+
expect(result.message).toMatch(/0 route bundles/);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it("returns ok for production with empty bundles AND empty islands (pure-SSR)", async () => {
|
|
92
|
+
await writeFile(rootDir, ".mandu/manifest.json", JSON.stringify({
|
|
93
|
+
version: 1, buildTime: "x", env: "production",
|
|
94
|
+
bundles: {}, shared: { runtime: "", vendor: "" },
|
|
95
|
+
}));
|
|
96
|
+
const result = await checkManifestFreshness(rootDir);
|
|
97
|
+
expect(result.ok).toBe(true);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it("returns error on corrupted JSON", async () => {
|
|
101
|
+
await writeFile(rootDir, ".mandu/manifest.json", "{ not json }");
|
|
102
|
+
const result = await checkManifestFreshness(rootDir);
|
|
103
|
+
expect(result.ok).toBe(false);
|
|
104
|
+
expect(result.severity).toBe("error");
|
|
105
|
+
expect(result.message).toMatch(/corrupted/);
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// ──────────────────────────────────────────────────────────────────
|
|
110
|
+
// prerender_pollution
|
|
111
|
+
// ──────────────────────────────────────────────────────────────────
|
|
112
|
+
|
|
113
|
+
describe("checkPrerenderPollution", () => {
|
|
114
|
+
let rootDir: string;
|
|
115
|
+
beforeEach(async () => { rootDir = await mkTmpRoot(); });
|
|
116
|
+
afterEach(async () => { await fs.rm(rootDir, { recursive: true, force: true }); });
|
|
117
|
+
|
|
118
|
+
it("returns ok when no prerendered output exists", async () => {
|
|
119
|
+
const result = await checkPrerenderPollution(rootDir);
|
|
120
|
+
expect(result.ok).toBe(true);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it("returns ok for clean route shapes", async () => {
|
|
124
|
+
await writeFile(rootDir, ".mandu/prerendered/index.html", "<html></html>");
|
|
125
|
+
await writeFile(rootDir, ".mandu/prerendered/blog/hello/index.html", "<html></html>");
|
|
126
|
+
await writeFile(rootDir, ".mandu/prerendered/docs/getting-started/index.html", "<html></html>");
|
|
127
|
+
const result = await checkPrerenderPollution(rootDir);
|
|
128
|
+
expect(result.ok).toBe(true);
|
|
129
|
+
expect(result.details?.scanned).toBe(3);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it("flags a literal 'path' placeholder route (#213)", async () => {
|
|
133
|
+
await writeFile(rootDir, ".mandu/prerendered/path/index.html", "<html></html>");
|
|
134
|
+
const result = await checkPrerenderPollution(rootDir);
|
|
135
|
+
expect(result.ok).toBe(false);
|
|
136
|
+
expect(result.severity).toBe("warning");
|
|
137
|
+
expect(result.message).toMatch(/placeholder/);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it("flags routes containing '...' inside a segment", async () => {
|
|
141
|
+
// We can't create a directory literally named "..." on Windows
|
|
142
|
+
// (trailing dots are stripped), so we use a segment that embeds
|
|
143
|
+
// "..." in the middle — the classifier still catches it.
|
|
144
|
+
await writeFile(rootDir, ".mandu/prerendered/blog/a...b/index.html", "<html></html>");
|
|
145
|
+
const result = await checkPrerenderPollution(rootDir);
|
|
146
|
+
expect(result.ok).toBe(false);
|
|
147
|
+
expect(result.severity).toBe("warning");
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it("flags uppercase-starting segments as suspicious", async () => {
|
|
151
|
+
await writeFile(rootDir, ".mandu/prerendered/Foo/index.html", "<html></html>");
|
|
152
|
+
const result = await checkPrerenderPollution(rootDir);
|
|
153
|
+
expect(result.ok).toBe(false);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it("scans legacy .mandu/static/ location too", async () => {
|
|
157
|
+
await writeFile(rootDir, ".mandu/static/path/index.html", "<html></html>");
|
|
158
|
+
const result = await checkPrerenderPollution(rootDir);
|
|
159
|
+
expect(result.ok).toBe(false);
|
|
160
|
+
expect(result.severity).toBe("warning");
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
// ──────────────────────────────────────────────────────────────────
|
|
165
|
+
// cloneelement_warnings
|
|
166
|
+
// ──────────────────────────────────────────────────────────────────
|
|
167
|
+
|
|
168
|
+
describe("checkCloneElementWarnings", () => {
|
|
169
|
+
let rootDir: string;
|
|
170
|
+
beforeEach(async () => { rootDir = await mkTmpRoot(); });
|
|
171
|
+
afterEach(async () => { await fs.rm(rootDir, { recursive: true, force: true }); });
|
|
172
|
+
|
|
173
|
+
it("returns ok when no build log exists", async () => {
|
|
174
|
+
const result = await checkCloneElementWarnings(rootDir);
|
|
175
|
+
expect(result.ok).toBe(true);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it("returns ok when log has no key warnings", async () => {
|
|
179
|
+
await writeFile(rootDir, ".mandu/build.log", "Build succeeded\nNo warnings\n");
|
|
180
|
+
const result = await checkCloneElementWarnings(rootDir);
|
|
181
|
+
expect(result.ok).toBe(true);
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it("returns info severity for 1-10 warnings", async () => {
|
|
185
|
+
const warn = 'Warning: Each child in a list should have a unique "key" prop.\n';
|
|
186
|
+
await writeFile(rootDir, ".mandu/build.log", warn.repeat(5));
|
|
187
|
+
const result = await checkCloneElementWarnings(rootDir);
|
|
188
|
+
expect(result.ok).toBe(false);
|
|
189
|
+
expect(result.severity).toBe("info");
|
|
190
|
+
expect(result.details?.count).toBe(5);
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it("returns warning severity for >10 warnings (#212)", async () => {
|
|
194
|
+
const warn = 'Warning: Each child in a list should have a unique "key" prop.\n';
|
|
195
|
+
await writeFile(rootDir, ".mandu/build.log", warn.repeat(25));
|
|
196
|
+
const result = await checkCloneElementWarnings(rootDir);
|
|
197
|
+
expect(result.ok).toBe(false);
|
|
198
|
+
expect(result.severity).toBe("warning");
|
|
199
|
+
expect(result.details?.count).toBe(25);
|
|
200
|
+
expect(result.suggestion).toMatch(/0\.32\.0/);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it("falls back to dev-server.stderr.log when build.log is absent", async () => {
|
|
204
|
+
const warn = 'Each child in a list should have a unique "key" prop\n';
|
|
205
|
+
await writeFile(rootDir, ".mandu/dev-server.stderr.log", warn.repeat(12));
|
|
206
|
+
const result = await checkCloneElementWarnings(rootDir);
|
|
207
|
+
expect(result.ok).toBe(false);
|
|
208
|
+
expect(result.details?.logPath).toMatch(/dev-server\.stderr\.log$/);
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
// ──────────────────────────────────────────────────────────────────
|
|
213
|
+
// dev_artifacts_in_prod
|
|
214
|
+
// ──────────────────────────────────────────────────────────────────
|
|
215
|
+
|
|
216
|
+
describe("checkDevArtifactsInProd", () => {
|
|
217
|
+
let rootDir: string;
|
|
218
|
+
beforeEach(async () => { rootDir = await mkTmpRoot(); });
|
|
219
|
+
afterEach(async () => { await fs.rm(rootDir, { recursive: true, force: true }); });
|
|
220
|
+
|
|
221
|
+
it("returns ok when no _devtools.js artifact exists", async () => {
|
|
222
|
+
const result = await checkDevArtifactsInProd(rootDir);
|
|
223
|
+
expect(result.ok).toBe(true);
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
it("flags _devtools.js in production manifest", async () => {
|
|
227
|
+
await writeFile(rootDir, ".mandu/manifest.json", JSON.stringify({
|
|
228
|
+
version: 1, buildTime: "x", env: "production",
|
|
229
|
+
bundles: {}, shared: { runtime: "", vendor: "" },
|
|
230
|
+
}));
|
|
231
|
+
await writeFile(rootDir, ".mandu/client/_devtools.js", "console.log('devtools');");
|
|
232
|
+
const result = await checkDevArtifactsInProd(rootDir);
|
|
233
|
+
expect(result.ok).toBe(false);
|
|
234
|
+
expect(result.severity).toBe("error");
|
|
235
|
+
expect(result.message).toMatch(/production/);
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
it("flags _devtools.js when mandu.config sets dev.devtools: false", async () => {
|
|
239
|
+
await writeFile(rootDir, "mandu.config.ts", `export default { dev: { devtools: false } };`);
|
|
240
|
+
await writeFile(rootDir, ".mandu/client/_devtools.js", "x");
|
|
241
|
+
const result = await checkDevArtifactsInProd(rootDir);
|
|
242
|
+
expect(result.ok).toBe(false);
|
|
243
|
+
expect(result.severity).toBe("error");
|
|
244
|
+
expect(result.message).toMatch(/dev\.devtools: false/);
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
it("flags prerendered HTML with a <script src=\".../devtools.js\"> reference", async () => {
|
|
248
|
+
await writeFile(rootDir, ".mandu/prerendered/index.html",
|
|
249
|
+
'<html><head><script src="/.mandu/client/_devtools.js"></script></head></html>'
|
|
250
|
+
);
|
|
251
|
+
const result = await checkDevArtifactsInProd(rootDir);
|
|
252
|
+
expect(result.ok).toBe(false);
|
|
253
|
+
expect(result.message).toMatch(/prerendered HTML/);
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
it("stays ok in dev builds (env=development, devtools expected)", async () => {
|
|
257
|
+
await writeFile(rootDir, ".mandu/manifest.json", JSON.stringify({
|
|
258
|
+
version: 1, buildTime: "x", env: "development",
|
|
259
|
+
bundles: {}, shared: { runtime: "", vendor: "" },
|
|
260
|
+
}));
|
|
261
|
+
await writeFile(rootDir, ".mandu/client/_devtools.js", "x");
|
|
262
|
+
const result = await checkDevArtifactsInProd(rootDir);
|
|
263
|
+
expect(result.ok).toBe(true);
|
|
264
|
+
});
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
// ──────────────────────────────────────────────────────────────────
|
|
268
|
+
// package_export_gaps
|
|
269
|
+
// ──────────────────────────────────────────────────────────────────
|
|
270
|
+
|
|
271
|
+
describe("checkPackageExportGaps", () => {
|
|
272
|
+
let rootDir: string;
|
|
273
|
+
beforeEach(async () => { rootDir = await mkTmpRoot(); });
|
|
274
|
+
afterEach(async () => { await fs.rm(rootDir, { recursive: true, force: true }); });
|
|
275
|
+
|
|
276
|
+
async function seedCore(exports: Record<string, unknown>): Promise<void> {
|
|
277
|
+
await writeFile(rootDir, "node_modules/@mandujs/core/package.json", JSON.stringify({
|
|
278
|
+
name: "@mandujs/core", version: "0.32.0", exports,
|
|
279
|
+
}));
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
it("skips gracefully when @mandujs/core is not installed", async () => {
|
|
283
|
+
await writeFile(rootDir, "src/foo.ts", `import { x } from "@mandujs/core/unknown";`);
|
|
284
|
+
const result = await checkPackageExportGaps(rootDir);
|
|
285
|
+
expect(result.ok).toBe(true);
|
|
286
|
+
expect(result.details?.skipped).toBe(true);
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
it("returns ok when all user imports match the exports map", async () => {
|
|
290
|
+
await seedCore({ ".": "./src/index.ts", "./client": "./src/client/index.ts" });
|
|
291
|
+
await writeFile(rootDir, "src/a.ts", `import { foo } from "@mandujs/core";`);
|
|
292
|
+
await writeFile(rootDir, "src/b.ts", `import { island } from "@mandujs/core/client";`);
|
|
293
|
+
const result = await checkPackageExportGaps(rootDir);
|
|
294
|
+
expect(result.ok).toBe(true);
|
|
295
|
+
expect(result.details?.uniqueSubpaths).toBe(2);
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
it("flags an import missing from the exports map", async () => {
|
|
299
|
+
await seedCore({ ".": "./src/index.ts", "./client": "./src/client/index.ts" });
|
|
300
|
+
await writeFile(rootDir, "src/bad.ts", `import { x } from "@mandujs/core/nonexistent";`);
|
|
301
|
+
const result = await checkPackageExportGaps(rootDir);
|
|
302
|
+
expect(result.ok).toBe(false);
|
|
303
|
+
expect(result.severity).toBe("error");
|
|
304
|
+
expect(result.message).toMatch(/nonexistent/);
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
it("honors ./* wildcard export as a catch-all", async () => {
|
|
308
|
+
await seedCore({ ".": "./src/index.ts", "./*": "./src/*" });
|
|
309
|
+
await writeFile(rootDir, "src/a.ts", `import { x } from "@mandujs/core/anything";`);
|
|
310
|
+
const result = await checkPackageExportGaps(rootDir);
|
|
311
|
+
expect(result.ok).toBe(true);
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
it("recognizes require() specifiers in addition to import", async () => {
|
|
315
|
+
await seedCore({ ".": "./src/index.ts" });
|
|
316
|
+
await writeFile(rootDir, "src/a.cjs", `const { x } = require("@mandujs/core/ghost");`);
|
|
317
|
+
const result = await checkPackageExportGaps(rootDir);
|
|
318
|
+
expect(result.ok).toBe(false);
|
|
319
|
+
expect(result.severity).toBe("error");
|
|
320
|
+
});
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
// ──────────────────────────────────────────────────────────────────
|
|
324
|
+
// nested_internal_core (#261)
|
|
325
|
+
// ──────────────────────────────────────────────────────────────────
|
|
326
|
+
|
|
327
|
+
describe("checkNestedInternalCore", () => {
|
|
328
|
+
let rootDir: string;
|
|
329
|
+
beforeEach(async () => { rootDir = await mkTmpRoot(); });
|
|
330
|
+
afterEach(async () => { await fs.rm(rootDir, { recursive: true, force: true }); });
|
|
331
|
+
|
|
332
|
+
async function seedHoistedCore(version: string): Promise<void> {
|
|
333
|
+
await writeFile(rootDir, "node_modules/@mandujs/core/package.json", JSON.stringify({
|
|
334
|
+
name: "@mandujs/core", version,
|
|
335
|
+
}));
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
async function seedNestedCore(parent: string, version: string): Promise<void> {
|
|
339
|
+
await writeFile(
|
|
340
|
+
rootDir,
|
|
341
|
+
`node_modules/@mandujs/${parent}/node_modules/@mandujs/core/package.json`,
|
|
342
|
+
JSON.stringify({ name: "@mandujs/core", version }),
|
|
343
|
+
);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
it("skips when hoisted @mandujs/core is not installed", async () => {
|
|
347
|
+
const result = await checkNestedInternalCore(rootDir);
|
|
348
|
+
expect(result.ok).toBe(true);
|
|
349
|
+
expect(result.details?.skipped).toBe(true);
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
it("passes when no nested core exists", async () => {
|
|
353
|
+
await seedHoistedCore("0.53.2");
|
|
354
|
+
// Seed a sibling without nested core
|
|
355
|
+
await writeFile(rootDir, "node_modules/@mandujs/mcp/package.json", JSON.stringify({
|
|
356
|
+
name: "@mandujs/mcp", version: "0.36.2",
|
|
357
|
+
}));
|
|
358
|
+
const result = await checkNestedInternalCore(rootDir);
|
|
359
|
+
expect(result.ok).toBe(true);
|
|
360
|
+
expect(result.details?.scannedSiblings).toBe(1);
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
it("passes when nested core matches hoisted version exactly", async () => {
|
|
364
|
+
await seedHoistedCore("0.53.2");
|
|
365
|
+
await seedNestedCore("mcp", "0.53.2");
|
|
366
|
+
const result = await checkNestedInternalCore(rootDir);
|
|
367
|
+
expect(result.ok).toBe(true);
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
it("flags a stale nested core that shadows hoisted (#261 repro)", async () => {
|
|
371
|
+
await seedHoistedCore("0.53.2");
|
|
372
|
+
await seedNestedCore("mcp", "0.47.0");
|
|
373
|
+
const result = await checkNestedInternalCore(rootDir);
|
|
374
|
+
expect(result.ok).toBe(false);
|
|
375
|
+
expect(result.severity).toBe("error");
|
|
376
|
+
expect(result.message).toMatch(/@mandujs\/mcp/);
|
|
377
|
+
expect(result.message).toMatch(/0\.47\.0/);
|
|
378
|
+
expect(result.suggestion).toMatch(/rm -rf node_modules\/@mandujs\/mcp\/node_modules/);
|
|
379
|
+
expect(result.details?.mismatchCount).toBe(1);
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
it("reports every mismatched sibling, not just one", async () => {
|
|
383
|
+
await seedHoistedCore("0.53.2");
|
|
384
|
+
await seedNestedCore("mcp", "0.47.0");
|
|
385
|
+
await seedNestedCore("ate", "0.50.0");
|
|
386
|
+
const result = await checkNestedInternalCore(rootDir);
|
|
387
|
+
expect(result.ok).toBe(false);
|
|
388
|
+
expect(result.details?.mismatchCount).toBe(2);
|
|
389
|
+
});
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
// ──────────────────────────────────────────────────────────────────
|
|
393
|
+
// aggregator
|
|
394
|
+
// ──────────────────────────────────────────────────────────────────
|
|
395
|
+
|
|
396
|
+
describe("runExtendedDiagnose", () => {
|
|
397
|
+
let rootDir: string;
|
|
398
|
+
beforeEach(async () => { rootDir = await mkTmpRoot(); });
|
|
399
|
+
afterEach(async () => { await fs.rm(rootDir, { recursive: true, force: true }); });
|
|
400
|
+
|
|
401
|
+
it("runs all 7 extended checks and returns a structured report", async () => {
|
|
402
|
+
const report = await runExtendedDiagnose(rootDir);
|
|
403
|
+
// #261 added `nested_internal_core` — total is now 7.
|
|
404
|
+
expect(report.summary.total).toBe(7);
|
|
405
|
+
// manifest is missing → at least one error
|
|
406
|
+
expect(report.healthy).toBe(false);
|
|
407
|
+
expect(report.errorCount).toBeGreaterThanOrEqual(1);
|
|
408
|
+
const rules = report.checks.map((c) => c.rule);
|
|
409
|
+
expect(rules).toContain("manifest_freshness");
|
|
410
|
+
expect(rules).toContain("prerender_pollution");
|
|
411
|
+
expect(rules).toContain("cloneelement_warnings");
|
|
412
|
+
expect(rules).toContain("dev_artifacts_in_prod");
|
|
413
|
+
expect(rules).toContain("package_export_gaps");
|
|
414
|
+
expect(rules).toContain("nested_internal_core");
|
|
415
|
+
expect(rules).toContain("a11y_hints");
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
it("returns healthy=true when all checks pass (production manifest, no gaps)", async () => {
|
|
419
|
+
await writeFile(rootDir, ".mandu/manifest.json", JSON.stringify({
|
|
420
|
+
version: 1, buildTime: "x", env: "production",
|
|
421
|
+
bundles: { h: { js: "/h.js", dependencies: [], priority: "immediate" } },
|
|
422
|
+
shared: { runtime: "/r.js", vendor: "/v.js" },
|
|
423
|
+
}));
|
|
424
|
+
const report = await runExtendedDiagnose(rootDir);
|
|
425
|
+
expect(report.healthy).toBe(true);
|
|
426
|
+
expect(report.errorCount).toBe(0);
|
|
427
|
+
});
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
describe("buildReport", () => {
|
|
431
|
+
it("computes healthy=true when no error-severity check fires", () => {
|
|
432
|
+
const report = buildReport([
|
|
433
|
+
{ ok: true, rule: "a", message: "ok" },
|
|
434
|
+
{ ok: false, rule: "b", severity: "warning", message: "warn" },
|
|
435
|
+
{ ok: false, rule: "c", severity: "info", message: "info" },
|
|
436
|
+
]);
|
|
437
|
+
expect(report.healthy).toBe(true);
|
|
438
|
+
expect(report.errorCount).toBe(0);
|
|
439
|
+
expect(report.warningCount).toBe(1);
|
|
440
|
+
expect(report.summary.failed).toBe(2);
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
it("computes healthy=false when at least one error fires", () => {
|
|
444
|
+
const report = buildReport([
|
|
445
|
+
{ ok: false, rule: "a", severity: "error", message: "fail" },
|
|
446
|
+
{ ok: true, rule: "b", message: "ok" },
|
|
447
|
+
]);
|
|
448
|
+
expect(report.healthy).toBe(false);
|
|
449
|
+
expect(report.errorCount).toBe(1);
|
|
450
|
+
});
|
|
451
|
+
});
|