Portal Community

Strict Type Configuration

EdgeStream uses strict TypeScript settings across all packages. The tsconfig enforces no implicit any, strict null checks, and exact optional property types:

// tsconfig.base.json
{
  "compilerOptions": {
    "strict": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "exactOptionalPropertyTypes": true,
    "noUncheckedIndexedAccess": true
  }
}

Generic Envelope — Type Safety End to End

The IEnvelope<TBody> generic parameter flows from subscription registration all the way to the callback. Define your message type once; TypeScript enforces it everywhere.

// 1. Define your message shape
interface WorkflowNodeEvent {
  nodeId: string;
  eventType: 'started' | 'completed' | 'failed';
  status: 'running' | 'success' | 'error';
  output?: Record<string, unknown>;
  error?: { message: string; stack?: string };
  durationMs?: number;
}

// 2. Subscribe with typed envelope
const sub = edgeStream.subscribe<WorkflowNodeEvent>(
  'bas',
  'workflow.execution.*',
  (envelope: IEnvelope<WorkflowNodeEvent>) => {
    // body is typed as WorkflowNodeEvent
    const { nodeId, eventType, output } = envelope.body;  // ✅ fully typed
    const topic = envelope.meta.topic;                    // ✅ string
    const at = envelope.meta.receivedAt;                  // ✅ Date
  }
);

Interface-First Design

Every major component in EdgeStream is defined as an interface first, with implementation classes depending on those interfaces. This enables clean testing and custom implementations:

// Core interfaces
IEdgeStream     // top-level facade
IServer         // single server/domain
ITransport      // network layer
IHook           // pipeline processor
IPipelineContext // hook execution context
ISubscription   // subscription handle
ISubscriptionManager // pub/sub registry
IPipelineObserver    // observability sink

Typed Hook Context

Hook implementations can extend IPipelineContext with domain-specific context types using TypeScript generics:

// Custom context for auth hooks
interface IAuthPipelineContext extends IPipelineContext {
  readonly claims: JwtClaims;
  readonly tenantId: string;
}

// Typed hook using custom context
class AuthorizationHook implements IHook<IAuthPipelineContext> {
  readonly name = 'AuthorizationHook';
  readonly priority = 30;

  async execute(context: IAuthPipelineContext): Promise<HookResult> {
    if (!context.claims.roles.includes('workflow.read')) {
      context.abort('Insufficient permissions');
      return { continue: false };
    }
    return { continue: true };
  }
}

Discriminated Unions for Events

EdgeStream event types are discriminated unions — exhaustive switch handling with TypeScript's type narrowing:

type AgentStreamEvent =
  | { eventType: 'token'; token: string }
  | { eventType: 'tool-call'; toolName: string; args: Record<string, unknown> }
  | { eventType: 'tool-result'; result: unknown }
  | { eventType: 'error'; message: string }
  | { eventType: 'done' };

edgeStream.subscribe<AgentStreamEvent>('bas', 'agent.chat.*', (envelope) => {
  const event = envelope.body;
  switch (event.eventType) {
    case 'token':
      appendToken(event.token);        // event.token: string ✅
      break;
    case 'tool-call':
      startTool(event.toolName);       // event.toolName: string ✅
      break;
    case 'done':
      markComplete();
      break;
    // TypeScript warns if you miss a case
  }
});

Transport Status as Union Type

type TransportStatus = 'disconnected' | 'connecting' | 'connected' | 'reconnecting' | 'error';
type EdgeStreamStatus = 'idle' | 'starting' | 'running' | 'stopping' | 'error';
type TransportType = 'signalr' | 'http-polling' | 'sse' | 'websocket';
type ServerType = 'bas' | 'chat';
type EnvelopeProtocol = 'json' | 'didcomm-v2' | 'cloudevents' | 'custom';

Type-Only Imports

EdgeStream uses import type throughout to minimize compiled output and make tree-shaking more effective:

// Internal usage in EdgeStream.ts
import type {
  IEdgeStream,
  EdgeStreamConfig,
  EdgeStreamStatus,
  IServer,
  ISubscription,
  SubscriberCallback,
} from './types';
Type Exports All types are re-exported from edge-stream-js/index.ts via export * from './types'. Import them with import type { IEnvelope, IHook } from 'edge-stream-js' for zero runtime cost.