Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"changes": [
{
"packageName": "@rushstack/rush-daemon",
"comment": "Add an opt-in all-project engine component factory with explicit phase/plugin shape, retained invalidation reconciliation, fail-closed graph recreation boundaries, and a deterministic engine shutdown contract.",
"type": "minor"
}
],
"packageName": "@rushstack/rush-daemon",
"email": "mojazayeri@users.noreply.github.com"
}
115 changes: 114 additions & 1 deletion common/reviews/api/rush-daemon.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,36 @@

/// <reference types="node" />

import type { GetInputsSnapshotAsyncFn } from '@microsoft/rush-lib';
import type { IDaemonPaths } from '@rushstack/rush-daemon-transport';
import type { IInputsSnapshot } from '@microsoft/rush-lib';
import type { IOperationGraph } from '@microsoft/rush-lib';
import type { Operation } from '@microsoft/rush-lib';
import { RushConfiguration } from '@microsoft/rush-lib';
import type { RushConfigurationProject } from '@microsoft/rush-lib';
import type { RushSession } from '@microsoft/rush-lib';

// @beta
export type CreateWorkspaceEngineComponentsAsync = (options: ICreateWorkspaceEngineComponentsOptions) => Promise<IWorkspaceEngineComponents>;

// @beta
export type CreateWorkspaceSessionComponentsAsync = (options: ICreateWorkspaceSessionComponentsOptions) => Promise<IWorkspaceSessionComponents>;

// @beta
export interface IClassifyWorkspaceInvalidationsOptions {
// (undocumented)
readonly changedPaths: ReadonlyArray<string>;
// (undocumented)
readonly rushConfiguration: RushConfiguration;
}

// @beta
export interface ICreateWorkspaceEngineComponentsOptions extends IWorkspaceEngineShape {
readonly projectSelection: ReadonlySet<RushConfigurationProject>;
// (undocumented)
readonly rushConfiguration: RushConfiguration;
}

