| author | |
| committer | |
| log | 683aa87096899aa191e150429415ecc78966f538 |
| tree | 7c9414892116ee106b0f6c44e025590d3dc9afca |
| parent | d81278aa6b272bb3d3314266c6d17f22504d19d6 |
| signature | Signed by PGP key B5690EEEBB952194 |
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>4 files changed, 142 insertions(+), 45 deletions(-)
apps/desktop/vite.config.ts+12-1| ... | ... | @@ -1,9 +1,18 @@ |
| 1 | 1 | import "vite-plus/test/config"; |
| 2 | 2 | import { defineConfig } from "vite-plus"; |
| 3 | 3 | |
| 4 | import { isDesktopRuntimeExternalDependency } from "../../scripts/lib/desktop-external-packages.ts"; | |
| 4 | 5 | import { loadRepoEnv } from "../../scripts/lib/public-config.ts"; |
| 5 | 6 | |
| 6 | 7 | const repoEnv = loadRepoEnv(); |
| 8 | ||
| 9 | // The main process is bundled the same way the server CLI is: every JS | |
| 10 | // dependency is inlined and only packages Node must load from disk stay | |
| 11 | // external. The packaged app then installs just those externals, instead of a | |
| 12 | // full production install of apps/desktop's dependency tree next to a server | |
| 13 | // bundle that already carries its own copy of the same libraries. | |
| 14 | const isMainProcessExternal = (id: string) => | |
| 15 | id === "electron" || id.startsWith("electron/") || isDesktopRuntimeExternalDependency(id); | |
| 7 | 16 | const shouldLaunchElectronAfterPack = process.env.T3CODE_DESKTOP_DEV === "1"; |
| 8 | 17 | const publicConfigDefine = { |
| 9 | 18 | __T3CODE_BUILD_CLERK_PUBLISHABLE_KEY__: JSON.stringify( |
| ... | ... | @@ -55,7 +64,9 @@ export default defineConfig({ |
| 55 | 64 | ], |
| 56 | 65 | clean: true, |
| 57 | 66 | deps: { |
| 58 | alwaysBundle: (id) => id.startsWith("@t3tools/"), | |
| 67 | alwaysBundle: (id) => !id.startsWith("node:") && !isMainProcessExternal(id), | |
| 68 | neverBundle: isMainProcessExternal, | |
| 69 | onlyBundle: false, | |
| 59 | 70 | }, |
| 60 | 71 | ...(shouldLaunchElectronAfterPack ? { onSuccess: "node scripts/dev-electron.mjs" } : {}), |
| 61 | 72 | }, |
scripts/build-desktop-artifact.test.ts+51-14| ... | ... | @@ -28,6 +28,7 @@ import { |
| 28 | 28 | DESKTOP_EXTRA_RESOURCES, |
| 29 | 29 | LINUX_CAPTURE_EXTRA_RESOURCES, |
| 30 | 30 | LINUX_BROWSER_SECRET_EXTRA_RESOURCES, |
| 31 | LINUX_FILE_EXCLUSIONS, | |
| 31 | 32 | MAC_FILE_EXCLUSIONS, |
| 32 | 33 | InvalidMacPasskeyRpDomainError, |
| 33 | 34 | InvalidMacPasskeyPublishableKeyError, |
| ... | ... | @@ -47,7 +48,7 @@ import { |
| 47 | 48 | resolveClerkPasskeyNativeArtifacts, |
| 48 | 49 | resolveMacPasskeySigningConfiguration, |
| 49 | 50 | resolveDesktopRuntimeDependencies, |
| 50 | resolveMacStageDependencies, | |
| 51 | resolveMergedStageDependencies, | |
| 51 | 52 | resolveFffNativeDependencies, |
| 52 | 53 | resolveBuildOptions, |
| 53 | 54 | resolveDesktopBuildIconAssets, |
| ... | ... | @@ -367,26 +368,37 @@ it.layer(NodeServices.layer)("build-desktop-artifact", (it) => { |
| 367 | 368 | ), |
| 368 | 369 | ); |
| 369 | 370 | |
| 370 | it("omits bundled workspace packages from staged desktop dependencies", () => { | |
| 371 | it("stages only the desktop main-process externals", () => { | |
| 371 | 372 | assert.deepStrictEqual( |
| 372 | 373 | resolveDesktopRuntimeDependencies( |
| 373 | 374 | { |
| 375 | "@clerk/electron": "catalog:", | |
| 376 | "@clerk/electron-passkeys": "catalog:", | |
| 377 | "@crowecawcaw/xa11y": "0.13.0", | |
| 374 | 378 | "@effect/platform-node": "catalog:", |
| 379 | "@napi-rs/keyring": "^1.3.0", | |
| 375 | 380 | "@t3tools/contracts": "workspace:*", |
| 376 | 381 | "@t3tools/shared": "workspace:*", |
| 377 | "@t3tools/ssh": "workspace:*", | |
| 378 | "@t3tools/tailscale": "workspace:*", | |
| 382 | "dbus-next": "0.10.2", | |
| 379 | 383 | effect: "catalog:", |
| 380 | 384 | electron: "41.5.0", |
| 385 | "electron-updater": "^6.6.2", | |
| 386 | "ffi-rs": "1.3.2", | |
| 387 | "playwright-core": "1.60.0", | |
| 381 | 388 | }, |
| 382 | 389 | { |
| 390 | "@clerk/electron": "0.0.37", | |
| 391 | "@clerk/electron-passkeys": "0.0.3", | |
| 383 | 392 | "@effect/platform-node": "4.0.0-beta.59", |
| 384 | 393 | effect: "4.0.0-beta.59", |
| 385 | 394 | }, |
| 386 | 395 | ), |
| 387 | 396 | { |
| 388 | "@effect/platform-node": "4.0.0-beta.59", | |
| 389 | effect: "4.0.0-beta.59", | |
| 397 | "@clerk/electron-passkeys": "0.0.3", | |
| 398 | "@crowecawcaw/xa11y": "0.13.0", | |
| 399 | "@napi-rs/keyring": "^1.3.0", | |
| 400 | "ffi-rs": "1.3.2", | |
| 401 | "playwright-core": "1.60.0", | |
| 390 | 402 | }, |
| 391 | 403 | ); |
| 392 | 404 | }); |
| ... | ... | @@ -559,6 +571,8 @@ it.layer(NodeServices.layer)("build-desktop-artifact", (it) => { |
| 559 | 571 | |
| 560 | 572 | assert.deepStrictEqual(DESKTOP_FILE_EXCLUSIONS, [ |
| 561 | 573 | "!**/node_modules/@anthropic-ai/claude-agent-sdk-*/**/*", |
| 574 | "!**/*.map", | |
| 575 | "!**/*.d.cts", | |
| 562 | 576 | "!apps/desktop/resources/browser-secret", |
| 563 | 577 | "!apps/desktop/resources/browser-secret/**/*", |
| 564 | 578 | "!apps/desktop/prod-resources/browser-secret", |
| ... | ... | @@ -661,6 +675,7 @@ it.layer(NodeServices.layer)("build-desktop-artifact", (it) => { |
| 661 | 675 | "**/node_modules/@anthropic-ai/claude-agent-sdk-*/**", |
| 662 | 676 | "**/node_modules/.bin", |
| 663 | 677 | "**/node_modules/.bin/**", |
| 678 | "**/*.map", | |
| 664 | 679 | ]); |
| 665 | 680 | assert.deepStrictEqual(mac.dmg, { |
| 666 | 681 | title: "T3 Code (Alpha) 1.2.3 Installer", |
| ... | ... | @@ -679,7 +694,7 @@ it.layer(NodeServices.layer)("build-desktop-artifact", (it) => { |
| 679 | 694 | { name: "T3 Code", schemes: ["t3code", "t3code-dev"] }, |
| 680 | 695 | ]); |
| 681 | 696 | assert.deepStrictEqual(mac.files, [...DESKTOP_FILE_EXCLUSIONS, ...MAC_FILE_EXCLUSIONS]); |
| 682 | assert.deepStrictEqual(linux.files, DESKTOP_FILE_EXCLUSIONS); | |
| 697 | assert.deepStrictEqual(linux.files, [...DESKTOP_FILE_EXCLUSIONS, ...LINUX_FILE_EXCLUSIONS]); | |
| 683 | 698 | assert.deepStrictEqual(win.files, DESKTOP_FILE_EXCLUSIONS); |
| 684 | 699 | assert.deepStrictEqual(winWithoutWslPrebuild.files, win.files); |
| 685 | 700 | assert.notProperty(mac.mac as Record<string, unknown>, "sign"); |
| ... | ... | @@ -690,11 +705,15 @@ it.layer(NodeServices.layer)("build-desktop-artifact", (it) => { |
| 690 | 705 | }).pipe(Effect.provide(ConfigProvider.layer(ConfigProvider.fromEnv({ env: {} })))), |
| 691 | 706 | ); |
| 692 | 707 | |
| 693 | it("excludes Windows terminal binaries only from macOS packages", () => { | |
| 708 | it("excludes foreign node-pty prebuilds from macOS and Linux packages", () => { | |
| 694 | 709 | assert.deepStrictEqual(MAC_FILE_EXCLUSIONS, [ |
| 695 | 710 | "!**/node_modules/node-pty/prebuilds/win32-*/**/*", |
| 696 | 711 | "!**/node_modules/node-pty/third_party/conpty/**/*", |
| 697 | 712 | ]); |
| 713 | assert.deepStrictEqual(LINUX_FILE_EXCLUSIONS, [ | |
| 714 | ...MAC_FILE_EXCLUSIONS, | |
| 715 | "!**/node_modules/node-pty/prebuilds/darwin-*/**/*", | |
| 716 | ]); | |
| 698 | 717 | }); |
| 699 | 718 | |
| 700 | 719 | it("unpacks native binaries while keeping their JavaScript and metadata archived", () => { |
| ... | ... | @@ -725,9 +744,10 @@ it.layer(NodeServices.layer)("build-desktop-artifact", (it) => { |
| 725 | 744 | } |
| 726 | 745 | }); |
| 727 | 746 | |
| 728 | it("stages only server runtime externals in macOS packages", () => { | |
| 747 | it("stages only the externals of both bundles in merged packages", () => { | |
| 729 | 748 | assert.deepStrictEqual( |
| 730 | resolveMacStageDependencies({ | |
| 749 | resolveMergedStageDependencies({ | |
| 750 | platform: "mac", | |
| 731 | 751 | serverDependencies: { |
| 732 | 752 | "@anthropic-ai/claude-agent-sdk": "^0.3.170", |
| 733 | 753 | "@ff-labs/fff-node": "0.9.4", |
| ... | ... | @@ -737,8 +757,8 @@ it.layer(NodeServices.layer)("build-desktop-artifact", (it) => { |
| 737 | 757 | "node-pty": "1.1.0", |
| 738 | 758 | }, |
| 739 | 759 | desktopDependencies: { |
| 740 | "@clerk/electron": "0.0.34", | |
| 741 | effect: "4.0.0-beta.103", | |
| 760 | "@napi-rs/keyring": "1.3.0", | |
| 761 | "playwright-core": "1.60.0", | |
| 742 | 762 | }, |
| 743 | 763 | arch: "arm64", |
| 744 | 764 | fffNodeVersion: "0.9.4", |
| ... | ... | @@ -747,11 +767,28 @@ it.layer(NodeServices.layer)("build-desktop-artifact", (it) => { |
| 747 | 767 | "@ff-labs/fff-node": "0.9.4", |
| 748 | 768 | "msgpackr-extract": "3.0.4", |
| 749 | 769 | "node-pty": "1.1.0", |
| 750 | "@clerk/electron": "0.0.34", | |
| 751 | effect: "4.0.0-beta.103", | |
| 770 | "@napi-rs/keyring": "1.3.0", | |
| 771 | "playwright-core": "1.60.0", | |
| 752 | 772 | "@ff-labs/fff-bin-darwin-arm64": "0.9.4", |
| 753 | 773 | }, |
| 754 | 774 | ); |
| 775 | ||
| 776 | assert.deepStrictEqual( | |
| 777 | resolveMergedStageDependencies({ | |
| 778 | platform: "linux", | |
| 779 | serverDependencies: { "@ff-labs/fff-node": "0.9.4", "node-pty": "1.1.0", effect: "4.0.0" }, | |
| 780 | desktopDependencies: { "@crowecawcaw/xa11y": "0.13.0" }, | |
| 781 | arch: "x64", | |
| 782 | fffNodeVersion: "0.9.4", | |
| 783 | }), | |
| 784 | { | |
| 785 | "@ff-labs/fff-node": "0.9.4", | |
| 786 | "node-pty": "1.1.0", | |
| 787 | "@crowecawcaw/xa11y": "0.13.0", | |
| 788 | "@ff-labs/fff-bin-linux-x64-gnu": "0.9.4", | |
| 789 | "@ff-labs/fff-bin-linux-x64-musl": "0.9.4", | |
| 790 | }, | |
| 791 | ); | |
| 755 | 792 | }); |
| 756 | 793 | |
| 757 | 794 | it("excludes node-pty binaries for the other Windows architecture", () => { |
scripts/build-desktop-artifact.ts+41-30| ... | ... | @@ -34,6 +34,7 @@ import { |
| 34 | 34 | selectCliRuntimeExternalDependencies, |
| 35 | 35 | } from "./lib/cli-external-packages.ts"; |
| 36 | 36 | import { loadRepoEnv } from "./lib/public-config.ts"; |
| 37 | import { selectDesktopRuntimeExternalDependencies } from "./lib/desktop-external-packages.ts"; | |
| 37 | 38 | import { resolveCatalogDependencies } from "./lib/resolve-catalog.ts"; |
| 38 | 39 | |
| 39 | 40 | import * as NodeRuntime from "@effect/platform-node/NodeRuntime"; |
| ... | ... | @@ -959,6 +960,10 @@ export const DESKTOP_FILE_EXCLUSIONS = [ |
| 959 | 960 | // so the SDK's optional platform packages (each a ~200MB bundled executable) |
| 960 | 961 | // are dead weight. The trailing dash keeps the SDK's own JS package. |
| 961 | 962 | "!**/node_modules/@anthropic-ai/claude-agent-sdk-*/**/*", |
| 963 | // Nothing in the packaged app enables source maps or serves them: the web | |
| 964 | // client's maps alone were 50 MB of app.asar that no request ever read. | |
| 965 | "!**/*.map", | |
| 966 | "!**/*.d.cts", | |
| 962 | 967 | "!apps/desktop/resources/browser-secret", |
| 963 | 968 | "!apps/desktop/resources/browser-secret/**/*", |
| 964 | 969 | "!apps/desktop/prod-resources/browser-secret", |
| ... | ... | @@ -978,6 +983,12 @@ export const MAC_FILE_EXCLUSIONS = [ |
| 978 | 983 | "!**/node_modules/node-pty/prebuilds/win32-*/**/*", |
| 979 | 984 | "!**/node_modules/node-pty/third_party/conpty/**/*", |
| 980 | 985 | ] as const; |
| 986 | // Linux builds node-pty from source, so every prebuild in the package is for | |
| 987 | // another platform (58 MB of it Windows debug symbols). | |
| 988 | export const LINUX_FILE_EXCLUSIONS = [ | |
| 989 | ...MAC_FILE_EXCLUSIONS, | |
| 990 | "!**/node_modules/node-pty/prebuilds/darwin-*/**/*", | |
| 991 | ] as const; | |
| 981 | 992 | |
| 982 | 993 | // node-pty publishes both Darwin prebuilds in one package. Single-architecture |
| 983 | 994 | // apps only need the native target; universal apps need both. An omitted arch |
| ... | ... | @@ -1013,6 +1024,7 @@ export const WINDOWS_SERVER_ASAR_IGNORE_GLOBS = [ |
| 1013 | 1024 | "**/node_modules/@anthropic-ai/claude-agent-sdk-*/**", |
| 1014 | 1025 | "**/node_modules/.bin", |
| 1015 | 1026 | "**/node_modules/.bin/**", |
| 1027 | "**/*.map", | |
| 1016 | 1028 | ] as const; |
| 1017 | 1029 | |
| 1018 | 1030 | export function resolveWindowsServerAsarIgnoreGlobs(arch: typeof BuildArch.Type) { |
| ... | ... | @@ -1357,7 +1369,10 @@ export function resolveFffNativeDependencies( |
| 1357 | 1369 | ); |
| 1358 | 1370 | } |
| 1359 | 1371 | |
| 1360 | export function resolveMacStageDependencies(input: { | |
| 1372 | // macOS and Linux run both processes from one app.asar, so the stage installs | |
| 1373 | // the union of what each bundle leaves external and nothing else. | |
| 1374 | export function resolveMergedStageDependencies(input: { | |
| 1375 | readonly platform: "mac" | "linux"; | |
| 1361 | 1376 | readonly serverDependencies: Record<string, string>; |
| 1362 | 1377 | readonly desktopDependencies: Record<string, string>; |
| 1363 | 1378 | readonly arch: typeof BuildArch.Type; |
| ... | ... | @@ -1366,7 +1381,7 @@ export function resolveMacStageDependencies(input: { |
| 1366 | 1381 | return { |
| 1367 | 1382 | ...selectCliRuntimeExternalDependencies(input.serverDependencies), |
| 1368 | 1383 | ...input.desktopDependencies, |
| 1369 | ...resolveFffNativeDependencies("mac", input.arch, input.fffNodeVersion), | |
| 1384 | ...resolveFffNativeDependencies(input.platform, input.arch, input.fffNodeVersion), | |
| 1370 | 1385 | }; |
| 1371 | 1386 | } |
| 1372 | 1387 | |
| ... | ... | @@ -2551,6 +2566,11 @@ function validateBundledClientAssets(clientDir: string) { |
| 2551 | 2566 | }); |
| 2552 | 2567 | } |
| 2553 | 2568 | |
| 2569 | // The main-process bundle inlines every JS dependency (see | |
| 2570 | // apps/desktop/vite.config.ts), so the packaged app only installs the packages | |
| 2571 | // that bundle leaves external: native addons and playwright-core. Everything | |
| 2572 | // else already lives inside dist-electron and would only duplicate what the | |
| 2573 | // server bundle carries too. | |
| 2554 | 2574 | export function resolveDesktopRuntimeDependencies( |
| 2555 | 2575 | dependencies: Record<string, string> | undefined, |
| 2556 | 2576 | catalog: Record<string, string>, |
| ... | ... | @@ -2559,14 +2579,11 @@ export function resolveDesktopRuntimeDependencies( |
| 2559 | 2579 | return {}; |
| 2560 | 2580 | } |
| 2561 | 2581 | |
| 2562 | const runtimeDependencies = Object.fromEntries( | |
| 2563 | Object.entries(dependencies).filter( | |
| 2564 | ([dependencyName, dependencySpec]) => | |
| 2565 | dependencyName !== "electron" && !dependencySpec.startsWith("workspace:"), | |
| 2566 | ), | |
| 2582 | return resolveCatalogDependencies( | |
| 2583 | selectDesktopRuntimeExternalDependencies(dependencies), | |
| 2584 | catalog, | |
| 2585 | "apps/desktop", | |
| 2567 | 2586 | ); |
| 2568 | ||
| 2569 | return resolveCatalogDependencies(runtimeDependencies, catalog, "apps/desktop"); | |
| 2570 | 2587 | } |
| 2571 | 2588 | |
| 2572 | 2589 | export const resolveGitHubPublishConfig = Effect.fn("resolveGitHubPublishConfig")(function* ( |
| ... | ... | @@ -2672,7 +2689,11 @@ export const createBuildConfig = Effect.fn("createBuildConfig")(function* ( |
| 2672 | 2689 | electronLanguages: [...DESKTOP_ELECTRON_LANGUAGES], |
| 2673 | 2690 | files: [ |
| 2674 | 2691 | ...DESKTOP_FILE_EXCLUSIONS, |
| 2675 | ...(platform === "mac" ? resolveMacFileExclusions(arch) : []), | |
| 2692 | ...(platform === "mac" | |
| 2693 | ? resolveMacFileExclusions(arch) | |
| 2694 | : platform === "linux" | |
| 2695 | ? LINUX_FILE_EXCLUSIONS | |
| 2696 | : []), | |
| 2676 | 2697 | ], |
| 2677 | 2698 | directories: { |
| 2678 | 2699 | buildResources: "apps/desktop/resources", |
| ... | ... | @@ -3735,29 +3756,19 @@ const buildDesktopArtifact = Effect.fn("buildDesktopArtifact")(function* ( |
| 3735 | 3756 | } |
| 3736 | 3757 | |
| 3737 | 3758 | // Windows splits dependencies per process: app.asar carries only the |
| 3738 | // desktop main-process runtime deps, while the server bundle's deps live in | |
| 3739 | // the server.asar sidecar (see stageWindowsServerSidecar). macOS adds only | |
| 3740 | // server packages that remain external to its merged app.asar. Linux retains | |
| 3741 | // its existing full dependency tree. | |
| 3759 | // desktop main-process externals, while the server bundle's externals live | |
| 3760 | // in the server.asar sidecar (see stageWindowsServerSidecar). macOS and | |
| 3761 | // Linux merge both sets into one app.asar. | |
| 3742 | 3762 | const stageDependencies = |
| 3743 | 3763 | options.platform === "win" |
| 3744 | 3764 | ? { ...resolvedDesktopRuntimeDependencies } |
| 3745 | : options.platform === "mac" | |
| 3746 | ? resolveMacStageDependencies({ | |
| 3747 | serverDependencies: resolvedServerDependencies, | |
| 3748 | desktopDependencies: resolvedDesktopRuntimeDependencies, | |
| 3749 | arch: options.arch, | |
| 3750 | fffNodeVersion: serverPackageJson.dependencies["@ff-labs/fff-node"], | |
| 3751 | }) | |
| 3752 | : { | |
| 3753 | ...resolvedServerDependencies, | |
| 3754 | ...resolvedDesktopRuntimeDependencies, | |
| 3755 | ...resolveFffNativeDependencies( | |
| 3756 | options.platform, | |
| 3757 | options.arch, | |
| 3758 | serverPackageJson.dependencies["@ff-labs/fff-node"], | |
| 3759 | ), | |
| 3760 | }; | |
| 3765 | : resolveMergedStageDependencies({ | |
| 3766 | platform: options.platform, | |
| 3767 | serverDependencies: resolvedServerDependencies, | |
| 3768 | desktopDependencies: resolvedDesktopRuntimeDependencies, | |
| 3769 | arch: options.arch, | |
| 3770 | fffNodeVersion: serverPackageJson.dependencies["@ff-labs/fff-node"], | |
| 3771 | }); | |
| 3761 | 3772 | const stagePatchedDependencies = createStagePatchedDependencies( |
| 3762 | 3773 | workspacePatchedDependencies, |
| 3763 | 3774 | stageDependencies, |
scripts/lib/desktop-external-packages.ts created+38| ... | ... | @@ -0,0 +1,38 @@ |
| 1 | /** | |
| 2 | * Packages the desktop main-process bundle must NOT inline. | |
| 3 | * | |
| 4 | * The desktop bundle follows the same policy as the server CLI bundle (see | |
| 5 | * cli-external-packages.ts): everything is inlined except what Node has to | |
| 6 | * load from the real filesystem. Both `apps/desktop/vite.config.ts` and the | |
| 7 | * artifact stage in scripts/build-desktop-artifact.ts derive from this list, | |
| 8 | * so a package that is external is also the only kind of package the staged | |
| 9 | * production install carries. Anything not listed here ships inside | |
| 10 | * `dist-electron/*.cjs` and has no `node_modules` presence at all. | |
| 11 | * | |
| 12 | * Entries are matched as prefixes so platform-specific siblings are covered. | |
| 13 | */ | |
| 14 | export const DESKTOP_RUNTIME_EXTERNAL_PREFIXES = [ | |
| 15 | // Native addons and the wrappers that dlopen them by real path. | |
| 16 | "@napi-rs/keyring", | |
| 17 | "@crowecawcaw/xa11y", | |
| 18 | "@clerk/electron-passkeys", | |
| 19 | "ffi-rs", | |
| 20 | "@yuuang/", | |
| 21 | // Reads its own bundle from disk by resolving `playwright-core/package.json` | |
| 22 | // at runtime and ships the browser driver alongside; there is nothing to | |
| 23 | // gain from inlining a 10 MB file the code re-reads as text. | |
| 24 | "playwright-core", | |
| 25 | ] as const; | |
| 26 | ||
| 27 | export function isDesktopRuntimeExternalDependency(id: string): boolean { | |
| 28 | return DESKTOP_RUNTIME_EXTERNAL_PREFIXES.some((prefix) => id.startsWith(prefix)); | |
| 29 | } | |
| 30 | ||
| 31 | /** Select the desktop dependency roots whose runtime closure the stage must install. */ | |
| 32 | export function selectDesktopRuntimeExternalDependencies( | |
| 33 | dependencies: Readonly<Record<string, string>>, | |
| 34 | ): Record<string, string> { | |
| 35 | return Object.fromEntries( | |
| 36 | Object.entries(dependencies).filter(([name]) => isDesktopRuntimeExternalDependency(name)), | |
| 37 | ); | |
| 38 | } |