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
    • What a version includes
    • How publish builds your files
    • Publish a bundler's output
    • Preview a change with testers
    • Choose who finds your app
    • Review for listed apps
    • How open clients update
    • Change the rules of a live app
    • Read data that earlier versions wrote
    • Go back to an earlier version
    • Unpublish or delete an app
    • Your app's slug
  • Errors and offline
  • Platforms

Client API

  • tinylib
  • tinylib.data
  • tinylib.rooms
  • 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

Publishing

npx tinylib-sdk publish uploads your app's folder as a new version, a numbered copy of your files and rules that never changes. The newest version goes live for everyone at once. In a listed app, each new version first waits for Tinylib's review.

Terminal
npx tinylib-sdk publish

A run looks like this:

Text
◆ Tinylib publish ✓ Built Tic-tac-toe: 4 files, 18.2 KB, with rules. Uses notifications (ctx.notify at rules.js:31). ✓ Published Tic-tac-toe as version 3. Its 2 open rooms run the new rules from their next input, after migrate. https://tinylib.app/tic-tac-toe
npx tinylib-sdk publish Everyone 1 2 3 all at once time Open client Client 2 Client 3 reloads Each room Same state Rules 2 Rules 3 migrate(state, 2) You and testers Preview npx tinylib-sdk preview Your code Tinylib
npx tinylib-sdk publish Everyone 1 2 3 all at once time Open client Client 2 Client 3 reloads Each room Same state Rules 2 Rules 3 migrate(state, 2) You and testers Preview npx tinylib-sdk preview Your code Tinylib

What a version includes

Every file in the folder and its subfolders ships, at the same path, except:

  • files and folders whose names start with a dot, such as .env and .git
  • node_modules
  • package.json and package-lock.json
  • tinylib.json, rules.js or rules.ts, and files that only the rules import, since the rules run on Tinylib's servers and reach a device only if your page imports them

Every file that ships is public, a README or tsconfig.json included. Keep secrets out of the folder, or publish a bundler's output.

A version needs index.html and the icon tinylib.json names, a square PNG at least 512 pixels on each side. It's refused past 5 MB or 500 files, or past one of the other limits on versions.

How publish builds your files

publish builds each file the way npx tinylib-sdk dev serves it:

FileWhat publish does
.html
What publish does
Adds the tag that loads Tinylib's script first in <head>. Moves each inline <script> into a file of its own. Rewrites each relative path the page loads into a path from the app's folder.
.js, .mjs, .ts, .tsx, .jsx
What publish does
Bundles it and everything it imports with esbuild, and serves it as JavaScript at the same path
Anything else
What publish does
Ships it unchanged

The rewrite covers src, srcset, poster, a <link>'s href, and url() in the page's styles, so img.png in sub/page.html loads sub/img.png at every address. Links in <a href> stay as written and resolve from the current address (Places).

Tinylib serves index.html at every address that has no file extension and isn't one of your files, such as game/abc. A missing file with an extension, such as img/cat.png, is a 404.

Paths your JavaScript builds aren't rewritten, so start them with /: fetch('/levels.json').

A page can load a TypeScript or JSX file directly:

HTML
<script type="module" src="app.ts"></script>

Imports of npm packages resolve from node_modules, and imported JSON is inlined. TypeScript and JSX are compiled without checking types.

JSX compiles to React.createElement calls, so a file that uses JSX imports React. To use React's automatic JSX runtime instead, put a tsconfig.json in the folder:

tsconfig.json
{ "compilerOptions": { "jsx": "react-jsx" } }

A script that imports CSS or an image fails the build. Link stylesheets from the page, and load images by their path.

Every script in the folder is bundled, even one no page loads, so a test file that imports node:test stops publish with "path didn't build". Keep tests out of the folder you publish.

Publish a bundler's output

To use Vite or another bundler, publish its output folder. publish reads tinylib.json, the icon, and the rules from that folder, so put them and the files the rules import where the bundler copies files unchanged, like Vite's public/ folder:

Terminal
npx vite build npx tinylib-sdk publish dist

Set the bundler's base path to /.

Preview a change with testers

npx tinylib-sdk preview publishes the folder as your app's preview, a separate copy that only you and the testers you invite can open. Publish the app first.

Terminal
npx tinylib-sdk preview --tester ben@example.com

Each tester gets an email with the preview's link and signs in to Tinylib with that address to open it. A preview has its own data and rooms.

An app has one preview. Running preview again replaces its client and rules and restarts its expiry. Its data, rooms, and testers stay, and its rooms move to the new rules as the live app's do.

A preview never becomes the live app: to go live, run publish. A preview ends when it expires or when you run npx tinylib-sdk preview --end, and its data and rooms go with it. The developer page can also add and remove testers and end the preview.

Choose who finds your app