// @beta
export interface ICreateWorkspaceSessionComponentsOptions {
// (undocumented)
Expand All @@ -25,6 +46,18 @@ export interface ICreateWorkspaceSessionComponentsOptions {
readonly rushConfiguration: RushConfiguration;
}

// @beta
export interface IMapWorkspaceInvalidationsOptions {
// (undocumented)
readonly changedPaths: ReadonlyArray<string>;
// (undocumented)
readonly currentInputsSnapshot: IInputsSnapshot;
// (undocumented)
readonly nextInputsSnapshot: IInputsSnapshot;
// (undocumented)
readonly operationGraph: IOperationGraph;
}

// @public
export interface IRequestLease {
// (undocumented)
Expand Down Expand Up @@ -58,6 +91,54 @@ export interface IRushDaemonServeOptions extends IRushDaemonHostOptions {
readonly shutdownSignal?: AbortSignal;
}

// @beta
export type IsWorkspaceEngineRecreationRequiredAsync = (options: IClassifyWorkspaceInvalidationsOptions) => Promise<boolean>;

// @beta
export interface IWorkspaceEngineComponentFactoryOptions {
// (undocumented)
readonly createEngineComponentsAsync: CreateWorkspaceEngineComponentsAsync;
// (undocumented)
readonly isEngineRecreationRequiredAsync?: IsWorkspaceEngineRecreationRequiredAsync;
// (undocumented)
readonly mapInvalidationsToOperationsAsync: MapWorkspaceInvalidationsToOperationsAsync;
// (undocumented)
readonly shape: IWorkspaceEngineShape;
}

// @beta
export interface IWorkspaceEngineComponents extends AsyncDisposable {
[Symbol.asyncDispose](): Promise<void>;
// (undocumented)
readonly getInputsSnapshotAsync: GetInputsSnapshotAsyncFn;
// (undocumented)
readonly inputsSnapshot: IInputsSnapshot;
// (undocumented)
readonly operationGraph: IOperationGraph;
// (undocumented)
readonly rushSession: RushSession;
}

// @beta
export interface IWorkspaceEngineShape {
// (undocumented)
readonly phaseNames: ReadonlyArray<string>;
// (undocumented)
readonly pluginNames: ReadonlyArray<string>;
}

// @beta
export interface IWorkspaceInvalidationReconciliation {
// (undocumented)
readonly inputsSnapshot: IInputsSnapshot;
// (undocumented)
readonly invalidatedOperationCount: number;
// (undocumented)
readonly isFullInvalidation: boolean;
// (undocumented)
readonly sequence: number;
}

// @beta
export interface IWorkspaceInvalidationSnapshot {
readonly changedPaths: ReadonlyArray<string>;
Expand All @@ -74,6 +155,8 @@ export interface IWorkspaceInvalidationWatcher extends AsyncDisposable {

// @beta
export interface IWorkspaceSession extends AsyncDisposable {
// (undocumented)
readonly engineShape: IWorkspaceEngineShape | undefined;
// (undocumented)
readonly inputsSnapshot: IInputsSnapshot | undefined;
// (undocumented)
Expand All @@ -83,19 +166,25 @@ export interface IWorkspaceSession extends AsyncDisposable {
// (undocumented)
readonly operationGraph: IOperationGraph | undefined;
// (undocumented)
reconcileInvalidationsAsync(): Promise<IWorkspaceInvalidationReconciliation | undefined>;
// (undocumented)
readonly rushConfiguration: RushConfiguration;
// (undocumented)
readonly rushSession: RushSession | undefined;
}

// @beta
export interface IWorkspaceSessionComponents extends AsyncDisposable {
// (undocumented)
readonly engineShape?: IWorkspaceEngineShape;
// (undocumented)
readonly inputsSnapshot?: IInputsSnapshot;
// (undocumented)
readonly operationGraph?: IOperationGraph;
readonly projectWatcher?: IWorkspaceInvalidationWatcher;
// (undocumented)
readonly reconcileInvalidationsAsync?: () => Promise<IWorkspaceInvalidationReconciliation>;
// (undocumented)
readonly rushSession?: RushSession;
}

Expand Down Expand Up @@ -125,6 +214,9 @@ export interface IWorkspaceSessionOptions {
readonly rushVersion: string;
}

// @beta
export type MapWorkspaceInvalidationsToOperationsAsync = (options: IMapWorkspaceInvalidationsOptions) => Promise<Iterable<Operation>>;

// @public
export enum RequestExclusivityClass {
// (undocumented)
Expand Down Expand Up @@ -171,11 +263,29 @@ export class RushDaemonHost {
// @beta
export function serveRushDaemonAsync(options: IRushDaemonServeOptions): Promise<void>;

// @beta
export class WorkspaceEngineComponentFactory {
constructor(options: IWorkspaceEngineComponentFactoryOptions);
// (undocumented)
readonly createAsync: CreateWorkspaceSessionComponentsAsync;
// (undocumented)
readonly shape: IWorkspaceEngineShape;
}

// @beta
export class WorkspaceEngineRecreationRequiredError extends Error {
constructor();
}

// @beta
export class WorkspaceInvalidationTracker {
acknowledgeThrough(sequence: number): void;
getSnapshot(): IWorkspaceInvalidationSnapshot;
// @internal (undocumented)
get hasUnattributedUnknownChanges(): boolean;
invalidate(changedPath?: string): void;
// @internal (undocumented)
invalidateForInitialization(): void;
markWatcherUnhealthy(): void;
}

Expand All @@ -184,13 +294,16 @@ export class WorkspaceSession implements IWorkspaceSession {
[Symbol.asyncDispose](): Promise<void>;
static createAsync(options: IWorkspaceSessionOptions): Promise<WorkspaceSession>;
// (undocumented)
readonly inputsSnapshot: IInputsSnapshot | undefined;
get engineShape(): IWorkspaceEngineShape | undefined;
// (undocumented)
get inputsSnapshot(): IInputsSnapshot | undefined;
// (undocumented)
readonly invalidations: WorkspaceInvalidationTracker;
// (undocumented)
readonly metadata: IWorkspaceSessionMetadata;
// (undocumented)
readonly operationGraph: IOperationGraph | undefined;
reconcileInvalidationsAsync(): Promise<IWorkspaceInvalidationReconciliation | undefined>;
// (undocumented)
readonly rushConfiguration: RushConfiguration;
// (undocumented)
Expand Down
19 changes: 15 additions & 4 deletions libraries/rush-daemon/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,18 @@ The host loads `RushConfiguration` once before signaling readiness and keeps a h
active for the daemon lifetime. Its invalidation tracker retains changes while no clients are
connected so a later request can reconcile them. The tracker starts with a conservative unknown
invalidation covering session startup, and excessive distinct paths are compacted into the same
full-workspace signal. Reusable operation graph, plugin, and input snapshot state can be supplied
through the session component factory; the default session does not construct those command-specific
resources while the reusable runner lifetime tracked by
[rushstack#5895](https://github.com/microsoft/rushstack/issues/5895) remains incomplete.
full-workspace signal.

`WorkspaceEngineComponentFactory` provides the opt-in seam for a command integration to supply a real
all-project operation graph, its `RushSession`, and a refreshable inputs snapshot. The integration must
declare the complete phase and plugin shape because Rush plugins can currently vary that shape by command.
The factory validates graph ownership, serializes retained invalidation reconciliation, and maps path-specific
changes through the integration. The engine owner must supply one deterministic async disposer because
`IOperationGraph` does not yet expose an operation that both stops the lifetime and awaits runner cleanup.
After the initial conservative startup reconciliation, changes to Rush configuration, project package manifests,
or integration-classified plugin graph inputs fail closed with `WorkspaceEngineRecreationRequiredError` before
the input baseline advances or the invalidation is acknowledged. The startup watcher-registration boundary has
no paths to classify and therefore remains a full invalidation. The routing layer must replace the complete
workspace session rather than run a stale graph.
The default daemon executable does not construct or route this graph while the command-independent plugin shape and per-iteration runner
lifetime tracked by [rushstack#5895](https://github.com/microsoft/rushstack/issues/5895) remain incomplete.
Loading