@mandujs/core 0.53.3 → 0.54.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 +1 -1
- 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 -451
- package/src/diagnose/checks.ts +832 -832
- package/src/diagnose/index.ts +17 -17
- package/src/diagnose/run.ts +93 -93
- 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/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,248 +1,248 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @mandujs/core/desktop — window factory tests
|
|
3
|
-
*
|
|
4
|
-
* These tests run **without actually loading `webview-bun`**: we either
|
|
5
|
-
* stub the optional peer via a mocked import cache or exercise option
|
|
6
|
-
* validation / size-hint mapping / error paths that never need the FFI
|
|
7
|
-
* peer at all. CI does not have WebView2 / WKWebView available, so any
|
|
8
|
-
* test that would open a real window is marked describe.skipIf.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { describe, it, expect, beforeEach } from "bun:test";
|
|
12
|
-
import {
|
|
13
|
-
_DEFAULTS,
|
|
14
|
-
_mapSizeHint,
|
|
15
|
-
_resetWebviewBunCache,
|
|
16
|
-
_validateOptions,
|
|
17
|
-
createWindow,
|
|
18
|
-
} from "../window";
|
|
19
|
-
import type { WindowOptions } from "../types";
|
|
20
|
-
|
|
21
|
-
describe("@mandujs/core/desktop — types & validation", () => {
|
|
22
|
-
it("_DEFAULTS match the documented contract", () => {
|
|
23
|
-
expect(_DEFAULTS.title).toBe("Mandu Desktop");
|
|
24
|
-
expect(_DEFAULTS.width).toBe(1024);
|
|
25
|
-
expect(_DEFAULTS.height).toBe(768);
|
|
26
|
-
expect(_DEFAULTS.hint).toBe("none");
|
|
27
|
-
expect(_DEFAULTS.debug).toBe(false);
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it("_mapSizeHint translates strings to peer enum values", () => {
|
|
31
|
-
const fakeEnum = { NONE: 0, MIN: 1, MAX: 2, FIXED: 3 };
|
|
32
|
-
expect(_mapSizeHint("none", fakeEnum)).toBe(0);
|
|
33
|
-
expect(_mapSizeHint("min", fakeEnum)).toBe(1);
|
|
34
|
-
expect(_mapSizeHint("max", fakeEnum)).toBe(2);
|
|
35
|
-
expect(_mapSizeHint("fixed", fakeEnum)).toBe(3);
|
|
36
|
-
expect(_mapSizeHint(undefined, fakeEnum)).toBe(0); // default → NONE
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it("_validateOptions rejects missing url", () => {
|
|
40
|
-
expect(() => _validateOptions({} as WindowOptions)).toThrow(/url/);
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it("_validateOptions rejects non-string url", () => {
|
|
44
|
-
expect(() =>
|
|
45
|
-
_validateOptions({ url: 123 as unknown as string }),
|
|
46
|
-
).toThrow(/url/);
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it("_validateOptions rejects malformed url", () => {
|
|
50
|
-
expect(() => _validateOptions({ url: "not a url" })).toThrow(/valid URL/);
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
it("_validateOptions rejects forbidden protocols", () => {
|
|
54
|
-
expect(() =>
|
|
55
|
-
_validateOptions({ url: "javascript:alert(1)" }),
|
|
56
|
-
).toThrow(/protocol/);
|
|
57
|
-
expect(() =>
|
|
58
|
-
_validateOptions({ url: "chrome://version" }),
|
|
59
|
-
).toThrow(/protocol/);
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it("_validateOptions accepts http, https, file, and data URLs", () => {
|
|
63
|
-
expect(() =>
|
|
64
|
-
_validateOptions({ url: "http://127.0.0.1:3333/" }),
|
|
65
|
-
).not.toThrow();
|
|
66
|
-
expect(() =>
|
|
67
|
-
_validateOptions({ url: "https://example.com/" }),
|
|
68
|
-
).not.toThrow();
|
|
69
|
-
expect(() =>
|
|
70
|
-
_validateOptions({ url: "file:///tmp/index.html" }),
|
|
71
|
-
).not.toThrow();
|
|
72
|
-
expect(() =>
|
|
73
|
-
_validateOptions({ url: "data:text/html,<h1>hi</h1>" }),
|
|
74
|
-
).not.toThrow();
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
it("_validateOptions rejects non-positive width/height", () => {
|
|
78
|
-
expect(() =>
|
|
79
|
-
_validateOptions({ url: "http://x", width: 0 }),
|
|
80
|
-
).toThrow(/width/);
|
|
81
|
-
expect(() =>
|
|
82
|
-
_validateOptions({ url: "http://x", height: -50 }),
|
|
83
|
-
).toThrow(/height/);
|
|
84
|
-
expect(() =>
|
|
85
|
-
_validateOptions({
|
|
86
|
-
url: "http://x",
|
|
87
|
-
width: Number.NaN,
|
|
88
|
-
}),
|
|
89
|
-
).toThrow(/width/);
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
it("_validateOptions rejects unknown hint values", () => {
|
|
93
|
-
expect(() =>
|
|
94
|
-
_validateOptions({
|
|
95
|
-
url: "http://x",
|
|
96
|
-
hint: "resizable" as never,
|
|
97
|
-
}),
|
|
98
|
-
).toThrow(/hint/);
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
it("_validateOptions rejects non-function handlers", () => {
|
|
102
|
-
expect(() =>
|
|
103
|
-
_validateOptions({
|
|
104
|
-
url: "http://x",
|
|
105
|
-
handlers: { foo: "not a function" as unknown as () => void },
|
|
106
|
-
}),
|
|
107
|
-
).toThrow(/handlers\.foo/);
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
it("_validateOptions accepts a fully-specified options bag", () => {
|
|
111
|
-
expect(() =>
|
|
112
|
-
_validateOptions({
|
|
113
|
-
url: "http://127.0.0.1:3333/",
|
|
114
|
-
title: "Test",
|
|
115
|
-
width: 1280,
|
|
116
|
-
height: 800,
|
|
117
|
-
hint: "fixed",
|
|
118
|
-
debug: true,
|
|
119
|
-
handlers: { greet: () => "hi" },
|
|
120
|
-
}),
|
|
121
|
-
).not.toThrow();
|
|
122
|
-
});
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
describe("@mandujs/core/desktop — createWindow peer loading", () => {
|
|
126
|
-
beforeEach(() => {
|
|
127
|
-
_resetWebviewBunCache();
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
it("validates options before attempting to load the peer", async () => {
|
|
131
|
-
// Bad options should throw TypeError *before* the peer import runs, so
|
|
132
|
-
// this test passes even on CI without webview-bun installed.
|
|
133
|
-
await expect(
|
|
134
|
-
createWindow({ url: "" } as WindowOptions),
|
|
135
|
-
).rejects.toThrow(TypeError);
|
|
136
|
-
await expect(
|
|
137
|
-
createWindow({} as WindowOptions),
|
|
138
|
-
).rejects.toThrow(TypeError);
|
|
139
|
-
await expect(
|
|
140
|
-
createWindow({
|
|
141
|
-
url: "javascript:evil()",
|
|
142
|
-
} as WindowOptions),
|
|
143
|
-
).rejects.toThrow(/protocol/);
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
it("throws an actionable error when webview-bun is not installed", async () => {
|
|
147
|
-
// Only run this when the peer is genuinely absent — if a developer has
|
|
148
|
-
// `webview-bun` installed locally, the import will succeed and we'd
|
|
149
|
-
// open a real window. Probe via dynamic import.
|
|
150
|
-
//
|
|
151
|
-
// @ts-ignore -- optional peer, resolution may fail at typecheck time
|
|
152
|
-
const probe = () => import("webview-bun");
|
|
153
|
-
let peerInstalled = false;
|
|
154
|
-
try {
|
|
155
|
-
await probe();
|
|
156
|
-
peerInstalled = true;
|
|
157
|
-
} catch {
|
|
158
|
-
peerInstalled = false;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
if (peerInstalled) {
|
|
162
|
-
// On Windows with the peer installed, we'd end up creating a real
|
|
163
|
-
// window. Skip the assertion — the actionable-error guarantee is only
|
|
164
|
-
// meaningful when the peer is missing.
|
|
165
|
-
return;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// Ensure the FFI fallback also can't resolve — otherwise `createWindow`
|
|
169
|
-
// silently flips to the FFI path. We force an unreachable library path
|
|
170
|
-
// so both paths deterministically fail, which is the state the
|
|
171
|
-
// actionable error contract guards.
|
|
172
|
-
const originalFFIPath = process.env.MANDU_LIBWEBVIEW_PATH;
|
|
173
|
-
process.env.MANDU_LIBWEBVIEW_PATH =
|
|
174
|
-
"/path/that/does/not/exist/libwebview.so";
|
|
175
|
-
try {
|
|
176
|
-
await expect(
|
|
177
|
-
createWindow({ url: "http://127.0.0.1:1" }),
|
|
178
|
-
).rejects.toThrow(/webview-bun/);
|
|
179
|
-
} finally {
|
|
180
|
-
if (originalFFIPath !== undefined) {
|
|
181
|
-
process.env.MANDU_LIBWEBVIEW_PATH = originalFFIPath;
|
|
182
|
-
} else {
|
|
183
|
-
delete process.env.MANDU_LIBWEBVIEW_PATH;
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
it("MANDU_DESKTOP_INLINE_FFI=1 routes to the FFI fallback path", async () => {
|
|
189
|
-
// With the env flag set, `createWindow` must import
|
|
190
|
-
// `./webview-fallback` instead of `webview-bun`. We don't assert on
|
|
191
|
-
// the exact success/failure of the FFI call (that depends on the
|
|
192
|
-
// machine's libwebview install state) — we only assert that the
|
|
193
|
-
// branch is taken. The simplest observable is: when the library
|
|
194
|
-
// actually loads AND the test machine has a webview backend
|
|
195
|
-
// (WebView2 on Windows etc.), a real window would open. To stay
|
|
196
|
-
// CI-safe we monkey-patch dynamic import of the fallback module to
|
|
197
|
-
// detect invocation.
|
|
198
|
-
//
|
|
199
|
-
// Bun's ESM cache makes this tricky — instead we verify the call
|
|
200
|
-
// selector by checking that `createWindow` does NOT throw the
|
|
201
|
-
// webview-bun "install me" error when the flag is set. That error
|
|
202
|
-
// is only reachable via the primary `_loadWebviewBun` path; if the
|
|
203
|
-
// FFI branch is skipped or the webview-bun import is tried in its
|
|
204
|
-
// place, the test would observe the install-me error.
|
|
205
|
-
const originalFlag = process.env.MANDU_DESKTOP_INLINE_FFI;
|
|
206
|
-
const originalFFIPath = process.env.MANDU_LIBWEBVIEW_PATH;
|
|
207
|
-
process.env.MANDU_DESKTOP_INLINE_FFI = "1";
|
|
208
|
-
// Force an unreachable libwebview so the fallback fails deterministically
|
|
209
|
-
// and the error surfaces the fallback's distinctive language (which
|
|
210
|
-
// is what we assert on).
|
|
211
|
-
process.env.MANDU_LIBWEBVIEW_PATH = "/nonexistent/libwebview-test-stub.so";
|
|
212
|
-
|
|
213
|
-
try {
|
|
214
|
-
// Resolve whatever error comes back and assert it's NOT the
|
|
215
|
-
// webview-bun peer-missing error. The exact failure mode of the
|
|
216
|
-
// FFI path varies by CI machine (libwebview missing, dlopen
|
|
217
|
-
// succeeds on a stub, or the FFI cstring call errors), but none
|
|
218
|
-
// of those produce the distinctive "bun add webview-bun" string
|
|
219
|
-
// from the primary path.
|
|
220
|
-
let caught: unknown;
|
|
221
|
-
try {
|
|
222
|
-
const handle = await createWindow({ url: "http://127.0.0.1:1" });
|
|
223
|
-
// Unexpected success — close to clean up, then fail.
|
|
224
|
-
await handle.close();
|
|
225
|
-
throw new Error(
|
|
226
|
-
"unreachable — createWindow should fail with MANDU_DESKTOP_INLINE_FFI=1 + bogus MANDU_LIBWEBVIEW_PATH",
|
|
227
|
-
);
|
|
228
|
-
} catch (err) {
|
|
229
|
-
caught = err;
|
|
230
|
-
}
|
|
231
|
-
const msg = caught instanceof Error ? caught.message : String(caught);
|
|
232
|
-
// The webview-bun install-me message always mentions "bun add webview-bun".
|
|
233
|
-
// The fallback path's failure never does.
|
|
234
|
-
expect(msg).not.toContain("bun add webview-bun");
|
|
235
|
-
} finally {
|
|
236
|
-
if (originalFlag !== undefined) {
|
|
237
|
-
process.env.MANDU_DESKTOP_INLINE_FFI = originalFlag;
|
|
238
|
-
} else {
|
|
239
|
-
delete process.env.MANDU_DESKTOP_INLINE_FFI;
|
|
240
|
-
}
|
|
241
|
-
if (originalFFIPath !== undefined) {
|
|
242
|
-
process.env.MANDU_LIBWEBVIEW_PATH = originalFFIPath;
|
|
243
|
-
} else {
|
|
244
|
-
delete process.env.MANDU_LIBWEBVIEW_PATH;
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
});
|
|
248
|
-
});
|
|
1
|
+
/**
|
|
2
|
+
* @mandujs/core/desktop — window factory tests
|
|
3
|
+
*
|
|
4
|
+
* These tests run **without actually loading `webview-bun`**: we either
|
|
5
|
+
* stub the optional peer via a mocked import cache or exercise option
|
|
6
|
+
* validation / size-hint mapping / error paths that never need the FFI
|
|
7
|
+
* peer at all. CI does not have WebView2 / WKWebView available, so any
|
|
8
|
+
* test that would open a real window is marked describe.skipIf.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { describe, it, expect, beforeEach } from "bun:test";
|
|
12
|
+
import {
|
|
13
|
+
_DEFAULTS,
|
|
14
|
+
_mapSizeHint,
|
|
15
|
+
_resetWebviewBunCache,
|
|
16
|
+
_validateOptions,
|
|
17
|
+
createWindow,
|
|
18
|
+
} from "../window";
|
|
19
|
+
import type { WindowOptions } from "../types";
|
|
20
|
+
|
|
21
|
+
describe("@mandujs/core/desktop — types & validation", () => {
|
|
22
|
+
it("_DEFAULTS match the documented contract", () => {
|
|
23
|
+
expect(_DEFAULTS.title).toBe("Mandu Desktop");
|
|
24
|
+
expect(_DEFAULTS.width).toBe(1024);
|
|
25
|
+
expect(_DEFAULTS.height).toBe(768);
|
|
26
|
+
expect(_DEFAULTS.hint).toBe("none");
|
|
27
|
+
expect(_DEFAULTS.debug).toBe(false);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("_mapSizeHint translates strings to peer enum values", () => {
|
|
31
|
+
const fakeEnum = { NONE: 0, MIN: 1, MAX: 2, FIXED: 3 };
|
|
32
|
+
expect(_mapSizeHint("none", fakeEnum)).toBe(0);
|
|
33
|
+
expect(_mapSizeHint("min", fakeEnum)).toBe(1);
|
|
34
|
+
expect(_mapSizeHint("max", fakeEnum)).toBe(2);
|
|
35
|
+
expect(_mapSizeHint("fixed", fakeEnum)).toBe(3);
|
|
36
|
+
expect(_mapSizeHint(undefined, fakeEnum)).toBe(0); // default → NONE
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("_validateOptions rejects missing url", () => {
|
|
40
|
+
expect(() => _validateOptions({} as WindowOptions)).toThrow(/url/);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("_validateOptions rejects non-string url", () => {
|
|
44
|
+
expect(() =>
|
|
45
|
+
_validateOptions({ url: 123 as unknown as string }),
|
|
46
|
+
).toThrow(/url/);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("_validateOptions rejects malformed url", () => {
|
|
50
|
+
expect(() => _validateOptions({ url: "not a url" })).toThrow(/valid URL/);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("_validateOptions rejects forbidden protocols", () => {
|
|
54
|
+
expect(() =>
|
|
55
|
+
_validateOptions({ url: "javascript:alert(1)" }),
|
|
56
|
+
).toThrow(/protocol/);
|
|
57
|
+
expect(() =>
|
|
58
|
+
_validateOptions({ url: "chrome://version" }),
|
|
59
|
+
).toThrow(/protocol/);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("_validateOptions accepts http, https, file, and data URLs", () => {
|
|
63
|
+
expect(() =>
|
|
64
|
+
_validateOptions({ url: "http://127.0.0.1:3333/" }),
|
|
65
|
+
).not.toThrow();
|
|
66
|
+
expect(() =>
|
|
67
|
+
_validateOptions({ url: "https://example.com/" }),
|
|
68
|
+
).not.toThrow();
|
|
69
|
+
expect(() =>
|
|
70
|
+
_validateOptions({ url: "file:///tmp/index.html" }),
|
|
71
|
+
).not.toThrow();
|
|
72
|
+
expect(() =>
|
|
73
|
+
_validateOptions({ url: "data:text/html,<h1>hi</h1>" }),
|
|
74
|
+
).not.toThrow();
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("_validateOptions rejects non-positive width/height", () => {
|
|
78
|
+
expect(() =>
|
|
79
|
+
_validateOptions({ url: "http://x", width: 0 }),
|
|
80
|
+
).toThrow(/width/);
|
|
81
|
+
expect(() =>
|
|
82
|
+
_validateOptions({ url: "http://x", height: -50 }),
|
|
83
|
+
).toThrow(/height/);
|
|
84
|
+
expect(() =>
|
|
85
|
+
_validateOptions({
|
|
86
|
+
url: "http://x",
|
|
87
|
+
width: Number.NaN,
|
|
88
|
+
}),
|
|
89
|
+
).toThrow(/width/);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it("_validateOptions rejects unknown hint values", () => {
|
|
93
|
+
expect(() =>
|
|
94
|
+
_validateOptions({
|
|
95
|
+
url: "http://x",
|
|
96
|
+
hint: "resizable" as never,
|
|
97
|
+
}),
|
|
98
|
+
).toThrow(/hint/);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it("_validateOptions rejects non-function handlers", () => {
|
|
102
|
+
expect(() =>
|
|
103
|
+
_validateOptions({
|
|
104
|
+
url: "http://x",
|
|
105
|
+
handlers: { foo: "not a function" as unknown as () => void },
|
|
106
|
+
}),
|
|
107
|
+
).toThrow(/handlers\.foo/);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it("_validateOptions accepts a fully-specified options bag", () => {
|
|
111
|
+
expect(() =>
|
|
112
|
+
_validateOptions({
|
|
113
|
+
url: "http://127.0.0.1:3333/",
|
|
114
|
+
title: "Test",
|
|
115
|
+
width: 1280,
|
|
116
|
+
height: 800,
|
|
117
|
+
hint: "fixed",
|
|
118
|
+
debug: true,
|
|
119
|
+
handlers: { greet: () => "hi" },
|
|
120
|
+
}),
|
|
121
|
+
).not.toThrow();
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
describe("@mandujs/core/desktop — createWindow peer loading", () => {
|
|
126
|
+
beforeEach(() => {
|
|
127
|
+
_resetWebviewBunCache();
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it("validates options before attempting to load the peer", async () => {
|
|
131
|
+
// Bad options should throw TypeError *before* the peer import runs, so
|
|
132
|
+
// this test passes even on CI without webview-bun installed.
|
|
133
|
+
await expect(
|
|
134
|
+
createWindow({ url: "" } as WindowOptions),
|
|
135
|
+
).rejects.toThrow(TypeError);
|
|
136
|
+
await expect(
|
|
137
|
+
createWindow({} as WindowOptions),
|
|
138
|
+
).rejects.toThrow(TypeError);
|
|
139
|
+
await expect(
|
|
140
|
+
createWindow({
|
|
141
|
+
url: "javascript:evil()",
|
|
142
|
+
} as WindowOptions),
|
|
143
|
+
).rejects.toThrow(/protocol/);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it("throws an actionable error when webview-bun is not installed", async () => {
|
|
147
|
+
// Only run this when the peer is genuinely absent — if a developer has
|
|
148
|
+
// `webview-bun` installed locally, the import will succeed and we'd
|
|
149
|
+
// open a real window. Probe via dynamic import.
|
|
150
|
+
//
|
|
151
|
+
// @ts-ignore -- optional peer, resolution may fail at typecheck time
|
|
152
|
+
const probe = () => import("webview-bun");
|
|
153
|
+
let peerInstalled = false;
|
|
154
|
+
try {
|
|
155
|
+
await probe();
|
|
156
|
+
peerInstalled = true;
|
|
157
|
+
} catch {
|
|
158
|
+
peerInstalled = false;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (peerInstalled) {
|
|
162
|
+
// On Windows with the peer installed, we'd end up creating a real
|
|
163
|
+
// window. Skip the assertion — the actionable-error guarantee is only
|
|
164
|
+
// meaningful when the peer is missing.
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Ensure the FFI fallback also can't resolve — otherwise `createWindow`
|
|
169
|
+
// silently flips to the FFI path. We force an unreachable library path
|
|
170
|
+
// so both paths deterministically fail, which is the state the
|
|
171
|
+
// actionable error contract guards.
|
|
172
|
+
const originalFFIPath = process.env.MANDU_LIBWEBVIEW_PATH;
|
|
173
|
+
process.env.MANDU_LIBWEBVIEW_PATH =
|
|
174
|
+
"/path/that/does/not/exist/libwebview.so";
|
|
175
|
+
try {
|
|
176
|
+
await expect(
|
|
177
|
+
createWindow({ url: "http://127.0.0.1:1" }),
|
|
178
|
+
).rejects.toThrow(/webview-bun/);
|
|
179
|
+
} finally {
|
|
180
|
+
if (originalFFIPath !== undefined) {
|
|
181
|
+
process.env.MANDU_LIBWEBVIEW_PATH = originalFFIPath;
|
|
182
|
+
} else {
|
|
183
|
+
delete process.env.MANDU_LIBWEBVIEW_PATH;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it("MANDU_DESKTOP_INLINE_FFI=1 routes to the FFI fallback path", async () => {
|
|
189
|
+
// With the env flag set, `createWindow` must import
|
|
190
|
+
// `./webview-fallback` instead of `webview-bun`. We don't assert on
|
|
191
|
+
// the exact success/failure of the FFI call (that depends on the
|
|
192
|
+
// machine's libwebview install state) — we only assert that the
|
|
193
|
+
// branch is taken. The simplest observable is: when the library
|
|
194
|
+
// actually loads AND the test machine has a webview backend
|
|
195
|
+
// (WebView2 on Windows etc.), a real window would open. To stay
|
|
196
|
+
// CI-safe we monkey-patch dynamic import of the fallback module to
|
|
197
|
+
// detect invocation.
|
|
198
|
+
//
|
|
199
|
+
// Bun's ESM cache makes this tricky — instead we verify the call
|
|
200
|
+
// selector by checking that `createWindow` does NOT throw the
|
|
201
|
+
// webview-bun "install me" error when the flag is set. That error
|
|
202
|
+
// is only reachable via the primary `_loadWebviewBun` path; if the
|
|
203
|
+
// FFI branch is skipped or the webview-bun import is tried in its
|
|
204
|
+
// place, the test would observe the install-me error.
|
|
205
|
+
const originalFlag = process.env.MANDU_DESKTOP_INLINE_FFI;
|
|
206
|
+
const originalFFIPath = process.env.MANDU_LIBWEBVIEW_PATH;
|
|
207
|
+
process.env.MANDU_DESKTOP_INLINE_FFI = "1";
|
|
208
|
+
// Force an unreachable libwebview so the fallback fails deterministically
|
|
209
|
+
// and the error surfaces the fallback's distinctive language (which
|
|
210
|
+
// is what we assert on).
|
|
211
|
+
process.env.MANDU_LIBWEBVIEW_PATH = "/nonexistent/libwebview-test-stub.so";
|
|
212
|
+
|
|
213
|
+
try {
|
|
214
|
+
// Resolve whatever error comes back and assert it's NOT the
|
|
215
|
+
// webview-bun peer-missing error. The exact failure mode of the
|
|
216
|
+
// FFI path varies by CI machine (libwebview missing, dlopen
|
|
217
|
+
// succeeds on a stub, or the FFI cstring call errors), but none
|
|
218
|
+
// of those produce the distinctive "bun add webview-bun" string
|
|
219
|
+
// from the primary path.
|
|
220
|
+
let caught: unknown;
|
|
221
|
+
try {
|
|
222
|
+
const handle = await createWindow({ url: "http://127.0.0.1:1" });
|
|
223
|
+
// Unexpected success — close to clean up, then fail.
|
|
224
|
+
await handle.close();
|
|
225
|
+
throw new Error(
|
|
226
|
+
"unreachable — createWindow should fail with MANDU_DESKTOP_INLINE_FFI=1 + bogus MANDU_LIBWEBVIEW_PATH",
|
|
227
|
+
);
|
|
228
|
+
} catch (err) {
|
|
229
|
+
caught = err;
|
|
230
|
+
}
|
|
231
|
+
const msg = caught instanceof Error ? caught.message : String(caught);
|
|
232
|
+
// The webview-bun install-me message always mentions "bun add webview-bun".
|
|
233
|
+
// The fallback path's failure never does.
|
|
234
|
+
expect(msg).not.toContain("bun add webview-bun");
|
|
235
|
+
} finally {
|
|
236
|
+
if (originalFlag !== undefined) {
|
|
237
|
+
process.env.MANDU_DESKTOP_INLINE_FFI = originalFlag;
|
|
238
|
+
} else {
|
|
239
|
+
delete process.env.MANDU_DESKTOP_INLINE_FFI;
|
|
240
|
+
}
|
|
241
|
+
if (originalFFIPath !== undefined) {
|
|
242
|
+
process.env.MANDU_LIBWEBVIEW_PATH = originalFFIPath;
|
|
243
|
+
} else {
|
|
244
|
+
delete process.env.MANDU_LIBWEBVIEW_PATH;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
});
|