Member Junction
    Preparing search index...

    Singleton pipeline orchestrator for runtime schema changes.

    Usage: const rsm = RuntimeSchemaManager.Instance; if (!rsm.IsEnabled) throw new Error('RSU is disabled'); const result = await rsm.RunPipeline(input);

    Hierarchy (View Summary)

    Index

    Constructors

    Properties

    PipelineObserver: RSUPipelineObserver = null

    Optional observer notified of every step and run boundary. Set once at process startup (see the RSU progress bridge in MJServer) — a single observer is sufficient because RSU runs are serialized by the pipeline lock, so events can never interleave between runs.

    Never awaited, never allowed to fail the pipeline. See RSUPipelineObserver.

    Static ReadonlyEXPECTED_STEPS_POST_RESTART

    EXPECTED_STEPS_POST_RESTART: readonly ["CreateEntityMaps", "StartSync"] = ...

    The steps that run AFTER the pipeline restarts the API on itself, in order.

    They belong to the same run — the setup journey the user is watching is not finished until the sync starts — but they execute in a DIFFERENT PROCESS, so nothing in this class ever runs them. They are declared here because this is where the run's shape is defined: they count towards ExpectedStepTotal, so the determinate progress bar does not reach 100% and then sit there while the connector is still not live, and they are named here so the post-restart consumer and this pipeline cannot drift apart on what the remaining steps are called.

    OBSERVER_DURABLE_TIMEOUT_MS: 5000 = 5_000

    How long the pipeline will wait for an observer to make a restart.pending event durable.

    Bounded on purpose. The wait exists because the process is seconds from being killed, but an observer that hangs must not wedge a schema migration — the invariant "progress reporting can never break a migration" outranks the checkpoint. Five seconds is orders of magnitude more than appending a few lines to a local file needs, and far less than the restart path's own timeouts.

    Accessors

    • get GlobalKey(): string

      Returns string

    Methods

    • Returns the complete set of schema names this instance will block in DDL.

      Always includes __mj (the MemberJunction system schema, never modifiable). Also includes any additional schemas configured via the RSU_PROTECTED_SCHEMAS environment variable (comma-separated list, e.g. RSU_PROTECTED_SCHEMAS=dbo,sys).

      Callers such as the Database Designer Validator use this for early-exit UX feedback before DDL is even generated — the same set that ValidateMigrationSQL() enforces at the SQL level.

      Returns ReadonlySet<string>

      A readonly lowercase set of all protected schema names.

    • The Global Object Store is a place to store global objects that need to be shared across the application. Depending on the execution environment, this could be the window object in a browser, or the global object in a node environment, or something else in other contexts. The key here is that in some cases static variables are not truly shared because it is possible that a given class might have copies of its code in multiple paths in a deployed application. This approach ensures that no matter how many code copies might exist, there is only one instance of the object in question by using the Global Object Store.

      Returns GlobalObjectStore

    • Returns Promise<RSUPendingWork[]>

      Use ReadPendingWork instead, and mark each item terminal with CompletePendingWork / FailPendingWork once you have processed it.

      Retained with its original signature so consumers taking this minor upgrade still compile. It is NO LONGER FUNCTIONAL: pending work lives in MJ: RSU Pending Works rather than in .rsu_pending files, and reading it requires a context user this signature has no way to supply. Returns an empty array — the value that previously meant "no pending work" — rather than pretending to have drained a queue.

    • Read every still-Pending work row. Never deletes or mutates — the caller marks each row Completed/Failed once it has actually finished processing it. Rows whose payload fails to parse are reported as Failed so they stop being retried forever.

      Parameters

      • contextUser: UserInfo
      • OptionalstaleAfterMinutes: number

        when set, rows created more than this many minutes ago are logged as stale so an operator can see work that a previous process abandoned.

      • Optionalprovider: IMetadataProvider

      Returns Promise<{ CreatedAt: Date; ID: string; Work: RSUPendingWork }[]>

    • Re-queues failed work for one more attempt, narrowed to what has NOT yet been done.

      RSU is a long chain — migrations, CodeGen, a git commit, a compile, a restart — and a failure partway through it is frequently transient: the process was restarted mid-consumption, or a provider call failed once. Marking such an item Failed terminally means the objects it would have mapped are silently never mapped, and the only recovery is for someone to notice and re-apply the connector by hand.

      The remainder matters as much as the retry. Re-running an item whole would redo the objects that already succeeded; carrying only the outstanding ones makes each attempt strictly smaller and keeps a single poison object from blocking its siblings forever.

      Returns false when the budget is spent, in which case the caller should fail the item terminally — the message it writes is the operator's only signal, so it should name what was left undone.

      Parameters

      Returns Promise<boolean>

    • Execute the RSU pipeline for a batch of inputs.

      Correctness guarantee: migrations and CodeGen never overlap. All migrations execute under one lock, then CodeGen runs once against the fully-committed schema.

      Flow:

      1. Validate all inputs (fail fast — reject entire batch on validation error)
      2. Lock
      3. For each input: write migration file → execute SQL (independent success/failure)
      4. Unlock
      5. ONE CodeGen run (covers all successful migrations)
      6. ONE compile
      7. ONE restart
      8. ONE git commit (all migration files)
      9. Resolve each caller with per-item migration result + shared post-migration result

      Parameters

      Returns Promise<RSUPipelineBatchResult>

    • Run the RSU pipeline with automatic retry for transient failures. Only retries steps that are known to be transient (SQL execution, CodeGen, compilation, restart). Validation and git steps are not retried.

      Parameters

      • input: RSUPipelineInput

        Pipeline input

      • maxRetries: number = 2

        Maximum number of retries (default: 2, so 3 total attempts)

      • baseDelayMs: number = 5000

        Base delay for exponential backoff (default: 5000ms)

      Returns Promise<RSUPipelineResult>

    • Set the additionalSchemaInfo file path that CodeGen reads (from mj.config.cjs additionalSchemaInfo). RSU writes its soft PK/FK config to THIS path so the subsequent CodeGen run actually finds it. Injected by the server at startup, mirroring SetCodeGenOutputPaths.

      Without injection, RSU falls back to RSU_ADDITIONAL_SCHEMA_INFO_PATH / 'additionalSchemaInfo.json' — a path that need not match CodeGen's configured one. When they diverge, RSU writes soft PKs to a file CodeGen never reads, and every integration table is skipped with "No primary key found". Keeping the two in lockstep is the whole point of this setter.

      Parameters

      • configuredPath: string

      Returns void

    • Set the CodeGen output directory paths (from mj.config.cjs output[].directory). Used by the git step to stage only CodeGen-produced source files.

      Parameters

      • paths: string[]

      Returns void

    • Maps a finished batch onto its terminal run.end event.

      result is undefined when a throw escaped the pipeline before a result existed — that case must still produce a FAILED run.end, otherwise an observer's run would hang in-flight forever.

      Public + static so the mapping is unit-testable without a live pipeline.

      Parameters

      Returns {
          ErrorMessage?: string;
          ErrorStep?: string;
          FailureCount: number;
          Kind: "run.end";
          Success: boolean;
          SuccessCount: number;
          TotalCount: number;
      }

      • OptionalErrorMessage?: string

        Present when the run failed — the first failing step's message.

      • OptionalErrorStep?: string

        Present when the run failed — the name of the step that failed.

      • FailureCount: number
      • Kind: "run.end"
      • Success: boolean
      • SuccessCount: number
      • TotalCount: number
    • The de-duplicated set of connections a batch touches, in input order.

      Resolution order per input: the explicit RSUPipelineInput.CompanyIntegrationID first, then the CompanyIntegrationID of any RSUPipelineInput.PendingWork entry. The fallback is not a convenience — it is the compatibility path that gives EVERY production caller today an identity without changing a single call site, because every one of them registers post-restart work against a connection. An input with neither contributes nothing, and a batch where no input has either yields an empty set: that run stays unreadable through the per-connection endpoints, which is the conservative answer, not a bug.

      Public and static so the identity rule is assertable on its own.

      Parameters

      Returns string[]

    • The full expected step sequence for a batch of itemCount migrations, in execution order — including the two that run after the restart.

      Public and static so the arithmetic is directly assertable without a live pipeline; the number it produces is the denominator every progress UI divides by.

      Parameters

      • itemCount: number

      Returns string[]

    • Returns the singleton instance of the class. If the instance does not exist, it is created and stored in the Global Object Store. If className is provided it will be used as part of the key in the Global Object Store, otherwise the actual class name will be used. NOTE: the class name used by default is the lowest level of the object hierarchy, so if you have a class that extends another class, the lowest level class name will be used.

      Type Parameters

      Parameters

      • this: new () => T
      • OptionalclassName: string

      Returns T