liveblocks 1.0.6 → 1.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -14,11 +14,19 @@ npx liveblocks dev
|
|
|
14
14
|
|
|
15
15
|
Options:
|
|
16
16
|
|
|
17
|
-
| Flag | Description
|
|
18
|
-
| -------------- |
|
|
19
|
-
| `-p`, `--port` | Port to listen on
|
|
20
|
-
| `--host` | Host to bind to
|
|
21
|
-
|
|
|
17
|
+
| Flag | Description | Default |
|
|
18
|
+
| -------------- | --------------------------------------------------------------------------------------------- | ----------------------------------- |
|
|
19
|
+
| `-p`, `--port` | Port to listen on. | `1153` |
|
|
20
|
+
| `--host` | Host to bind to. | `localhost` |
|
|
21
|
+
| `--ci` | Best defaults for running the Liveblocks dev server in CI. Alias of `--ephemeral --no-check`. | |
|
|
22
|
+
| `--ephemeral` | Do not persist state between restarts. Recommended for running unit tests. | Persist between restarts by default |
|
|
23
|
+
| `--no-check` | Skip project setup check on start. | Checks by default |
|
|
24
|
+
| `-h`, `--help` | Show help. | |
|
|
25
|
+
|
|
26
|
+
By default, the dev server scans your project on startup for common Liveblocks
|
|
27
|
+
call sites (`<LiveblocksProvider>`, `createClient()`, `new Liveblocks()`) and
|
|
28
|
+
warns if any of them are missing a `baseUrl` pointing at the local dev server.
|
|
29
|
+
Use `--no-check` to skip this check.
|
|
22
30
|
|
|
23
31
|
### `liveblocks upgrade`
|
|
24
32
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import{parse as
|
|
1
|
+
import{parse as jt}from"@bomb.sh/args";import{WebsocketCloseCodes as Mt}from"@liveblocks/core";import{ZenRelay as Bt}from"@liveblocks/zenrouter";import Jt from"bun";function A(t){return`\x1B[33m${t}\x1B[0m`}function ee(t){return`\x1B[34m${t}\x1B[0m`}function U(t){return`\x1B[35m${t}\x1B[0m`}function N(t){return`\x1B[32m${t}\x1B[0m`}function V(t){return`\x1B[31m${t}\x1B[0m`}function J(t){return`\x1B[1m${t}\x1B[0m`}function T(t){return`\x1B[2m${t}\x1B[0m`}import{nanoid as Me,Permission as Be}from"@liveblocks/core";import{ProtocolVersion as se}from"@liveblocks/server";import{nanoid as je}from"@liveblocks/core";function te(t){return btoa(t).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function oe(t){let e=t.replace(/-/g,"+").replace(/_/g,"/"),o=e+"=".repeat((4-e.length%4)%4);return atob(o)}function re(t){let e=Math.floor(Date.now()/1e3),o={...t,iat:e,exp:e+60*60,jti:je(12)},s=te(JSON.stringify({alg:"none",typ:"JWT"})),i=te(JSON.stringify(o));return`${s}.${i}.`}function z(t){let e=t.split(".");if(e.length!==3)return null;let[o,s,i]=e;try{if(JSON.parse(oe(o)).alg!=="none")return null}catch{return null}let d;try{d=JSON.parse(oe(s))}catch{return null}if(i!==""||d.pid!=="localdev")return null;let u=d.exp;return typeof u=="number"&&u<Math.floor(Date.now()/1e3)?null:d}function Je(t,e){if(e[t])return e[t];for(let[o,s]of Object.entries(e))if(o.endsWith("*")){let i=o.slice(0,-1);if(t.startsWith(i))return s}return[]}function ne(t){let e=new URL(t.url),o=e.pathname==="/v7"?se.V7:e.pathname==="/v8"?se.V8:null;if(o===null)return{ok:!1};let s=e.searchParams.get("roomId");if(!s)return{ok:!1};let i=e.searchParams.get("tok");if(i!==null){let u=z(i);if(!u)return{ok:!1};if(u.k==="acc"){let g=Je(s,u.perms);return g.length===0?{ok:!1}:{ok:!0,roomId:s,ticketData:{version:o,id:u.uid,info:u.ui,scopes:g}}}else if(u.k==="id")return{ok:!0,roomId:s,ticketData:{version:o,id:u.uid,info:u.ui,scopes:[Be.Write]}};return{ok:!1}}let d=e.searchParams.get("pubkey");return d!==null?d!=="pk_localdev"?{ok:!1,xwarn:"You can only use 'pk_localdev' as the public key"}:{ok:!0,roomId:s,ticketData:{version:o,anonymousId:Me(),scopes:["room:write"]}}:{ok:!1}}import{DefaultMap as We,Room as qe}from"@liveblocks/server";import{mkdirSync as le,mkdtempSync as Ze,readdirSync as Qe,rmSync as et}from"fs";import{tmpdir as tt}from"os";import{join as ot,resolve as ue}from"path";import{asPos as Ge,CrdtType as O,nn as Ye}from"@liveblocks/core";import{makeInMemorySnapshot as $e,NestedMap as Ve,plainLsonToNodeStream as ze,quote as S}from"@liveblocks/server";import{Database as Ke}from"bun:sqlite";function P(t){try{return t!==void 0?JSON.parse(t):void 0}catch{return}}function de(t){if(t>512)throw new Error("More than 512 params not supported");return new Array(t).fill("?").join(",")}function Fe(t){return Array.isArray(t)?t:Array.from(t)}function Xe(t){return t.query("SELECT node_id, crdt_json FROM nodes").values().map(([e,o])=>[e,P(o)])}function G(t,e){let o=t.prepare(`INSERT INTO nodes (node_id, crdt_json)
|
|
2
2
|
VALUES (?, ?)
|
|
3
|
-
ON CONFLICT (node_id) DO UPDATE SET crdt_json = ?`);
|
|
3
|
+
ON CONFLICT (node_id) DO UPDATE SET crdt_json = ?`);t.transaction(i=>{for(let[d,u]of i){let g=JSON.stringify(u);o.run(d,g,g)}})(e)}function ce(t,e){let o=Fe(e),s=o.length;t.query(`DELETE FROM nodes WHERE node_id IN (${de(s)})`).run(...o)}function ie(t){let e=new Ve;for(let[o,s]of t){if(s.parentId===void 0)continue;let i=e.get(s.parentId,s.parentKey);(i===void 0||o>i)&&e.set(s.parentId,s.parentKey,o)}return e}function He(t,e){let o=new Map(Xe(t));o.has("root")||o.set("root",{type:O.OBJECT,data:{}});let s=new Set,i=["root"],d=new Map,u=ie(o);for(;i.length>0;){let c=i.pop(),f=Ye(o.get(c));if(f.type===O.OBJECT)for(let v of u.keysAt(c))Object.prototype.hasOwnProperty.call(f.data,v)&&(delete f.data[v],s.add(c),e.warn(`[integrity] Found data key ${S(v)} from ${S(c)} (conflicted with child node)`));if(f.type!==O.REGISTER)i.push(...u.valuesAt(c));else if(o.get(f.parentId)?.type===O.OBJECT)continue;d.set(c,f)}let g=new Set;for(let[c,f]of o)d.has(c)||(f.parentId!==void 0&&d.has(f.parentId)?d.get(f.parentId)?.type===O.REGISTER?e.warn(`[integrity] Found unreachable node ${S(c)} (child of live register)`):e.warn(`[integrity] Found conflicting sibling ${S(c)} (conflicted with ${S(u.get(f.parentId,f.parentKey))} at ${S(f.parentKey)})`):e.warn(`[integrity] Found orphan ${S(c)}`),g.add(c),s.delete(c));if(s.size>0||g.size>0){if(s.size>0){let c=new Map;for(let f of s){let v=d.get(f);v!==void 0&&c.set(f,v)}G(t,c)}g.size>0&&ce(t,g)}let l=g.size===0?u:ie(d);return{nodes:d,revNodes:l}}function ae(t,e){return t?.type===O.OBJECT&&Object.prototype.hasOwnProperty.call(t.data,e)&&t.data[e]!==void 0}var K=class{db;constructor(e){let o=new Ke(e,{create:!0});o.run("PRAGMA journal_mode = WAL"),o.run("PRAGMA case_sensitive_like = ON"),o.run(`CREATE TABLE IF NOT EXISTS system (
|
|
4
4
|
setting TEXT NOT NULL,
|
|
5
5
|
jval TEXT NOT NULL,
|
|
6
6
|
PRIMARY KEY (setting)
|
|
@@ -24,31 +24,31 @@ import{parse as Lt}from"@bomb.sh/args";import{WebsocketCloseCodes as Rt}from"@li
|
|
|
24
24
|
juserinfo TEXT NOT NULL,
|
|
25
25
|
ttl INTEGER NOT NULL,
|
|
26
26
|
actor_id INTEGER NOT NULL
|
|
27
|
-
)`),o.run("CREATE INDEX IF NOT EXISTS idx_leased_sessions_expiry ON leased_sessions(updated_at, ttl)"),this.db=o}load_nodes_api(
|
|
27
|
+
)`),o.run("CREATE INDEX IF NOT EXISTS idx_leased_sessions_expiry ON leased_sessions(updated_at, ttl)"),this.db=o}load_nodes_api(e){let o=this.db,{nodes:s,revNodes:i}=He(o,e),d=p=>s.get(p),u=(p,m)=>i.get(p,m),g=(p,m)=>i.has(p,m);function l(p,m){let h;for(let I of i.keysAt(p)){let b=Ge(I);b>m&&(h===void 0||b<h)&&(h=b)}return h}function c(p,m,h=!1){let I=d(m.parentId);if(I===void 0)throw new Error(`No such parent ${S(m.parentId)}`);if(m.type===O.REGISTER&&I.type===O.OBJECT)throw new Error("Cannot add register under object");let b=u(m.parentId,m.parentKey);if(b!==p){let C=ae(I,m.parentKey);if(b!==void 0||C)if(h)L(m.parentId,m.parentKey);else throw new Error(`Key ${S(m.parentKey)} already exists`);i.set(m.parentId,m.parentKey,p)}s.set(p,m),G(o,[[p,m]])}function f(p,m){let h=d(p);if(h?.parentId===void 0)return;if(g(h.parentId,m))throw new Error(`Pos ${S(m)} already taken`);i.delete(h.parentId,h.parentKey);let I={...h,parentKey:m};s.set(p,I),i.set(h.parentId,m,p),G(o,[[p,I]])}function v(p,m,h=!1){let I=d(p);if(I?.type!==O.OBJECT)return;for(let C of Object.keys(m)){let k=u(p,C);if(k!==void 0)if(h)y(k);else throw new Error(`Child node already exists under ${C}`)}let b={...I,data:{...I.data,...m}};s.set(p,b),G(o,[[p,b]])}function y(p){let m=d(p);if(m?.parentId===void 0)return;i.delete(m.parentId,m.parentKey);let h=[],I=[p];for(;I.length>0;){let b=I.pop();I.push(...i.valuesAt(b)),s.delete(b),i.deleteAll(b),h.push(b)}ce(o,h)}function L(p,m){let h=d(p);if(ae(h,m)){let{[m]:b,...C}=h.data,k={...h,data:C};s.set(p,k),G(o,[[p,k]])}let I=u(p,m);I!==void 0&&y(I)}return{get_node:d,iter_nodes:()=>s.entries(),has_node:p=>s.has(p),get_child_at:u,has_child_at:g,get_next_sibling:l,set_child:c,move_sibling:f,delete_node:y,delete_child_key:L,set_object_data:v,get_snapshot(p){return $e(s)}}}DANGEROUSLY_reset_nodes(e){let o=this.db.prepare("DELETE FROM nodes"),s=this.db.prepare("INSERT INTO nodes (node_id, crdt_json) VALUES (?, ?)");this.db.transaction(()=>{o.run();for(let[d,u]of ze(e))s.run(d,JSON.stringify(u))})()}raw_iter_nodes(){return this.db.query("SELECT node_id, crdt_json FROM nodes").values().map(([e,o])=>[e,P(o)])}get_meta(e){let s=this.db.query("SELECT jval FROM metadata WHERE key = ?").get(e)?.jval;if(s!==void 0)return P(s)??null}put_meta(e,o){let s=JSON.stringify(o);this.db.run(`INSERT INTO metadata (key, jval)
|
|
28
28
|
VALUES (?, ?)
|
|
29
29
|
ON CONFLICT (key) DO UPDATE SET jval = ?
|
|
30
|
-
`,[
|
|
30
|
+
`,[e,s,s])}delete_meta(e){this.db.query("DELETE FROM metadata WHERE key = ?").run(e)}next_actor(){let e=this.db.query(`INSERT INTO system (setting, jval)
|
|
31
31
|
VALUES ('last_actor_id', '0')
|
|
32
32
|
ON CONFLICT (setting) DO UPDATE SET jval = json(CAST(jval AS INTEGER) + 1)
|
|
33
|
-
RETURNING jval`).get();return JSON.parse(
|
|
33
|
+
RETURNING jval`).get();return JSON.parse(e.jval)}iter_y_updates(e){return this.db.query("SELECT key, data FROM ydocs WHERE doc_id = ?").values(e)}write_y_updates(e,o,s){this.db.query(`INSERT INTO ydocs
|
|
34
34
|
VALUES (?, ?, ?)
|
|
35
|
-
ON CONFLICT (doc_id, key) DO UPDATE SET data = ?`).run(
|
|
35
|
+
ON CONFLICT (doc_id, key) DO UPDATE SET data = ?`).run(e,o,s,s)}delete_y_updates(e,o){let s=o.length;this.db.query(`DELETE FROM ydocs WHERE doc_id = ? AND key IN (${de(s)})`).run(e,...o)}DANGEROUSLY_wipe_all_y_updates(){this.db.query("DELETE FROM ydocs").run()}list_leased_sessions(){let e=this.db.query("SELECT session_id, jpresence, updated_at, juserinfo, ttl, actor_id FROM leased_sessions").all();return Array.from(e,o=>[o.session_id,{sessionId:o.session_id,presence:P(o.jpresence)??null,updatedAt:o.updated_at,info:P(o.juserinfo)??{name:""},ttl:o.ttl,actorId:o.actor_id}])}get_leased_session(e){let o=this.db.query("SELECT session_id, jpresence, updated_at, juserinfo, ttl, actor_id FROM leased_sessions WHERE session_id = ?").get(e);if(o!=null)return{sessionId:o.session_id,presence:P(o.jpresence)??null,updatedAt:o.updated_at,info:P(o.juserinfo)??{name:""},ttl:o.ttl,actorId:o.actor_id}}put_leased_session(e){this.db.query(`INSERT INTO leased_sessions (session_id, jpresence, updated_at, juserinfo, ttl, actor_id)
|
|
36
36
|
VALUES (?, ?, ?, ?, ?, ?)
|
|
37
37
|
ON CONFLICT (session_id) DO UPDATE SET
|
|
38
38
|
jpresence = excluded.jpresence,
|
|
39
39
|
updated_at = excluded.updated_at,
|
|
40
40
|
juserinfo = excluded.juserinfo,
|
|
41
41
|
ttl = excluded.ttl,
|
|
42
|
-
actor_id = excluded.actor_id`).run(
|
|
43
|
-
`).filter(Boolean).map(
|
|
44
|
-
`);for(let{match:
|
|
42
|
+
actor_id = excluded.actor_id`).run(e.sessionId,JSON.stringify(e.presence),e.updatedAt,JSON.stringify(e.info),e.ttl,e.actorId)}delete_leased_session(e){this.db.query("DELETE FROM leased_sessions WHERE session_id = ?").run(e)}close(){this.db.close()}};var rt=".liveblocks/v1/rooms",R=rt,me=!1;function pe(){return R=Ze(ot(tt(),"liveblocks-dev-")),me=!0,R}var j=new We(t=>{le(R,{recursive:!0});let e=new K(H(t));return new qe(t,{storage:e})});function H(t){let e=ue(R,`${encodeURIComponent(t)}.db`);if(!e.startsWith(ue(R)+"/"))throw new Error("Invalid room ID");return e}function _(t){return j.getOrCreate(t)}async function x(t){let e=H(t);return await Bun.file(e).exists()}function fe(){try{return le(R,{recursive:!0}),Qe(R).filter(o=>o.endsWith(".db")).map(o=>decodeURIComponent(o.replace(/\.db$/,"")))}catch{return[]}}async function ge(t){if(await x(t))throw new Error(`Room with id "${t}" already exists`);let e=j.getOrCreate(t);await e.load();let o={liveblocksType:"LiveObject",data:{}};await e.driver.DANGEROUSLY_reset_nodes(o),e.unload()}async function he(t){j.get(t)?.unload(),j.delete(t);let e=H(t);try{await Bun.write(e,""),await Bun.file(e).unlink()}catch{}}function ve(){for(let t of j.values())t.unload();j.clear(),me&&et(R,{recursive:!0,force:!0})}function st(t){return t.trim().split(`
|
|
43
|
+
`).filter(Boolean).map(e=>{let o=e.match(/^(.+?):(\d+):(.*)$/);return o?{file:o[1],line:parseInt(o[2],10),text:o[3]}:null}).filter(e=>e!==null)}async function Ee(...t){try{let e=["git","grep","-nF",...t.flatMap(i=>["-e",i]),"--","."],o=Bun.spawn(e,{stdout:"pipe",stderr:"pipe"}),s=await new Response(o.stdout).text();return await o.exited,st(s)}catch{return[]}}function nt(t){let e=t.trim();return e.startsWith("#")||e.startsWith("//")||e.startsWith("*")||e.startsWith("/*")}var W=[{pattern:"<LiveblocksProvider",expected:"baseUrl=",fixSnippet:t=>`${U("baseUrl")}=${N(`"${t}"`)}`,closePattern:">"},{pattern:"createClient(",expected:"baseUrl:",fixSnippet:t=>`${U("baseUrl")}: ${N(`"${t}"`)}`,closePattern:")"},{pattern:"new Liveblocks(",expected:"baseUrl:",fixSnippet:t=>`${U("baseUrl")}: ${N(`"${t}"`)}`,closePattern:")"}];async function be(t){let e=`http://localhost:${t}`,o=W.map(l=>l.pattern),s=[...new Set(W.map(l=>l.expected))],[i,d]=await Promise.all([Ee(...o),Ee(...s)]);if(i.length===0)return[];let u=new Set(d.map(l=>l.file)),g=[];for(let l of i){if(nt(l.text)||u.has(l.file))continue;let c=W.find(f=>l.text.includes(f.pattern));c&&g.push({match:l,check:c})}if(g.length>0){console.log(),console.warn(A("\u26A0 Your project may not be configured for the local dev server.")),console.log(),console.log(` Missing baseUrl in the following location(s):
|
|
44
|
+
`);for(let{match:l,check:c}of g)console.log(` ${ee(`${l.file}:${l.line}`)}`),console.log(` To fix, add ${c.fixSnippet(e)} to ${U(c.pattern)}${U(c.closePattern)}`),console.log();console.log(T(" \u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E")),console.log(T(" \u2502 \u{1F4A1} ")+"Press "+J("p")+" to copy an AI fix prompt to your clipboard"+T(" \u2502")),console.log(T(" \u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F")),console.log(),console.log(T(" To skip this check, use --no-check")),console.log()}return g}var it={"<LiveblocksProvider":"baseUrl={...}","createClient(":"baseUrl: ...","new Liveblocks(":"baseUrl: ..."};function Te(t,e){let o=t.map(({match:s,check:i})=>{let d=it[i.pattern]??"baseUrl: ...";return` - In \`${s.file}\` at line ${s.line}, add \`${d}\` to the \`${i.pattern}\` call`}).join(`
|
|
45
45
|
`);return`# Set up Liveblocks dev server
|
|
46
46
|
|
|
47
47
|
1. Check which system the application is using for environment variables, then
|
|
48
48
|
add a new environment variable called \`USE_LIVEBLOCKS_DEV_SERVER="true"\`.
|
|
49
49
|
This will be used to enable the dev server. It needs to be available on the client, so it may need to be called
|
|
50
50
|
\`NEXT_PUBLIC_USE_LIVEBLOCKS_DEV_SERVER\` or similar. Do not rename or modify existing variables.
|
|
51
|
-
2. You must use \`"${
|
|
51
|
+
2. You must use \`"${e}"\` as a \`baseUrl\` when the dev server is
|
|
52
52
|
enabled. In the following files add a \`baseUrl\` property for this when the
|
|
53
53
|
dev server is enabled, otherwise set \`baseUrl\` to \`undefined\`:
|
|
54
54
|
${o}
|
|
@@ -75,7 +75,7 @@ NEXT_PUBLIC_USE_LIVEBLOCKS_DEV_SERVER="true"
|
|
|
75
75
|
|
|
76
76
|
\`\`\`tsx file="src/app/Providers.tsx"
|
|
77
77
|
<LiveblocksProvider
|
|
78
|
-
baseUrl={process.env.NEXT_PUBLIC_USE_LIVEBLOCKS_DEV_SERVER === "true" ? "${
|
|
78
|
+
baseUrl={process.env.NEXT_PUBLIC_USE_LIVEBLOCKS_DEV_SERVER === "true" ? "${e}" : undefined}
|
|
79
79
|
publicApiKey={process.env.NEXT_PUBLIC_USE_LIVEBLOCKS_DEV_SERVER === "true" ? "pk_localdev" : process.env.NEXT_PUBLIC_LIVEBLOCKS_PUBLIC_KEY}
|
|
80
80
|
\`\`\`
|
|
81
81
|
|
|
@@ -89,12 +89,12 @@ NEXT_PUBLIC_USE_LIVEBLOCKS_DEV_SERVER="true"
|
|
|
89
89
|
|
|
90
90
|
\`\`\`tsx file="src/app/Providers.tsx"
|
|
91
91
|
<LiveblocksProvider
|
|
92
|
-
baseUrl={process.env.NEXT_PUBLIC_USE_LIVEBLOCKS_DEV_SERVER === "true" ? "${
|
|
92
|
+
baseUrl={process.env.NEXT_PUBLIC_USE_LIVEBLOCKS_DEV_SERVER === "true" ? "${e}" : undefined}
|
|
93
93
|
\`\`\`
|
|
94
94
|
|
|
95
95
|
\`\`\`ts file="src/app/api/liveblocks-auth/route.ts"
|
|
96
96
|
new Liveblocks({
|
|
97
|
-
baseUrl={process.env.NEXT_PUBLIC_USE_LIVEBLOCKS_DEV_SERVER === "true" ? "${
|
|
97
|
+
baseUrl={process.env.NEXT_PUBLIC_USE_LIVEBLOCKS_DEV_SERVER === "true" ? "${e}" : undefined}
|
|
98
98
|
secret={process.env.NEXT_PUBLIC_USE_LIVEBLOCKS_DEV_SERVER === "true" ? "sk_localdev" : process.env.LIVEBLOCKS_SECRET_KEY}
|
|
99
99
|
\`\`\`
|
|
100
100
|
|
|
@@ -102,7 +102,7 @@ new Liveblocks({
|
|
|
102
102
|
|
|
103
103
|
Explain to the user that you've enabled the Liveblocks dev server by adding a an
|
|
104
104
|
environment variable to the application, and share which file it is in. Explain
|
|
105
|
-
that the user can disable this by setting it to "false".`}import{execSync as
|
|
105
|
+
that the user can disable this by setting it to "false".`}import{execSync as at}from"node:child_process";function ye(t){let e=process.platform==="darwin"?"pbcopy":process.platform==="win32"?"clip.exe":"xclip -selection clipboard";at(e,{input:t})}import dt from"bun";function Se(t,e){let{promise:o,resolve:s}=Promise.withResolvers();return dt.connect({hostname:e,port:t,socket:{data(){},open(i){i.end(),s(!0)},error(){s(!1)},connectError(){s(!1)}}}),o}function q(t,e=!1){t&&console.log((e?V:A)(` \u26A0 ${t}`))}import{Permission as lt}from"@liveblocks/core";import{ZenRouter as mt}from"@liveblocks/zenrouter";import{array as pt,enum_ as ft,object as gt,optional as ht,record as vt,string as It}from"decoders";import{json as ct}from"@liveblocks/zenrouter";import{json as xe}from"@liveblocks/zenrouter";function Y(t,e,o){return xe(t,e,{"X-LB-Warn":o})}function w(t,e,o="This is a dummy response."){return Y(t,e,o)}function r(t="This endpoint isn't implemented in the Liveblocks dev server."){return xe({error:"Not implemented",message:t},501,{"X-LB-Warn":t})}function F(t){let e=t.headers.get("Authorization");if(e==="Bearer sk_localdev")return!0;if(!e)throw ct({error:"Unauthorized",message:"Missing secret key"},401);if(e.startsWith("Bearer "))throw Y({error:"Forbidden",message:"Invalid secret key. You can only use 'sk_localdev' as a secret key"},403,"You can only use 'sk_localdev' as the secret key");return!1}import{inexact as ut,optional as Ce,string as Oe}from"decoders";var _e=ut({name:Ce(Oe),avatar:Ce(Oe)}).refineType();var Et=ft(lt),$=new mt({authorize:({req:t})=>F(t)});$.route("POST /v2/authorize-user",gt({userId:It,userInfo:ht(_e),permissions:vt(pt(Et))}),({body:t})=>({token:re({k:"acc",pid:"localdev",uid:t.userId,perms:t.permissions,ui:t.userInfo})}));$.route("POST /v2/identify-user",()=>r("ID tokens are not supported in the Liveblocks dev server. To develop locally, use access tokens instead (via POST /v2/authorize-user)"));import{ZenRouter as bt}from"@liveblocks/zenrouter";var a=new bt({cors:{allowCredentials:!0,maxAge:600,exposeHeaders:["X-LB-Warn"]},authorize:({req:t})=>{let e=t.headers.get("Authorization");if(!e?.startsWith("Bearer "))return!1;let o=e.slice(7);return z(o)!==null}});a.route("GET /v2/c/threads",()=>w({threads:[],inboxNotifications:[],subscriptions:[],meta:{nextCursor:null,requestedAt:new Date().toISOString(),permissionHints:{}}}));a.route("GET /v2/c/threads/delta",()=>w({threads:[],inboxNotifications:[],subscriptions:[],meta:{requestedAt:new Date().toISOString(),permissionHints:{}}}));a.route("GET /v2/c/inbox-notifications",()=>w({inboxNotifications:[],threads:[],subscriptions:[],groups:[],meta:{nextCursor:null,requestedAt:new Date().toISOString()}}));a.route("GET /v2/c/inbox-notifications/count",()=>w({count:0}));a.route("GET /v2/c/inbox-notifications/delta",()=>w({inboxNotifications:[],threads:[],subscriptions:[],groups:[],deletedInboxNotifications:[],deletedThreads:[],deletedSubscriptions:[],meta:{requestedAt:new Date().toISOString()}}));a.route("GET /v2/c/rooms/<roomId>/threads",()=>w({data:[],inboxNotifications:[],subscriptions:[],meta:{nextCursor:null,requestedAt:new Date().toISOString(),permissionHints:{}}}));a.route("GET /v2/c/rooms/<roomId>/threads/delta",()=>w({data:[],inboxNotifications:[],subscriptions:[],deletedThreads:[],deletedInboxNotifications:[],deletedSubscriptions:[],meta:{requestedAt:new Date().toISOString(),permissionHints:{}}}));a.route("POST /v2/c/rooms/<roomId>/text-metadata",()=>w({status:"ok"}));a.route("PUT /v2/c/rooms/<roomId>/attachments/<attachmentId>/upload/<name>",()=>r()),a.route("POST /v2/c/rooms/<roomId>/attachments/<attachmentId>/multipart/<name>",()=>r()),a.route("PUT /v2/c/rooms/<roomId>/attachments/<attachmentId>/multipart/<uploadId>/<partNumber>",()=>r()),a.route("POST /v2/c/rooms/<roomId>/attachments/<attachmentId>/multipart/<uploadId>/complete",()=>r()),a.route("DELETE /v2/c/rooms/<roomId>/attachments/<attachmentId>/multipart/<uploadId>",()=>r()),a.route("POST /v2/c/rooms/<roomId>/attachments/presigned-urls",()=>r()),a.route("POST /v2/c/rooms/<roomId>/send-message",()=>r()),a.route("GET /v2/c/rooms/<roomId>/storage",()=>r()),a.route("POST /v2/c/rooms/<roomId>/version",()=>r()),a.route("GET /v2/c/rooms/<roomId>/y-version/<version>",()=>r()),a.route("POST /v2/c/rooms/<roomId>/ai/contextual-prompt",()=>r()),a.route("POST /v2/c/rooms/<roomId>/threads",()=>r()),a.route("POST /v2/c/rooms/<roomId>/threads/search",()=>r()),a.route("DELETE /v2/c/rooms/<roomId>/threads/<threadId>",()=>r()),a.route("POST /v2/c/rooms/<roomId>/threads/<threadId>/metadata",()=>r()),a.route("POST /v2/c/rooms/<roomId>/threads/<threadId>/mark-as-resolved",()=>r()),a.route("POST /v2/c/rooms/<roomId>/threads/<threadId>/mark-as-unresolved",()=>r()),a.route("POST /v2/c/rooms/<roomId>/threads/<threadId>/subscribe",()=>r()),a.route("POST /v2/c/rooms/<roomId>/threads/<threadId>/unsubscribe",()=>r()),a.route("POST /v2/c/rooms/<roomId>/threads/<threadId>/comments",()=>r()),a.route("GET /v2/c/rooms/<roomId>/threads/<threadId>/comments/<commentId>",()=>r()),a.route("POST /v2/c/rooms/<roomId>/threads/<threadId>/comments/<commentId>",()=>r()),a.route("POST /v2/c/rooms/<roomId>/threads/<threadId>/comments/<commentId>/metadata",()=>r()),a.route("DELETE /v2/c/rooms/<roomId>/threads/<threadId>/comments/<commentId>",()=>r()),a.route("POST /v2/c/rooms/<roomId>/threads/<threadId>/comments/<commentId>/reactions",()=>r()),a.route("DELETE /v2/c/rooms/<roomId>/threads/<threadId>/comments/<commentId>/reactions/<emoji>",()=>r()),a.route("GET /v2/c/rooms/<roomId>/threads/comments/search",()=>r()),a.route("GET /v2/c/rooms/<roomId>/threads/<threadId>/participants",()=>r()),a.route("GET /v2/c/rooms/<roomId>/notification-settings",()=>r()),a.route("GET /v2/c/rooms/<roomId>/subscription-settings",()=>r()),a.route("POST /v2/c/rooms/<roomId>/notification-settings",()=>r()),a.route("POST /v2/c/rooms/<roomId>/subscription-settings",()=>r()),a.route("DELETE /v2/c/inbox-notifications",()=>r()),a.route("POST /v2/c/inbox-notifications/read",()=>r()),a.route("DELETE /v2/c/inbox-notifications/<inboxNotificationId>",()=>r()),a.route("POST /v2/c/rooms/<roomId>/inbox-notifications/read",()=>r()),a.route("POST /v2/c/rooms/<roomId>/text-mentions",()=>r()),a.route("DELETE /v2/c/rooms/<roomId>/text-mentions/<mentionId>",()=>r()),a.route("GET /v2/c/notification-settings",()=>r()),a.route("POST /v2/c/notification-settings",()=>r()),a.route("GET /v2/c/rooms/<roomId>/thread-with-notification/<threadId>",()=>r()),a.route("GET /v2/c/urls/metadata",()=>r()),a.route("GET /v2/c/rooms/<roomId>/versions",()=>r()),a.route("GET /v2/c/rooms/<roomId>/versions/delta",()=>r()),a.route("POST /v2/c/groups/find",()=>r());import{abort as Le,html as yt,json as St,ZenRouter as xt}from"@liveblocks/zenrouter";var we=`<!doctype html>
|
|
106
106
|
<html lang="en">
|
|
107
107
|
<head>
|
|
108
108
|
<meta charset="UTF-8" />
|
|
@@ -334,7 +334,6 @@ that the user can disable this by setting it to "false".`}import{execSync as Qe}
|
|
|
334
334
|
</div>
|
|
335
335
|
|
|
336
336
|
<div class="center">
|
|
337
|
-
<h1>You're all set</h1>
|
|
338
337
|
<p class="description">
|
|
339
338
|
This is the Liveblocks dev server. Point your app to
|
|
340
339
|
<code>http://localhost:1153</code> and start building. This page is
|
|
@@ -359,5 +358,5 @@ that the user can disable this by setting it to "false".`}import{execSync as Qe}
|
|
|
359
358
|
</main>
|
|
360
359
|
</body>
|
|
361
360
|
</html>
|
|
362
|
-
`;var A=new ht({authorize:()=>!0});A.route("GET /v7",()=>xe(426));A.route("GET /v8",()=>xe(426));A.route("GET /health",()=>gt({status:"ok"}));A.route("GET /",()=>ft(Ce.replace("__VERSION__","1.0.6")));import{jsonObjectYolo as bt,ROOT_YDOC_ID as q,snapshotToLossyJson_eager as Tt,snapshotToPlainLson_eager as we}from"@liveblocks/server";import{json as yt,ZenRouter as St}from"@liveblocks/zenrouter";import{constant as Ct,enum_ as xt,object as Le,string as Ot}from"decoders";import{Base64 as _t}from"js-base64";import*as X from"yjs";import*as E from"yjs";function vt(e){return e.content instanceof E.ContentFormat||e.content instanceof E.ContentEmbed?"text":"arr"in e.content?"array":"str"in e.content?"text":"type"in e.content?"xml":"unknown"}function It(e){let t=[],o=e;for(;o!==null;){if(!o.deleted)if(o.content instanceof E.ContentType)t.push(o.content.type.toJSON());else if(o.content instanceof E.ContentString)t.push(o.content.str);else if(o.content instanceof E.ContentFormat){let{key:s,value:i}=o.content;t.push({key:s,value:i})}else o.content instanceof E.ContentEmbed&&t.push(o.content.embed);o=o.right}return t}function Oe(e,t,o,s=!1){if(!t._first&&t._map instanceof Map&&t._map.size>0)return e.getMap(o).toJSON();if(t._first!==null){let i=vt(t._first);if(i==="text")return s?It(t._first):e.getText(o).toJSON();if(i==="array")return e.getArray(o).toJSON();if(i==="xml")return e.getXmlFragment(o).toJSON()}return t.toJSON()}var Et={ytext:E.Text,yxmlfragment:E.XmlFragment,yxmltext:E.XmlText,ymap:E.Map,yarray:E.Array};function _e(e,t="",o=!1,s=""){let i={};if(t.length){if(e.share.has(t)){if(s.length){let d=Et[s];if(d)return e.get(t,d).toJSON()}return{[t]:Oe(e,e.share.get(t),t,o)}}return{[t]:""}}for(let[d,u]of e.share)i[d]=Oe(e,u,d,o);return i}var Re=(o=>(o.PlainLson="plain-lson",o.LossyJson="json",o))(Re||{}),wt=xt(Re),n=new St({authorize:({req:e})=>F(e)});function N(e){return yt({error:"ROOM_NOT_FOUND",message:`Room with id "${e}" not found.`},404)}n.route("GET /v2/rooms/<roomId>",async({p:e})=>{if(!await S(e.roomId))throw N(e.roomId);return{type:"room",id:e.roomId,createdAt:new Date().toISOString(),metadata:{},defaultAccesses:["room:write"],groupsAccesses:{},usersAccesses:{}}});n.route("GET /v2/rooms",()=>{let t=le().map(o=>({type:"room",id:o,createdAt:new Date().toISOString(),metadata:{},defaultAccesses:["room:write"],groupsAccesses:{},usersAccesses:{}}));return J({data:t,nextPage:null,nextCursor:null},200,"The Liveblocks dev server doesn't implement room permissions or pagination yet.")});n.route("POST /v2/rooms",Le({id:Ot}),async({body:e})=>{if(await S(e.id))return new Response(JSON.stringify({error:"ROOM_ALREADY_EXISTS",message:`Room with id "${e.id}" already exists.`}),{status:409,headers:{"Content-Type":"application/json"}});try{await me(e.id)}catch(o){if(o instanceof Error&&o.message.includes("already exists"))return new Response(JSON.stringify({error:"ROOM_ALREADY_EXISTS",message:`Room with id "${e.id}" already exists.`}),{status:409,headers:{"Content-Type":"application/json"}});throw o}return{type:"room",id:e.id,createdAt:new Date().toISOString(),metadata:{},defaultAccesses:["room:write"],groupsAccesses:{},usersAccesses:{}}});n.route("POST /v2/rooms/<roomId>",()=>r());n.route("DELETE /v2/rooms/<roomId>",async({p:e})=>(await pe(e.roomId),new Response(null,{status:204})));n.route("GET /v2/rooms/<roomId>/storage",async({url:e,p:t})=>{if(!await S(t.roomId))throw N(t.roomId);let s=wt.value(e.searchParams.get("format"))??"plain-lson",i=O(t.roomId);await i.load();let d=i.storage.loadedDriver.get_snapshot(!1),u=s==="json"?Tt(d):we(d);return new Response(JSON.stringify(u),{status:200,headers:{"Content-Type":"application/json"}})});n.route("POST /v2/rooms/<roomId>/storage",Le({liveblocksType:Ct("LiveObject"),data:bt}).refineType(),async({p:e,body:t})=>{if(!await S(e.roomId))throw N(e.roomId);let s=O(e.roomId);await s.load();let i=s.storage.loadedDriver.get_snapshot(!1),d=we(i);return Object.keys(d.data).length>0?new Response(JSON.stringify({error:"CANNOT_UPDATE_EXISTING_STORAGE",message:"The room already has storage data. It's only possible to initialize the storage for an empty room.",suggestion:"Create another room or clear this room storage first."}),{status:409,headers:{"Content-Type":"application/json"}}):(await s.driver.DANGEROUSLY_reset_nodes(t),s.unload(),new Response(JSON.stringify(t),{status:200,headers:{"Content-Type":"application/json"}}))});n.route("DELETE /v2/rooms/<roomId>/storage",async({p:e})=>{if(!await S(e.roomId))throw N(e.roomId);let o=O(e.roomId);await o.load();let s={liveblocksType:"LiveObject",data:{}};return await o.driver.DANGEROUSLY_reset_nodes(s),o.unload(),new Response(null,{status:204})});n.route("GET /v2/rooms/<roomId>/ydoc",async({url:e,p:t})=>{if(!await S(t.roomId))throw N(t.roomId);let s=O(t.roomId);await s.load();let i=e.searchParams.get("key")??"",d=e.searchParams.get("type")??"",u=e.searchParams.get("guid")??q,c=e.searchParams.get("formatting")!==null,v=await s.yjsStorage.getYDoc(u),p=_e(v,i,c,d);return new Response(JSON.stringify(p),{status:200,headers:{"Content-Type":"application/json"}})});n.route("PUT /v2/rooms/<roomId>/ydoc",async({req:e,url:t,p:o})=>{if(e.headers.get("content-type")!=="application/octet-stream")return new Response(JSON.stringify({error:"BAD_REQUEST",message:'Expected "Content-Type" header to be "application/octet-stream", and the HTTP body to be a valid binary Yjs update.'}),{status:400,headers:{"Content-Type":"application/json"}});if(!await S(o.roomId))throw N(o.roomId);let i=O(o.roomId);await i.load();let d=await e.arrayBuffer(),u=_t.fromUint8Array(new Uint8Array(d)),c=t.searchParams.get("guid"),p=t.searchParams.get("encoder")==="v2",f=c&&c!==q?c:void 0;try{return await i.mutex.runExclusive(()=>i.yjsStorage.addYDocUpdate({},u,f,p)),new Response(JSON.stringify({success:!0}),{status:200,headers:{"Content-Type":"application/json"}})}catch(I){return new Response(JSON.stringify({error:"UNPROCESSABLE_ENTITY",message:I instanceof Error?I.message:"Could not apply update",suggestion:"Please ensure the update is correct, and you selected the correct encoder to use (v1 or v2)."}),{status:422,headers:{"Content-Type":"application/json"}})}});n.route("GET /v2/rooms/<roomId>/ydoc-binary",async({url:e,p:t})=>{if(!await S(t.roomId))throw N(t.roomId);let s=O(t.roomId);await s.load();let i=e.searchParams.get("guid")??q,d=e.searchParams.get("encoder"),u=await s.yjsStorage.getYDoc(i),c=d==="v2"?X.encodeStateAsUpdateV2(u):X.encodeStateAsUpdate(u);return new Response(c,{status:200,headers:{"Content-Type":"application/octet-stream"}})});n.route("GET /v2/rooms/<roomId>/threads",()=>r()),n.route("POST /v2/rooms/<roomId>/upsert",()=>r()),n.route("POST /v2/rooms/<roomId>/update-room-id",()=>r()),n.route("POST /v2/rooms/<roomId>/update-tenant-id",()=>r()),n.route("POST /v2/rooms/<roomId>/update-organization-id",()=>r()),n.route("GET /v2/rooms/<roomId>/prewarm",()=>r()),n.route("POST /v2/rooms/<roomId>/request-storage-mutation",()=>r()),n.route("GET /v2/rooms/<roomId>/rippling/text-editor",()=>r()),n.route("POST /v2/rooms/<roomId>/rippling/text-editor",()=>r()),n.route("GET /v2/rooms/<roomId>/active_users",()=>r()),n.route("POST /v2/rooms/<roomId>/send-message",()=>r()),n.route("POST /v2/rooms/<roomId>/broadcast_event",()=>r()),n.route("GET /v2/rooms/<roomId>/versions",()=>r()),n.route("GET /v2/rooms/<roomId>/version/<version>",()=>r()),n.route("POST /v2/rooms/<roomId>/version",()=>r()),n.route("POST /v2/rooms/<roomId>/threads",()=>r()),n.route("GET /v2/rooms/<roomId>/threads/<threadId>",()=>r()),n.route("POST /v2/rooms/<roomId>/threads/<threadId>/mark-as-resolved",()=>r()),n.route("POST /v2/rooms/<roomId>/threads/<threadId>/mark-as-unresolved",()=>r()),n.route("POST /v2/rooms/<roomId>/threads/<threadId>/subscribe",()=>r()),n.route("POST /v2/rooms/<roomId>/threads/<threadId>/unsubscribe",()=>r()),n.route("GET /v2/rooms/<roomId>/threads/<threadId>/subscriptions",()=>r()),n.route("POST /v2/rooms/<roomId>/threads/<threadId>/metadata",()=>r()),n.route("DELETE /v2/rooms/<roomId>/threads/<threadId>",()=>r()),n.route("GET /v2/rooms/<roomId>/threads/<threadId>/participants",()=>r()),n.route("GET /v2/rooms/<roomId>/threads/<threadId>/inbox-notifications",()=>r()),n.route("POST /v2/rooms/<roomId>/threads/<threadId>/comments",()=>r()),n.route("GET /v2/rooms/<roomId>/threads/<threadId>/comments/<commentId>",()=>r()),n.route("POST /v2/rooms/<roomId>/threads/<threadId>/comments/<commentId>",()=>r()),n.route("POST /v2/rooms/<roomId>/threads/<threadId>/comments/<commentId>/metadata",()=>r()),n.route("DELETE /v2/rooms/<roomId>/threads/<threadId>/comments/<commentId>",()=>r()),n.route("POST /v2/rooms/<roomId>/threads/<threadId>/comments/<commentId>/add-reaction",()=>r()),n.route("POST /v2/rooms/<roomId>/threads/<threadId>/comments/<commentId>/remove-reaction",()=>r()),n.route("GET /v2/rooms/<roomId>/users/<userId>/notification-settings",()=>r()),n.route("GET /v2/rooms/<roomId>/users/<userId>/subscription-settings",()=>r()),n.route("POST /v2/rooms/<roomId>/users/<userId>/notification-settings",()=>r()),n.route("POST /v2/rooms/<roomId>/users/<userId>/subscription-settings",()=>r()),n.route("DELETE /v2/rooms/<roomId>/users/<userId>/notification-settings",()=>r()),n.route("DELETE /v2/rooms/<roomId>/users/<userId>/subscription-settings",()=>r()),n.route("GET /v2/users/<userId>/inbox-notifications/<inboxNotificationId>",()=>r()),n.route("DELETE /v2/users/<userId>/inbox-notifications/<inboxNotificationId>",()=>r()),n.route("GET /v2/users/<userId>/inbox-notifications",()=>r()),n.route("DELETE /v2/users/<userId>/inbox-notifications",()=>r()),n.route("GET /v2/users/<userId>/notification-settings",()=>r()),n.route("POST /v2/users/<userId>/notification-settings",()=>r()),n.route("DELETE /v2/users/<userId>/notification-settings",()=>r()),n.route("GET /v2/users/<userId>/room-subscription-settings",()=>r()),n.route("GET /v2/users/<userId>/threads",()=>r()),n.route("POST /v2/inbox-notifications/trigger",()=>r()),n.route("POST /v2/inbox-notifications/<inboxNotificationId>/read",()=>r()),n.route("GET /v2/threads",()=>r()),n.route("POST /v2/groups",()=>r()),n.route("GET /v2/groups/<groupId>",()=>r()),n.route("POST /v2/groups/<groupId>/add-members",()=>r()),n.route("POST /v2/groups/<groupId>/remove-members",()=>r()),n.route("DELETE /v2/groups/<groupId>",()=>r()),n.route("GET /v2/groups",()=>r()),n.route("GET /v2/users/<userId>/groups",()=>r());function Pt(e,t,o,s){if(!e.upgrade(t,{data:{refuseConnection:{code:o,message:s}}}))return new Response("Could not upgrade to WebSocket",{status:426})}var U=new Nt;U.relay("/v2/authorize-user/*",Y);U.relay("/v2/identify-user/*",Y);U.relay("/v2/c/*",a);U.relay("/v2/*",n);U.relay("/*",A);var Ne=1153;function ke(e){if(e===void 0)return;let t=Number(e);return Number.isInteger(t)&&t>0&&t<=65535?t:void 0}var Dt={description:"Start the local Liveblocks dev server",async run(e){let t=Lt(e,{string:["port","host"],boolean:["help"],alias:{h:"help",p:"port"}});if(t.help){console.log("Usage: liveblocks dev [options]"),console.log(),console.log("Start the local Liveblocks dev server"),console.log(),console.log("Options:"),console.log(` -p, --port Port to listen on (default: ${Ne})`),console.log(" --host Host to bind to (default: localhost)"),console.log(" -h, --help Show this help message");return}let o=ke(t.port)??ke(process.env.LIVEBLOCKS_DEVSERVER_PORT)??Ne,s=t.host||process.env.LIVEBLOCKS_DEVSERVER_HOST||"localhost";await Ee(o,s)&&(console.error(`Port ${o} is already in use.
|
|
363
|
-
Is another dev server already running?`),process.exit(1));let
|
|
361
|
+
`;var M=new xt({authorize:()=>!0});M.route("GET /v7",()=>Le(426));M.route("GET /v8",()=>Le(426));M.route("GET /health",()=>St({status:"ok"}));M.route("GET /",()=>yt(we.replace("__VERSION__","1.0.8")));import{jsonObjectYolo as wt,ROOT_YDOC_ID as Z,snapshotToLossyJson_eager as Lt,snapshotToPlainLson_eager as Ne}from"@liveblocks/server";import{json as Rt,ZenRouter as kt}from"@liveblocks/zenrouter";import{constant as Nt,enum_ as Pt,object as Pe,string as Dt}from"decoders";import{Base64 as At}from"js-base64";import*as X from"yjs";import*as E from"yjs";function Ct(t){return t.content instanceof E.ContentFormat||t.content instanceof E.ContentEmbed?"text":"arr"in t.content?"array":"str"in t.content?"text":"type"in t.content?"xml":"unknown"}function Ot(t){let e=[],o=t;for(;o!==null;){if(!o.deleted)if(o.content instanceof E.ContentType)e.push(o.content.type.toJSON());else if(o.content instanceof E.ContentString)e.push(o.content.str);else if(o.content instanceof E.ContentFormat){let{key:s,value:i}=o.content;e.push({key:s,value:i})}else o.content instanceof E.ContentEmbed&&e.push(o.content.embed);o=o.right}return e}function Re(t,e,o,s=!1){if(!e._first&&e._map instanceof Map&&e._map.size>0)return t.getMap(o).toJSON();if(e._first!==null){let i=Ct(e._first);if(i==="text")return s?Ot(e._first):t.getText(o).toJSON();if(i==="array")return t.getArray(o).toJSON();if(i==="xml")return t.getXmlFragment(o).toJSON()}return e.toJSON()}var _t={ytext:E.Text,yxmlfragment:E.XmlFragment,yxmltext:E.XmlText,ymap:E.Map,yarray:E.Array};function ke(t,e="",o=!1,s=""){let i={};if(e.length){if(t.share.has(e)){if(s.length){let d=_t[s];if(d)return t.get(e,d).toJSON()}return{[e]:Re(t,t.share.get(e),e,o)}}return{[e]:""}}for(let[d,u]of t.share)i[d]=Re(t,u,d,o);return i}var De=(o=>(o.PlainLson="plain-lson",o.LossyJson="json",o))(De||{}),Ut=Pt(De),n=new kt({authorize:({req:t})=>F(t)});function D(t){return Rt({error:"ROOM_NOT_FOUND",message:`Room with id "${t}" not found.`},404)}n.route("GET /v2/rooms/<roomId>",async({p:t})=>{if(!await x(t.roomId))throw D(t.roomId);return{type:"room",id:t.roomId,createdAt:new Date().toISOString(),metadata:{},defaultAccesses:["room:write"],groupsAccesses:{},usersAccesses:{}}});n.route("GET /v2/rooms",()=>{let e=fe().map(o=>({type:"room",id:o,createdAt:new Date().toISOString(),metadata:{},defaultAccesses:["room:write"],groupsAccesses:{},usersAccesses:{}}));return Y({data:e,nextPage:null,nextCursor:null},200,"The Liveblocks dev server doesn't implement room permissions or pagination yet.")});n.route("POST /v2/rooms",Pe({id:Dt}),async({body:t})=>{if(await x(t.id))return new Response(JSON.stringify({error:"ROOM_ALREADY_EXISTS",message:`Room with id "${t.id}" already exists.`}),{status:409,headers:{"Content-Type":"application/json"}});try{await ge(t.id)}catch(o){if(o instanceof Error&&o.message.includes("already exists"))return new Response(JSON.stringify({error:"ROOM_ALREADY_EXISTS",message:`Room with id "${t.id}" already exists.`}),{status:409,headers:{"Content-Type":"application/json"}});throw o}return{type:"room",id:t.id,createdAt:new Date().toISOString(),metadata:{},defaultAccesses:["room:write"],groupsAccesses:{},usersAccesses:{}}});n.route("POST /v2/rooms/<roomId>",()=>r());n.route("DELETE /v2/rooms/<roomId>",async({p:t})=>(await he(t.roomId),new Response(null,{status:204})));n.route("GET /v2/rooms/<roomId>/storage",async({url:t,p:e})=>{if(!await x(e.roomId))throw D(e.roomId);let s=Ut.value(t.searchParams.get("format"))??"plain-lson",i=_(e.roomId);await i.load();let d=i.storage.loadedDriver.get_snapshot(!1),u=s==="json"?Lt(d):Ne(d);return new Response(JSON.stringify(u),{status:200,headers:{"Content-Type":"application/json"}})});n.route("POST /v2/rooms/<roomId>/storage",Pe({liveblocksType:Nt("LiveObject"),data:wt}).refineType(),async({p:t,body:e})=>{if(!await x(t.roomId))throw D(t.roomId);let s=_(t.roomId);await s.load();let i=s.storage.loadedDriver.get_snapshot(!1),d=Ne(i);return Object.keys(d.data).length>0?new Response(JSON.stringify({error:"CANNOT_UPDATE_EXISTING_STORAGE",message:"The room already has storage data. It's only possible to initialize the storage for an empty room.",suggestion:"Create another room or clear this room storage first."}),{status:409,headers:{"Content-Type":"application/json"}}):(await s.driver.DANGEROUSLY_reset_nodes(e),s.unload(),new Response(JSON.stringify(e),{status:200,headers:{"Content-Type":"application/json"}}))});n.route("DELETE /v2/rooms/<roomId>/storage",async({p:t})=>{if(!await x(t.roomId))throw D(t.roomId);let o=_(t.roomId);await o.load();let s={liveblocksType:"LiveObject",data:{}};return await o.driver.DANGEROUSLY_reset_nodes(s),o.unload(),new Response(null,{status:204})});n.route("GET /v2/rooms/<roomId>/ydoc",async({url:t,p:e})=>{if(!await x(e.roomId))throw D(e.roomId);let s=_(e.roomId);await s.load();let i=t.searchParams.get("key")??"",d=t.searchParams.get("type")??"",u=t.searchParams.get("guid")??Z,g=t.searchParams.get("formatting")!==null,l=await s.yjsStorage.getYDoc(u),c=ke(l,i,g,d);return new Response(JSON.stringify(c),{status:200,headers:{"Content-Type":"application/json"}})});n.route("PUT /v2/rooms/<roomId>/ydoc",async({req:t,url:e,p:o})=>{if(t.headers.get("content-type")!=="application/octet-stream")return new Response(JSON.stringify({error:"BAD_REQUEST",message:'Expected "Content-Type" header to be "application/octet-stream", and the HTTP body to be a valid binary Yjs update.'}),{status:400,headers:{"Content-Type":"application/json"}});if(!await x(o.roomId))throw D(o.roomId);let i=_(o.roomId);await i.load();let d=await t.arrayBuffer(),u=At.fromUint8Array(new Uint8Array(d)),g=e.searchParams.get("guid"),c=e.searchParams.get("encoder")==="v2",f=g&&g!==Z?g:void 0;try{return await i.mutex.runExclusive(()=>i.yjsStorage.addYDocUpdate({},u,f,c)),new Response(JSON.stringify({success:!0}),{status:200,headers:{"Content-Type":"application/json"}})}catch(v){return new Response(JSON.stringify({error:"UNPROCESSABLE_ENTITY",message:v instanceof Error?v.message:"Could not apply update",suggestion:"Please ensure the update is correct, and you selected the correct encoder to use (v1 or v2)."}),{status:422,headers:{"Content-Type":"application/json"}})}});n.route("GET /v2/rooms/<roomId>/ydoc-binary",async({url:t,p:e})=>{if(!await x(e.roomId))throw D(e.roomId);let s=_(e.roomId);await s.load();let i=t.searchParams.get("guid")??Z,d=t.searchParams.get("encoder"),u=await s.yjsStorage.getYDoc(i),g=d==="v2"?X.encodeStateAsUpdateV2(u):X.encodeStateAsUpdate(u);return new Response(g,{status:200,headers:{"Content-Type":"application/octet-stream"}})});n.route("GET /v2/rooms/<roomId>/threads",()=>r()),n.route("POST /v2/rooms/<roomId>/upsert",()=>r()),n.route("POST /v2/rooms/<roomId>/update-room-id",()=>r()),n.route("POST /v2/rooms/<roomId>/update-tenant-id",()=>r()),n.route("POST /v2/rooms/<roomId>/update-organization-id",()=>r()),n.route("GET /v2/rooms/<roomId>/prewarm",()=>r()),n.route("POST /v2/rooms/<roomId>/request-storage-mutation",()=>r()),n.route("GET /v2/rooms/<roomId>/rippling/text-editor",()=>r()),n.route("POST /v2/rooms/<roomId>/rippling/text-editor",()=>r()),n.route("GET /v2/rooms/<roomId>/active_users",()=>r()),n.route("POST /v2/rooms/<roomId>/send-message",()=>r()),n.route("POST /v2/rooms/<roomId>/broadcast_event",()=>r()),n.route("GET /v2/rooms/<roomId>/versions",()=>r()),n.route("GET /v2/rooms/<roomId>/version/<version>",()=>r()),n.route("POST /v2/rooms/<roomId>/version",()=>r()),n.route("POST /v2/rooms/<roomId>/threads",()=>r()),n.route("GET /v2/rooms/<roomId>/threads/<threadId>",()=>r()),n.route("POST /v2/rooms/<roomId>/threads/<threadId>/mark-as-resolved",()=>r()),n.route("POST /v2/rooms/<roomId>/threads/<threadId>/mark-as-unresolved",()=>r()),n.route("POST /v2/rooms/<roomId>/threads/<threadId>/subscribe",()=>r()),n.route("POST /v2/rooms/<roomId>/threads/<threadId>/unsubscribe",()=>r()),n.route("GET /v2/rooms/<roomId>/threads/<threadId>/subscriptions",()=>r()),n.route("POST /v2/rooms/<roomId>/threads/<threadId>/metadata",()=>r()),n.route("DELETE /v2/rooms/<roomId>/threads/<threadId>",()=>r()),n.route("GET /v2/rooms/<roomId>/threads/<threadId>/participants",()=>r()),n.route("GET /v2/rooms/<roomId>/threads/<threadId>/inbox-notifications",()=>r()),n.route("POST /v2/rooms/<roomId>/threads/<threadId>/comments",()=>r()),n.route("GET /v2/rooms/<roomId>/threads/<threadId>/comments/<commentId>",()=>r()),n.route("POST /v2/rooms/<roomId>/threads/<threadId>/comments/<commentId>",()=>r()),n.route("POST /v2/rooms/<roomId>/threads/<threadId>/comments/<commentId>/metadata",()=>r()),n.route("DELETE /v2/rooms/<roomId>/threads/<threadId>/comments/<commentId>",()=>r()),n.route("POST /v2/rooms/<roomId>/threads/<threadId>/comments/<commentId>/add-reaction",()=>r()),n.route("POST /v2/rooms/<roomId>/threads/<threadId>/comments/<commentId>/remove-reaction",()=>r()),n.route("GET /v2/rooms/<roomId>/users/<userId>/notification-settings",()=>r()),n.route("GET /v2/rooms/<roomId>/users/<userId>/subscription-settings",()=>r()),n.route("POST /v2/rooms/<roomId>/users/<userId>/notification-settings",()=>r()),n.route("POST /v2/rooms/<roomId>/users/<userId>/subscription-settings",()=>r()),n.route("DELETE /v2/rooms/<roomId>/users/<userId>/notification-settings",()=>r()),n.route("DELETE /v2/rooms/<roomId>/users/<userId>/subscription-settings",()=>r()),n.route("GET /v2/users/<userId>/inbox-notifications/<inboxNotificationId>",()=>r()),n.route("DELETE /v2/users/<userId>/inbox-notifications/<inboxNotificationId>",()=>r()),n.route("GET /v2/users/<userId>/inbox-notifications",()=>r()),n.route("DELETE /v2/users/<userId>/inbox-notifications",()=>r()),n.route("GET /v2/users/<userId>/notification-settings",()=>r()),n.route("POST /v2/users/<userId>/notification-settings",()=>r()),n.route("DELETE /v2/users/<userId>/notification-settings",()=>r()),n.route("GET /v2/users/<userId>/room-subscription-settings",()=>r()),n.route("GET /v2/users/<userId>/threads",()=>r()),n.route("POST /v2/inbox-notifications/trigger",()=>r()),n.route("POST /v2/inbox-notifications/<inboxNotificationId>/read",()=>r()),n.route("GET /v2/threads",()=>r()),n.route("POST /v2/groups",()=>r()),n.route("GET /v2/groups/<groupId>",()=>r()),n.route("POST /v2/groups/<groupId>/add-members",()=>r()),n.route("POST /v2/groups/<groupId>/remove-members",()=>r()),n.route("DELETE /v2/groups/<groupId>",()=>r()),n.route("GET /v2/groups",()=>r()),n.route("GET /v2/users/<userId>/groups",()=>r());function Gt(t,e,o,s){if(!t.upgrade(e,{data:{refuseConnection:{code:o,message:s}}}))return new Response("Could not upgrade to WebSocket",{status:426})}var B=new Bt;B.relay("/v2/authorize-user/*",$);B.relay("/v2/identify-user/*",$);B.relay("/v2/c/*",a);B.relay("/v2/*",n);B.relay("/*",M);var Ae=1153;function Ue(t){if(t===void 0)return;let e=Number(t);return Number.isInteger(e)&&e>0&&e<=65535?e:void 0}var Yt={description:"Start the local Liveblocks dev server",async run(t){let e=jt(t,{string:["port","host"],boolean:["help","check","ephemeral","ci"],default:{check:!0},alias:{h:"help",p:"port"}});if(e.help){console.log("Usage: liveblocks dev [options]"),console.log(),console.log("Start the local Liveblocks dev server"),console.log(),console.log("Options:"),console.log(` --port, -p Port to listen on (default: ${Ae})`),console.log(" --host Host to bind to (default: localhost)"),console.log(" --ci Best defaults for CI (same as --ephemeral --no-check)"),console.log(" --no-check Skip project setup check"),console.log(" --ephemeral Do not persist state between restarts"),console.log(" (Recommended for running unit tests.)"),console.log(" --help, -h Show this help message");return}e.ci&&(e.ephemeral=!0,e.check=!1);let o=Ue(e.port)??Ue(process.env.LIVEBLOCKS_DEVSERVER_PORT)??Ae,s=e.host||process.env.LIVEBLOCKS_DEVSERVER_HOST||"localhost",i=e.ephemeral?pe():null;await Se(o,s)&&(console.error(`Port ${o} is already in use.
|
|
362
|
+
Is another dev server already running?`),process.exit(1));let d=Jt.serve({hostname:s,port:o,async fetch(l,c){if(l.headers.get("Upgrade")==="websocket"){let m=ne(l);if(!m.ok)return q(m.xwarn,!0),Gt(c,l,Mt.NOT_ALLOWED,"You have no access to this room");let{roomId:h,ticketData:I}=m,b=_(h);await b.load();let C=await b.createTicket(I),k=C.sessionKey;if(c.upgrade(l,{data:{room:b,ticket:C,sessionKey:k}})){console.log(`${N("101")} WS ${new URL(l.url).pathname}`);return}return new Response("Could not upgrade to WebSocket",{status:426})}let f=new URL(l.url),v=`${l.method} ${f.pathname}`,y=await B.fetch(l),L=y.status,Q=L>=500?V(String(L)):L>=400?A(String(L)):N(String(L));console.log(`${Q} ${v}`);let p=y.headers.get("X-LB-Warn")??void 0;return q(p,!y.ok),y},error(l){return console.error(l),new Response("An unknown error occurred",{status:500})},websocket:{async open(l){let{refuseConnection:c,room:f,ticket:v}=l.data;if(c){l.close(c.code,c.message);return}f&&v&&await f.startBrowserSession(v,l)},async message(l,c){let{room:f,sessionKey:v}=l.data;f&&v&&await f.handleData(v,c)},close(l,c,f){let{room:v,sessionKey:y}=l.data;v&&y&&v.endBrowserSession(y,c,f)}}});console.log(`Liveblocks dev server running at http://${d.hostname}:${d.port}`),i&&console.log(T(`Ephemeral mode, using ${i}`));let u=e.check?await be(o):[],g=`http://localhost:${o}`;console.log(T("Press ")+J("q")+T(" to quit, ")+J("c")+T(" to clear")),process.stdin.isTTY&&(process.stdin.setRawMode(!0),process.stdin.resume(),process.stdin.on("data",l=>{let c=l.toString();if(c==="q"||c==="")d.stop().then(()=>{ve(),process.exit(0)});else if(c==="c")console.clear();else if(c==="p")if(u.length>0){let f=Te(u,g);ye(f),console.log(T("Copied AI fix prompt to clipboard"))}else console.log(T("No setup issues detected"))}))}},ar=Yt;export{ar as default};
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{execFileSync as c,spawnSync as t}from"node:child_process";import{parse as l}from"@bomb.sh/args";function a(){try{return c("bun",["--version"],{stdio:"ignore"}),!0}catch{return!1}}function d(){if(a()){let e=t("bun",process.argv.slice(1),{stdio:"inherit"});process.exit(e.status??1)}else console.error("The Liveblocks local dev server requires Bun."),console.error("See https://liveblocks.io/docs/get-started/dev-server for more information."),process.exit(1)}var s=process.argv.slice(2),o=s.findIndex(e=>!e.startsWith("-")),r=l(o>=0?s.slice(0,o):s,{boolean:["help","version"],alias:{h:"help",v:"version"}}),n=o>=0?s[o]:void 0,u=o>=0?s.slice(o+1):[];async function m(e){switch(e){case"dev":return typeof Bun>"u"&&d(),(await import("./dev-server-
|
|
2
|
+
import{execFileSync as c,spawnSync as t}from"node:child_process";import{parse as l}from"@bomb.sh/args";function a(){try{return c("bun",["--version"],{stdio:"ignore"}),!0}catch{return!1}}function d(){if(a()){let e=t("bun",process.argv.slice(1),{stdio:"inherit"});process.exit(e.status??1)}else console.error("The Liveblocks local dev server requires Bun."),console.error("See https://liveblocks.io/docs/get-started/dev-server for more information."),process.exit(1)}var s=process.argv.slice(2),o=s.findIndex(e=>!e.startsWith("-")),r=l(o>=0?s.slice(0,o):s,{boolean:["help","version"],alias:{h:"help",v:"version"}}),n=o>=0?s[o]:void 0,u=o>=0?s.slice(o+1):[];async function m(e){switch(e){case"dev":return typeof Bun>"u"&&d(),(await import("./dev-server-7ZSOLBUN.js")).default;case"upgrade":return(await import("./upgrade-AJIPXALV.js")).default;default:return}}var v={dev:"Start the local Liveblocks dev server",upgrade:"Upgrade all Liveblocks packages"};function p(){console.log("liveblocks v1.0.8"),console.log(),console.log("Usage: liveblocks <command> [options]"),console.log(),console.log("Commands:");for(let[e,i]of Object.entries(v))console.log(` ${e.padEnd(12)} ${i}`);console.log(),console.log("Options:"),console.log(" --help, -h Show this help message"),console.log(" --version, -v Show version number")}async function f(){r.version&&(console.log("1.0.8"),process.exit(0)),(r.help||!n)&&(p(),process.exit(n?0:1));let e=await m(n);e?await e.run(u):(console.error(`Unknown command: ${n}`),console.error('Run "liveblocks --help" for usage.'),process.exit(1))}f();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{parse as b}from"@bomb.sh/args";import{execFileSync as k}from"child_process";import{existsSync as c,readFileSync as f}from"fs";import{resolve as n}from"path";function p(e){return Object.keys(e??{}).filter(s=>s.startsWith("@liveblocks/"))}function v(){let e=process.cwd();return c(n(e,"yarn.lock"))?"yarn":c(n(e,"pnpm-lock.yaml"))?"pnpm":c(n(e,"bun.lockb"))||c(n(e,"bun.lock"))?"bun":"npm"}var h={description:"Upgrade all Liveblocks packages",run(e){let s=b(e,{boolean:["help"],alias:{h:"help"}});if(s.help){console.log("Usage: liveblocks upgrade [version]"),console.log(),console.log("Upgrade all @liveblocks/* packages in your project to the same version."),console.log(),console.log("Arguments:"),console.log(' version Target version or tag (default: "latest")'),console.log(),console.log("Options:"),console.log(" -h
|
|
1
|
+
import{parse as b}from"@bomb.sh/args";import{execFileSync as k}from"child_process";import{existsSync as c,readFileSync as f}from"fs";import{resolve as n}from"path";function p(e){return Object.keys(e??{}).filter(s=>s.startsWith("@liveblocks/"))}function v(){let e=process.cwd();return c(n(e,"yarn.lock"))?"yarn":c(n(e,"pnpm-lock.yaml"))?"pnpm":c(n(e,"bun.lockb"))||c(n(e,"bun.lock"))?"bun":"npm"}var h={description:"Upgrade all Liveblocks packages",run(e){let s=b(e,{boolean:["help"],alias:{h:"help"}});if(s.help){console.log("Usage: liveblocks upgrade [version]"),console.log(),console.log("Upgrade all @liveblocks/* packages in your project to the same version."),console.log(),console.log("Arguments:"),console.log(' version Target version or tag (default: "latest")'),console.log(),console.log("Options:"),console.log(" --help, -h Show this help message");return}let g=String(s._[0]??"latest"),u=n(process.cwd(),"package.json"),l;try{l=JSON.parse(f(u,"utf-8"))}catch{console.error("No package.json found in the current directory."),process.exit(1)}let r=Array.from(new Set([...p(l.dependencies),...p(l.devDependencies),...p(l.peerDependencies)]));r.length===0&&(console.error("No @liveblocks/* packages found in package.json."),process.exit(1));let d=r.map(o=>o==="@liveblocks/react-comments"?"@liveblocks/react-ui":o),m=r.filter(o=>o==="@liveblocks/react-comments"),t=v(),i,a;switch(t){case"yarn":case"bun":case"pnpm":i="add",a="remove";break;case"npm":default:i="install",a="uninstall";break}console.log(),console.log(`Upgrading all @liveblocks/* packages to ${g}...`),console.log(),m.length>0&&k(t,[a,...m],{stdio:"inherit"}),d.length>0&&k(t,[i,...d.map(o=>`${o}@${g}`)],{stdio:"inherit"}),r.includes("@liveblocks/core")&&(console.log(),console.warn("Warning: @liveblocks/core contains private APIs only. It is recommended to only rely on @liveblocks/client."),console.log())}},P=h;export{P as default};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "liveblocks",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.8",
|
|
4
4
|
"description": "Liveblocks command line interface",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -40,8 +40,8 @@
|
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"@bomb.sh/args": "^0.3.1",
|
|
42
42
|
"@liveblocks/core": "3.14.0-rc1",
|
|
43
|
-
"@liveblocks/server": "1.0.
|
|
44
|
-
"@liveblocks/zenrouter": "1.0.
|
|
43
|
+
"@liveblocks/server": "1.0.8",
|
|
44
|
+
"@liveblocks/zenrouter": "1.0.8",
|
|
45
45
|
"decoders": "^2.8.0-1",
|
|
46
46
|
"js-base64": "^3.7.5",
|
|
47
47
|
"yjs": "^13.6.10"
|