A new app starts unlisted. Choose who finds it on the developer page:

VisibilityWho finds it
Unlisted
Who finds it
Anyone with its link or an invitation, and anyone a friend sends it to
Friends
Who finds it
Your friends too, in Discover
Listed
Who finds it
Everyone, in Tinylib's directory, once Tinylib approves it

Review for listed apps

Listing an app asks for review. A person at Tinylib checks it against the Developer Terms. The app stays out of the directory until they approve it.

After that, each new version waits for the same review:

  • publish puts the new version in the app's preview, replacing what it held, so your testers try what Tinylib reviews.
  • Everyone keeps the current version until Tinylib approves the new one.
  • On approval, the room check runs again and the version goes live.

Tinylib emails your contact address when it approves or rejects a version, and the developer page shows the reason for a rejection. To ask again, publish a fixed version.

Choosing Unlisted or Friends takes the app out of the directory and withdraws any version waiting for review; later versions go live at once. Listing the app again asks for a new review.

How open clients update

When a version goes live, a client open in a room reloads at its current address, with no event first, and any other client uses the new version at its next open. A client that was offline reloads once it's back online, and an action it sends before reloading is refused rather than run under the new rules. The reload loses anything kept only in the page's memory, so save a half-typed message to tinylib.data as the user types.

Change the rules of a live app

Rooms run the current version's rules on state that earlier rules may have written, so new rules must handle the old shape or rooms in progress break.

To reshape old state, give the kind a migrate handler. It runs once in each room under new rules, before anything else touches the state. fromVersion is the version whose rules last handled an input in the room, which can be several versions back.

rules.js
export const game = { // ... migrate(state, fromVersion, ctx) { if (fromVersion < 4) state.scores ??= {} // version 4 added scores }, }

Once migrate succeeds, ctx.version becomes the current version's number.

Before a version goes live, Tinylib runs a room check on every room that hasn't ended, idle ones included. It runs migrate on a copy of the room's state, then each member's view, status, and canInvite. If one of them throws, migrate refuses, or the result passes one of a room's limits, the version doesn't go live and nothing changes:

Text
✗ The new rules fail on 2 of the app's 5 open rooms, so this version didn't go live. Nothing changed. - 2 rooms of the kind "game": Cannot read properties of undefined (reading 'x')

The check also runs for every preview and for every earlier version you make current again. It refuses a version that removes a kind while rooms of that kind haven't ended, or that has no rules.js while any room hasn't ended. To retire a kind, make its create refuse, and remove the kind once its last room has ended.

In an offline kind, an action a member made without signal under an earlier version is refused when it reaches the room, even when only the client changed. Tinylib takes it out of room.pending and tells the user a change couldn't be saved.

Read data that earlier versions wrote

Publishing doesn't reshape the user's data, so a new client reads whatever shape earlier clients and rules wrote. Read old shapes as well as new ones:

JavaScript
const sound = tinylib.data.get('sound') ?? !tinylib.data.get('muted') // earlier versions saved `muted`

The same goes for a member's data under rulesKeys and for room data: migrate reshapes only the room's state.

Go back to an earlier version

To undo a bad version, choose Make current beside an earlier version on the app's developer page. The command line has no rollback. Tinylib copies that version into a new one with the next number, which goes live like any other: it must pass the room check, and in a listed app it waits for review.

Rooms don't go back. They run the copy's migrate with fromVersion set to the version they ran last, so the earlier rules meet state that later rules may have reshaped. If they can't handle it, the room check refuses the rollback.

Unpublish or delete an app

Both are on the developer page. After Unpublish, people who have the app keep using it, but nobody new can open it, and it leaves the directory and Discover. Publishing again brings it back, unlisted.

Delete deletes every version and everything people saved in the app, and removes it from every library. You can't undo it.

Your app's slug

Your app's links carry the slug from tinylib.json, as in tinylib.app/tic-tac-toe, and the app's first version claims it. A slug that belongs to another developer's app or to one of Tinylib's own addresses is taken, and a slug is never used again, even after its app is deleted.

When the slug is taken the first time you publish, publish offers an available one like it, such as tic-tac-toe-k7q, and writes it to tinylib.json if you accept. Outside a terminal, it prints the available slug and stops.

A slug or a name that Tinylib's word filter catches, or that names Tinylib or another company, is refused as taken.

An app's slug can't change. A new slug in tinylib.json publishes a separate new app, and the old one stays as it was, with its users.

PreviousReminders and notificationsNextErrors and offline

On this page

  • What a version includes
  • How publish builds your files
  • Publish a bundler's output
  • Preview a change with testers
  • Choose who finds your app
  • Review for listed apps
  • How open clients update
  • Change the rules of a live app
  • Read data that earlier versions wrote
  • Go back to an earlier version
  • Unpublish or delete an app
  • Your app's slug