Giter Site home page Giter Site logo

liveblocks / liveblocks Goto Github PK

View Code? Open in Web Editor NEW
3.0K 15.0 234.0 288.05 MB

Liveblocks is a platform to ship collaborative features like comments, notifications, text editors in minutes instead of months.

Home Page: https://liveblocks.io

License: Apache License 2.0

JavaScript 2.03% TypeScript 78.64% CSS 2.57% Shell 0.57% HTML 0.09% PEG.js 0.24% MDX 15.86%
websockets real-time multiplayer presence collaboration toolkit liveblocks crdt react database

liveblocks's Introduction

Liveblocks Liveblocks

X Discord YouTube License

Liveblocks is a real-time collaboration infrastructure for building performant collaborative experiences.

Liveblocks

How Liveblocks works

The foundations of Liveblocks are built upon four core concepts: products, rooms, integrations, and platform.

Products

Liveblocks is a fully integrated solution built around core products, each enabling a different facet of collaborative experiences: Presence, Broadcast, Document, and Comments (public beta). You can decide what products you want to use based on your requirements and collaborative experiences you’re looking to build.

Rooms

A room is the digital space in which people collaborate. You can require your users to be authenticated to interact with rooms, and each room can have specific permissions and metadata associated with them.

Integrations

Integrations for specific libraries and frameworks to add Liveblocks-powered collaborative experiences to your product. Integrations are designed to serve various collaboration use cases such as text editors, comments, creative tools, forms, and more.

Platform

Liveblocks provides a fully-hosted platform built around a WebSocket edge infrastructure that effortlessly scales to millions of users. The platform equips you with a set of powerful tools such as our REST API, webhooks, schema validation, analytics, and more.

Developers

You can read our release notes here.

Community and support

  • GitHub issues to file bugs and errors you encounter using Liveblocks.
  • Discord to get involved with the Liveblocks community, ask questions and share tips.
  • Email to contact us directly for support and sales enquiries.
  • X to receive updates, announcements, blog posts, and general Liveblocks tips.

License

Licensed under the Apache License 2.0, Copyright © 2021-present Liveblocks.

See LICENSE for more information.

liveblocks's People

Contributors

adamyonk avatar adigau avatar antoinewg avatar bitphinix avatar captaindev404 avatar ctnicholas avatar daikiojm avatar dependabot[bot] avatar flowflorent avatar gsciarra avatar guillaumesalles avatar guswillemann avatar itsmapleleaf avatar jrowny avatar lantianyou avatar marcbouchenoire avatar martolini avatar mxstbr avatar nimeshnayaju avatar nvie avatar ofoucherot avatar pierrelevaillant avatar rayoverweij avatar rickyc0626 avatar stevenfabre avatar taylorpwhipp avatar tobias-hubql avatar vinodsantharam avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

liveblocks's Issues

Preparing a room's storage

Is there a way to populate the storage for a room on the server?

What I'm after

For TLDraw, I'd like to implement a "copy to multiplayer file" feature, where a use can create a new multiplayer file with the content of their current file. Usually, this would be for turning an offline file into a new multiplayer one, but it could also be used to "fork" multiplayer projects.

image

There are probably a few different ways to do this. Since I'm using Next.js, my idea was to send the current document to an endpoint that would reply with the room id; which I could then navigate to.

  const handleCopyToMultiplayerRoom = React.useCallback(async () => {
    const res = await fetch('http://tldraw.com/api/create-multiplayer-room', {
      // ...
      body: JSON.stringify(state.document),
    }).then((res) => res.json())

    window.location.href = `http://tldraw.com/r/${res.roomId}`
  }, [])

The endpoint would then authenticate with Liveblocks and enter a new room with data from that document as part of its defaultStorageRoot. This would "prepare" the room with content; and once it was ready, the endpoint would return the roomId and the client would take it from there.

When the user arrives at the room, their content would be waiting for them.

What I tried

