* fix(plugins): resolve public artifacts from installed plugin roots
Externalized official plugins ship gateway-auth/doctor/secret/message-tool
public artifacts in their installed npm package, but the public-surface
loader only searched bundled locations. On packaged installs this emptied
the gateway auth bypass set for /api/channels/mattermost/command, so every
Mattermost slash callback was rejected 401 before the plugin ran.
Fall back to the plugin's installed package root (install-record index)
when bundled resolution misses; bundled/source locations keep precedence.
Fixes#98740
* fix(gateway): resolve channel auth-bypass artifacts via activation-gated facade
Route gateway-auth bypass artifact loading through the activation-gated
facade seam so externalized (installed) channel plugins keep their
unauthenticated callback paths, while denied/disabled plugins contribute
nothing and never execute artifact code. Reject hardlinked artifacts for
installed plugin roots in the facade loader per hardlink-policy.
Replaces the earlier install-record fallback in the shared bundled
artifact loader, which broadened every artifact surface past its trust
boundary (see PR review); other externalized-plugin surfaces are #98842.
Fixes#98740
* fix(gateway): load facade activation runtime via async lazy import
The sync createRequire/jiti candidate loader for
facade-activation-check.runtime.js only resolves from built dist; under
vitest source runs it cannot follow .js->.ts specifiers or workspace
package subpaths, so the gateway auth-bypass path threw 'Unable to load
facade activation check runtime' (CI shard failure). Add an async
activated-load API backed by dynamic import (vitest- and dist-native),
warm the shared runtime memo, and make the bypass consumer async - its
single caller already awaits behind the config-snapshot promise cache.
* revert(plugins): drop install-record fallback from public-surface loader
Superseded by the activation-gated facade seam in gateway-auth-bypass;
review found the loader-level fallback broadened every artifact surface
past its trust boundary (see PR #98819 discussion).
* fix(plugin-sdk): drop unused oxlint disable on async facade loader
* test(plugin-sdk): cover facade hardlink policy for installed vs core roots
* test(plugin-sdk): cover facade hardlink policy for installed vs core roots
---------
Co-authored-by: Alex Knight <15041791+amknight@users.noreply.github.com>
Place a sentinel object in the loadedFacadeModules cache before the Jiti
sync load begins. Re-entrant calls (caused by circular facade references
from constant exports evaluated at module-evaluation time) now receive the
sentinel instead of recursing infinitely. Once the real module finishes
loading, Object.assign() back-fills the sentinel so any references
captured during the circular load phase see the final exports.
The Jiti load is wrapped in try/catch: on failure the sentinel is removed
from the cache so that subsequent retry attempts re-execute the load
instead of silently returning an empty object. The function returns the
sentinel (not the raw loaded module) to guarantee a single object identity
for all callers, including those that captured a reference during the
circular load phase.
Also tightens the generic constraint from <T> to <T extends object> so
Object.assign() is type-safe, and propagates the constraint to the
test-utils callers in bundled-plugin-public-surface.ts.
Fixes#57394