import { type DeploymentConsistencyResult } from './registry/deployment-consistency.ts'; import { type RegistrationRejectionEntry } from './registry/rejections.ts'; import { type WorkerDeploymentSummary } from './registry/summary.ts'; import type { InFlightTask, RoutingOptions, RoutingPolicy, WorkerDrainMutationResult, WorkerDrainOptions, WorkerInfo, WorkerRegistrationInfo, WorkerRegistryOptions, WorkerSummary } from './registry/types.ts'; export type { RegistrationRejectionEntry } from './registry/rejections.ts'; export type { WorkerDeploymentSummary } from './registry/summary.ts'; export type { InFlightTask, RoutingOptions, RoutingPolicy, WorkerDrainMutationResult, WorkerDrainOptions, WorkerHealth, WorkerInfo, WorkerRegistrationInfo, WorkerRegistryOptions, WorkerSummary, } from './registry/types.ts'; /** * Server-side registry of connected remote workers with pluggable routing * policies. Tracks which workers are connected, which activities they support, * and how many tasks they have in flight. `findWorker` selects the best worker * for a given activity per the configured {@link RoutingPolicy} (default * `'least-loaded'`). Used internally by `serve()`; most applications access it * through {@link WeftServer.registry}. * * @example * ```ts * import { WorkerRegistry, type WorkerManifest } from '@lostgradient/weft'; * * const manifest: WorkerManifest = { * manifestVersion: 1, * protocolVersion: 3, * sdkVersion: '0.18.0', * runtime: { name: 'bun', version: '1.3.14' }, * deployment: { name: 'billing', buildId: 'b3', artifactDigest: 'sha256:41d0' }, * workflows: {}, * capabilities: {}, * }; * * const registry = new WorkerRegistry({ policy: 'least-loaded' }); * registry.register({ * id: 'worker-1', * queue: 'default', * activities: ['sendEmail'], * concurrency: 10, * manifest, * acceptedManifestDigest: 'sha256:deadbeef', * }); * const best = registry.findWorker('sendEmail', { queue: 'default' }); * ``` */ export declare class WorkerRegistry { #private; constructor(options?: WorkerRegistryOptions); /** The routing policy this registry was configured with. */ get policy(): RoutingPolicy; /** * Read-only check of a manifest's `(deploymentName, buildId, * artifactDigest)` against digests previously seen for that deployment and * build. Callers with a later rejection gate must call * {@link recordDeploymentConsistency} only once every such gate has * passed, so a declined worker cannot poison a deployment/build slot. */ checkDeploymentConsistency(deploymentName: string, buildId: string, artifactDigest: string): DeploymentConsistencyResult; /** Record `artifactDigest` for `(deploymentName, buildId)` once every rejection gate has passed. */ recordDeploymentConsistency(deploymentName: string, buildId: string, artifactDigest: string): void; /** Register a worker. */ register(info: WorkerRegistrationInfo): void; /** Unregister a worker. Purges fair-share counters and in-flight task entries for this worker. */ unregister(workerId: string): WorkerInfo | undefined; /** Record a heartbeat from a worker. */ heartbeat(workerId: string): void; /** Increment in-flight count for a worker. */ taskAssigned(workerId: string): void; /** Decrement in-flight count. */ taskCompleted(workerId: string): void; /** * Find the best worker for a task using the configured {@link RoutingPolicy}. * * Common preconditions for every policy: * 1. If `options.queue` is set, only workers on that queue are considered. * 2. Only workers that advertise `activityName` in their `activities` list are * considered. * 3. Workers at `inFlight >= concurrency` are excluded. * 4. A `sticky` worker that also satisfies the above wins regardless of policy. */ findWorker(activityName: string, options?: RoutingOptions): WorkerInfo | undefined; /** Track a task assignment with a visibility timeout deadline. */ assignTask(workerId: string, operationId: string, visibilityTimeout: number, fairShareKey: string | undefined, attemptToken: string, workflowRevision?: string): void; /** * Undo exactly `assignTask`'s effects — capacity increment, fair-share * count, and the in-flight entry — when the durable claim that was * supposed to follow the reservation never committed. Distinct from * {@link completeTask} even though the body is identical: this path means * "the reservation was never realized," not "the task finished," and * keeping the names apart stops a reader from mistaking an aborted claim * for a normal completion at the call site. */ releaseReservation(operationId: string): InFlightTask | undefined; /** Return tasks whose deadline has passed and remove them from tracking. */ checkExpiredTasks(now: number): InFlightTask[]; /** * Reset the deadline for an in-flight task to `now + extension`. Returns the * new deadline, or `undefined` if the task was not found. */ extendVisibility(operationId: string, extension: number): number | undefined; /** Return all in-flight tasks assigned to a given worker. */ getWorkerTasks(workerId: string): InFlightTask[]; /** True when `operationId` is in flight on `workerId` — used at the trust boundary to reject stale completions after takeover. */ isAssignedToWorker(operationId: string, workerId: string): boolean; /** * True when `operationId` is in flight on `workerId` for the specific dispatch * attempt identified by `attemptToken`. Layered after {@link isAssignedToWorker} * to reject a stale completion from an EARLIER attempt that was reassigned to the * same worker — the only case the workerId guard alone cannot catch. * * The workerId and token must both match the current assignment exactly. */ isAssignedToAttempt(operationId: string, workerId: string, attemptToken: string): boolean; /** Check whether an operation is currently assigned to a worker. */ isAssigned(operationId: string): boolean; /** Look up an in-flight task by operationId in O(1). */ getTask(operationId: string): InFlightTask | undefined; /** Complete an in-flight task: remove tracking and decrement the worker's counter. */ completeTask(operationId: string): InFlightTask | undefined; /** Look up a worker by ID. */ getWorker(workerId: string): WorkerInfo | undefined; /** Get all registered workers. */ getAll(): WorkerInfo[]; /** Record one declined `register` attempt, evicting the oldest once the bounded log is full. */ recordRejection(entry: RegistrationRejectionEntry): void; /** Return the `limit` most recently recorded rejections, newest first. */ getRecentRejections(limit: number): RegistrationRejectionEntry[]; /** Mark one connected worker as draining so routing excludes it from new tasks. */ markWorkerDraining(workerId: string, options?: WorkerDrainOptions): WorkerDrainMutationResult | undefined; /** Clear an explicit worker drain marker. Deployment-level drains can still apply. */ clearWorkerDrain(workerId: string): WorkerDrainMutationResult | undefined; /** Mark every current and future worker with this deployment name as draining. */ markDeploymentDraining(deploymentName: string, options?: WorkerDrainOptions): WorkerDrainMutationResult; /** Clear the deployment-level drain marker for matching current and future workers. */ clearDeploymentDrain(deploymentName: string): WorkerDrainMutationResult; /** * Stable, sorted-by-id snapshot of every connected worker for the public * `weft.workers.list` operation. The caller passes a per-request `now` * so heartbeat ages across the response use one consistent clock; the * same `now` should be reused by the task-queue operation when both run * in the same request to keep the join honest. */ getWorkerSummaries(now: number): WorkerSummary[]; /** * Deployment-group snapshot for operator views. Workers without deployment * metadata are grouped together under `null` identity fields so anonymous * fleets remain visible without inventing placeholder names. */ getDeploymentSummaries(_now: number): WorkerDeploymentSummary[]; /** Get worker count. */ get size(): number; }