@mandujs/core 0.54.31 → 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 +39 -20
- 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
package/src/testing/db.ts
CHANGED
|
@@ -1,157 +1,157 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @mandujs/core/testing/db
|
|
3
|
-
*
|
|
4
|
-
* In-memory / file-backed SQLite fixture for integration tests.
|
|
5
|
-
*
|
|
6
|
-
* Wraps `@mandujs/core/db` so callers do not have to learn Bun.SQL's URL
|
|
7
|
-
* conventions just to stand up a throwaway database. The default is
|
|
8
|
-
* `sqlite::memory:` — fully isolated, survives exactly one test, zero
|
|
9
|
-
* filesystem footprint.
|
|
10
|
-
*
|
|
11
|
-
* ```ts
|
|
12
|
-
* import { createTestDb } from "@mandujs/core/testing";
|
|
13
|
-
*
|
|
14
|
-
* const db = await createTestDb({
|
|
15
|
-
* schema: `
|
|
16
|
-
* CREATE TABLE users (id TEXT PRIMARY KEY, email TEXT NOT NULL);
|
|
17
|
-
* CREATE INDEX users_email ON users(email);
|
|
18
|
-
* `,
|
|
19
|
-
* });
|
|
20
|
-
* afterEach(async () => await db.close());
|
|
21
|
-
*
|
|
22
|
-
* await db.db`INSERT INTO users (id, email) VALUES (${"u1"}, ${"a@b.c"})`;
|
|
23
|
-
* const rows = await db.db<{ id: string; email: string }>`SELECT * FROM users`;
|
|
24
|
-
* expect(rows).toHaveLength(1);
|
|
25
|
-
* ```
|
|
26
|
-
*
|
|
27
|
-
* ## Design
|
|
28
|
-
*
|
|
29
|
-
* - **Isolation first**: each call to `createTestDb()` returns a fresh
|
|
30
|
-
* `Db` handle. SQLite in-memory dbs are scoped to the connection, so
|
|
31
|
-
* there is no cross-fixture leakage.
|
|
32
|
-
* - **DDL delivered as plain SQL**: callers pass schema as a string (or an
|
|
33
|
-
* array of statements). No dependency on the resource migration runner —
|
|
34
|
-
* that's a Phase 12.3 concern.
|
|
35
|
-
* - **Transaction helper**: `transaction(fn)` is just a re-export of the
|
|
36
|
-
* underlying `Db.transaction` — convenient to avoid threading `db.db.*`.
|
|
37
|
-
* - **Async-dispose**: `using db = await createTestDb(...)` works via
|
|
38
|
-
* `Symbol.asyncDispose` (ES2023 Explicit Resource Management). Pair with
|
|
39
|
-
* Bun.test's per-test cleanup for maximum terseness.
|
|
40
|
-
*
|
|
41
|
-
* ## SQLite caveats
|
|
42
|
-
*
|
|
43
|
-
* Bun.SQL's SQLite adapter requires non-null columns to be typed.
|
|
44
|
-
* Tests that want rich schemas should use `TEXT`, `INTEGER`, `REAL`,
|
|
45
|
-
* `BLOB`. Higher-level typed schemas come with the Phase 12.3 resource
|
|
46
|
-
* migration fixture.
|
|
47
|
-
*
|
|
48
|
-
* @module testing/db
|
|
49
|
-
*/
|
|
50
|
-
|
|
51
|
-
import { createDb, type Db } from "../db/index";
|
|
52
|
-
|
|
53
|
-
/** Options for {@link createTestDb}. */
|
|
54
|
-
export interface CreateTestDbOptions {
|
|
55
|
-
/**
|
|
56
|
-
* Connection URL. Default: `"sqlite::memory:"`.
|
|
57
|
-
*
|
|
58
|
-
* Any `sqlite:` URL is accepted — `sqlite:./fixture.db` for a file-backed
|
|
59
|
-
* fixture that survives across fixture instances, for example. Non-sqlite
|
|
60
|
-
* providers are accepted but not recommended for unit tests (you lose
|
|
61
|
-
* isolation across fixtures).
|
|
62
|
-
*/
|
|
63
|
-
url?: string;
|
|
64
|
-
/**
|
|
65
|
-
* DDL to apply on open. Accepts a multi-statement SQL string or an array
|
|
66
|
-
* of pre-split statements. Statements are run sequentially — if any
|
|
67
|
-
* fails, subsequent ones are skipped and the error re-throws.
|
|
68
|
-
*/
|
|
69
|
-
schema?: string | string[];
|
|
70
|
-
/** Optional seed block to run after `schema` — convenient for row-level setup. */
|
|
71
|
-
seed?: (db: Db) => Promise<void> | void;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/** Handle returned by {@link createTestDb}. */
|
|
75
|
-
export interface TestDb {
|
|
76
|
-
/** The underlying Db handle — use as a tagged-template query function. */
|
|
77
|
-
readonly db: Db;
|
|
78
|
-
/** Re-export of `db.transaction`. */
|
|
79
|
-
transaction: Db["transaction"];
|
|
80
|
-
/**
|
|
81
|
-
* Apply additional DDL after the fixture has been created. Useful when the
|
|
82
|
-
* schema depends on per-test parameters (e.g., random suffixes to avoid
|
|
83
|
-
* SQLite's reserved-words).
|
|
84
|
-
*/
|
|
85
|
-
apply(ddl: string | string[]): Promise<void>;
|
|
86
|
-
/** Idempotent cleanup — safe to call multiple times. */
|
|
87
|
-
close(): Promise<void>;
|
|
88
|
-
/** `using db = await createTestDb(...)` support. */
|
|
89
|
-
[Symbol.asyncDispose](): Promise<void>;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/** Split a multi-statement SQL string into individual statements. */
|
|
93
|
-
function splitSql(source: string): string[] {
|
|
94
|
-
// Naïve splitter: works for vanilla DDL without embedded `;` inside quoted
|
|
95
|
-
// strings — the realistic shape of test fixtures. A full lexer lives in
|
|
96
|
-
// `db/migrations/runner.ts`; we intentionally do not re-use it here to
|
|
97
|
-
// avoid coupling the testing fixture to migration-runner internals.
|
|
98
|
-
return source
|
|
99
|
-
.split(";")
|
|
100
|
-
.map((s) => s.trim())
|
|
101
|
-
.filter((s) => s.length > 0);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
async function applyDdl(db: Db, ddl: string | string[]): Promise<void> {
|
|
105
|
-
const statements = Array.isArray(ddl) ? ddl : splitSql(ddl);
|
|
106
|
-
for (const stmt of statements) {
|
|
107
|
-
// Bun.SQL's tagged-template form does not support raw DDL composition —
|
|
108
|
-
// but a 1-argument template with no placeholders is safe. The value
|
|
109
|
-
// inside `strings.raw[0]` is the literal SQL, never an interpolated value.
|
|
110
|
-
const raw = stmt.trim();
|
|
111
|
-
if (raw.length === 0) continue;
|
|
112
|
-
const strings = Object.assign([raw], { raw: [raw] }) as TemplateStringsArray;
|
|
113
|
-
await db(strings);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Boot a fixture-scoped database handle.
|
|
119
|
-
*
|
|
120
|
-
* The default URL (`sqlite::memory:`) creates a per-connection in-memory
|
|
121
|
-
* database — ideal for per-test isolation.
|
|
122
|
-
*
|
|
123
|
-
* @throws if `url` is non-empty but Bun.SQL rejects it on the first query.
|
|
124
|
-
* Validation is lazy — construction never throws on unreachable targets.
|
|
125
|
-
*/
|
|
126
|
-
export async function createTestDb(
|
|
127
|
-
options: CreateTestDbOptions = {},
|
|
128
|
-
): Promise<TestDb> {
|
|
129
|
-
const url = options.url ?? "sqlite::memory:";
|
|
130
|
-
const db = createDb({ url });
|
|
131
|
-
|
|
132
|
-
if (options.schema !== undefined) {
|
|
133
|
-
await applyDdl(db, options.schema);
|
|
134
|
-
}
|
|
135
|
-
if (options.seed) {
|
|
136
|
-
await options.seed(db);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
let closed = false;
|
|
140
|
-
const close = async (): Promise<void> => {
|
|
141
|
-
if (closed) return;
|
|
142
|
-
closed = true;
|
|
143
|
-
await db.close();
|
|
144
|
-
};
|
|
145
|
-
|
|
146
|
-
return {
|
|
147
|
-
db,
|
|
148
|
-
transaction: db.transaction.bind(db),
|
|
149
|
-
async apply(ddl) {
|
|
150
|
-
await applyDdl(db, ddl);
|
|
151
|
-
},
|
|
152
|
-
close,
|
|
153
|
-
async [Symbol.asyncDispose]() {
|
|
154
|
-
await close();
|
|
155
|
-
},
|
|
156
|
-
};
|
|
157
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* @mandujs/core/testing/db
|
|
3
|
+
*
|
|
4
|
+
* In-memory / file-backed SQLite fixture for integration tests.
|
|
5
|
+
*
|
|
6
|
+
* Wraps `@mandujs/core/db` so callers do not have to learn Bun.SQL's URL
|
|
7
|
+
* conventions just to stand up a throwaway database. The default is
|
|
8
|
+
* `sqlite::memory:` — fully isolated, survives exactly one test, zero
|
|
9
|
+
* filesystem footprint.
|
|
10
|
+
*
|
|
11
|
+
* ```ts
|
|
12
|
+
* import { createTestDb } from "@mandujs/core/testing";
|
|
13
|
+
*
|
|
14
|
+
* const db = await createTestDb({
|
|
15
|
+
* schema: `
|
|
16
|
+
* CREATE TABLE users (id TEXT PRIMARY KEY, email TEXT NOT NULL);
|
|
17
|
+
* CREATE INDEX users_email ON users(email);
|
|
18
|
+
* `,
|
|
19
|
+
* });
|
|
20
|
+
* afterEach(async () => await db.close());
|
|
21
|
+
*
|
|
22
|
+
* await db.db`INSERT INTO users (id, email) VALUES (${"u1"}, ${"a@b.c"})`;
|
|
23
|
+
* const rows = await db.db<{ id: string; email: string }>`SELECT * FROM users`;
|
|
24
|
+
* expect(rows).toHaveLength(1);
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* ## Design
|
|
28
|
+
*
|
|
29
|
+
* - **Isolation first**: each call to `createTestDb()` returns a fresh
|
|
30
|
+
* `Db` handle. SQLite in-memory dbs are scoped to the connection, so
|
|
31
|
+
* there is no cross-fixture leakage.
|
|
32
|
+
* - **DDL delivered as plain SQL**: callers pass schema as a string (or an
|
|
33
|
+
* array of statements). No dependency on the resource migration runner —
|
|
34
|
+
* that's a Phase 12.3 concern.
|
|
35
|
+
* - **Transaction helper**: `transaction(fn)` is just a re-export of the
|
|
36
|
+
* underlying `Db.transaction` — convenient to avoid threading `db.db.*`.
|
|
37
|
+
* - **Async-dispose**: `using db = await createTestDb(...)` works via
|
|
38
|
+
* `Symbol.asyncDispose` (ES2023 Explicit Resource Management). Pair with
|
|
39
|
+
* Bun.test's per-test cleanup for maximum terseness.
|
|
40
|
+
*
|
|
41
|
+
* ## SQLite caveats
|
|
42
|
+
*
|
|
43
|
+
* Bun.SQL's SQLite adapter requires non-null columns to be typed.
|
|
44
|
+
* Tests that want rich schemas should use `TEXT`, `INTEGER`, `REAL`,
|
|
45
|
+
* `BLOB`. Higher-level typed schemas come with the Phase 12.3 resource
|
|
46
|
+
* migration fixture.
|
|
47
|
+
*
|
|
48
|
+
* @module testing/db
|
|
49
|
+
*/
|
|
50
|
+
|
|
51
|
+
import { createDb, type Db } from "../db/index";
|
|
52
|
+
|
|
53
|
+
/** Options for {@link createTestDb}. */
|
|
54
|
+
export interface CreateTestDbOptions {
|
|
55
|
+
/**
|
|
56
|
+
* Connection URL. Default: `"sqlite::memory:"`.
|
|
57
|
+
*
|
|
58
|
+
* Any `sqlite:` URL is accepted — `sqlite:./fixture.db` for a file-backed
|
|
59
|
+
* fixture that survives across fixture instances, for example. Non-sqlite
|
|
60
|
+
* providers are accepted but not recommended for unit tests (you lose
|
|
61
|
+
* isolation across fixtures).
|
|
62
|
+
*/
|
|
63
|
+
url?: string;
|
|
64
|
+
/**
|
|
65
|
+
* DDL to apply on open. Accepts a multi-statement SQL string or an array
|
|
66
|
+
* of pre-split statements. Statements are run sequentially — if any
|
|
67
|
+
* fails, subsequent ones are skipped and the error re-throws.
|
|
68
|
+
*/
|
|
69
|
+
schema?: string | string[];
|
|
70
|
+
/** Optional seed block to run after `schema` — convenient for row-level setup. */
|
|
71
|
+
seed?: (db: Db) => Promise<void> | void;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Handle returned by {@link createTestDb}. */
|
|
75
|
+
export interface TestDb {
|
|
76
|
+
/** The underlying Db handle — use as a tagged-template query function. */
|
|
77
|
+
readonly db: Db;
|
|
78
|
+
/** Re-export of `db.transaction`. */
|
|
79
|
+
transaction: Db["transaction"];
|
|
80
|
+
/**
|
|
81
|
+
* Apply additional DDL after the fixture has been created. Useful when the
|
|
82
|
+
* schema depends on per-test parameters (e.g., random suffixes to avoid
|
|
83
|
+
* SQLite's reserved-words).
|
|
84
|
+
*/
|
|
85
|
+
apply(ddl: string | string[]): Promise<void>;
|
|
86
|
+
/** Idempotent cleanup — safe to call multiple times. */
|
|
87
|
+
close(): Promise<void>;
|
|
88
|
+
/** `using db = await createTestDb(...)` support. */
|
|
89
|
+
[Symbol.asyncDispose](): Promise<void>;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Split a multi-statement SQL string into individual statements. */
|
|
93
|
+
function splitSql(source: string): string[] {
|
|
94
|
+
// Naïve splitter: works for vanilla DDL without embedded `;` inside quoted
|
|
95
|
+
// strings — the realistic shape of test fixtures. A full lexer lives in
|
|
96
|
+
// `db/migrations/runner.ts`; we intentionally do not re-use it here to
|
|
97
|
+
// avoid coupling the testing fixture to migration-runner internals.
|
|
98
|
+
return source
|
|
99
|
+
.split(";")
|
|
100
|
+
.map((s) => s.trim())
|
|
101
|
+
.filter((s) => s.length > 0);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async function applyDdl(db: Db, ddl: string | string[]): Promise<void> {
|
|
105
|
+
const statements = Array.isArray(ddl) ? ddl : splitSql(ddl);
|
|
106
|
+
for (const stmt of statements) {
|
|
107
|
+
// Bun.SQL's tagged-template form does not support raw DDL composition —
|
|
108
|
+
// but a 1-argument template with no placeholders is safe. The value
|
|
109
|
+
// inside `strings.raw[0]` is the literal SQL, never an interpolated value.
|
|
110
|
+
const raw = stmt.trim();
|
|
111
|
+
if (raw.length === 0) continue;
|
|
112
|
+
const strings = Object.assign([raw], { raw: [raw] }) as TemplateStringsArray;
|
|
113
|
+
await db(strings);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Boot a fixture-scoped database handle.
|
|
119
|
+
*
|
|
120
|
+
* The default URL (`sqlite::memory:`) creates a per-connection in-memory
|
|
121
|
+
* database — ideal for per-test isolation.
|
|
122
|
+
*
|
|
123
|
+
* @throws if `url` is non-empty but Bun.SQL rejects it on the first query.
|
|
124
|
+
* Validation is lazy — construction never throws on unreachable targets.
|
|
125
|
+
*/
|
|
126
|
+
export async function createTestDb(
|
|
127
|
+
options: CreateTestDbOptions = {},
|
|
128
|
+
): Promise<TestDb> {
|
|
129
|
+
const url = options.url ?? "sqlite::memory:";
|
|
130
|
+
const db = createDb({ url });
|
|
131
|
+
|
|
132
|
+
if (options.schema !== undefined) {
|
|
133
|
+
await applyDdl(db, options.schema);
|
|
134
|
+
}
|
|
135
|
+
if (options.seed) {
|
|
136
|
+
await options.seed(db);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
let closed = false;
|
|
140
|
+
const close = async (): Promise<void> => {
|
|
141
|
+
if (closed) return;
|
|
142
|
+
closed = true;
|
|
143
|
+
await db.close();
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
return {
|
|
147
|
+
db,
|
|
148
|
+
transaction: db.transaction.bind(db),
|
|
149
|
+
async apply(ddl) {
|
|
150
|
+
await applyDdl(db, ddl);
|
|
151
|
+
},
|
|
152
|
+
close,
|
|
153
|
+
async [Symbol.asyncDispose]() {
|
|
154
|
+
await close();
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
}
|
package/src/testing/index.ts
CHANGED
|
@@ -337,8 +337,8 @@ export {
|
|
|
337
337
|
|
|
338
338
|
// ========== Phase 18.σ — Unified reporter ==========
|
|
339
339
|
|
|
340
|
-
export {
|
|
341
|
-
formatReport,
|
|
340
|
+
export {
|
|
341
|
+
formatReport,
|
|
342
342
|
formatHuman,
|
|
343
343
|
formatJson,
|
|
344
344
|
formatJunit,
|
|
@@ -362,4 +362,10 @@ export {
|
|
|
362
362
|
type ReporterFormat,
|
|
363
363
|
type ReportSummary,
|
|
364
364
|
type FormatOptions,
|
|
365
|
-
} from "./reporter";
|
|
365
|
+
} from "./reporter";
|
|
366
|
+
|
|
367
|
+
export {
|
|
368
|
+
mergeAndWriteLcov,
|
|
369
|
+
type LcovMergeInput,
|
|
370
|
+
type LcovMergeSummary,
|
|
371
|
+
} from "./lcov";
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
export interface LcovMergeInput {
|
|
5
|
+
label: string;
|
|
6
|
+
source: { kind: "file"; path: string } | { kind: "text"; body: string };
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
interface LcovRecord {
|
|
10
|
+
sourceFile: string;
|
|
11
|
+
lines: Map<number, number>;
|
|
12
|
+
functions: Map<string, number>;
|
|
13
|
+
functionLines: Map<string, number>;
|
|
14
|
+
branches: Map<string, number>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface LcovMergeSummary {
|
|
18
|
+
files: number;
|
|
19
|
+
linesFound: number;
|
|
20
|
+
linesHit: number;
|
|
21
|
+
branchesFound: number;
|
|
22
|
+
branchesHit: number;
|
|
23
|
+
functionsFound: number;
|
|
24
|
+
functionsHit: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function createRecord(sourceFile: string): LcovRecord {
|
|
28
|
+
return {
|
|
29
|
+
sourceFile,
|
|
30
|
+
lines: new Map(),
|
|
31
|
+
functions: new Map(),
|
|
32
|
+
functionLines: new Map(),
|
|
33
|
+
branches: new Map(),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function add(map: Map<number, number>, key: number, value: number): void;
|
|
38
|
+
function add(map: Map<string, number>, key: string, value: number): void;
|
|
39
|
+
function add(
|
|
40
|
+
map: Map<number, number> | Map<string, number>,
|
|
41
|
+
key: number | string,
|
|
42
|
+
value: number,
|
|
43
|
+
): void {
|
|
44
|
+
const target = map as Map<number | string, number>;
|
|
45
|
+
target.set(key, (target.get(key) ?? 0) + value);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function ingestLcov(body: string, records: Map<string, LcovRecord>): void {
|
|
49
|
+
let current: LcovRecord | null = null;
|
|
50
|
+
|
|
51
|
+
for (const rawLine of body.split(/\r?\n/)) {
|
|
52
|
+
const line = rawLine.trim();
|
|
53
|
+
if (line.startsWith("SF:")) {
|
|
54
|
+
const sourceFile = line.slice(3);
|
|
55
|
+
current = records.get(sourceFile) ?? createRecord(sourceFile);
|
|
56
|
+
records.set(sourceFile, current);
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
if (!current) continue;
|
|
60
|
+
if (line === "end_of_record") {
|
|
61
|
+
current = null;
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
if (line.startsWith("DA:")) {
|
|
65
|
+
const [lineNumber, hits] = line.slice(3).split(",").map(Number);
|
|
66
|
+
if (Number.isFinite(lineNumber) && Number.isFinite(hits)) {
|
|
67
|
+
add(current.lines, lineNumber!, hits!);
|
|
68
|
+
}
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (line.startsWith("FN:")) {
|
|
72
|
+
const separator = line.indexOf(",", 3);
|
|
73
|
+
if (separator > 3) {
|
|
74
|
+
const lineNumber = Number(line.slice(3, separator));
|
|
75
|
+
const name = line.slice(separator + 1);
|
|
76
|
+
if (Number.isFinite(lineNumber) && name) current.functionLines.set(name, lineNumber);
|
|
77
|
+
}
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
if (line.startsWith("FNDA:")) {
|
|
81
|
+
const separator = line.indexOf(",", 5);
|
|
82
|
+
if (separator > 5) {
|
|
83
|
+
const hits = Number(line.slice(5, separator));
|
|
84
|
+
const name = line.slice(separator + 1);
|
|
85
|
+
if (Number.isFinite(hits) && name) add(current.functions, name, hits);
|
|
86
|
+
}
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
if (line.startsWith("BRDA:")) {
|
|
90
|
+
const parts = line.slice(5).split(",");
|
|
91
|
+
if (parts.length >= 4) {
|
|
92
|
+
const key = parts.slice(0, 3).join(",");
|
|
93
|
+
const hits = parts[3] === "-" ? 0 : Number(parts[3]);
|
|
94
|
+
if (Number.isFinite(hits)) add(current.branches, key, hits);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function renderRecord(record: LcovRecord): string {
|
|
101
|
+
const lines = [`SF:${record.sourceFile}`];
|
|
102
|
+
const functionNames = new Set([
|
|
103
|
+
...record.functionLines.keys(),
|
|
104
|
+
...record.functions.keys(),
|
|
105
|
+
]);
|
|
106
|
+
|
|
107
|
+
for (const name of [...functionNames].sort()) {
|
|
108
|
+
const line = record.functionLines.get(name);
|
|
109
|
+
if (line !== undefined) lines.push(`FN:${line},${name}`);
|
|
110
|
+
}
|
|
111
|
+
for (const name of [...functionNames].sort()) {
|
|
112
|
+
lines.push(`FNDA:${record.functions.get(name) ?? 0},${name}`);
|
|
113
|
+
}
|
|
114
|
+
lines.push(`FNF:${functionNames.size}`);
|
|
115
|
+
lines.push(`FNH:${[...functionNames].filter((name) => (record.functions.get(name) ?? 0) > 0).length}`);
|
|
116
|
+
|
|
117
|
+
for (const key of [...record.branches.keys()].sort()) {
|
|
118
|
+
lines.push(`BRDA:${key},${record.branches.get(key) ?? 0}`);
|
|
119
|
+
}
|
|
120
|
+
lines.push(`BRF:${record.branches.size}`);
|
|
121
|
+
lines.push(`BRH:${[...record.branches.values()].filter((hits) => hits > 0).length}`);
|
|
122
|
+
|
|
123
|
+
for (const lineNumber of [...record.lines.keys()].sort((left, right) => left - right)) {
|
|
124
|
+
lines.push(`DA:${lineNumber},${record.lines.get(lineNumber) ?? 0}`);
|
|
125
|
+
}
|
|
126
|
+
lines.push(`LF:${record.lines.size}`);
|
|
127
|
+
lines.push(`LH:${[...record.lines.values()].filter((hits) => hits > 0).length}`);
|
|
128
|
+
lines.push("end_of_record");
|
|
129
|
+
return lines.join("\n");
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function summarize(records: Map<string, LcovRecord>): LcovMergeSummary {
|
|
133
|
+
const summary: LcovMergeSummary = {
|
|
134
|
+
files: records.size,
|
|
135
|
+
linesFound: 0,
|
|
136
|
+
linesHit: 0,
|
|
137
|
+
branchesFound: 0,
|
|
138
|
+
branchesHit: 0,
|
|
139
|
+
functionsFound: 0,
|
|
140
|
+
functionsHit: 0,
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
for (const record of records.values()) {
|
|
144
|
+
const functionNames = new Set([
|
|
145
|
+
...record.functionLines.keys(),
|
|
146
|
+
...record.functions.keys(),
|
|
147
|
+
]);
|
|
148
|
+
summary.linesFound += record.lines.size;
|
|
149
|
+
summary.linesHit += [...record.lines.values()].filter((hits) => hits > 0).length;
|
|
150
|
+
summary.branchesFound += record.branches.size;
|
|
151
|
+
summary.branchesHit += [...record.branches.values()].filter((hits) => hits > 0).length;
|
|
152
|
+
summary.functionsFound += functionNames.size;
|
|
153
|
+
summary.functionsHit += [...functionNames].filter((name) => (record.functions.get(name) ?? 0) > 0).length;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return summary;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Merge LCOV sources into the canonical Mandu coverage artifact. This lives
|
|
161
|
+
* in Core so the product CLI does not need the optional ATE Labs package for
|
|
162
|
+
* ordinary unit/integration coverage.
|
|
163
|
+
*/
|
|
164
|
+
export function mergeAndWriteLcov(params: {
|
|
165
|
+
repoRoot: string;
|
|
166
|
+
inputs: LcovMergeInput[];
|
|
167
|
+
outputPath?: string;
|
|
168
|
+
}): { summary: LcovMergeSummary; outputPath: string | null } {
|
|
169
|
+
const records = new Map<string, LcovRecord>();
|
|
170
|
+
|
|
171
|
+
for (const input of params.inputs) {
|
|
172
|
+
if (input.source.kind === "file" && !fs.existsSync(input.source.path)) continue;
|
|
173
|
+
const body = input.source.kind === "file"
|
|
174
|
+
? fs.readFileSync(input.source.path, "utf8")
|
|
175
|
+
: input.source.body;
|
|
176
|
+
ingestLcov(body, records);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const summary = summarize(records);
|
|
180
|
+
if (records.size === 0) return { summary, outputPath: null };
|
|
181
|
+
|
|
182
|
+
const outputPath = path.resolve(
|
|
183
|
+
params.outputPath ?? path.join(params.repoRoot, ".mandu", "coverage", "lcov.info"),
|
|
184
|
+
);
|
|
185
|
+
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
|
|
186
|
+
const body = [...records.values()]
|
|
187
|
+
.sort((left, right) => left.sourceFile.localeCompare(right.sourceFile))
|
|
188
|
+
.map(renderRecord)
|
|
189
|
+
.join("\n");
|
|
190
|
+
fs.writeFileSync(outputPath, `${body}\n`, "utf8");
|
|
191
|
+
return { summary, outputPath };
|
|
192
|
+
}
|