New:Socket for Asana Is Now Available.Learn more
Get Started

@memberjunction/action-runtime-host

Package Overview
Dependencies
Maintainers
11
Versions
34
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@memberjunction/action-runtime-host

Host-side bridge that exposes MJ services (metadata, views, queries, entity CRUD, action invocation, agent run, AI prompt execution) to sandboxed Runtime-action code via `utilities.*`. Ships a default implementation of `RuntimeActionBridgeBuilder` that `@

lts-5
latest
Source
npmnpm
Version
5.51.1
Version published
Weekly downloads
533
42.9%
Maintainers
11
Weekly downloads
 
Created
Source

@memberjunction/action-runtime-host

Host-side bridge that exposes MJ services (metadata, views, queries, entity CRUD, action invocation, agent run, AI prompt execution) to sandboxed Runtime-action code via the utilities.* namespace.

Ships the default concrete implementation of RuntimeActionBridgeBuilder from @memberjunction/actions-base. @memberjunction/actions resolves it at runtime through MJGlobal.ClassFactory.CreateInstance(...).

Why this package exists (the cycle)

Building the bridge handler map requires touching:

  • ActionEngineServer (from @memberjunction/actions) — for utilities.actions.Invoke
  • AgentRunner (from @memberjunction/ai-agents) — for utilities.agents.Run
  • AIEngine (from @memberjunction/aiengine) — for agent / prompt metadata
  • AIPromptRunner (from @memberjunction/ai-prompts) — for utilities.ai.ExecutePrompt

Those last three packages already depend on @memberjunction/actions. Putting the bridge source inside @memberjunction/actions (where it used to live) created a cycle that could only be hidden with await import(...) calls and as never casts — fragile, untyped, and easy to break silently.

This package sits at the top of the Actions stack, above every package it touches, so every import can be fully static and fully type-checked.

core · global · core-entities
              │
              ├── actions-base           ← contract: RuntimeActionBridgeBuilder abstract
              ├── code-execution         ← sandbox runner
              ├── action-runtime         ← RuntimeActionExecutor
              ├── aiengine · ai-prompts · ai-core-plus
              │
              ├── actions                ← ClassFactory lookup, no AI deps
              │     │
              │     └── ai-agents
              │           │
              │           └── action-runtime-host  ← THIS PACKAGE (default bridge)

How ActionEngine uses it

// Inside @memberjunction/actions (ActionEngine.RunRuntimeAction):
const builder = MJGlobal.Instance.ClassFactory.CreateInstance<RuntimeActionBridgeBuilder>(
  RuntimeActionBridgeBuilder
);
if (builder) {
  bridgeHandlers = builder.BuildHandlers({ action, config, contextUser, abortSignal });
  preamble = builder.GetPreamble();
}
// If no builder is registered, Runtime actions still run in pure-compute mode —
// they just can't call any utilities.* bridge namespaces.

Two things to notice:

  • No static dependency on this package from Engine. Engine imports only the abstract from actions-base; the concrete is resolved by name at runtime.
  • Graceful degradation. If no subclass is registered (e.g., this package was never loaded), pure-compute Runtime actions still work. That makes minimal builds possible.

Bootstrap wiring

For the default bridge to register itself, this package has to be imported somewhere so its @RegisterClass decorator fires. The standard MJ flow handles this for you:

  • @memberjunction/server-bootstrap lists action-runtime-host as a dependency
  • The bootstrap manifest (ServerBootstrap/src/generated/mj-class-registrations.ts) is regenerated by mj codegen manifest and statically imports DefaultRuntimeActionBridgeBuilder alongside every other @RegisterClass-decorated class
  • MJAPI's entry point imports @memberjunction/server-bootstrap/mj-class-registrations, which transitively triggers our registration

No manual wiring needed in consuming apps.

Overriding the default bridge

The default uses @RegisterClass(RuntimeActionBridgeBuilder) with no key so any subclass registered later wins automatically via MJ's auto-priority tiebreak. Subclass, register, done:

import { RegisterClass } from '@memberjunction/global';
import { RuntimeActionBridgeBuilder, BridgeContext } from '@memberjunction/actions-base';
import type { BridgeHandlerMap } from '@memberjunction/code-execution';

@RegisterClass(RuntimeActionBridgeBuilder)
export class MyCustomBridge extends RuntimeActionBridgeBuilder {
  protected constructor() { super(); }
  public static get Instance(): MyCustomBridge { return super.getInstance<MyCustomBridge>(); }

  public BuildHandlers(ctx: BridgeContext): BridgeHandlerMap {
    return {
      'md.GetEntity':    /* … your handler … */,
      'utilities.custom.Ping': async () => 'pong',
      // … whatever you want to expose or override
    };
  }

  public GetPreamble(): string {
    return `
      globalThis.utilities = {
        custom: { Ping: (...a) => __bridgeCall('utilities.custom.Ping', a[0]) },
        // … match the handler keys above
      };
    `;
  }
}

Import your custom package from MJAPI's entry point (or add it to the bootstrap manifest flow) so @RegisterClass fires at startup.

The default utilities.* surface

DefaultRuntimeActionBridgeBuilder.BuildHandlers(ctx) wires these handler keys to permissioned handlers that enforce RuntimeActionConfiguration.permissions and thread contextUser through every downstream call:

NamespaceNamesPurpose
md.*GetEntity, GetEntityFields, GetRelatedEntities, ListEntitiesMetadata read-only
rv.*RunView, RunViewsView queries (batched form supported)
rq.*RunQueryNamed-query execution
entity.*Load, Create, Update, Save, DeleteEntity CRUD (subject to allowedEntities)
actions.*Invoke, InvokeAllInvoke other actions (subject to allowedActions)
agents.*Run, GetAvailableRun AI agents (subject to allowedAgents)
ai.*ExecutePrompt, GetEmbeddingExecute AI prompts by name / get embeddings

Each handler checks:

  • Caller's RuntimeActionConfiguration.permissions allowlist (or allowAny* flags)
  • The context's abortSignal (for cancellation)
  • User context is threaded through so MJ row-level security still applies

On permission denial, handlers throw; the sandbox re-throws inside the user's code as a runtime error.

Testing

cd packages/Actions/RuntimeHost
npm run test

Unit tests validate the architectural contract (ClassFactory resolution, handler map shape, preamble-to-handlers consistency) without spinning up isolated-vm or a database. The end-to-end regression is packages/Actions/Runtime/harness/run-demos.ts — runs all 5 demo Runtime actions through the real stack.

  • @memberjunction/actions-base — abstract RuntimeActionBridgeBuilder contract
  • @memberjunction/actions — consumes the builder via ClassFactory
  • @memberjunction/action-runtime — sandbox executor (what the bridge handler map gets passed to)
  • @memberjunction/code-executionWorkerPool + CodeExecutionService (isolated-vm sandbox)

FAQs

Package last updated on 19 Aug 2026

Related posts