6c3d4a4af7
runtime.ts captured the SDK namespaces (the plugin SDK, React and the two jsx runtimes) in a module-scope object literal, and that module sits in an import cycle: sdk/index -> @/contrib/* -> contrib/runtime-loader -> sdk/runtime -> sdk/index. In an unbundled (dev) graph the namespace object is live, so the capture works. In a production bundle the bundler emits the SDK namespace as a hoisted `var` whose assignment lands AFTER the literal that reads it, so the captured value is `undefined` (no TDZ error) and `Object.keys(GLOBALS[globalKey])` in `shimUrl()` throws "Cannot convert undefined or null to object". That throw happens inside `loadRuntimePlugin()` -- via `unsupportedImports()` -> `sdkImportMap()` -> `shimUrl()`, which run for every source before it is evaluated -- so it is content-independent: EVERY plugin loaded from $HERMES_HOME/desktop-plugins/<id>/plugin.js (and the desktop/plugin.js half of a unified package) fails to load, showing status "failed" in Capabilities -> Plugins. Resolve the namespaces at call time instead: `installPluginSdk()` and the shim builder only ever run once the app is up, so reading them there is always safe and statement ordering can no longer matter. Repro (production build only): build apps/desktop for production, drop any plugin.js into ~/.hermes/desktop-plugins/<id>/, start the app -> [plugins] runtime load failed (<id>) TypeError: Cannot convert undefined or null to object (.../assets/sdk-<hash>.js:5). Note: a vitest unit test cannot catch this (dev module graph keeps the namespace live); the faithful guard is a production-build smoke test that loads a fixture plugin through loadRuntimePlugin().