feat(ts): complete schema-first phase 2

This commit is contained in:
elpresidank 2026-06-11 07:37:59 -05:00
parent 0746d7ffd5
commit be2370ee7b
24 changed files with 465 additions and 433 deletions

View file

@ -18,7 +18,7 @@ import {
} from "./async-processor.js";
import type { Spec } from "../spec/types.js";
import type { BackendConsumer, PubSubBackend } from "../backend/types.js";
import type { FlowDefinition } from "./flow.js";
import { FlowDefinition } from "./flow.js";
import { Flow, } from "./flow.js";
import { topics } from "../schema/topics.js";
import type {
@ -128,14 +128,9 @@ const ConfigPushSchema = S.Struct({
config: S.Record(S.String, S.Unknown),
});
const FlowDefinitionSchema = S.Struct({
topics: S.optionalKey(S.Record(S.String, S.String)),
parameters: S.optionalKey(S.Record(S.String, S.Unknown)),
});
const FlowDefinitions = S.Record(S.String, FlowDefinition);
const FlowDefinitionsSchema = S.Record(S.String, FlowDefinitionSchema);
const decodeFlowDefinitions = S.decodeUnknownOption(FlowDefinitionsSchema);
const decodeFlowDefinitions = S.decodeUnknownOption(FlowDefinitions);
export function runFlowProcessorDefinitionScoped<
FlowRequirements = never,

View file

@ -43,12 +43,14 @@ import type { ProducerSpec } from "../spec/producer-spec.js";
import type { RequestResponseSpec } from "../spec/request-response-spec.js";
import type { Spec, SpecRuntimeRequirements } from "../spec/types.js";
export interface FlowDefinition {
export class FlowDefinition extends S.Class<FlowDefinition>("FlowDefinition")({
/** Topic overrides keyed by spec name */
topics?: Record<string, string>;
topics: S.optionalKey(S.Record(S.String, S.String)),
/** Parameter values keyed by spec name */
parameters?: Record<string, unknown>;
}
parameters: S.optionalKey(S.Record(S.String, S.Unknown)),
}, {
description: "Per-flow configuration: topic overrides and parameter values keyed by spec name.",
}) {}
export interface FlowProducer<T> {
readonly send: (id: string, message: T) => Effect.Effect<void, MessagingDeliveryError>;

View file

@ -3,22 +3,25 @@
*/
import { Config, Duration, Effect } from "effect";
import * as S from "effect/Schema";
export interface MessagingRuntimeConfig {
readonly consumerReceiveTimeout: Duration.Duration;
readonly consumerErrorBackoff: Duration.Duration;
readonly rateLimitRetry: Duration.Duration;
readonly rateLimitTimeout: Duration.Duration;
readonly requestTimeout: Duration.Duration;
}
export class MessagingRuntimeConfig extends S.Class<MessagingRuntimeConfig>("MessagingRuntimeConfig")({
consumerReceiveTimeout: S.Duration,
consumerErrorBackoff: S.Duration,
rateLimitRetry: S.Duration,
rateLimitTimeout: S.Duration,
requestTimeout: S.Duration,
}, {
description: "Messaging runtime timing windows for consumer receive, backoff, rate-limit retry, and request timeout.",
}) {}
export const defaultMessagingRuntimeConfig: MessagingRuntimeConfig = {
export const defaultMessagingRuntimeConfig: MessagingRuntimeConfig = MessagingRuntimeConfig.make({
consumerReceiveTimeout: Duration.millis(2_000),
consumerErrorBackoff: Duration.millis(1_000),
rateLimitRetry: Duration.millis(10_000),
rateLimitTimeout: Duration.millis(7_200_000),
requestTimeout: Duration.millis(300_000),
};
});
const durationConfig = (name: string, defaultValue: Duration.Duration) =>
Config.duration(name).pipe(
@ -48,11 +51,11 @@ export const loadMessagingRuntimeConfig = Effect.fn("loadMessagingRuntimeConfig"
defaultMessagingRuntimeConfig.requestTimeout,
);
return {
return MessagingRuntimeConfig.make({
consumerReceiveTimeout,
consumerErrorBackoff,
rateLimitRetry,
rateLimitTimeout,
requestTimeout,
} satisfies MessagingRuntimeConfig;
});
});