Remove native classes from TS runtime

This commit is contained in:
elpresidank 2026-06-01 20:26:47 -05:00
parent 952daf325d
commit dca2786828
79 changed files with 7622 additions and 6703 deletions

View file

@ -8,7 +8,6 @@ import { Effect } from "effect";
import * as S from "effect/Schema";
import type { Spec } from "./types.js";
import type { SpecRuntimeRequirements } from "./types.js";
import type { PubSubBackend } from "../backend/types.js";
import type { Flow, FlowDefinition } from "../processor/flow.js";
import { type MessageHandler } from "../messaging/consumer.js";
import {
@ -24,64 +23,71 @@ import {
const isTooManyRequestsError = S.is(TooManyRequestsError);
export class ConsumerSpec<T, E = never, R = never> implements Spec<R> {
public readonly name: string;
private readonly handler: EffectMessageHandler<T, E, R>;
private readonly concurrency: number;
declare const ConsumerSpecType: unique symbol;
constructor(
name: string,
handler: EffectMessageHandler<T, E, R>,
concurrency = 1,
export interface ConsumerSpec<T, E = never, R = never> extends Spec<R> {
readonly [ConsumerSpecType]?: {
readonly message: T;
readonly error: E;
};
readonly addEffect: (
flow: Flow<R>,
definition: FlowDefinition,
) => Effect.Effect<void, PubSubError, SpecRuntimeRequirements | R>;
}
export function makeConsumerSpec<T, E = never, R = never>(
name: string,
handler: EffectMessageHandler<T, E, R>,
concurrency = 1,
): ConsumerSpec<T, E, R> {
const addEffect = Effect.fn("ConsumerSpec.addEffect")(function* (
flow: Flow<R>,
definition: FlowDefinition,
) {
this.name = name;
this.handler = handler;
this.concurrency = concurrency;
}
static fromPromise<T>(
name: string,
handler: MessageHandler<T>,
concurrency = 1,
): ConsumerSpec<T, TooManyRequestsError | MessagingHandlerError> {
return new ConsumerSpec<T, TooManyRequestsError | MessagingHandlerError>(
name,
(message, properties, flow) =>
Effect.tryPromise({
try: () => handler(message, properties, flow),
catch: (error) =>
isTooManyRequestsError(error)
? error
: messagingHandlerError(name, `${flow.id}-${flow.name}-${name}`, error),
}),
concurrency,
);
}
addEffect(flow: Flow<R>, definition: FlowDefinition) {
const spec = this;
return Effect.gen(function* () {
const topic = definition.topics?.[spec.name] ?? spec.name;
const topic = definition.topics?.[name] ?? name;
const factory = yield* ConsumerFactory;
const consumer = yield* factory.run<T, E, R>(
{
topic,
subscription: `${flow.processorId}-${flow.name}-${spec.name}`,
handler: spec.handler,
concurrency: spec.concurrency,
subscription: `${flow.processorId}-${flow.name}-${name}`,
handler,
concurrency,
},
{ id: flow.processorId, name: flow.name, flow },
);
flow.registerConsumer(spec.name, consumer);
});
}
flow.registerConsumer(name, consumer);
});
async add(flow: Flow, pubsub: PubSubBackend, definition: FlowDefinition): Promise<void> {
const effect = this.addEffect(flow, definition) as Effect.Effect<
void,
PubSubError,
SpecRuntimeRequirements
>;
await flow.runInCompatibilityScope(effect, pubsub);
}
return {
name,
addEffect,
add: async (flow, pubsub, definition) => {
const effect = addEffect(flow as Flow<R>, definition) as Effect.Effect<
void,
PubSubError,
SpecRuntimeRequirements
>;
await flow.runInCompatibilityScope(effect, pubsub);
},
};
}
export function makeConsumerSpecFromPromise<T>(
name: string,
handler: MessageHandler<T>,
concurrency = 1,
): ConsumerSpec<T, TooManyRequestsError | MessagingHandlerError> {
return makeConsumerSpec<T, TooManyRequestsError | MessagingHandlerError>(
name,
(message, properties, flow) =>
Effect.tryPromise({
try: () => handler(message, properties, flow),
catch: (error) =>
isTooManyRequestsError(error)
? error
: messagingHandlerError(name, `${flow.id}-${flow.name}-${name}`, error),
}),
concurrency,
);
}

View file

@ -1,5 +1,5 @@
export type { Spec, SpecRuntimeError, SpecRuntimeRequirements } from "./types.js";
export { ConsumerSpec } from "./consumer-spec.js";
export { ProducerSpec } from "./producer-spec.js";
export { ParameterSpec } from "./parameter-spec.js";
export { RequestResponseSpec } from "./request-response-spec.js";
export { makeConsumerSpec, makeConsumerSpecFromPromise, type ConsumerSpec } from "./consumer-spec.js";
export { makeProducerSpec, type ProducerSpec } from "./producer-spec.js";
export { makeParameterSpec, type ParameterSpec } from "./parameter-spec.js";
export { makeRequestResponseSpec, type RequestResponseSpec } from "./request-response-spec.js";

View file

@ -6,25 +6,22 @@
import { Effect } from "effect";
import type { Spec } from "./types.js";
import type { PubSubBackend } from "../backend/types.js";
import type { Flow, FlowDefinition } from "../processor/flow.js";
export class ParameterSpec implements Spec {
public readonly name: string;
export interface ParameterSpec extends Spec {}
constructor(name: string) {
this.name = name;
}
addEffect(flow: Flow, definition: FlowDefinition) {
const spec = this;
return Effect.sync(() => {
const value = definition.parameters?.[spec.name];
flow.setParameter(spec.name, value);
export function makeParameterSpec(name: string): ParameterSpec {
const addEffect = (flow: Flow, definition: FlowDefinition) =>
Effect.sync(() => {
const value = definition.parameters?.[name];
flow.setParameter(name, value);
});
}
async add(flow: Flow, _pubsub: PubSubBackend, definition: FlowDefinition): Promise<void> {
await Effect.runPromise(this.addEffect(flow, definition));
}
return {
name,
addEffect,
add: async (flow, _pubsub, definition) => {
await Effect.runPromise(addEffect(flow, definition));
},
};
}

View file

@ -6,31 +6,34 @@
import { Effect } from "effect";
import type { Spec } from "./types.js";
import type { PubSubBackend } from "../backend/types.js";
import type { Flow, FlowDefinition } from "../processor/flow.js";
import {
ProducerFactory,
type EffectProducer,
} from "../messaging/runtime.js";
export class ProducerSpec<T> implements Spec {
public readonly name: string;
declare const ProducerSpecType: unique symbol;
constructor(name: string) {
this.name = name;
}
export interface ProducerSpec<T> extends Spec {
readonly [ProducerSpecType]?: (_: T) => T;
}
addEffect(flow: Flow, definition: FlowDefinition) {
const spec = this;
return Effect.gen(function* () {
const topic = definition.topics?.[spec.name] ?? spec.name;
export function makeProducerSpec<T>(name: string): ProducerSpec<T> {
const addEffect = Effect.fn("ProducerSpec.addEffect")(function* (
flow: Flow,
definition: FlowDefinition,
) {
const topic = definition.topics?.[name] ?? name;
const factory = yield* ProducerFactory;
const producer = yield* factory.make<T>({ topic });
flow.registerProducer(spec.name, producer as EffectProducer<unknown>);
});
}
flow.registerProducer(name, producer as EffectProducer<unknown>);
});
async add(flow: Flow, pubsub: PubSubBackend, definition: FlowDefinition): Promise<void> {
await flow.runInCompatibilityScope(this.addEffect(flow, definition), pubsub);
}
return {
name,
addEffect,
add: async (flow, pubsub, definition) => {
await flow.runInCompatibilityScope(addEffect(flow, definition), pubsub);
},
};
}

View file

@ -9,44 +9,46 @@
import { Effect } from "effect";
import type { Spec } from "./types.js";
import type { PubSubBackend } from "../backend/types.js";
import type { Flow, FlowDefinition } from "../processor/flow.js";
import {
RequestResponseFactory,
type EffectRequestResponse,
} from "../messaging/runtime.js";
export class RequestResponseSpec<TReq, TRes> implements Spec {
public readonly name: string;
private readonly requestTopicName: string;
private readonly responseTopicName: string;
declare const RequestResponseSpecType: unique symbol;
constructor(
name: string,
requestTopicName: string,
responseTopicName: string,
export interface RequestResponseSpec<TReq, TRes> extends Spec {
readonly [RequestResponseSpecType]?: {
readonly request: TReq;
readonly response: TRes;
};
}
export function makeRequestResponseSpec<TReq, TRes>(
name: string,
requestTopicName: string,
responseTopicName: string,
): RequestResponseSpec<TReq, TRes> {
const addEffect = Effect.fn("RequestResponseSpec.addEffect")(function* (
flow: Flow,
definition: FlowDefinition,
) {
this.name = name;
this.requestTopicName = requestTopicName;
this.responseTopicName = responseTopicName;
}
addEffect(flow: Flow, definition: FlowDefinition) {
const spec = this;
return Effect.gen(function* () {
const requestTopic = definition.topics?.[spec.requestTopicName] ?? spec.requestTopicName;
const responseTopic = definition.topics?.[spec.responseTopicName] ?? spec.responseTopicName;
const requestTopic = definition.topics?.[requestTopicName] ?? requestTopicName;
const responseTopic = definition.topics?.[responseTopicName] ?? responseTopicName;
const factory = yield* RequestResponseFactory;
const requestor = yield* factory.make<TReq, TRes>({
requestTopic,
responseTopic,
subscription: `${flow.processorId}-${flow.name}-${spec.name}`,
subscription: `${flow.processorId}-${flow.name}-${name}`,
});
flow.registerRequestor(spec.name, requestor as EffectRequestResponse<unknown, unknown>);
});
}
flow.registerRequestor(name, requestor as EffectRequestResponse<unknown, unknown>);
});
async add(flow: Flow, pubsub: PubSubBackend, definition: FlowDefinition): Promise<void> {
await flow.runInCompatibilityScope(this.addEffect(flow, definition), pubsub);
}
return {
name,
addEffect,
add: async (flow, pubsub, definition) => {
await flow.runInCompatibilityScope(addEffect(flow, definition), pubsub);
},
};
}