My first naive attempt at this didn't work—I could create the room on the server, however the room state would never change and room.getStorage promise would never resolve. Either way, the content wouldn't be there when the user arrived.

Perhaps you know a better way to do it?

(The alternative I'm considering would be to save the JSON in a separate database and then request it in the getServerSideProps of the room's dynamic route and use it as the defaultStorageRoot for that route; however I'd like to avoid it if possible.)

Expose the room Object when using useSelector hook

Expose the Room object when using useSelector hook.
When using the Zustand package, it is possible to spread undo and redo functions from the room object: const undo = useStore((state) => state.liveblocks.room?.history.undo);
const redo = useStore((state) => state.liveblocks.room?.history.redo);

When using the Redux package, we have to use client.getRoom(roomId).history.undo.

docs

image

This says to use your "secret key". Shouldn't it be the public one?

Build internals.d.ts "on top" of public index.d.ts

The current way the public vs internal libraries are built make it their definitions potentially conflicting and incompatible when you're using and mixing both in secondary packages.

For example:

  • We have a centrally defined public type, e.g. Json
  • In the internals module, we define a function that uses this Json type, say privateFn(value: Json)

The way rollup is configured now is that it duplicates the Json definition and the private function and rolls them all up in internal.d.ts. This is wrong.

Why?

In an external package, suppose we have:

import { Json } from '@liveblocks/client';
import { privateFn } from '@liveblocks/client/internal';

const value: Json = ...;
privateFn(value);
//             ^^^^^ Error!

To fix this, we can:

import { privateFn, Json } from '@liveblocks/client/internal';

const value: Json = ...;
privateFn(value);
//             ^^^^^ OK!

Why is this? It's because technically TypeScript cannot be sure that the types are the same.

import { Json } from '@liveblocks/client';
import { Json } from '@liveblocks/client/internal';
import { Json } from 'whatever';

To TypeScript, all of the above imports might be completely different types. We'll need to have a way of telling Typescript that the public and internal Json types are the same! One way to do that would be to not duplicate all those public APIs, but instead let rollup build this output in a way where the internals module locally imports from "./index.d.ts" somehow.

Automate package publishing to NPM

Publishing the packages to NPM involves a lot of subtle steps that are currently carried out manually. There's a lot of room for error if you miss a subtle step or make a mistake.

By automating this process, we can perform various checks upfront, and help the package maintainers to more quickly release a version to NPM in the future.

Feature request: "Match making"

Feature request from @CTNicholas

https://discord.com/channels/913109211746009108/913157273046646795/938057663055560764


Another handy feature would be a room that automatically generates its own ID, and autofills up to a certain capacity. Once the current room is full, users start filling up a new room. This would be great for public facing pages; Next.js Conf had this feature, with random people's live cursors appearing on screen. It would also be handy for any games built on Liveblocks, as a matchmaking mechanic. Quick example:

<RoomProvider
  matchmaking="busiest"
  prefix="live-cursors-"
  maxUsers={20}
  onCreate={id => {
    // 'live-cursors-cwg3h46shkd4h4hjshs'
    console.log(id)
  }
/>

I've written matchmaking="busiest" because there could be multiple algorithms for filling the rooms.
Examples:
busiest: When all rooms are full, create a new room. The busiest room is then filled first.
emptiest: When all rooms are full, create a new room. The emptiest room is then filled first.


Feature request: React suspense support

A few of our react hooks are currently returning null while the storage is loading (useObject, useList, useMap). Supporting React Suspense would be great way to avoid all the null checks for the (lucky) developers that already are using Suspense.

Grant, from our Discord made an awesome proposal and a gist that could be a great starting point. Copy pasting here for visibility:


Checking in on my experiments with Liveblocks! I noted some ideas about the React client hooks earlier. After integrating it into a real-life project, I found that null checks were propagating everywhere (since the hooks return null if storage isn't initialized). That quickly gets messy.

I had the idea of using Suspense, inspired by react-three-fiber. It's not... strictly officially supported by React, but it's a much better DX. Loading states bubble up to the nearest Suspense block until the data is all ready, so components don't need to worry about the live object being null. I also updated the useEffect which instantiates a default value to be a useMemo instead - which makes the default value get populated immediately, instead of waiting for another re-render (again, to avoid null).

Here's a snippet of my custom hooks: https://gist.github.com/a-type/803b7f991cfe8cc74d504f29c6558d10

They seem to work well so far. I put a Suspense boundary just inside my RoomProvider with a loading spinner.


[BUG] - Not able to initialize a room storage with API endpoint

Hi there, i am trying to do POST call to https://liveblocks.net/api/v1/room/:roomId/storage/json with some sample data which contains shapes of tldraw board, but its not working. It's not saving that json on liveblocks storage against that room. Not able to figure out what is the expected JSON for API, also nothing specific mentioned in documentations or examples as well.

Here is the sample JSON i am trying to pass
{ shapes: { heading: { id: "heading", childIndex: 1, parentId: "page1", name: "Text", type: "text", point: [200, 10], style: { dash: "draw", size: "small", color: "black", }, text: "Something exciting here...", }, __liveblocksType: "LiveMap", }, bindings: { __liveblocksType: "LiveMap", }, assets: { __liveblocksType: "LiveMap", }, }

Feature request: Clear a part of the storage from the client

It's currently possible to delete a storage room with our REST API or manually from https://liveblocks.io dashboard.

It's technically possible to get a token and delete the storage from the client but it's far from ideal.

There is no direct API to reset a part of the storage from @liveblocks/react.

The only to way to reset a LiveObject created by useObject("key", { ... }) is to do when necessary

const { root } = await room.getStorage();
root.set("key", new LiveObject({...}));

Initial thoughts:

  • Do we need a client API that would mainly be used during the development phase? (Rapidly changing schema?)
  • We need an API to do schema/data migration, is there an overlap?

Unexptected behavior if setting a default presence

If setting a default presence, I would expect the username to be set and beeing available afterwards.

My personal expectation would be, that the shown code examples would trigger the same behaviour.
Setting the name via updatePresence works as expected. Using the defaultPresence (second example) will not return the name if calling getPresence.

Bildschirmfoto 2022-04-14 um 11 20 24

I am not sure if this is a Feature Request or a bug.

Poison shapes can cause desync.

If a client creates a "big" shape (something with 2000+ characters worth of points) and then follow it up with another "big" shape, the second big shape sometimes does not sync. It also "blocks" any future syncs until this shape is deleted on the client that created it. Any idea of what's happening?

Kapture 2021-11-22 at 12 38 32

Some notes:

  • This bug is found both on the 0.12.x and v0.13.0.beta.1.
  • In an earlier implementation, I was storing the whole document state in a single LiveObject. After a refactor, I am storing shapes in a LiveMap. The bug happens in both cases.

Flashing? While performing fast updates.

I'm not sure how to describe this one. In an app where I'm making an update to state on each frame, some frames appear to show an older state. In this example, this causes a dragging shape will sometimes flash a position from a few frames back. CodeSandbox here.

Kapture 2021-11-13 at 22 56 02

I'm running this on a new MacBook with a >60fps screen, so it could be related? Kind of a torture test, but really this is the old hiccup I've seen.

Rest API response improvements

It would be great if the API endpoints returned appropriate error messages rather than 404 status codes.

Maybe a JSON object with a message as to why it failed, or simply changing the returned status code from 404 to something a little more relevant to the issue with the request.

Ideas and feedback for @liveblocks/vue

Liveblocks does not have a specific package for Vue.js.

Currently, all our Vue examples are using @liveblocks/client directly.

If you're interested in having a specific package for Vue.js or if you have an opinion about the shape of the API, please let us know by answering this issue.

Thank you!

Liveblocks storage cannot be assigned to Vue ref (reactive variable)

@ctnicholas

Hi, this is more of a "let you know" than a "this needs fixing" message—someone else may come across this.

If you try to assign Liveblocks storage to a Vue ref (reactive variable) it won't work:

const { root } = await room.getStorage()
root.set('list', new LiveList())

const list = ref()
list.value = root.get('list')

// ERROR: Cannot read private member from an object whose class did not declare it
list.value.push(1)

It works fine if you use a function to return the storage instead:

const { root } = await room.getStorage()
root.set('list', new LiveList())

const list = ref()
list.value = () => root.get('list')

// Works fine
list.value().push(1)`

I've written exactly why this doesn't work in a CodeSandbox example:
https://codesandbox.io/s/liveblocks-proxy-storage-fix-eitb1?file=/src/index.js

I don't know if this is a problem that needs fixing, but it might be something to be aware of!
Discord: https://discord.com/channels/913109211746009108/913158542565994606/941328794130722868

[BUG] New TypeScript data types require string index signature when using interfaces

Describe the bug

When defining expected types for my Liveblocks storage data using interfaces, the typing system requires me to implement a generic { [key: string]: any } on every object type to satisfy type constraints.

2344: Type 'TDAsset' does not satisfy the constraint 'Lson'.
  Type 'TDImageAsset' is not assignable to type 'Lson'.
    Type 'TDImageAsset' is not assignable to type 'JsonObject'.
      Index signature is missing in type 'TDImageAsset'.

To Reproduce

Live example

Expected behavior

Interfaces should behave equivalently to types for the purposes of typechecking storage data.

Illustrations

Environment (please complete the following information):

Additional context

Workarounds are available for my own code (i.e. change all interface to type), but not for library code (like the example from TLDraw).

I'm not honestly sure why types and interfaces differ in this way. It may not be trivial to implement the Lson typings to accommodate the difference. But it is important to be able to, for example, pass the shape of a TLDraw as a type constraint for Liveblocks data (and a TLDraw shape is a deeply nested recursive interface).

Couldn't find any versions for "@liveblocks/react" that matches "0.13.0-beta.1"

When I install @liveblocks/react@beta, I got the response as below:

yarn add @liveblocks/react@beta

yarn add v1.22.17
[1/4] 🔍  Resolving packages...
Couldn't find any versions for "@liveblocks/react" that matches "0.13.0-beta.1"
? Please choose a version of "@liveblocks/react" from this list: (Use arrow keys)
❯ 0.12.3 
  0.12.2 
  0.12.1 
  0.12.0 
  0.12.0-beta.14 
  0.12.0-beta.13 
  0.12.0-beta.12 
  0.12.0-beta.11 
  0.12.0-beta.10 
  0.12.0-beta.9 
  0.12.0-beta.8 
  0.12.0-beta.7 
  0.12.0-beta.6 
  0.12.0-beta.5 
  0.12.0-beta.4 
  0.12.0-beta.3 
  0.12.0-beta.2 
  0.12.0-beta.1 
  0.11.0 
  0.10.0 
  0.9.0 
  0.8.2 
  0.8.0 
  0.7.3 
  0.7.1 
  0.7.0 
  0.6.0-beta.5 
  0.6.0-beta.4 

Installed via npm, npm i @liveblocks/react@beta got a similar erro:

npm ERR! code ETARGET
npm ERR! notarget No matching version found for @liveblocks/[email protected].
npm ERR! notarget In most cases you or one of your dependencies are requesting
npm ERR! notarget a package version that doesn't exist.

--- update ---

Figure out it's the problem of my private npm registory.

Verify untrusted inputs in client code

As partially addressed in #161, we should verify all untrusted inputs coming in from the outside, like all WebSocket messages that we receive, before using them. Not doing so has the risk that our TypeScript annotations (aka assumptions) don't match the values at runtime.

For large object structures like the ServerMessage type, for example, these validation conditionals can become pretty unwieldy if you use simple if-statements for them, but we might be able to use a validation library for this. This is exactly the use case that I wrote https://github.com/nvie/decoders for.

Are we open to using it, or vendoring-in a subset of it? (Since it's fully tree-shakable, it would only add the minimum amount of overhead to the bundle.) Would love to discuss!

Feature request: temporary storage

Feature request from @CTNicholas

https://discord.com/channels/913109211746009108/913157273046646795/938057663055560764


Room storage doesn’t always need to be permanent, in some cases you want the room to reset when all users are offline. Perhaps something like this would be handy, allowing you to erase the current storage when everyone’s gone:

<RoomProvider
  onEmpty={storage => {
    storage.clear()
  }
/>

// Or do it automatically:

<RoomProvider
  storage="temporary"
/>

API Endpoint for listing rooms

Is your feature request related to a problem?

Not really related to a problem, but more of an enhancement for API consumption capabilities.

Describe the solution you'd like

The following would return a list of all of the rooms.

GET: https://liveblocks.net/api/v1/rooms
or
GET: https://liveblocks.net/api/v1/rooms/json

Additional context

N/A

Improve token types documentation

The "authorize" endpoint can generate tokens usable by the client or the HTTP API.
When no room identifier is sent to the authorized endpoint, the endpoint generates a specific token type.

The client cannot use the generated token.
These attention points might need to be detailed in the documentation.
The error message thrown by the parseToken method may not give enough details, thus making the error challenging to understand.

if (typeof data.actor !== "number") {
    throw new AuthenticationError(
      `Authentication error. Liveblocks could not parse the response of your authentication endpoint`
    );
  }

Discord: https://discord.com/channels/913109211746009108/913158542565994606/941606262796328960

React Native support

Cant find anything about this topic in documentation, but im wondering if the library supports React Native?
If not, do you plan to support this platform as well in future?

@liveblocks/client and @liveblocks/react does not work Vite

Describe the bug

Using @liveblocks/react and @liveblocks/client with Vite does not work. Vite bundles @liveblocks/client in @liveblocks/react during pre-bundling which causes duplicated classes in final bundle. Vite behavior is different from webpack configuration in Next or create-react-app.

To Reproduce

Steps to reproduce the behavior:

  • Create a new Vite project with react
  • Setup liveblocks
  • Try using useList, useMap or useObject
  • It will throw the obscure error [object Object] is not a valid event name

Expected behavior

@liveblocks/client and @liveblocks/react should work with Vite by default

Environment (please complete the following information):

Update the storage / room from the REST API

Our HTTP API allows users to initialize, get and delete a room but does not support update scenarios.

In some use cases, users need to mutate the state of a LiveMap, for example, directly from a cloud function or backend environment.

The goal would be to handle storage mutation (LiveObject/LiveMap/LiveList) from a backend environment (cloud functions, nodejs, ...).

Related issue: #33
Discord Message: https://discord.com/channels/913109211746009108/913157273046646795/940963713068957778

Improve TypeScript types in the public API

We're using unsafe types (think any or Object) in quite a few places and those effectively disable the type checker in those places. This allows bugs to creep in on our end, makes contributing to the code base harder, and lead to a suboptimal DX for our users.

Ideally, there would be no anys in the public API.

Remove private field usage to simplify bundling

We're currently using private fields and private methods.

While this is better for encapsulation, they are not widely supported (less than 90% based on Caniuse). Babel injects a lot of code on hot paths which arguably outweighs the benefit of encapsulation. Removing private fields and methods in favor of @internal to hide them from auto-completion would improve our final bundle.

Client: re-work authentication error messages

There are lot of point of failures during the authentication process.

Here is a non exhaustive list:

Public authentication:

  • Unknown public key
  • Public key is deactivated
  • Private key used instead of public key
  • Network error while calling /api/authorize/public

Private authentication:

  • Invalid secret key
  • Public key used instead of private key
  • Invalid endpoint
  • Network error between client and auth endpoint
  • Network error in @liveblocks/node
  • Invalid payload returned by authentication endpoint

Custom authentication (callback)

  • Function throw
  • Invalid return

Providing a descriptive error message would probably improve our activation rate.

This task can be split in smaller one to ship a few error message at a time.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.