sveltekit-sse v0.12.4
SvelteKit SSE
This library provides an easy way to produce and consume server sent events.
Install with:
npm i -D sveltekit-sse
Create your server sent event with:
// src/routes/custom-event/+server.js
import { events } from 'sveltekit-sse'
/**
* @param {number} milliseconds
* @returns
*/
function delay(milliseconds) {
return new Promise(function run(resolve) {
setTimeout(resolve, milliseconds)
})
}
export function POST({ request }) {
return events({
request,
async start({ emit }) {
while (true) {
emit('message', `the time is ${Date.now()}`)
await delay(1000)
}
},
})
}
and consume the source on your client with:
<script>
// src/routes/+page.svelte
import { source } from 'sveltekit-sse'
const value = source('/custom-event').select('message')
</script>
{$value}
!CAUTION Due to how the beacon api works, you must write all your logic within the
start()
function while on the server.\ You can read more on this here.
In other words, this is wrong
export function POST({ request }) {
const message = `the time is ${Date.now()}` // <=== wrong, move this below
return events({
request,
async start({ emit }) {
while (true) {
emit('message', message)
await delay(1000)
}
},
})
}
And this is the correct way to do it
export function POST({ request }) {
return events({
request,
async start({ emit }) {
const message = `the time is ${Date.now()}` // <=== this is correct
while (true) {
emit('message', message)
await delay(1000)
}
},
})
}
Reconnect
You can reconnect to the stream whenever the stream closes
<script>
import { source } from 'sveltekit-sse'
const data = source('/custom-event', {
close({ connect }) {
console.log('reconnecting...')
connect()
},
})
setTimeout(function run() {
data.close()
}, 3000)
</script>
{$data}
Cancel detection
You can run code when a connection is canceled
by setting
cancel()
export function POST({ request }) { return events({ request, start({ emit, lock }) { emit('message', 'hello') lock.set(false) }, cancel() { console.log('Connection canceled.') }, }) }
or by returning a function from
start()
export function POST({ request }) { return events({ request, start({emit, lock}) { emit('message', 'hello') lock.set(false) return cancel(){ console.log("Connection canceled.") } } }) }
Both ways are valid.
Custom Headers
You can apply custom headers to the connection
<script>
import { source } from 'sveltekit-sse'
const connection = source('/event', {
options: {
headers: {
Authorization: 'Bearer ...',
},
},
})
const data = connection.select('message')
</script>
{$data}
Transform
While on the client, you can transform the stream into any type of object you want by using source::select::transform
.
The transform
method receives a string
, which is the value of the store.
Here's an example how to use it.
<script>
import { source } from 'sveltekit-sse'
const connection = source('/custom-event')
const channel = connection.select('message')
const transformed = channel.transform(function run(data) {
return `transformed: ${data}`
})
$: console.log({ $transformed })
</script>
Json
You can parse incoming messages from the source as json using source::select::json
.
<script>
import { source } from 'sveltekit-sse'
const connection = source('/custom-event')
const json = connection.select('message').json(
function or({error, raw, previous}){
console.error(`Could not parse "${raw}" as json.`, error)
return previous // This will be the new value of the store
}
)
$: console.log({$json})
</svelte>
When a parsing error occurs, or
is invoked.\
Whatever this function returns will become the new value of the store, in the example above previous
, which is the previous (valid) value of the store.
Locking
All streams are locked server side by default, meaning the server will keep the connection alive indefinitely.
The locking mechanism is achieved through a Writable<bool>
, which you can access from the start
function.
export function POST({ request }) {
return events({
request,
start({ emit, lock }) {
emit('message', 'hello world')
setTimeout(function unlock() {
lock.set(false)
}, 2000)
},
})
}
The above code emit
s the hello world
string to the message
event and closes the stream after 2 seconds.
!WARNING You should not send any more messages after invoking
lock.set(false)
otherwise youremit
function will result into an error.\ The resulting error is wrapped inUnsafe<void>
, which you can manage using conditionals
lock.set(false)
const { error } = emit('message', 'I have a bad feeling about this...')
if (error) {
console.error(error)
return
}
Beacon
Currently there is no way to detect canceled http connections in SvelteKit.
This poses a big issue for server sent events because that means there is no way to detect when to automatically unlock
the stream and stop emitting data.
The current solution to this problem is using beacons to keep the stream alive.
The algorithm is simple in theory, but it requires both server and client to cooperate
- Server: Accept client connection.
- Server: Open a stream to the client.
- Server: Schedule a stream destructor in
T
milliseconds. - Client: Send a beacon to the server to verify you're alive.
- Server: Reset the stream destructor if the beacon is valid.
- Repeat from step 3.
The key part here is obviously T
, which lives on both the client and the server.\
Let's call them TClient
and TServer
.
In order for this to work TClient
should always be lesser than TServer
.\
If possible, you should also take into account network latency and add a bit more padding to either TClient
or TServer
.
You can set TClient
as you're invoking source
const connection = source('/events', {
beacon: 3000, // <=== this is TClient
})
And TServer
as you're invoking events
export function POST({ request }) {
return events({
request,
timeout: 5000, // <=== this is TServer
start() {
// ...
},
})
}
You don't need to manually set these variables up yourself, but you can.\
The default values are beacon: 5000
and timeout: 7000
.
!NOTE You can set both
timeout
andbeacon
to0
or any negative value to disable beacons completely.
Other notes
- Multiple sources connecting to the same path will use the same cached connection.
- When the readable store becomes inactive, meaning when the last subscriber unsubscribes from the store, the background connection is closed.
- (Then) When the first subscription is issued to the store, the store will attempt to connect (again) to the server.
5 days ago
8 days ago
10 days ago
11 days ago
12 days ago
11 days ago
11 days ago
11 days ago
11 days ago
11 days ago
12 days ago
18 days ago
19 days ago
19 days ago
23 days ago
23 days ago
25 days ago
25 days ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
4 months ago
5 months ago
5 months ago
5 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
7 months ago
6 months ago
7 months ago
6 months ago
6 months ago
7 months ago
6 months ago
6 months ago
6 months ago
6 months ago
7 months ago
6 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
11 months ago
11 months ago
11 months ago
11 months ago
11 months ago