Core Concepts
Six foundational concepts that appear throughout all EdgeStream APIs — understand these and everything else clicks into place.
1. Envelope (IEnvelope)
Every message in EdgeStream is wrapped in an IEnvelope. The envelope is the universal container that all hooks, subscribers, and pipelines work with — regardless of transport or protocol.
export interface IEnvelope<TBody = unknown> {
meta: EnvelopeMeta; // id, transport, topic, timestamps
direction: 'incoming' | 'outgoing';
raw: string | ArrayBuffer; // original bytes as received/to-send
body: TBody; // parsed, typed message body
headers?: Record<string, string>;
attributes?: Record<string, unknown>; // set by hooks during pipeline
}
EnvelopeMeta
export interface EnvelopeMeta {
id: string; // UUID for this envelope
serverId: string; // which server received this
transport: 'signalr' | 'http-polling' | 'sse' | 'websocket';
protocol: 'json' | 'didcomm-v2' | 'cloudevents' | 'custom';
receivedAt: Date;
topic?: string; // routing key, e.g. 'workflow.started'
correlationId?: string; // link related messages
source?: string; // originating system
type?: string; // message type discriminator
}
IEnvelope<WorkflowStartedEvent>. The body property is then typed as WorkflowStartedEvent.
2. Hook
A hook is a composable, single-responsibility message processor. Hooks are ordered by priority and execute sequentially within a pipeline stage. Any hook can abort processing or pause the pipeline waiting for user input.
// Implementing a custom hook
import { BaseHook, IPipelineContext, HookResult } from 'edge-stream-js';
export class TenantValidationHook extends BaseHook {
readonly name = 'TenantValidationHook';
readonly priority = 50; // runs after decrypt (90) but before normalization (110)
async execute(context: IPipelineContext): Promise<HookResult> {
const tenantId = (context.body as any)?.tenantId;
if (!tenantId) {
context.abort('Missing tenantId in message body');
return { continue: false };
}
context.metadata.validatedTenantId = tenantId;
return { continue: true };
}
}
Built-in Hook Priority Reference
| Hook | Priority | Stage |
|---|---|---|
HookActivityLogger | 5 | Both |
DecryptHook | 90 | Incoming |
VerifySignatureHook | 95 | Incoming |
ValidationHook | 100 | Incoming |
NormalizationHook | 110 | Incoming |
PublishToSubscribersHook | Terminal | Incoming |
3. Subscriber
A subscriber is a callback function registered against a topic pattern. When a message envelope is delivered, all matching subscribers are invoked concurrently. Errors in one subscriber do not affect others.
// Subscribing to a topic
const subscription = edgeStream.subscribe(
'bas', // server ID
'workflow.execution.*', // topic pattern (wildcard)
(envelope: IEnvelope<WorkflowEvent>) => {
console.log('Event:', envelope.body.eventType);
console.log('Workflow:', envelope.meta.correlationId);
}
);
// Cleanup when done
subscription.unsubscribe();
4. Server
A server represents a single logical connection to a backend message hub. Each server has its own transport, pipeline, and subscription list. Most applications use a single 'bas' server. Multi-server patterns are used when you need separate pipelines for different message domains.
// Registering a server
edgeStream.registerServer({
id: 'bas',
type: 'bas',
name: 'BizFirst Application Server',
url: 'https://app.bizfirst.com/edge-stream-hub',
transportConfig: {
type: 'signalr',
url: 'https://app.bizfirst.com/edge-stream-hub',
accessToken: authToken,
reconnect: {
maxAttempts: 0, // infinite
initialDelayMs: 1000,
maxDelayMs: 30000,
backoffMultiplier: 2
}
}
});
5. Transport
A transport is the network layer. It knows how to connect to a remote endpoint and exchange raw bytes. Transports are swappable — the rest of the pipeline never knows which transport is in use.
| Transport | Best For | Direction |
|---|---|---|
SignalR | Browser apps with auto-reconnect and group routing | Full duplex |
WebSocket | Performance-critical paths, server-to-server | Full duplex |
SSE | Server push only, HTTP/1.1 firewalls | Server → Client |
HTTP Polling | Strict firewall environments with no WebSocket | Emulated duplex |
6. Topic
Topics are dot-delimited string identifiers that route messages to subscribers. EdgeStream supports exact matches and single-level wildcards (.*).
// Topic pattern rules
'workflow.started' // exact match
'workflow.*' // matches: workflow.started, workflow.completed, workflow.failed
// does NOT match: workflow.execution.started (multi-level)
'process.engine.node.*' // matches: process.engine.node.started
// Valid pattern characters: alphanumeric, dots, underscores, hyphens
// Wildcard: only allowed at end as .*
// Invalid: 'workflow.*.events', 'workflow..started'
.* wildcard matches exactly one additional segment. workflow.* does not match workflow.execution.started. For deeper matching, subscribe to the explicit topic or use a more specific wildcard pattern.