Skip to content
Ilha
Esc
↑↓navigate↵open⌘Jpreview
On this page

Server-sent events

Turn an EventSource into an Effect Stream that reconnects with backoff.

Ilha paints any Effect Stream. You wrap EventSource in Stream.callback, and Stream.retry reconnects after the connection drops.

import * as import CauseCause from "effect/Cause";
import * as import EffectEffect from "effect/Effect";
import * as import QueueQueue from "effect/Queue";
import * as import ScheduleSchedule from "effect/Schedule";
import * as import StreamStream from "effect/Stream";

/** Messages from `url`; reconnects with backoff capped at 30 seconds. */
export const const eventSource: (url: string, event?: string) => Stream.Stream<string, Event, never>
Messages from `url`; reconnects with backoff capped at 30 seconds.
eventSource
= (url: stringurl: string, event: stringevent = "message") =>
import StreamStream.
const callback: <string, Event, never>(f: (queue: Queue.Queue<string, Event | Cause.Done<void>>) => Effect.Effect<unknown, Event, Scope>, options?: {
    readonly bufferSize?: number | undefined;
    readonly strategy?: "sliding" | "dropping" | "suspend" | undefined;
}) => Stream.Stream<string, Event, never>
Creates a stream from a callback that can emit values into a queue. **When to use** Use when you need callback-based code to emit stream values by offering to a `Queue`, or signal stream completion through the `Queue` module APIs. By default it uses an "unbounded" buffer size. You can customize the buffer size and strategy by passing an object as the second argument with the `bufferSize` and `strategy` fields. **Example** (Creating a stream from a callback that can emit values into a queue) ```ts import.meta.vitest import { Effect, Queue, Stream } from "effect" const stream = Stream.callback<number>((queue) => Effect.sync(() => { // Emit values to the stream Queue.offerUnsafe(queue, 1) Queue.offerUnsafe(queue, 2) Queue.offerUnsafe(queue, 3) // Signal completion Queue.endUnsafe(queue) }) ) await Effect.runPromise(Stream.runCollect(stream)) // => [1, 2, 3] ```
@categoryconstructors@since4.0.0
callback
<string, Event>((queue: Queue.Queue<string, Event | Cause.Done<void>>queue) =>
import EffectEffect.
const acquireRelease: <EventSource, never, never, never>(acquire: Effect.Effect<EventSource, never, never>, release: (a: EventSource, exit: Exit<unknown, unknown>) => Effect.Effect<unknown, never, never>, options?: {
    readonly interruptible?: boolean;
}) => Effect.Effect<EventSource, never, Scope>
Constructs a scoped resource from an acquisition effect and a release finalizer. **When to use** Use to acquire a scoped resource with an explicit release finalizer. **Details** If acquisition succeeds, the release finalizer is added to the current scope and is guaranteed to run when that scope closes. The finalizer receives the `Exit` value used to close the scope. By default, acquisition is protected by an uninterruptible region. Pass `{ interruptible: true }` to allow the acquisition effect to be interrupted. **Example** (Acquiring and releasing a resource) ```ts import.meta.vitest import { Effect, Exit } from "effect" const output: Array<unknown> = [] // Simulate a resource that needs cleanup interface FileHandle { readonly path: string readonly content: string } // Acquire a file handle const acquire = Effect.gen(function*() { yield* Effect.sync(() => { output.push("Opening file") }) return { path: "/tmp/file.txt", content: "file content" } }) // Release the file handle const release = (handle: FileHandle, exit: Exit.Exit<unknown, unknown>) => Effect.sync(() => { output.push( `Closing file ${handle.path} with exit: ${ Exit.isSuccess(exit) ? "success" : "failure" }` ) }) // Create a scoped resource const resource = Effect.acquireRelease(acquire, release) // Use the resource within a scope const program = Effect.scoped( Effect.gen(function*() { const handle = yield* resource yield* Effect.sync(() => { output.push(`Using file: ${handle.path}`) }) return handle.content }) ) void output.push(Effect.runSync(program)) output // => ["Opening file", "Using file: /tmp/file.txt", "Closing file /tmp/file.txt with exit: success", "file content"] ```
@see{@link acquireDisposable} for resources that implement JavaScript disposal protocols@see{@link acquireUseRelease} for bracketing acquire, use, and release in one effect@categoryresource management@since2.0.0
acquireRelease
(
import EffectEffect.const sync: <EventSource>(thunk: LazyArg<EventSource>) => Effect.Effect<EventSource, never, never>
Creates an `Effect` that represents a synchronous side-effectful computation. **When to use** Use when you need to wrap a synchronous side-effectful operation that is not expected to throw. **Details** The provided function is evaluated lazily when the effect runs. **Gotchas** The function must not throw. If it throws, the thrown value is treated as a defect, not as a typed failure. Use `try` when throwing is expected. **Example** (Capturing synchronous logging in an Effect) ```ts import.meta.vitest import { Effect } from "effect" const output: Array<unknown> = [] const log = (message: string) => Effect.sync(() => { void output.push(message) // side effect }) // ┌─── Effect<void, never, never> // ▼ const program = log("Hello, World!") Effect.runSync(program) output // => ["Hello, World!"] ```
@see{@link try_try} for a version that can handle failures.@categoryconstructors@since2.0.0
sync
(() => {
const const source: EventSourcesource = new var EventSource: new (url: string | URL, eventSourceInitDict?: EventSourceInit) => EventSource
The **`EventSource`** interface is web content's interface to server-sent events. [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource)
EventSource
(url: stringurl);
const source: EventSourcesource.EventSource.addEventListener(type: string, listener: (this: EventSource, event: MessageEvent) => any, options?: boolean | AddEventListenerOptions): void (+2 overloads)
The **`addEventListener()`** method of the EventTarget interface sets up a function that will be called whenever the specified event is delivered to the target. [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventTarget/addEventListener)
addEventListener
(event: stringevent, (e: MessageEvent<any>e) => {
if (e: MessageEvent<any>e instanceof
var MessageEvent: {
    new <T>(type: string, eventInitDict?: MessageEventInit<T>): MessageEvent<T>;
    prototype: MessageEvent;
}
The **`MessageEvent`** interface represents a message received by a target object. [MDN Reference](https://developer.mozilla.org/docs/Web/API/MessageEvent)
MessageEvent
&& typeof e: MessageEvent<any>e.MessageEvent<any>.data: any
The **`data`** read-only property of the MessageEvent interface represents the data sent by the message emitter. [MDN Reference](https://developer.mozilla.org/docs/Web/API/MessageEvent/data)
data
=== "string") {
import QueueQueue.const offerUnsafe: <string, Event | Cause.Done<void>>(self: Queue.Enqueue<string, Event | Cause.Done<void>>, message: string) => boolean
Adds a message to the queue synchronously. Returns `false` if the queue is done. **When to use** Use when you are already in synchronous queue internals or a performance boundary where wrapping the mutation in `Effect` is intentionally avoided. **Gotchas** This is an unsafe operation that directly modifies the queue without Effect wrapping. Use this only when you're certain about the synchronous nature of the operation. **Example** (Offering a value synchronously) ```ts import.meta.vitest import { Effect, Queue } from "effect" // Create a queue effect and extract the queue for unsafe operations const program = Effect.gen(function*() { const queue = yield* Queue.bounded<number>(3) // Add messages synchronously using unsafe API const success1 = Queue.offerUnsafe(queue, 1) const success2 = Queue.offerUnsafe(queue, 2) // Check current size const size = Queue.sizeUnsafe(queue) return { offered: [success1, success2], size } }) await Effect.runPromise(program) // => { offered: [true, true], size: 2 } ```
@categoryoffering@since4.0.0
offerUnsafe
(queue: Queue.Queue<string, Event | Cause.Done<void>>queue, e: MessageEvent<any>e.MessageEvent<any>.data: string
The **`data`** read-only property of the MessageEvent interface represents the data sent by the message emitter. [MDN Reference](https://developer.mozilla.org/docs/Web/API/MessageEvent/data)
data
);
} }); const source: EventSourcesource.EventSource.addEventListener<"error">(type: "error", listener: (this: EventSource, ev: Event) => any, options?: boolean | AddEventListenerOptions): void (+2 overloads)
The **`addEventListener()`** method of the EventTarget interface sets up a function that will be called whenever the specified event is delivered to the target. [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventTarget/addEventListener)
addEventListener
("error", (e: Evente) => {
if (const source: EventSourcesource.EventSource.readyState: number
The **`readyState`** read-only property of the EventSource interface returns a number representing the state of the connection. [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/readyState)
readyState
===
var EventSource: {
    new (url: string | URL, eventSourceInitDict?: EventSourceInit): EventSource;
    prototype: EventSource;
    readonly CONNECTING: 0;
    readonly OPEN: 1;
    readonly CLOSED: 2;
}
The **`EventSource`** interface is web content's interface to server-sent events. [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource)
EventSource
.type CLOSED: 2CLOSED) {
import QueueQueue.const failCauseUnsafe: <string, Event>(self: Queue.Enqueue<string, Event>, cause: Cause.Cause<Event>) => boolean
Fails the queue with a cause synchronously. If the queue is already done, `false` is returned. **When to use** Use when queue completion must be driven from synchronous internals while preserving the full failure `Cause`. **Gotchas** This is an unsafe operation that directly modifies the queue without Effect wrapping. **Example** (Failing queues with a cause synchronously) ```ts import.meta.vitest import { Cause, Effect, Exit, Queue } from "effect" const program = Effect.gen(function*() { const queue = yield* Queue.bounded<number, string>(10) // Create a cause and fail the queue synchronously const cause = Cause.fail("Processing error") const failed = Queue.failCauseUnsafe(queue, cause) // The queue is now done with the specified failure cause const exit = Queue.takeUnsafe(queue) return [failed, exit] }) await Effect.runPromise(program) // => [true, Exit.failCause(Cause.fail("Processing error"))] ```
@categorycompletion@since4.0.0
failCauseUnsafe
(queue: Queue.Queue<string, Event | Cause.Done<void>>queue, import CauseCause.const fail: <Event>(error: Event) => Cause.Cause<Event>
Creates a `Cause` containing a single `Fail` reason with the given typed error. **When to use** Use to construct a cause from an expected typed error. **Example** (Creating a fail cause) ```ts import.meta.vitest import { Cause } from "effect" Cause.fail("Something went wrong") // => Cause.fromReasons([Cause.makeFailReason("Something went wrong")]) ```
@see{@link die} — for untyped defects@see{@link interrupt} — for fiber interruptions@categoryconstructors@since2.0.0
fail
(e: Evente));
} }); return const source: EventSourcesource; }), (source: EventSourcesource) => import EffectEffect.const sync: <void>(thunk: LazyArg<void>) => Effect.Effect<void, never, never>
Creates an `Effect` that represents a synchronous side-effectful computation. **When to use** Use when you need to wrap a synchronous side-effectful operation that is not expected to throw. **Details** The provided function is evaluated lazily when the effect runs. **Gotchas** The function must not throw. If it throws, the thrown value is treated as a defect, not as a typed failure. Use `try` when throwing is expected. **Example** (Capturing synchronous logging in an Effect) ```ts import.meta.vitest import { Effect } from "effect" const output: Array<unknown> = [] const log = (message: string) => Effect.sync(() => { void output.push(message) // side effect }) // ┌─── Effect<void, never, never> // ▼ const program = log("Hello, World!") Effect.runSync(program) output // => ["Hello, World!"] ```
@see{@link try_try} for a version that can handle failures.@categoryconstructors@since2.0.0
sync
(() => source: EventSourcesource.EventSource.close(): void
The **`close()`** method of the EventSource interface closes the connection, if one is made, and sets the EventSource.readyState attribute to 2 (closed). [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/close)
close
())
) ).Pipeable.pipe<Stream.Stream<string, Event, never>, Stream.Stream<string, Event, never>>(this: Stream.Stream<string, Event, never>, ab: (_: Stream.Stream<string, Event, never>) => Stream.Stream<string, Event, never>): Stream.Stream<string, Event, never> (+21 overloads)pipe( import StreamStream.const retry: <Event, Duration, never, never>(policy: Schedule.Schedule<Duration, Event, never, never> | (($: <SO, SE, SR>(_: Schedule.Schedule<SO, Event, SE, SR>) => Schedule.Schedule<SO, Event, SE, SR>) => Schedule.Schedule<Duration, Event, never, never>)) => <A, R>(self: Stream.Stream<A, Event, R>) => Stream.Stream<A, Event, R> (+1 overload)
Retries the stream according to the given schedule when it fails. **Details** This retries the entire stream, so will re-execute all of the stream's acquire operations. The schedule is reset as soon as the first element passes through the stream again. **Example** (Retrying stream failures) ```ts import.meta.vitest import { Effect, Schedule, Stream } from "effect" const program = Effect.gen(function*() { const values = yield* Stream.make(1).pipe( Stream.concat(Stream.fail("boom")), Stream.retry(Schedule.recurs(1)), Stream.take(2), Stream.runCollect ) values // => [ 1, 1 ] }) await Effect.runPromise(program) ```
@categoryerror handling@since2.0.0
retry
(
import ScheduleSchedule.const min: <readonly [Schedule.Schedule<Duration, unknown, never, never>, Schedule.Schedule<number, unknown, never, never>]>(schedules: readonly [Schedule.Schedule<Duration, unknown, never, never>, Schedule.Schedule<number, unknown, never, never>]) => Schedule.Schedule<Duration, unknown, never, never>
Combines schedules by recurring while at least one schedule wants to recur, using the minimum delay between recurrences and outputting that minimum delay. **When to use** Use when a combined policy should continue while any schedule still recurs, and should wait for the fastest schedule between recurrences. **Example** (Combining retry schedules by their minimum delay) ```ts import.meta.vitest import { Schedule } from "effect" const schedule = Schedule.min([Schedule.fixed("5 seconds"), Schedule.spaced("10 seconds")]) Schedule.isSchedule(schedule) // => true ```
@categorycombining@since4.0.0
min
([
import ScheduleSchedule.const exponential: (base: Input, factor?: number) => Schedule.Schedule<Duration>
Schedule that always recurs, but will wait a certain amount between repetitions, given by `base * factor.pow(n)`, where `n` is the number of repetitions so far. Returns the current duration between recurrences. **Example** (Retrying with exponential backoff) ```ts import.meta.vitest import { Duration, Effect, Schedule } from "effect" const program = Effect.gen(function*() { const step = yield* Schedule.toStep(Schedule.exponential("100 millis")) return yield* step(0, undefined) }) await Effect.runPromise(program) // => [Duration.millis(100), Duration.millis(100)] ```
@categoryconstructors@since2.0.0
exponential
("1 second"),
import ScheduleSchedule.const spaced: (duration: Input) => Schedule.Schedule<number>
Returns a schedule that recurs continuously, each repetition spaced the specified duration from the last run. **When to use** Use when each delay should start after the previous action completes. **Example** (Repeating with fixed spacing) ```ts import.meta.vitest import { Duration, Effect, Schedule } from "effect" const program = Effect.gen(function*() { const step = yield* Schedule.toStep(Schedule.spaced("2 seconds")) return yield* step(0, undefined) }) await Effect.runPromise(program) // => [0, Duration.seconds(2)] ```
@see{@link fixed} for recurrence aligned to a regular cadence@categoryconstructors@since2.0.0
spaced
("30 seconds"),
]) ) );

Closing the stream closes the connection. Ilha closes it when the component unmounts.

Paint each message

Yield the mapped stream from a generator component:

import * as import StreamStream from "effect/Stream";
import { const eventSource: (url: string) => Stream.Stream<string, Event>eventSource } from "./sse";

export function* function Ticks(): Generator<Stream.Stream<View, Event, never>, void, unknown>Ticks() {
  yield function eventSource(url: string): Stream.Stream<string, Event>eventSource("/ticks").Pipeable.pipe<Stream.Stream<string, Event, never>, Stream.Stream<View, Event, never>>(this: Stream.Stream<string, Event, never>, ab: (_: Stream.Stream<string, Event, never>) => Stream.Stream<View, Event, never>): Stream.Stream<View, Event, never> (+21 overloads)pipe(import StreamStream.const map: <string, View>(f: (a: string, i: number) => View) => <E, R>(self: Stream.Stream<string, E, R>) => Stream.Stream<View, E, R> (+1 overload)
Transforms the elements of this stream using the supplied function. **Example** (Mapping stream values) ```ts import.meta.vitest import { Effect, Option, Stream } from "effect" const stream = Stream.fromArray([1, 2, 3]).pipe(Stream.map((n, i) => n + i)) await Effect.runPromise(Stream.runCollect(stream)) // => [1, 3, 5] ```
@categorymapping@since2.0.0
map
((msg: stringmsg) => <JSX.IntrinsicElements.p: HTMLAttributes<HTMLParagraphElement>p>{msg: stringmsg}</JSX.IntrinsicElements.p: HTMLAttributes<HTMLParagraphElement>p>));
}

Read the latest value

Pass the stream to atom() to keep the latest message in a sync component:

import { const atom: AtomFnatom } from "ilha";
import { const eventSource: (url: string) => Stream<string, Event>eventSource } from "./sse";

export const const Price: () => ViewPrice = () => {
  const const price: AtomHandle<AsyncResult<string, Event>>price = atom<string, Event>(init: Effect<string, Event, AtomRegistry> | Stream<string, Event, AtomRegistry>, options?: AtomOptions<AsyncResult<string, Event>> | undefined): AtomHandle<AsyncResult<string, Event>> (+1 overload)
An effect or stream runs once; the handle holds its `AsyncResult`.
atom
(function eventSource(url: string): Stream<string, Event>eventSource("/price"));
return <JSX.IntrinsicElements.p: HTMLAttributes<HTMLParagraphElement>p>{const price: AtomHandle<AsyncResult<string, Event>>price}</JSX.IntrinsicElements.p: HTMLAttributes<HTMLParagraphElement>p>; };

Decode messages

Parse data with Schema and drop invalid messages:

import * as import FilterFilter from "effect/Filter";
import * as import SchemaSchema from "effect/Schema";
import * as import StreamStream from "effect/Stream";
import { const eventSource: (url: string) => Stream.Stream<string, Event>eventSource } from "./sse";

const 
const Tick: Schema.fromJsonString<Schema.Struct<{
    readonly n: Schema.Number;
}>>
Tick
= import SchemaSchema.
function fromJsonString<Schema.Struct<{
    readonly n: Schema.Number;
}>>(schema: Schema.Struct<{
    readonly n: Schema.Number;
}>, options?: {
    readonly reviver?: Parameters<typeof JSON.parse>[1] | undefined;
    readonly replacer?: JsonReplacer | undefined;
    readonly space?: Parameters<typeof JSON.stringify>[2] | undefined;
}): Schema.fromJsonString<Schema.Struct<{
    readonly n: Schema.Number;
}>>
Returns a schema that decodes a JSON string and then decodes the parsed value using the given schema. **Details** This is useful when working with JSON-encoded strings where the actual structure of the value is known and described by an existing schema. During decoding, the resulting schema first parses the input string as JSON, using `reviver` when provided, and then runs the provided schema on the parsed result. During encoding, it first encodes with the provided schema and then passes the result to `JSON.stringify` with the optional `replacer` and `space`. **Example** (Formatting encoded JSON) ```ts import.meta.vitest import { Schema } from "effect" const schema = Schema.Struct({ a: Schema.Number }) const schemaFromJsonString = Schema.fromJsonString(schema, { space: 2 }) Schema.encodeSync(schemaFromJsonString)({ a: 1 }) // => "{\n \"a\": 1\n}" ```
@categoryschemas@since4.0.0
fromJsonString
(import SchemaSchema.
function Struct<{
    readonly n: Schema.Number;
}>(fields: {
    readonly n: Schema.Number;
}): Schema.Struct<{
    readonly n: Schema.Number;
}>
Defines a struct schema from a map of field schemas. **Details** Each field value is a schema. Use {@link optionalKey } or {@link optional } to mark fields as optional, and {@link mutableKey } to mark them as mutable. The resulting schema's `Type` is a readonly object type with the fields' decoded types. The `Encoded` form mirrors the field schemas' encoded types. Declared fields may be inherited and are copied to own properties in the output. The `__proto__` field is accepted only when it is an own property. Parsing does not guarantee that output keys retain their input order. **Example** (Defining a basic struct) ```ts import.meta.vitest import { Schema } from "effect" const Person = Schema.Struct({ name: Schema.String, age: Schema.Number, email: Schema.optionalKey(Schema.String) }) // { readonly name: string; readonly age: number; readonly email?: string } type Person = typeof Person.Type Schema.decodeUnknownSync(Person)({ name: "Alice", age: 30 }) // => { name: "Alice", age: 30 } ```
@categoryconstructors@since3.10.0
Struct
({ n: Schema.Numbern: import SchemaSchema.const Number: Schema.Number
Type-level representation of {@link Number } . Schema for `number` values, including `NaN`, `Infinity`, and `-Infinity`. **Details** Default JSON serializer: - Finite numbers are serialized as numbers. - Non-finite values are serialized as strings (`"NaN"`, `"Infinity"`, `"-Infinity"`).
@categorymodels@since4.0.0@see{@link Finite} for a schema that excludes non-finite values.@categoryschemas@since4.0.0
Number
}));
export const
const ticks: Stream.Stream<{
    readonly n: number;
}, Event, never>
ticks
= function eventSource(url: string): Stream.Stream<string, Event>eventSource("/ticks").
Pipeable.pipe<Stream.Stream<string, Event, never>, Stream.Stream<{
    readonly n: number;
}, Event, never>>(this: Stream.Stream<string, Event, never>, ab: (_: Stream.Stream<string, Event, never>) => Stream.Stream<{
    readonly n: number;
}, Event, never>): Stream.Stream<{
    readonly n: number;
}, Event, never> (+21 overloads)
pipe
(
import StreamStream.
const filterMap: <string, {
    readonly n: number;
}, unknown>(filter: Filter.Filter<string, {
    readonly n: number;
}, unknown>) => <E, R>(self: Stream.Stream<string, E, R>) => Stream.Stream<{
    readonly n: number;
}, E, R> (+1 overload)
Filters and maps stream elements in one pass using a `Filter`. **When to use** Use to keep only stream elements accepted by a `Filter` and emit each filter success value. **Details** `Result.succeed` values are emitted and `Result.fail` values are skipped.
@see{@link filter} for keeping original elements with a boolean predicate or refinement@see{@link filterMapEffect} for an effectful `Filter`@see{@link partition} for consuming both filter success and failure values@categoryfiltering@since2.0.0
filterMap
(import FilterFilter.
const fromPredicateOption: <unknown, {
    readonly n: number;
}>(predicate: (a: unknown) => Option<{
    readonly n: number;
}>) => Filter.Filter<unknown, {
    readonly n: number;
}, unknown>
Creates a `Filter` from a function that returns an `Option`; `Some(value)` passes with `value`, and `None` fails with the original input.
@categoryconstructors@since4.0.0
fromPredicateOption
(import SchemaSchema.
const decodeUnknownOption: <Schema.fromJsonString<Schema.Struct<{
    readonly n: Schema.Number;
}>>>(schema: Schema.fromJsonString<Schema.Struct<{
    readonly n: Schema.Number;
}>>, options?: ParseOptions) => (input: unknown, options?: ParseOptions) => Option<{
    readonly n: number;
}>
Decodes an `unknown` input against a schema, returning an `Option` that is `Some` with the decoded value on success or `None` for schema mismatches. **When to use** Use when you do not know the input type statically and only need to know whether decoding succeeded. **Details** Prefer this over {@link decodeUnknownExit } or {@link decodeUnknownEffect } when you don't need error details. For input already typed as the schema's `Encoded` type use {@link decodeOption } . Options may be provided either when creating the decoder or when applying it; application options override creation options. **Gotchas** Only causes made entirely of schema issues are converted to `None`. Causes that contain defects, interruptions, or other non-schema reasons throw instead.
@categorydecoding@since3.10.0
decodeUnknownOption
(
const Tick: Schema.fromJsonString<Schema.Struct<{
    readonly n: Schema.Number;
}>>
Tick
)))
);

Server rendering

EventSource does not exist on the server. Return Stream.empty there, or render the component only in the browser:

import * as import StreamStream from "effect/Stream";
import { const eventSource: (url: string) => Stream.Stream<string, Event>eventSource } from "./sse";

export const const feed: (url: string) => Stream.Stream<string, Event, never>feed = (url: stringurl: string) =>
  module globalThisglobalThis.
var EventSource: {
    new (url: string | URL, eventSourceInitDict?: EventSourceInit): EventSource;
    prototype: EventSource;
    readonly CONNECTING: 0;
    readonly OPEN: 1;
    readonly CLOSED: 2;
}
The **`EventSource`** interface is web content's interface to server-sent events. [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource)
EventSource
=== var undefinedundefined ? import StreamStream.const empty: Stream.Stream<never, never, never>
Creates an empty stream. **Example** (Creating an empty stream) ```ts import.meta.vitest import { Effect, Stream } from "effect" await Effect.runPromise(Stream.runCollect(Stream.empty)) // => [] ```
@categoryconstructors@since2.0.0
empty
: function eventSource(url: string): Stream.Stream<string, Event>eventSource(url: stringurl);

Was this page helpful?