Tinylib
DiscoverDocsSign in
Sign in
Tinylib
AboutDiscoverDocsMake an appPricingTermsPrivacyDeveloper TermsContact

Start

  • Quickstart
  • How Tinylib works

Guides

  • Data
  • Rooms and rules
  • Games
  • Friends
  • Places and links
  • Reminders and notifications
  • Publishing
  • Errors and offline
  • Platforms

Client API

  • tinylib
  • tinylib.data
  • tinylib.rooms
    • create()
    • open()
    • follow()
    • join()
    • invite()
    • getLink()
    • leave()
    • list()
    • onChange()
    • data()
    • OpenRoom
    • Following
    • RoomListItem
  • tinylib.invitations
  • tinylib.friends
  • tinylib.places
  • tinylib.reminders
  • Types
  • tinylib-sdk/kits

Rules API

  • Kinds
  • ctx

Reference

  • tinylib.json
  • Command line
  • Limits

Plain text

  • llms.txt
  • llms-full.txt

tinylib.rooms

Rooms and rules explains rooms.

tinylib.rooms.create()

tinylib.rooms.create(kind: RoomKind, options?: unknown, how?: { rematchOf?: string }): Promise<{ id: string }>
Creates a room of kind kind, with the user as its first member.
JavaScript
const { id } = await tinylib.rooms.create('game') const { id: rematchId } = await tinylib.rooms.create('game', {}, { rematchOf: id })

Parameters

kindRoomKind
The name of one of rules.js's exports.
optionsunknownoptional
Any value JSON can represent. The kind's create receives it as its first argument.
how{ rematchOf?: string }optional
how.rematchOfstringoptional
The id of a room the user is still in, ended or not. When the new room's canInvite lets the user invite, Tinylib invites that room's other members, except guests, and marks each invitation with rematchOf.

Returns

Promise<{ id: string }>
The new room's id, as { id }.

Rejects with

refused
When the kind's create refuses, the user is a guest and the kind has guests: false, or the user is a guest and rematchOf is given.
not_member
When the user isn't a member of the room rematchOf names.
limit
When the user is already in 50 rooms of your app, has created rooms faster than 60 per hour in your app, options is over 16 KB, or the state create returns is past the room's limits.
failed
When kind isn't an export of rules.js, or the kind's create throws.
offline
TypeError
When options can't be represented as JSON, or rematchOf isn't a string.

tinylib.rooms.open()

tinylib.rooms.open<K extends RoomKind = RoomKind>(id: string): Promise<OpenRoom<K>>
Opens the room in this client and connects the user to it. Most apps call tinylib.rooms.follow instead, which opens the room and keeps it open.

Opening a room right after this client made it with create or joined it with join or an invitation resolves at once, with the view that call brought, and connects behind it. A room.act made meanwhile waits for the connection, and rejects with offline if it doesn't come. Any other room opens once it's connected.

Without a connection, a room of an offline kind opens from the copy this device saved when it last had the room open, and connects once the device is back online. A room of any other kind opens once the device is back online.

While the room is open in any client of the user's, ctx.notify from the room doesn't reach them on any device. The room stays open until room.close(), until the user leaves your app, or until the device loses its connection. A tab out of view for a minute lets go of the connection, and connects again once it's back in view.

JavaScript
const room = await tinylib.rooms.open(id)

Parameters

<K>RoomKindoptional
The room's kind, which types its view and events.

Default: RoomKind

idstring
The room's id.

Returns

Promise<OpenRoom<K>>
The open room.

Rejects with

not_member
When the user isn't a member of the room, or no room has the id id.
ended
When the room has ended.
limit
When this client already has 10 rooms open. Close the ones you no longer show.
offline
Unless the room is of an offline kind this device has had open.

Caveats

  • Moving to another address doesn't close the room. Call room.close() when you stop showing it.

tinylib.rooms.follow()

tinylib.rooms.follow<K extends RoomKind = RoomKind>(id: string, onView: (following: Following<K>) => void): () => void
Opens the room and keeps it open, calling onView with each new view and each change to pending. Without a connection, a room of an offline kind opens from the copy this device saved when it last had the room open. Any other room opens once the device is back online, and onView isn't called until then.

When the room ends or the user leaves it, onView gets a last call with room.ended or room.left true. When the room can't be opened, onView gets one call with error. Either way, the room closes and onView isn't called again.

