import type { Encodable } from "@automate.ax/codec" import { branch, closed, collect, concurrent, correlate, debounce, dependentOn, each, failed, filter, funnel, gate, markSignificant, merge, onClose, onFailure, onSuccess, outcome, once, partition, race, rateLimit, serialize, succeeded, take, window, type AdmissionOptions, type BranchResult, type BranchSection, type BranchTail, type BranchTailReturn, type CollectionOptions, type ConcurrencyOptions, type CorrelationInput, type CorrelationOptions, type Duration, type FunnelOptions, type FunnelValue, type NonEmptyArray, type RateLimitOptions, type ValidBranchTail, type ValueInheritanceOptions, type WindowUntilOptions, } from "./signal-operators" import { registerSignalMethodImplementations, type GlobalSignal, type InferSignal, type InheritSignalCoordination, isSignal, type KeyedSignal, type Signal, type AutomationError, type SignalOutcome, type Synchronous, } from "./signal-protocol" import { correlationId, timestamp } from "./signal-resolution" import { delay, timeout } from "../components/core/delay" type AdditionalSignals = Signal | readonly [Signal, ...Signal[]] type AdditionalSignalValue = TSignals extends Signal ? InferSignal : TSignals extends readonly Signal[] ? InferSignal : never /** Core methods implemented directly by the signal proxy. */ export interface SignalCoreMethods { /** * Signals cannot be converted directly to strings. Import `t` from * `automate.ax` and use it as a tagged template to interpolate them. */ toString(): never /** Transforms the receiver after its value materializes. */ transform unknown>( transformer: Synchronous extends never ? never : TTransformer, ): Signal> transform unknown>( other: Signal, transformer: Synchronous extends never ? never : TTransformer, ): Signal> transform< const TOthers extends readonly [unknown, ...unknown[]], TTransformer extends (value: T, ...others: TOthers) => unknown, >( others: { readonly [TIndex in keyof TOthers]: Signal }, transformer: Synchronous extends never ? never : TTransformer, ): Signal> /** Uses one shared partition in each cross-context operator consuming it. */ globally(): GlobalSignal /** Returns an immutable view carrying a pure partition key. */ keyBy(getKey: (value: T) => Encodable): KeyedSignal } /** Source-first utilities adapted into fluent methods. */ export interface SignalMethods { /** Traverses a selected section and optional nested else-if sections. */ branch( this: Signal, whenTrue: TTrueSection, ...tail: TTail & ValidBranchTail, TTail> ): BranchResult, BranchTailReturn> /** Emits whether the receiver closes without a value. */ closed(this: Signal): Signal /** Derives a stable keyed identity from the receiver's durable boundary. */ correlationId(this: Signal, perDeclaration?: boolean): KeyedSignal /** Re-emits the receiver after a durable relative delay. */ delay( this: TSignal, duration: Duration, ): InheritSignalCoordination> /** Preserves the receiver while attaching additional dependencies. */ dependentOn( this: TSignal, prerequisites: AdditionalSignals, ): InheritSignalCoordination> /** Runs one durable section for every item in an array signal. */ each( this: Signal, section: (item: Signal) => Signal, ): Signal each( this: Signal, section: (item: Signal) => Signal | void, ): void /** Emits whether the receiver fails. */ failed(this: Signal): Signal /** Emits every occurrence from the receiver and additional streams. */ merge( this: TSignal, signals: TSignals, ): Signal | AdditionalSignalValue> /** Emits the receiver only when its value matches a predicate. */ filter>( this: TSignal, predicate: (value: InferSignal) => value is TNarrowed, ): InheritSignalCoordination filter( this: TSignal, predicate: (value: InferSignal) => boolean, ): InheritSignalCoordination> /** Emits the receiver only when a boolean signal opens its gate. */ gate( this: TSignal, condition: Signal, ): InheritSignalCoordination> /** Marks the containing causal run as significant when the receiver emits. */ markSignificant(this: TSignal): TSignal /** Emits null or traverses a section only when the receiver closes. */ onClose(this: TSignal): Signal onClose< TSignal extends Signal, TSection extends (closure: Signal, source: TSignal) => unknown, >( this: TSignal, section: Synchronous extends never ? never : TSection, ): ReturnType /** Emits failure details or traverses a section only when the receiver fails. */ onFailure(this: TSignal): Signal onFailure< TSignal extends Signal, TSection extends ( failure: Signal, source: TSignal, ) => unknown, >( this: TSignal, section: Synchronous extends never ? never : TSection, ): ReturnType /** Emits the value or traverses a section only when the receiver succeeds. */ onSuccess( this: TSignal, ): InheritSignalCoordination> onSuccess< TSignal extends Signal, TSection extends ( value: InheritSignalCoordination>, source: TSignal, ) => unknown, >( this: TSignal, section: Synchronous extends never ? never : TSection, ): ReturnType /** Converts every receiver terminal state into a tagged value. */ outcome( this: TSignal, ): Signal>> /** Splits the receiver into complementary matching and nonmatching paths. */ partition>( this: TSignal, predicate: (value: InferSignal) => value is TNarrowed, ): readonly [ matched: InheritSignalCoordination, unmatched: InheritSignalCoordination< TSignal, Exclude, TNarrowed> >, ] partition( this: TSignal, predicate: (value: InferSignal) => boolean, ): readonly [ matched: InheritSignalCoordination>, unmatched: InheritSignalCoordination>, ] /** Selects the first causally related receiver or additional signal. */ race( this: TSignal, signals: TSignals, ): Signal | AdditionalSignalValue> /** Runs one structured work region at a time in each receiver partition. */ serialize< TSignal extends KeyedSignal | GlobalSignal, TSection extends () => unknown, >( this: TSignal, section: Synchronous extends never ? never : TSection, ): ReturnType /** Runs up to `limit` structured work regions in each receiver partition. */ concurrent< TSignal extends KeyedSignal | GlobalSignal, TSection extends () => unknown, >( this: TSignal, options: ConcurrencyOptions, section: Synchronous extends never ? never : TSection, ): ReturnType /** Emits whether the receiver succeeds with a value. */ succeeded(this: Signal): Signal /** Resolves when the receiver's durable occurrence emitted. */ timestamp(this: Signal): Signal /** Fails unless the receiver settles before a durable deadline. */ timeout( this: TSignal, duration: Duration, ): InheritSignalCoordination> /** Collects a fixed number of keyed or global receiver occurrences. */ collect( this: TSignal, count: number | Signal, options?: CollectionOptions, ): InheritSignalCoordination>> /** Emits the first receiver occurrence in each partition. */ once( this: TSignal, options?: AdmissionOptions, ): InheritSignalCoordination> /** Emits only the first `count` receiver occurrences in each partition. */ take( this: TSignal, count: number, options?: AdmissionOptions, ): InheritSignalCoordination> /** Applies a rolling rate limit to each receiver partition. */ rateLimit( this: TSignal, options: RateLimitOptions, ): InheritSignalCoordination> /** Matches a keyed receiver with one or more other keyed streams. */ correlate( this: TSignal, stream: CorrelationInput, options?: CorrelationOptions, ): Signal correlate( this: TSignal, streams: readonly [CorrelationInput, ...CorrelationInput[]], options?: CorrelationOptions, ): Signal /** Emits the latest receiver occurrence after its partition stays quiet. */ debounce( this: TSignal, duration: Duration, ): InheritSignalCoordination> /** Applies one durable burst timing and output policy to the receiver. */ funnel< TSignal extends KeyedSignal | GlobalSignal, const TOptions extends FunnelOptions, >( this: TSignal, options: TOptions, ): InheritSignalCoordination< TSignal, FunnelValue, TOptions> > /** Collects receiver occurrences admitted by a fixed-duration window. */ window( this: TSignal, duration: Duration, options?: ValueInheritanceOptions, ): InheritSignalCoordination>> window( this: TSignal, options: WindowUntilOptions, ): InheritSignalCoordination>> } /** Complete property namespace reserved for fluent signal methods. */ export type SignalReservedMethods = SignalCoreMethods & SignalMethods /** Fluent methods installed through the shared source-first adapter. */ export type SignalRegisteredMethods = Omit< SignalReservedMethods, keyof SignalCoreMethods > registerSignalMethodImplementations({ branch, closed, collect, concurrent, correlate: correlateFromSignal, correlationId, debounce, delay: delaySignal, dependentOn, each, failed, filter, funnel, gate, markSignificant, merge: mergeFromSignal, onClose, onFailure, onSuccess, outcome, once, partition, race: raceFromSignal, rateLimit, serialize, succeeded, take, timestamp, timeout, window, }) /** * Prepends a keyed receiver to a correlation's remaining streams. * * @param signal - Fluent keyed receiver. * @param streams - Remaining keyed streams. * @param options - Optional temporal ordering. */ function correlateFromSignal( signal: KeyedSignal, streams: | CorrelationInput | readonly [CorrelationInput, ...CorrelationInput[]], options?: CorrelationOptions, ): Signal { const remaining: readonly [CorrelationInput, ...CorrelationInput[]] = isCorrelationInputTuple(streams) ? streams : [streams] return correlate([signal, ...remaining], options) } /** * Narrows the fluent correlation's single-or-tuple input. * * @param value - Remaining fluent correlation input. */ function isCorrelationInputTuple( value: CorrelationInput | readonly [CorrelationInput, ...CorrelationInput[]], ): value is readonly [CorrelationInput, ...CorrelationInput[]] { return Array.isArray(value) } /** * Reorders the source-first fluent delay form for the duration-first utility. * * @param signal - Fluent source signal. * @param duration - Durable relative delay. */ function delaySignal( signal: TSignal, duration: Duration, ): InheritSignalCoordination> { return delay(duration, signal) } /** * Prepends a receiver to an independent stream union. * * @param signal - Fluent source signal. * @param signals - Remaining selection inputs. */ function mergeFromSignal(signal: Signal, signals: AdditionalSignals): Signal { const remaining: readonly [Signal, ...Signal[]] = isSignal(signals) ? [signals] : signals return merge([signal, ...remaining]) } /** * Prepends a receiver to a causal race. * * @param signal - Fluent source signal. * @param signals - Remaining race inputs. */ function raceFromSignal(signal: Signal, signals: AdditionalSignals): Signal { const remaining: readonly [Signal, ...Signal[]] = isSignal(signals) ? [signals] : signals return race([signal, ...remaining]) }