Until you call the function follow returns, the room is open, so ctx.notify doesn't reach the user.

follow doesn't pass events: call room.onEvent on the first call without an error.

JavaScript
const stop = tinylib.rooms.follow(id, ({ view, room, error }) => { if (error) toast(error.message) else if (room.left) tinylib.places.go('') else if (view) drawBoard(view, room.ended) })

Parameters

<K>RoomKindoptional
The room's kind, which types its views.

Default: RoomKind

idstring
The room's id.
onView(following: Following<K>) => void
Called with a Following: the latest view, the open room, and any error.

Returns

() => void
A function that closes the room and stops the calls. You can call it from inside onView.

Throws

TypeError
When onView isn't a function.

tinylib.rooms.join()

tinylib.rooms.join(code: string): Promise<{ id: string }>
Joins the room whose short code is code.
JavaScript
const { id } = await tinylib.rooms.join('K7QD2M')

Parameters

codestring
A code from tinylib.rooms.getLink. Case and spaces around it don't matter.

Returns

Promise<{ id: string }>
The room's id, as { id }.

Rejects with

not_found
When the code doesn't let the user into a room of your app now.
refused
When the user or a member has blocked the other, or the user is a guest and the kind has guests: false.
limit
When the user is already in 50 rooms of your app, has tried codes faster than 10 per minute in your app, or the room already has 64 members.
offline

tinylib.rooms.invite()

tinylib.rooms.invite(id: string, userId?: string): Promise<void>
Invites userId to the room and resolves once the invitation is sent. Without userId, it opens Tinylib's invite sheet, where the user picks any of their friends, with or without your app, or shares the room's link. It then resolves when the sheet closes, without saying who was invited.
JavaScript
await tinylib.rooms.invite(id, friend.id)

Parameters

idstring
The room's id.
userIdstringoptional
The id of someone the user has met in your app, such as a friend from tinylib.friends.list or a member of one of the user's rooms.

Rejects with

refused
When the kind's canInvite doesn't let the user invite now, userId is a guest's or the user's own, or the user is a guest.
not_found
When userId isn't someone the user has met in your app.
not_member
When the user isn't a member of the room.
ended
When the room has ended.
limit
When the user has sent invitations faster than 60 per hour in your app.
offline

tinylib.rooms.getLink()

tinylib.rooms.getLink(id: string, options?: { code?: boolean }): Promise<{ url: string; code: string | null }>
Gets the room's link, and its short code if you ask for one. The link and code work while canInvite is true for at least one member.

Someone who opens the link joins the room, with or without a Tinylib account unless the kind has guests: false. Tinylib then opens your app at the kind's address.

The link changes from time to time, so call getLink each time you share it.

JavaScript
const { url, code } = await tinylib.rooms.getLink(id, { code: true })

Parameters

idstring
The room's id.
options{ code?: boolean }optional
options.codebooleanoptional
If true, also makes a short code, which people type into your app for tinylib.rooms.join.

Returns

Promise<{ url: string; code: string | null }>
url, the room's link, and code, its short code, or null when nobody has asked for one.

Rejects with

refused
When the kind's canInvite doesn't let the user invite now.
not_member
When the user isn't a member of the room.
ended
When the room has ended.
failed
When Tinylib couldn't make a short code.
offline

tinylib.rooms.leave()

tinylib.rooms.leave(id: string): Promise<void>
Removes the user from the room, which runs the kind's leave with 'left'. The room stays in list() with left true.
JavaScript
await tinylib.rooms.leave(id)

Parameters

idstring
The room's id.

Rejects with

ended
When the room has ended.
not_member
When the user isn't a member of the room, or no room has the id id.
offline

tinylib.rooms.list()

tinylib.rooms.list(): Promise<RoomListItem[]>
Lists the user's rooms in your app, including those that ended or that the user left. Offline, it resolves with only the ended and left rooms this device last heard of, so keep the id of a room your app needs offline, for example in tinylib.data.
JavaScript
const waiting = (await tinylib.rooms.list()).filter((room) => room.waiting)

Returns

Promise<RoomListItem[]>
The rooms, each a RoomListItem: current rooms, oldest first; then ended rooms, latest ended first; then rooms the user left, latest left first.

tinylib.rooms.onChange()

tinylib.rooms.onChange(fn: (list: RoomListItem[]) => void): () => void
Calls fn with the user's rooms after anything in the list changes, but not at once: call list() for the first list.
JavaScript
tinylib.rooms.onChange((rooms) => { drawRooms(rooms.filter((room) => !room.ended && !room.left)) })

Parameters

fn(list: RoomListItem[]) => void
Called with the rooms, in the order list() gives.

Returns

() => void
A function that stops the calls.

Throws

TypeError
When fn isn't a function.

tinylib.rooms.data()

tinylib.rooms.data(roomId: string): RemoteStore<unknown>
Returns a read-only store of the room's data, which the rules write with ctx.data.room. A user who left reads it as it was when they left.
JavaScript
const moves = await tinylib.rooms.data(id).values('move/')

Parameters

roomIdstring
The room's id.

Returns

RemoteStore<unknown>
A RemoteStore of the room's data. Its methods reject with not_member when the user was never a member of the room. Without a connection, they reject with offline, unless the room has ended or the user left it and this device has read its data before.

OpenRoom

interface OpenRoom<K extends string = RoomKind>
A room open in this client, as tinylib.rooms.open resolves with it.

Properties

idstringread-only
The room's id.
kindstringread-only
The room's kind: the name of the rules.js export it runs.
viewRoomView<K> | nullread-only
What the kind's view last returned for the user, or the whole state in an offline kind. Null until the first view arrives.
endedbooleanread-only
True once the room has ended with the user in it. A user who leaves gets left instead, even when leaving ends the room.
leftbooleanread-only
True once the user is no longer a member: they left, or the rules removed them.
pendingreadonly { readonly action: string; readonly data: unknown }[]read-only
In an offline kind, the user's actions whose view hasn't reached this page yet, oldest first, each with the action and data passed to act. One leaves once view includes it, or once the room refuses it. Always empty in other kinds. Draw them over view, in a way that's safe to repeat: an action can still be here after view includes it.
JavaScript
const items = room.view?.items ?? [] const adding = room.pending.filter((p) => p.action === 'add' && !items.some((item) => item.id === p.data.id))

Methods

close()
Closes the room in this client: views and events stop, and act rejects with failed.
act(action, data?)
Sends action, with data, to the rules.
relay(data)
Sends numbers straight to the room's other members, for live data such as positions and aim.
onRelay(fn)
Calls fn with each relay another member sends, with their id and when they sent it.
onView(fn)
Calls fn with the current view right after this call, if there is one, and then with each new view.
onEvent(fn)
Calls fn with each event the rules send the user with ctx.emit, after the view from the same input.
onChange(fn)
Calls fn when ended, left, or pending changes.

room.close()

room.close(): void
Closes the room in this client: views and events stop, and act rejects with failed. The user stays a member, and once no client of theirs has the room open, ctx.notify reaches them again.

room.act()

room.act(action: string, data?: unknown): Promise<void>
Sends action, with data, to the rules. With a connection, it resolves once the rules accept the action, after this client has the views and events it caused.

In an offline kind, it also works without a connection. The action waits in pending, the call resolves once the device has saved it, and Tinylib sends it when the device reconnects. If the rules then refuse it, it leaves pending and Tinylib tells the user a change couldn't be saved.

An action from a page older than the current version, online or offline, is refused without running your rules, and the page reloads onto the current version.

JavaScript
try { await room.act('move', 4) } catch (error) { if (error.code !== 'refused') throw error toast(error.message) }

Parameters

actionstring
The name of one of the kind's actions.
dataunknownoptional
Any value JSON can represent. The action's handler receives it as its second argument.

Rejects with

refused
When the rules refuse the action, with the message the rules gave ctx.refuse, or when the page is older than the current version.
limit
When data is over 16 KB, the user has sent the room 30 per second actions this second, or the action would leave the room's state or room data past its limit. The message is the room's, such as "This room is full."
not_member
When the user is no longer a member of the room.
ended
When the room has ended.
offline
When the device has no connection and the room's kind isn't offline.
failed
  • When the rules throw or have no action named action. Your developer page shows the error.
  • When the room was closed in this client.
TypeError
When action is empty or isn't a string, or data can't be represented as JSON.

room.relay()

room.relay(data: readonly number[]): void
Sends numbers straight to the room's other members, for live data such as positions and aim. Every other member with the room open gets them in onRelay, with when this client sent them, in the order it sent them, after up to 25 ms in the room, with each number between -10 billion and 10 billion rounded to 5 decimal places. A kind with a beat gets them too, at its next beat; otherwise your rules never see a relay. The room keeps none: a member who opens the room later gets none of the earlier ones, and a relay sent before the room has connected, or while the device is reconnecting, is lost. Use act for what happens once, such as a hit or a pickup.

See Send live data with a relay.

JavaScript
const sending = setInterval(() => { if (room.ended || room.left) return clearInterval(sending) room.relay([me.x, me.y, me.aim]) }, 50)

Parameters

datareadonly number[]
An array of at most 32 finite numbers.

Throws

limit
When this client has sent 30 per second relays in this room in the last second.
not_member
When the user is no longer a member of the room.
ended
When the room has ended.
failed
When the room was closed in this client.
TypeError
When data isn't an array of finite numbers, or is too long.

room.onRelay()

room.onRelay(fn: (from: string, data: number[], at: number) => void): () => void
Calls fn with each relay another member sends, with their id and when they sent it. Unlike events, relays that arrive while the room has no relay listener are dropped, not held for the next one.
JavaScript
room.onRelay((from, [x, y, aim], at) => players.get(from)?.heard({ x, y, aim, at }))

Parameters

fn(from: string, data: number[], at: number) => void
Called with the sender's id, their numbers, and when their client sent them, on the clock tinylib.now reads, up to 100 milliseconds ahead of it.

Returns

() => void
A function that stops the calls.

Throws

TypeError
When fn isn't a function.

room.onView()

room.onView(fn: (view: RoomView<K>) => void): () => void
Calls fn with the current view right after this call, if there is one, and then with each new view.
JavaScript
room.onView((view) => drawBoard(view))

Parameters

fn(view: RoomView<K>) => void
Called with the view.

Returns

() => void
A function that stops the calls.

Throws

TypeError
When fn isn't a function.

room.onEvent()

room.onEvent(fn: (event: RoomEvent<K>) => void): () => void
Calls fn with each event the rules send the user with ctx.emit, after the view from the same input. Events that arrive while the room has no event listener wait for the next one.
JavaScript
room.onEvent((event) => playSound(event.sound))

Parameters

fn(event: RoomEvent<K>) => void
Called with the event.

Returns

() => void
A function that stops the calls.

Throws

TypeError
When fn isn't a function.

room.onChange()

room.onChange(fn: () => void): () => void
Calls fn when ended, left, or pending changes.

Parameters

fn() => void
Called with no arguments. Read the room's properties for what changed.

Returns

() => void
A function that stops the calls.

Throws

TypeError
When fn isn't a function.

Following

interface Following<K extends string = RoomKind>
What tinylib.rooms.follow calls its onView with.

Properties

viewRoomView<K> | nullread-only
The room's latest view, the same as room.view.
roomOpenRoom<K> | nullread-only
The open room, or null when error is set.
errorTinylibError | nullread-only
The error tinylib.rooms.open would have rejected with, or null.

RoomListItem

interface RoomListItem
One of the user's rooms, as tinylib.rooms.list() resolves with it.

Properties

idstring
The room's id.
kindstring
The room's kind: the name of the rules.js export it runs.
namestring | null
The name the rules set with ctx.setName, or null.
statusstring | null
The user's status text, as the kind's status last returned it, or null.
waitingboolean
True when the user's last status said the room is waiting on them. Always false once the room has ended or the user has left.
membersMember[]
The room's members, each a Member: its current ones, or those it had when it ended or the user left. Leaves out anyone blocked either way, and anyone who has deleted their data.
endedboolean
True once the room has ended.
closedboolean
True when the room ended without the rules calling ctx.end: nobody used it for its kind's idleMs, or its last member left.
leftboolean
True when the user left the room or was removed from it, for example by the rules or by blocking a member. ended then stays false, even if the room ends later.
mutedboolean
True when the user muted this room's notifications. ctx.notify doesn't reach them, but status and waiting still update.
endedAtnumber | null
When the room ended, in milliseconds on Tinylib's clock, or null when it hasn't.
Previoustinylib.dataNexttinylib.invitations

On this page

  • create()
  • open()
  • follow()
  • join()
  • invite()
  • getLink()
  • leave()
  • list()
  • onChange()
  • data()
  • OpenRoom
  • close()
  • act()
  • relay()
  • onRelay()
  • onView()
  • onEvent()
  • onChange()
  • Following
  • RoomListItem