liveblocks 1.0.3 → 1.0.4

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.
@@ -0,0 +1,277 @@
1
+ import{parse as nt}from"@bomb.sh/args";import{WebsocketCloseCodes as it}from"@liveblocks/core";import{ZenRelay as at}from"@liveblocks/zenrouter";import dt from"bun";import{nanoid as ye,Permission as Ie}from"@liveblocks/core";import{ProtocolVersion as K}from"@liveblocks/server";import{nanoid as ve}from"@liveblocks/core";function B(t){return btoa(t).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function z(t){let e=t.replace(/-/g,"+").replace(/_/g,"/"),r=e+"=".repeat((4-e.length%4)%4);return atob(r)}function F(t){let e=Math.floor(Date.now()/1e3),r={...t,iat:e,exp:e+60*60,jti:ve(12)},s=B(JSON.stringify({alg:"none",typ:"JWT"})),n=B(JSON.stringify(r));return`${s}.${n}.`}function _(t){let e=t.split(".");if(e.length!==3)return null;let[r,s,n]=e;try{if(JSON.parse(z(r)).alg!=="none")return null}catch{return null}let d;try{d=JSON.parse(z(s))}catch{return null}if(n!==""||d.pid!=="localdev")return null;let c=d.exp;return typeof c=="number"&&c<Math.floor(Date.now()/1e3)?null:d}function Te(t,e){if(e[t])return e[t];for(let[r,s]of Object.entries(e))if(r.endsWith("*")){let n=r.slice(0,-1);if(t.startsWith(n))return s}return[]}function q(t){let e=new URL(t.url),r=e.pathname==="/v7"?K.V7:e.pathname==="/v8"?K.V8:null;if(r===null)return null;let s=e.searchParams.get("roomId");if(!s)return null;let n=e.searchParams.get("tok");if(n!==null){let c=_(n);if(!c)return null;if(c.k==="acc"){let u=Te(s,c.perms);return u.length===0?null:[s,{version:r,id:c.uid,info:c.ui,scopes:u}]}else if(c.k==="id")return[s,{version:r,id:c.uid,info:c.ui,scopes:[Ie.Write]}];return null}return e.searchParams.get("pubkey")==="pk_localdev"?[s,{version:r,anonymousId:ye(),scopes:["room:write"]}]:null}import{DefaultMap as xe,Room as Ne}from"@liveblocks/server";import{mkdirSync as Q,readdirSync as De}from"fs";import{resolve as Z}from"path";import{asPos as be,CrdtType as E,nn as Ee}from"@liveblocks/core";import{makeInMemorySnapshot as Se,NestedMap as ke,plainLsonToNodeStream as Oe,quote as b}from"@liveblocks/server";import{Database as we}from"bun:sqlite";function J(t){try{return t!==void 0?JSON.parse(t):void 0}catch{return}}function H(t){if(t>512)throw new Error("More than 512 params not supported");return new Array(t).fill("?").join(",")}function $e(t){return Array.isArray(t)?t:Array.from(t)}function Re(t){return t.query("SELECT node_id, crdt_json FROM nodes").values().map(([e,r])=>[e,J(r)])}function P(t,e){let r=t.prepare(`INSERT INTO nodes (node_id, crdt_json)
2
+ VALUES (?, ?)
3
+ ON CONFLICT (node_id) DO UPDATE SET crdt_json = ?`);t.transaction(n=>{for(let[d,c]of n){let u=JSON.stringify(c);r.run(d,u,u)}})(e)}function V(t,e){let r=$e(e),s=r.length;t.query(`DELETE FROM nodes WHERE node_id IN (${H(s)})`).run(...r)}function W(t){let e=new ke;for(let[r,s]of t){if(s.parentId===void 0)continue;let n=e.get(s.parentId,s.parentKey);(n===void 0||r>n)&&e.set(s.parentId,s.parentKey,r)}return e}function Pe(t,e){let r=new Map(Re(t));r.has("root")||r.set("root",{type:E.OBJECT,data:{}});let s=new Set,n=["root"],d=new Map,c=W(r);for(;n.length>0;){let p=n.pop(),f=Ee(r.get(p));if(f.type===E.OBJECT)for(let I of c.keysAt(p))Object.prototype.hasOwnProperty.call(f.data,I)&&(delete f.data[I],s.add(p),e.warn(`[integrity] Found data key ${b(I)} from ${b(p)} (conflicted with child node)`));if(f.type!==E.REGISTER)n.push(...c.valuesAt(p));else if(r.get(f.parentId)?.type===E.OBJECT)continue;d.set(p,f)}let u=new Set;for(let[p,f]of r)d.has(p)||(f.parentId!==void 0&&d.has(f.parentId)?d.get(f.parentId)?.type===E.REGISTER?e.warn(`[integrity] Found unreachable node ${b(p)} (child of live register)`):e.warn(`[integrity] Found conflicting sibling ${b(p)} (conflicted with ${b(c.get(f.parentId,f.parentKey))} at ${b(f.parentKey)})`):e.warn(`[integrity] Found orphan ${b(p)}`),u.add(p),s.delete(p));if(s.size>0||u.size>0){if(s.size>0){let p=new Map;for(let f of s){let I=d.get(f);I!==void 0&&p.set(f,I)}P(t,p)}u.size>0&&V(t,u)}let T=u.size===0?c:W(d);return{nodes:d,revNodes:T}}function X(t,e){return t?.type===E.OBJECT&&Object.prototype.hasOwnProperty.call(t.data,e)&&t.data[e]!==void 0}var C=class{db;constructor(e){let r=new we(e,{create:!0});r.run("PRAGMA journal_mode = WAL"),r.run("PRAGMA case_sensitive_like = ON"),r.run(`CREATE TABLE IF NOT EXISTS system (
4
+ setting TEXT NOT NULL,
5
+ jval TEXT NOT NULL,
6
+ PRIMARY KEY (setting)
7
+ )`),r.run(`CREATE TABLE IF NOT EXISTS nodes (
8
+ node_id TEXT NOT NULL,
9
+ crdt_json TEXT NOT NULL,
10
+ PRIMARY KEY (node_id)
11
+ )`),r.run(`CREATE TABLE IF NOT EXISTS metadata (
12
+ key TEXT NOT NULL,
13
+ jval TEXT NOT NULL,
14
+ PRIMARY KEY (key)
15
+ )`),r.run(`CREATE TABLE IF NOT EXISTS ydocs (
16
+ doc_id TEXT NOT NULL,
17
+ key TEXT NOT NULL,
18
+ data BLOB NOT NULL,
19
+ PRIMARY KEY (doc_id, key)
20
+ )`),this.db=r}load_nodes_api(e){let r=this.db,{nodes:s,revNodes:n}=Pe(r,e),d=m=>s.get(m),c=(m,l)=>n.get(m,l),u=(m,l)=>n.has(m,l);function T(m,l){let g;for(let v of n.keysAt(m)){let y=be(v);y>l&&(g===void 0||y<g)&&(g=y)}return g}function p(m,l,g=!1){let v=d(l.parentId);if(v===void 0)throw new Error(`No such parent ${b(l.parentId)}`);if(l.type===E.REGISTER&&v.type===E.OBJECT)throw new Error("Cannot add register under object");let y=c(l.parentId,l.parentKey);if(y!==m){let w=X(v,l.parentKey);if(y!==void 0||w)if(g)U(l.parentId,l.parentKey);else throw new Error(`Key ${b(l.parentKey)} already exists`);n.set(l.parentId,l.parentKey,m)}s.set(m,l),P(r,[[m,l]])}function f(m,l){let g=d(m);if(g?.parentId===void 0)return;if(u(g.parentId,l))throw new Error(`Pos ${b(l)} already taken`);n.delete(g.parentId,g.parentKey);let v={...g,parentKey:l};s.set(m,v),n.set(g.parentId,l,m),P(r,[[m,v]])}function I(m,l,g=!1){let v=d(m);if(v?.type!==E.OBJECT)return;for(let w of Object.keys(l)){let R=c(m,w);if(R!==void 0)if(g)L(R);else throw new Error(`Child node already exists under ${w}`)}let y={...v,data:{...v.data,...l}};s.set(m,y),P(r,[[m,y]])}function L(m){let l=d(m);if(l?.parentId===void 0)return;n.delete(l.parentId,l.parentKey);let g=[],v=[m];for(;v.length>0;){let y=v.pop();v.push(...n.valuesAt(y)),s.delete(y),n.deleteAll(y),g.push(y)}V(r,g)}function U(m,l){let g=d(m);if(X(g,l)){let{[l]:y,...w}=g.data,R={...g,data:w};s.set(m,R),P(r,[[m,R]])}let v=c(m,l);v!==void 0&&L(v)}return{get_node:d,iter_nodes:()=>s.entries(),has_node:m=>s.has(m),get_child_at:c,has_child_at:u,get_next_sibling:T,set_child:p,move_sibling:f,delete_node:L,delete_child_key:U,set_object_data:I,get_snapshot(m){return Se(s)}}}DANGEROUSLY_reset_nodes(e){let r=this.db.prepare("DELETE FROM nodes"),s=this.db.prepare("INSERT INTO nodes (node_id, crdt_json) VALUES (?, ?)");this.db.transaction(()=>{r.run();for(let[d,c]of Oe(e))s.run(d,JSON.stringify(c))})()}raw_iter_nodes(){return this.db.query("SELECT node_id, crdt_json FROM nodes").values().map(([e,r])=>[e,J(r)])}get_meta(e){let s=this.db.query("SELECT jval FROM metadata WHERE key = ?").get(e)?.jval;if(s!==void 0)return J(s)??null}put_meta(e,r){let s=JSON.stringify(r);this.db.run(`INSERT INTO metadata (key, jval)
21
+ VALUES (?, ?)
22
+ ON CONFLICT (key) DO UPDATE SET jval = ?
23
+ `,[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)
24
+ VALUES ('last_actor_id', '0')
25
+ ON CONFLICT (setting) DO UPDATE SET jval = json(CAST(jval AS INTEGER) + 1)
26
+ 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,r,s){this.db.query(`INSERT INTO ydocs
27
+ VALUES (?, ?, ?)
28
+ ON CONFLICT (doc_id, key) DO UPDATE SET data = ?`).run(e,r,s,s)}delete_y_updates(e,r){let s=r.length;this.db.query(`DELETE FROM ydocs WHERE doc_id = ? AND key IN (${H(s)})`).run(e,...r)}DANGEROUSLY_wipe_all_y_updates(){this.db.query("DELETE FROM ydocs").run()}close(){this.db.close()}};var x=".liveblocks/v1/rooms",ee=new xe(t=>{Q(x,{recursive:!0});let e=new C(te(t));return new Ne(t,{storage:e})});function te(t){let e=Z(x,`${encodeURIComponent(t)}.db`);if(!e.startsWith(Z(x)+"/"))throw new Error("Invalid room ID");return e}function O(t){return ee.getOrCreate(t)}async function S(t){let e=te(t);return await Bun.file(e).exists()}function oe(){try{return Q(x,{recursive:!0}),De(x).filter(r=>r.endsWith(".db")).map(r=>decodeURIComponent(r.replace(/\.db$/,"")))}catch{return[]}}async function re(t){if(await S(t))throw new Error(`Room with id "${t}" already exists`);let e=ee.getOrCreate(t);await e.load();let r={liveblocksType:"LiveObject",data:{}};await e.driver.DANGEROUSLY_reset_nodes(r),e.unload()}import{Permission as _e}from"@liveblocks/core";import{ZenRouter as Ce}from"@liveblocks/zenrouter";import{array as Ae,enum_ as je,object as Me,optional as Je,record as Ye,string as Ge}from"decoders";import{json as ne}from"@liveblocks/zenrouter";function A(t){let e=t.headers.get("Authorization");if(e==="Bearer sk_localdev")return!0;if(!e)throw ne({error:"Unauthorized",message:"Missing secret key"},401);if(e.startsWith("Bearer "))throw ne({error:"Forbidden",message:"Invalid secret key. The Liveblocks dev server can only be used with 'sk_localdev' as a secret key"},403);return!1}import{inexact as Le,optional as ie,string as ae}from"decoders";var de=Le({name:ie(ae),avatar:ie(ae)}).refineType();import{json as ce}from"@liveblocks/zenrouter";function k(t,e,r="This is a dummy response.",s){let n={};return n["X-LB-Dummy"]=r,s&&(n["X-LB-Dummy-Key"]=s),ce(t,e,n)}function o(t="This endpoint isn't implemented in the Liveblocks dev server (yet)"){return ce({error:"Not implemented",message:t},501)}var Ue=je(_e),j=new Ce({authorize:({req:t})=>A(t)});j.route("POST /v2/authorize-user",Me({userId:Ge,userInfo:Je(de),permissions:Ye(Ae(Ue))}),({body:t})=>({token:F({k:"acc",pid:"localdev",uid:t.userId,perms:t.permissions,ui:t.userInfo})}));j.route("POST /v2/identify-user",()=>o("ID tokens are not supported with the local dev server. To develop locally, use access tokens instead (via POST /v2/authorize-user)."));import{ZenRouter as Be}from"@liveblocks/zenrouter";var a=new Be({cors:{allowCredentials:!0,maxAge:600},authorize:({req:t})=>{let e=t.headers.get("Authorization");if(!e?.startsWith("Bearer "))return!1;let r=e.slice(7);return _(r)!==null}});a.route("GET /v2/c/threads",()=>k({threads:[],inboxNotifications:[],subscriptions:[],meta:{nextCursor:null,requestedAt:new Date().toISOString(),permissionHints:{}}}));a.route("GET /v2/c/threads/delta",()=>k({threads:[],inboxNotifications:[],subscriptions:[],meta:{requestedAt:new Date().toISOString(),permissionHints:{}}}));a.route("GET /v2/c/inbox-notifications",()=>k({inboxNotifications:[],threads:[],subscriptions:[],groups:[],meta:{nextCursor:null,requestedAt:new Date().toISOString()}}));a.route("GET /v2/c/inbox-notifications/count",()=>k({count:0}));a.route("GET /v2/c/rooms/<roomId>/threads",()=>k({data:[],inboxNotifications:[],subscriptions:[],meta:{nextCursor:null,requestedAt:new Date().toISOString(),permissionHints:{}}}));a.route("GET /v2/c/rooms/<roomId>/threads/delta",()=>k({data:[],inboxNotifications:[],subscriptions:[],deletedThreads:[],deletedInboxNotifications:[],deletedSubscriptions:[],meta:{requestedAt:new Date().toISOString(),permissionHints:{}}}));a.route("PUT /v2/c/rooms/<roomId>/attachments/<attachmentId>/upload/<name>",()=>o()),a.route("POST /v2/c/rooms/<roomId>/attachments/<attachmentId>/multipart/<name>",()=>o()),a.route("PUT /v2/c/rooms/<roomId>/attachments/<attachmentId>/multipart/<uploadId>/<partNumber>",()=>o()),a.route("POST /v2/c/rooms/<roomId>/attachments/<attachmentId>/multipart/<uploadId>/complete",()=>o()),a.route("DELETE /v2/c/rooms/<roomId>/attachments/<attachmentId>/multipart/<uploadId>",()=>o()),a.route("POST /v2/c/rooms/<roomId>/attachments/presigned-urls",()=>o()),a.route("POST /v2/c/rooms/<roomId>/text-metadata",()=>o()),a.route("POST /v2/c/rooms/<roomId>/send-message",()=>o()),a.route("GET /v2/c/rooms/<roomId>/storage",()=>o()),a.route("POST /v2/c/rooms/<roomId>/version",()=>o()),a.route("GET /v2/c/rooms/<roomId>/y-version/<version>",()=>o()),a.route("POST /v2/c/rooms/<roomId>/ai/contextual-prompt",()=>o()),a.route("POST /v2/c/rooms/<roomId>/threads",()=>o()),a.route("POST /v2/c/rooms/<roomId>/threads/search",()=>o()),a.route("DELETE /v2/c/rooms/<roomId>/threads/<threadId>",()=>o()),a.route("POST /v2/c/rooms/<roomId>/threads/<threadId>/metadata",()=>o()),a.route("POST /v2/c/rooms/<roomId>/threads/<threadId>/mark-as-resolved",()=>o()),a.route("POST /v2/c/rooms/<roomId>/threads/<threadId>/mark-as-unresolved",()=>o()),a.route("POST /v2/c/rooms/<roomId>/threads/<threadId>/subscribe",()=>o()),a.route("POST /v2/c/rooms/<roomId>/threads/<threadId>/unsubscribe",()=>o()),a.route("POST /v2/c/rooms/<roomId>/threads/<threadId>/comments",()=>o()),a.route("GET /v2/c/rooms/<roomId>/threads/<threadId>/comments/<commentId>",()=>o()),a.route("POST /v2/c/rooms/<roomId>/threads/<threadId>/comments/<commentId>",()=>o()),a.route("POST /v2/c/rooms/<roomId>/threads/<threadId>/comments/<commentId>/metadata",()=>o()),a.route("DELETE /v2/c/rooms/<roomId>/threads/<threadId>/comments/<commentId>",()=>o()),a.route("POST /v2/c/rooms/<roomId>/threads/<threadId>/comments/<commentId>/reactions",()=>o()),a.route("DELETE /v2/c/rooms/<roomId>/threads/<threadId>/comments/<commentId>/reactions/<emoji>",()=>o()),a.route("GET /v2/c/rooms/<roomId>/threads/comments/search",()=>o()),a.route("GET /v2/c/rooms/<roomId>/threads/<threadId>/participants",()=>o()),a.route("GET /v2/c/rooms/<roomId>/notification-settings",()=>o()),a.route("GET /v2/c/rooms/<roomId>/subscription-settings",()=>o()),a.route("POST /v2/c/rooms/<roomId>/notification-settings",()=>o()),a.route("POST /v2/c/rooms/<roomId>/subscription-settings",()=>o()),a.route("GET /v2/c/inbox-notifications/delta",()=>o()),a.route("DELETE /v2/c/inbox-notifications",()=>o()),a.route("POST /v2/c/inbox-notifications/read",()=>o()),a.route("DELETE /v2/c/inbox-notifications/<inboxNotificationId>",()=>o()),a.route("POST /v2/c/rooms/<roomId>/inbox-notifications/read",()=>o()),a.route("POST /v2/c/rooms/<roomId>/text-mentions",()=>o()),a.route("DELETE /v2/c/rooms/<roomId>/text-mentions/<mentionId>",()=>o()),a.route("GET /v2/c/notification-settings",()=>o()),a.route("POST /v2/c/notification-settings",()=>o()),a.route("GET /v2/c/rooms/<roomId>/thread-with-notification/<threadId>",()=>o()),a.route("GET /v2/c/urls/metadata",()=>o()),a.route("GET /v2/c/rooms/<roomId>/versions",()=>o()),a.route("GET /v2/c/rooms/<roomId>/versions/delta",()=>o()),a.route("POST /v2/c/groups/find",()=>o());import{abort as ue,html as Fe,ZenRouter as Ke}from"@liveblocks/zenrouter";var le=`<!DOCTYPE html>
29
+ <html lang="en">
30
+ <head>
31
+ <meta charset="UTF-8">
32
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
33
+ <title>Liveblocks Dev Server</title>
34
+ <style>
35
+ * { box-sizing: border-box; }
36
+ body {
37
+ font-family: system-ui, -apple-system, sans-serif;
38
+ background: #0f0f0f;
39
+ color: #e5e5e5;
40
+ min-height: 100vh;
41
+ margin: 0;
42
+ padding: 3rem 2rem;
43
+ }
44
+ main { max-width: 1000px; margin: 0 auto; }
45
+ h1 { color: #fff; font-size: 1.5rem; margin: 0 0 2rem; }
46
+ h2 { color: #ccc; font-size: 1rem; font-weight: 500; margin: 0 0 1rem; }
47
+ .pickers { display: flex; gap: 1rem; margin-bottom: 2rem; }
48
+ fieldset {
49
+ border: 1px solid #333;
50
+ border-radius: 6px;
51
+ padding: 1rem 1.25rem;
52
+ margin: 0;
53
+ flex: 1;
54
+ }
55
+ legend { color: #888; font-size: 0.75rem; text-transform: uppercase; padding: 0 0.5rem; }
56
+ label { display: block; cursor: pointer; padding: 0.25rem 0; }
57
+ label.nested { margin-left: 1.5rem; color: #999; }
58
+ label.disabled { color: #555; cursor: default; }
59
+ input[type="radio"] { margin-right: 0.5rem; accent-color: #7c6aef; }
60
+ .columns { display: grid; grid-template-columns: 1fr 1fr; gap: 1.5rem; align-items: start; }
61
+ .column h3 { color: #888; font-size: 0.75rem; font-weight: 500; margin: 0 0 0.75rem; text-transform: uppercase; letter-spacing: 0.05em; }
62
+ .column pre { margin: 0; min-height: 200px; }
63
+ .note { color: #555; font-style: italic; margin: 0; padding: 1rem 0; }
64
+ pre {
65
+ background: #1a1a1a;
66
+ border: 1px solid #333;
67
+ border-radius: 8px;
68
+ padding: 1.25rem;
69
+ overflow-x: auto;
70
+ font-size: 0.8rem;
71
+ line-height: 1.6;
72
+ }
73
+ code { font-family: "SF Mono", Consolas, monospace; }
74
+ .kw { color: #c586c0; }
75
+ .str { color: #ce9178; }
76
+ .fn { color: #dcdcaa; }
77
+ .prop { color: #9cdcfe; }
78
+ .cmt { color: #6a9955; }
79
+ .hl {
80
+ display: inline-block;
81
+ width: calc(100% + 2.5rem);
82
+ margin: 0 -1.25rem;
83
+ padding: 0 1.25rem;
84
+ background: rgba(124, 106, 239, 0.15);
85
+ animation: pulse 2s ease-in-out infinite;
86
+ }
87
+ @keyframes pulse {
88
+ 0%, 100% { background: rgba(124, 106, 239, 0.1); }
89
+ 50% { background: rgba(124, 106, 239, 0.22); }
90
+ }
91
+ </style>
92
+ </head>
93
+ <body>
94
+ <main>
95
+ <h1>Liveblocks Dev Server</h1>
96
+
97
+ <h2>How do you want to use Liveblocks?</h2>
98
+ <div class="pickers">
99
+ <fieldset>
100
+ <legend>Library</legend>
101
+ <label><input type="radio" name="library" value="client" checked onchange="update()"> @liveblocks/client</label>
102
+ <label><input type="radio" name="library" value="react" onchange="update()"> @liveblocks/react</label>
103
+ </fieldset>
104
+
105
+ <fieldset>
106
+ <legend>Authentication</legend>
107
+ <label><input type="radio" name="auth" value="public" checked onchange="update()"> Public key</label>
108
+ <label><input type="radio" name="auth" value="secret" onchange="update()"> Secret key <span style="color:#666">(production)</span></label>
109
+ <label class="nested disabled" id="label-id"><input type="radio" name="token" value="id" disabled onchange="update()"> ID tokens</label>
110
+ <label class="nested disabled" id="label-access"><input type="radio" name="token" value="access" checked disabled onchange="update()"> Access tokens</label>
111
+ </fieldset>
112
+ </div>
113
+
114
+ <div class="columns">
115
+ <div class="column">
116
+ <h3>Your frontend</h3>
117
+ <pre><code id="frontend-snippet"></code></pre>
118
+ </div>
119
+ <div class="column" id="backend-column">
120
+ <h3>Your backend</h3>
121
+ <p class="note" id="backend-note">No backend changes required for public key auth.</p>
122
+ <pre id="backend-pre"><code id="backend-snippet"></code></pre>
123
+ </div>
124
+ </div>
125
+ </main>
126
+
127
+ <script>
128
+ function h(cls, text) {
129
+ return \`<span class="\${cls}">\${text}</span>\`;
130
+ }
131
+
132
+ const kw = t => h('kw', t);
133
+ const str = t => h('str', \`"\${t}"\`);
134
+ const fn = t => h('fn', t);
135
+ const prop = t => h('prop', t);
136
+ const cmt = t => h('cmt', t);
137
+ const hl = t => h('hl', t); // highlighted line
138
+
139
+ function update() {
140
+ const auth = document.querySelector('input[name="auth"]:checked').value;
141
+ const token = document.querySelector('input[name="token"]:checked').value;
142
+ const library = document.querySelector('input[name="library"]:checked').value;
143
+
144
+ // Enable/disable token options
145
+ const isSecret = auth === 'secret';
146
+ document.querySelectorAll('input[name="token"]').forEach(el => el.disabled = !isSecret);
147
+ document.getElementById('label-id').classList.toggle('disabled', !isSecret);
148
+ document.getElementById('label-access').classList.toggle('disabled', !isSecret);
149
+
150
+ // Show note or code block for backend
151
+ const noteEl = document.getElementById('backend-note');
152
+ const preEl = document.getElementById('backend-pre');
153
+ if (!isSecret) {
154
+ noteEl.style.display = 'block';
155
+ noteEl.textContent = 'No backend changes required for public key auth.';
156
+ preEl.style.display = 'none';
157
+ } else if (token === 'id') {
158
+ noteEl.style.display = 'block';
159
+ noteEl.textContent = 'ID tokens are not supported with the local dev server. \u{1F622}';
160
+ preEl.style.display = 'block';
161
+ preEl.style.opacity = '0.5';
162
+ preEl.style.filter = 'grayscale(100%)';
163
+ } else {
164
+ noteEl.style.display = 'none';
165
+ preEl.style.display = 'block';
166
+ preEl.style.opacity = '1';
167
+ preEl.style.filter = 'none';
168
+ }
169
+
170
+ let frontend = '';
171
+ let backend = '';
172
+
173
+ if (library === 'client') {
174
+ if (auth === 'public') {
175
+ frontend = \`\${kw('import')} { \${fn('createClient')} } \${kw('from')} \${str('@liveblocks/client')};
176
+
177
+ \${kw('const')} \${prop('client')} = \${fn('createClient')}({
178
+ \${hl(\` \${prop('baseUrl')}: \${str('http://localhost:1153')},\`)}
179
+ \${hl(\` \${prop('publicApiKey')}: \${str('pk_localdev')},\`)}
180
+ });\`;
181
+ } else {
182
+ frontend = \`\${kw('import')} { \${fn('createClient')} } \${kw('from')} \${str('@liveblocks/client')};
183
+
184
+ \${kw('const')} \${prop('client')} = \${fn('createClient')}({
185
+ \${hl(\` \${prop('baseUrl')}: \${str('http://localhost:1153')},\`)}
186
+ \${hl(\` \${prop('authEndpoint')}: \${str('/api/liveblocks-auth')},\`)}
187
+ });\`;
188
+ }
189
+ } else {
190
+ // React
191
+ if (auth === 'public') {
192
+ frontend = \`\${kw('import')} { \${fn('LiveblocksProvider')}, \${fn('RoomProvider')} } \${kw('from')} \${str('@liveblocks/react')};
193
+
194
+ \${kw('function')} \${fn('App')}() {
195
+ \${kw('return')} (
196
+ &lt;\${fn('LiveblocksProvider')}
197
+ \${hl(\` \${prop('baseUrl')}=\${str('http://localhost:1153')}\`)}
198
+ \${hl(\` \${prop('publicApiKey')}=\${str('pk_localdev')}\`)}
199
+ &gt;
200
+ &lt;\${fn('RoomProvider')} \${prop('id')}=\${str('my-room')}&gt;
201
+ {\${cmt('/* your app */')}}
202
+ &lt;/\${fn('RoomProvider')}&gt;
203
+ &lt;/\${fn('LiveblocksProvider')}&gt;
204
+ );
205
+ }\`;
206
+ } else {
207
+ frontend = \`\${kw('import')} { \${fn('LiveblocksProvider')}, \${fn('RoomProvider')} } \${kw('from')} \${str('@liveblocks/react')};
208
+
209
+ \${kw('function')} \${fn('App')}() {
210
+ \${kw('return')} (
211
+ &lt;\${fn('LiveblocksProvider')}
212
+ \${hl(\` \${prop('baseUrl')}=\${str('http://localhost:1153')}\`)}
213
+ \${hl(\` \${prop('authEndpoint')}=\${str('/api/liveblocks-auth')}\`)}
214
+ &gt;
215
+ &lt;\${fn('RoomProvider')} \${prop('id')}=\${str('my-room')}&gt;
216
+ {\${cmt('/* your app */')}}
217
+ &lt;/\${fn('RoomProvider')}&gt;
218
+ &lt;/\${fn('LiveblocksProvider')}&gt;
219
+ );
220
+ }\`;
221
+ }
222
+ }
223
+
224
+ // Backend code (only for secret key auth)
225
+ if (auth === 'secret') {
226
+ if (token === 'access') {
227
+ backend = \`\${cmt('// In api/liveblocks-auth/route.ts')}
228
+ \${kw('import')} { \${fn('Liveblocks')} } \${kw('from')} \${str('@liveblocks/node')};
229
+
230
+ \${kw('const')} \${prop('liveblocks')} = \${kw('new')} \${fn('Liveblocks')}({
231
+ \${hl(\` \${prop('secret')}: \${str('sk_localdev')},\`)}
232
+ \${hl(\` \${prop('baseUrl')}: \${str('http://localhost:1153')},\`)}
233
+ });
234
+
235
+ \${kw('export async function')} \${fn('POST')}() {
236
+ \${kw('const')} \${prop('user')} = \${cmt('// ... get user from your own session')};
237
+
238
+ \${kw('const')} \${prop('session')} = \${prop('liveblocks')}.\${fn('prepareSession')}(
239
+ \${prop('user')}.\${prop('id')},
240
+ { \${prop('userInfo')}: { \${prop('name')}: \${prop('user')}.\${prop('name')} } }
241
+ );
242
+ \${prop('session')}.\${fn('allow')}(\${str('*')}, \${prop('session')}.\${prop('FULL_ACCESS')});
243
+
244
+ \${kw('const')} { \${prop('status')}, \${prop('body')} } = \${kw('await')} \${prop('session')}.\${fn('authorize')}();
245
+ \${kw('return new')} \${fn('Response')}(\${prop('body')}, { \${prop('status')} });
246
+ }\`;
247
+ } else {
248
+ backend = \`\${cmt('// In api/liveblocks-auth/route.ts')}
249
+ \${kw('import')} { \${fn('Liveblocks')} } \${kw('from')} \${str('@liveblocks/node')};
250
+
251
+ \${kw('const')} \${prop('liveblocks')} = \${kw('new')} \${fn('Liveblocks')}({
252
+ \${prop('secret')}: \${str('sk_localdev')},
253
+ \${prop('baseUrl')}: \${str('http://localhost:1153')},
254
+ });
255
+
256
+ \${kw('export async function')} \${fn('POST')}() {
257
+ \${kw('const')} \${prop('user')} = \${cmt('// ... get user from your own session')};
258
+
259
+ \${kw('const')} { \${prop('status')}, \${prop('body')} } = \${kw('await')} \${prop('liveblocks')}.\${fn('identifyUser')}({
260
+ \${prop('userId')}: \${prop('user')}.\${prop('id')},
261
+ \${prop('groupIds')}: [],
262
+ });
263
+
264
+ \${kw('return new')} \${fn('Response')}(\${prop('body')}, { \${prop('status')} });
265
+ }\`;
266
+ }
267
+ }
268
+
269
+ document.getElementById('frontend-snippet').innerHTML = frontend;
270
+ document.getElementById('backend-snippet').innerHTML = backend;
271
+ }
272
+
273
+ update();
274
+ </script>
275
+ </body>
276
+ </html>
277
+ `;var N=new Ke({authorize:()=>!0});N.route("GET /v7",()=>ue(426));N.route("GET /v8",()=>ue(426));N.route("GET /",()=>Fe(le));import{jsonObjectYolo as He,ROOT_YDOC_ID as Y,snapshotToLossyJson_eager as Ve,snapshotToPlainLson_eager as fe}from"@liveblocks/server";import{json as Ze,ZenRouter as Qe}from"@liveblocks/zenrouter";import{constant as et,enum_ as tt,object as ge,string as ot}from"decoders";import{Base64 as rt}from"js-base64";import*as M from"yjs";import*as h from"yjs";function qe(t){return t.content instanceof h.ContentFormat||t.content instanceof h.ContentEmbed?"text":"arr"in t.content?"array":"str"in t.content?"text":"type"in t.content?"xml":"unknown"}function We(t){let e=[],r=t;for(;r!==null;){if(!r.deleted)if(r.content instanceof h.ContentType)e.push(r.content.type.toJSON());else if(r.content instanceof h.ContentString)e.push(r.content.str);else if(r.content instanceof h.ContentFormat){let{key:s,value:n}=r.content;e.push({key:s,value:n})}else r.content instanceof h.ContentEmbed&&e.push(r.content.embed);r=r.right}return e}function me(t,e,r,s=!1){if(!e._first&&e._map instanceof Map&&e._map.size>0)return t.getMap(r).toJSON();if(e._first!==null){let n=qe(e._first);if(n==="text")return s?We(e._first):t.getText(r).toJSON();if(n==="array")return t.getArray(r).toJSON();if(n==="xml")return t.getXmlFragment(r).toJSON()}return e.toJSON()}var Xe={ytext:h.Text,yxmlfragment:h.XmlFragment,yxmltext:h.XmlText,ymap:h.Map,yarray:h.Array};function pe(t,e="",r=!1,s=""){let n={};if(e.length){if(t.share.has(e)){if(s.length){let d=Xe[s];if(d)return t.get(e,d).toJSON()}return{[e]:me(t,t.share.get(e),e,r)}}return{[e]:""}}for(let[d,c]of t.share)n[d]=me(t,c,d,r);return n}var he=(r=>(r.PlainLson="plain-lson",r.LossyJson="json",r))(he||{}),st=tt(he),i=new Qe({authorize:({req:t})=>A(t)});function $(t){return Ze({error:"ROOM_NOT_FOUND",message:`Room with id "${t}" not found.`},404)}i.route("GET /v2/rooms/<roomId>",async({p:t})=>{if(!await S(t.roomId))throw $(t.roomId);return{type:"room",id:t.roomId,createdAt:new Date().toISOString(),metadata:{},defaultAccesses:["room:write"],groupsAccesses:{},usersAccesses:{}}});i.route("GET /v2/rooms",()=>{let e=oe().map(r=>({type:"room",id:r,createdAt:new Date().toISOString(),metadata:{},defaultAccesses:["room:write"],groupsAccesses:{},usersAccesses:{}}));return k({data:e,nextPage:null,nextCursor:null},200,"The Liveblocks dev server doesn't implement room permissions or pagination yet, so all rooms are returned in a single page with nextPage set to null.","GET /v2/rooms")});i.route("POST /v2/rooms",ge({id:ot}),async({body:t})=>{if(await S(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 re(t.id)}catch(r){if(r instanceof Error&&r.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 r}return{type:"room",id:t.id,createdAt:new Date().toISOString(),metadata:{},defaultAccesses:["room:write"],groupsAccesses:{},usersAccesses:{}}});i.route("POST /v2/rooms/<roomId>",()=>o());i.route("GET /v2/rooms/<roomId>/storage",async({url:t,p:e})=>{if(!await S(e.roomId))throw $(e.roomId);let s=st.value(t.searchParams.get("format"))??"plain-lson",n=O(e.roomId);await n.load();let d=n.storage.loadedDriver.get_snapshot(!1),c=s==="json"?Ve(d):fe(d);return new Response(JSON.stringify(c),{status:200,headers:{"Content-Type":"application/json"}})});i.route("POST /v2/rooms/<roomId>/storage",ge({liveblocksType:et("LiveObject"),data:He}).refineType(),async({p:t,body:e})=>{if(!await S(t.roomId))throw $(t.roomId);let s=O(t.roomId);await s.load();let n=s.storage.loadedDriver.get_snapshot(!1),d=fe(n);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"}}))});i.route("GET /v2/rooms/<roomId>/ydoc",async({url:t,p:e})=>{if(!await S(e.roomId))throw $(e.roomId);let s=O(e.roomId);await s.load();let n=t.searchParams.get("key")??"",d=t.searchParams.get("type")??"",c=t.searchParams.get("guid")??Y,u=t.searchParams.get("formatting")!==null,T=await s.yjsStorage.getYDoc(c),p=pe(T,n,u,d);return new Response(JSON.stringify(p),{status:200,headers:{"Content-Type":"application/json"}})});i.route("PUT /v2/rooms/<roomId>/ydoc",async({req:t,url:e,p:r})=>{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 S(r.roomId))throw $(r.roomId);let n=O(r.roomId);await n.load();let d=await t.arrayBuffer(),c=rt.fromUint8Array(new Uint8Array(d)),u=e.searchParams.get("guid"),p=e.searchParams.get("encoder")==="v2",f=u&&u!==Y?u:void 0;try{return await n.mutex.runExclusive(()=>n.yjsStorage.addYDocUpdate({},c,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"}})}});i.route("GET /v2/rooms/<roomId>/ydoc-binary",async({url:t,p:e})=>{if(!await S(e.roomId))throw $(e.roomId);let s=O(e.roomId);await s.load();let n=t.searchParams.get("guid")??Y,d=t.searchParams.get("encoder"),c=await s.yjsStorage.getYDoc(n),u=d==="v2"?M.encodeStateAsUpdateV2(c):M.encodeStateAsUpdate(c);return new Response(u,{status:200,headers:{"Content-Type":"application/octet-stream"}})});i.route("DELETE /v2/rooms/<roomId>/storage",()=>o()),i.route("GET /v2/rooms/<roomId>/threads",()=>o()),i.route("POST /v2/rooms/<roomId>/upsert",()=>o()),i.route("DELETE /v2/rooms/<roomId>",()=>o()),i.route("POST /v2/rooms/<roomId>/update-room-id",()=>o()),i.route("POST /v2/rooms/<roomId>/update-tenant-id",()=>o()),i.route("POST /v2/rooms/<roomId>/update-organization-id",()=>o()),i.route("GET /v2/rooms/<roomId>/prewarm",()=>o()),i.route("POST /v2/rooms/<roomId>/request-storage-mutation",()=>o()),i.route("GET /v2/rooms/<roomId>/rippling/text-editor",()=>o()),i.route("POST /v2/rooms/<roomId>/rippling/text-editor",()=>o()),i.route("GET /v2/rooms/<roomId>/active_users",()=>o()),i.route("POST /v2/rooms/<roomId>/send-message",()=>o()),i.route("POST /v2/rooms/<roomId>/broadcast_event",()=>o()),i.route("GET /v2/rooms/<roomId>/versions",()=>o()),i.route("GET /v2/rooms/<roomId>/version/<version>",()=>o()),i.route("POST /v2/rooms/<roomId>/version",()=>o()),i.route("POST /v2/rooms/<roomId>/threads",()=>o()),i.route("GET /v2/rooms/<roomId>/threads/<threadId>",()=>o()),i.route("POST /v2/rooms/<roomId>/threads/<threadId>/mark-as-resolved",()=>o()),i.route("POST /v2/rooms/<roomId>/threads/<threadId>/mark-as-unresolved",()=>o()),i.route("POST /v2/rooms/<roomId>/threads/<threadId>/subscribe",()=>o()),i.route("POST /v2/rooms/<roomId>/threads/<threadId>/unsubscribe",()=>o()),i.route("GET /v2/rooms/<roomId>/threads/<threadId>/subscriptions",()=>o()),i.route("POST /v2/rooms/<roomId>/threads/<threadId>/metadata",()=>o()),i.route("DELETE /v2/rooms/<roomId>/threads/<threadId>",()=>o()),i.route("GET /v2/rooms/<roomId>/threads/<threadId>/participants",()=>o()),i.route("GET /v2/rooms/<roomId>/threads/<threadId>/inbox-notifications",()=>o()),i.route("POST /v2/rooms/<roomId>/threads/<threadId>/comments",()=>o()),i.route("GET /v2/rooms/<roomId>/threads/<threadId>/comments/<commentId>",()=>o()),i.route("POST /v2/rooms/<roomId>/threads/<threadId>/comments/<commentId>",()=>o()),i.route("POST /v2/rooms/<roomId>/threads/<threadId>/comments/<commentId>/metadata",()=>o()),i.route("DELETE /v2/rooms/<roomId>/threads/<threadId>/comments/<commentId>",()=>o()),i.route("POST /v2/rooms/<roomId>/threads/<threadId>/comments/<commentId>/add-reaction",()=>o()),i.route("POST /v2/rooms/<roomId>/threads/<threadId>/comments/<commentId>/remove-reaction",()=>o()),i.route("GET /v2/rooms/<roomId>/users/<userId>/notification-settings",()=>o()),i.route("GET /v2/rooms/<roomId>/users/<userId>/subscription-settings",()=>o()),i.route("POST /v2/rooms/<roomId>/users/<userId>/notification-settings",()=>o()),i.route("POST /v2/rooms/<roomId>/users/<userId>/subscription-settings",()=>o()),i.route("DELETE /v2/rooms/<roomId>/users/<userId>/notification-settings",()=>o()),i.route("DELETE /v2/rooms/<roomId>/users/<userId>/subscription-settings",()=>o()),i.route("GET /v2/users/<userId>/inbox-notifications/<inboxNotificationId>",()=>o()),i.route("DELETE /v2/users/<userId>/inbox-notifications/<inboxNotificationId>",()=>o()),i.route("GET /v2/users/<userId>/inbox-notifications",()=>o()),i.route("DELETE /v2/users/<userId>/inbox-notifications",()=>o()),i.route("GET /v2/users/<userId>/notification-settings",()=>o()),i.route("POST /v2/users/<userId>/notification-settings",()=>o()),i.route("DELETE /v2/users/<userId>/notification-settings",()=>o()),i.route("GET /v2/users/<userId>/room-subscription-settings",()=>o()),i.route("GET /v2/users/<userId>/threads",()=>o()),i.route("POST /v2/inbox-notifications/trigger",()=>o()),i.route("POST /v2/inbox-notifications/<inboxNotificationId>/read",()=>o()),i.route("GET /v2/threads",()=>o()),i.route("POST /v2/groups",()=>o()),i.route("GET /v2/groups/<groupId>",()=>o()),i.route("POST /v2/groups/<groupId>/add-members",()=>o()),i.route("POST /v2/groups/<groupId>/remove-members",()=>o()),i.route("DELETE /v2/groups/<groupId>",()=>o()),i.route("GET /v2/groups",()=>o()),i.route("GET /v2/users/<userId>/groups",()=>o());function ct(t,e,r,s){if(!t.upgrade(e,{data:{refuseConnection:{code:r,message:s}}}))return new Response("Could not upgrade to WebSocket",{status:426})}var D=new at;D.relay("/v2/authorize-user/*",j);D.relay("/v2/c/*",a);D.relay("/v2/*",i);D.relay("/*",N);var G=1153,lt={description:"Start the local Liveblocks dev server",run(t){let e=nt(t,{string:["port"],boolean:["help"],default:{port:G},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(` -p, --port Port to listen on (default: ${G})`),console.log(" -h, --help Show this help message");return}let r=Number(e.port)||G,s=dt.serve({hostname:"localhost",port:r,async fetch(n,d){if(n.headers.get("Upgrade")==="websocket"){let c=q(n);if(!c)return ct(d,n,it.NOT_ALLOWED,"You have no access to this room");let[u,T]=c,p=O(u);await p.load();let f=await p.createTicket(T),I=f.sessionKey;return d.upgrade(n,{data:{room:p,ticket:f,sessionKey:I}})?void 0:new Response("Could not upgrade to WebSocket",{status:426})}return D.fetch(n)},error(n){return console.error(n),new Response("An unknown error occurred",{status:500})},websocket:{open(n){let{refuseConnection:d,room:c,ticket:u}=n.data;if(d){n.close(d.code,d.message);return}c&&u&&c.startBrowserSession(u,n)},async message(n,d){let{room:c,sessionKey:u}=n.data;c&&u&&await c.handleData(u,d)},close(n,d,c){let{room:u,sessionKey:T}=n.data;u&&T&&u.endBrowserSession(T,d,c)}}});console.log(`Liveblocks dev server running at http://${s.hostname}:${s.port}`)}},po=lt;export{po as default};
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{execFileSync as c,spawnSync as l}from"node:child_process";import{parse as t}from"@bomb.sh/args";function a(){try{return c("bun",["--version"],{stdio:"ignore"}),!0}catch{return!1}}function d(){if(a()){let e=l("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=t(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-UWZNJSR7.js")).default;case"upgrade":return(await import("./upgrade-7ASDJF6I.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.3"),console.log(),console.log("Usage: liveblocks <command> [options]"),console.log(),console.log("SubCommands:");for(let[e,i]of Object.entries(v))console.log(` ${e.padEnd(12)} ${i}`);console.log(),console.log("Options:"),console.log(" -h, --help Show this help message"),console.log(" -v, --version Show version number")}async function g(){r.version&&(console.log("1.0.3"),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))}g();
2
+ import{execFileSync as c,spawnSync as l}from"node:child_process";import{parse as t}from"@bomb.sh/args";function a(){try{return c("bun",["--version"],{stdio:"ignore"}),!0}catch{return!1}}function d(){if(a()){let e=l("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=t(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-R3DRGRUK.js")).default;case"upgrade":return(await import("./upgrade-7ASDJF6I.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.4"),console.log(),console.log("Usage: liveblocks <command> [options]"),console.log(),console.log("SubCommands:");for(let[e,i]of Object.entries(v))console.log(` ${e.padEnd(12)} ${i}`);console.log(),console.log("Options:"),console.log(" -h, --help Show this help message"),console.log(" -v, --version Show version number")}async function g(){r.version&&(console.log("1.0.4"),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))}g();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "liveblocks",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "description": "Liveblocks command line interface",
5
5
  "type": "module",
6
6
  "bin": {
@@ -39,7 +39,11 @@
39
39
  },
40
40
  "dependencies": {
41
41
  "@bomb.sh/args": "^0.3.1",
42
- "@liveblocks/server": "1.0.3",
43
- "@liveblocks/zenrouter": "1.0.3"
42
+ "@liveblocks/core": "3.14.0-rc1",
43
+ "@liveblocks/server": "1.0.4",
44
+ "@liveblocks/zenrouter": "1.0.4",
45
+ "decoders": "^2.8.0-1",
46
+ "js-base64": "^3.7.5",
47
+ "yjs": "^13.6.10"
44
48
  }
45
49
  }
@@ -1,296 +0,0 @@
1
- import{parse as Dn}from"@bomb.sh/args";var ut=Object.defineProperty,lt=(e,t)=>{for(var n in t)ut(e,n,{get:t[n],enumerable:!0})},ve="@liveblocks/core",K="3.14.0-rc1",ft="esm",B=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:{},ht="https://liveblocks.io/docs/errors/cross-linked",pt="https://liveblocks.io/docs/errors/dupes",me=" ";function ge(e){if(process.env.NODE_ENV==="production")console.error(e);else throw new Error(e)}function mt(e,t,n){let r=Symbol.for(e),o=n?`${t||"dev"} (${n})`:t||"dev";if(!B[r])B[r]=o;else if(B[r]!==o){let i=[`Multiple copies of Liveblocks are being loaded in your project. This will cause issues! See ${pt+me}`,"","Conflicts:",`- ${e} ${B[r]} (already loaded)`,`- ${e} ${o} (trying to load this now)`].join(`
2
- `);ge(i)}t&&K&&t!==K&&ge([`Cross-linked versions of Liveblocks found, which will cause issues! See ${ht+me}`,"","Conflicts:",`- ${ve} is at ${K}`,`- ${e} is at ${t}`,"","Always upgrade all Liveblocks packages to the same version number."].join(`
3
- `))}function be(){let e=new Set;function t(s){return e.add(s),()=>e.delete(s)}function n(s){let a=t(l=>(a(),s(l)));return a}async function r(s){let a;return new Promise(l=>{a=t(c=>{(s===void 0||s(c))&&l(c)})}).finally(()=>a?.())}function o(s){let a=!1;for(let l of e)l(s),a=!0;return a}function i(){return e.size}return{notify:o,subscribe:t,subscribeOnce:n,count:i,waitUntil:r,dispose(){e.clear()},observable:{subscribe:t,subscribeOnce:n,waitUntil:r}}}var Kn=process.env.NODE_ENV==="production"?e=>e:Object.freeze;function we(e){throw new Error(e)}var T=Symbol("kSinks"),G=Symbol("kTrigger"),ye=null,$=null;function gt(e){ye||we("Expected to be in an active batch"),ye.add(e)}var yt=class{equals;#e;[T];constructor(e){this.equals=e??Object.is,this.#e=be(),this[T]=new Set,this.get=this.get.bind(this),this.subscribe=this.subscribe.bind(this),this.subscribeOnce=this.subscribeOnce.bind(this)}dispose(){this.#e.dispose(),this.#e="(disposed)",this.equals="(disposed)"}get hasWatchers(){if(this.#e.count()>0)return!0;for(let e of this[T])if(e.hasWatchers)return!0;return!1}[G](){this.#e.notify();for(let e of this[T])gt(e)}subscribe(e){return this.#e.count()===0&&this.get(),this.#e.subscribe(e)}subscribeOnce(e){let t=this.subscribe(()=>(t(),e()));return t}waitUntil(){throw new Error("waitUntil not supported on Signals")}markSinksDirty(){for(let e of this[T])e.markDirty()}addSink(e){this[T].add(e)}removeSink(e){this[T].delete(e)}asReadonly(){return this}};var vt=Symbol(),Vn=class Y extends yt{#e;#n;#t;#r;#o;static from(...t){let n=t.pop();if(typeof n!="function"&&we("Invalid .from() call, last argument expected to be a function"),typeof t[t.length-1]=="function"){let r=n,o=t.pop();return new Y(t,o,r)}else{let r=n;return new Y(t,r)}}constructor(t,n,r){super(r),this.#n=!0,this.#e=vt,this.#r=t,this.#t=new Set,this.#o=n}dispose(){for(let t of this.#t)t.removeSink(this);this.#e="(disposed)",this.#t="(disposed)",this.#r="(disposed)",this.#o="(disposed)"}get isDirty(){return this.#n}#s(){let t=$,n;$=new Set;try{n=this.#o(...this.#r.map(r=>r.get()))}finally{let r=this.#t;this.#t=new Set;for(let o of $)this.#t.add(o),r.delete(o);for(let o of r)o.removeSink(this);for(let o of this.#t)o.addSink(this);$=t}return this.#n=!1,this.equals(this.#e,n)?!1:(this.#e=n,!0)}markDirty(){this.#n||(this.#n=!0,this.markSinksDirty())}get(){return this.#n&&this.#s(),$?.add(this),this.#e}[G](){if(!this.hasWatchers)return;this.#s()&&super[G]()}};function bt(e,t){if(process.env.NODE_ENV!=="production"&&!e){let n=new Error(t);throw n.name="Assertion failure",n}}function Ee(e,t="Expected value to be non-nullable"){return bt(e!=null,t),e}var wt={};lt(wt,{error:()=>Te,errorWithTitle:()=>_t,warn:()=>Ie,warnWithTitle:()=>St});var Se="background:#0e0d12;border-radius:9999px;color:#fff;padding:3px 7px;font-family:sans-serif;font-weight:600;",Et="font-weight:600";function _e(e){return typeof window>"u"||process.env.NODE_ENV==="test"?console[e]:(t,...n)=>console[e]("%cLiveblocks",Se,t,...n)}var Ie=_e("warn"),Te=_e("error");function Ce(e){return typeof window>"u"||process.env.NODE_ENV==="test"?console[e]:(t,n,...r)=>console[e](`%cLiveblocks%c ${t}`,Se,Et,n,...r)}var St=Ce("warn"),_t=Ce("error");var V=(e=21)=>crypto.getRandomValues(new Uint8Array(e)).reduce((t,n)=>t+=(n&=63)<36?n.toString(36):n<62?(n-26).toString(36).toUpperCase():n<63?"_":"-","");var zn=Object.freeze({UPDATE_PRESENCE:100,USER_JOINED:101,USER_LEFT:102,BROADCASTED_EVENT:103,ROOM_STATE:104,STORAGE_STATE_V7:200,STORAGE_CHUNK:210,STORAGE_STREAM_END:211,UPDATE_STORAGE:201,UPDATE_YDOC:300,THREAD_CREATED:400,THREAD_DELETED:407,THREAD_METADATA_UPDATED:401,THREAD_UPDATED:408,COMMENT_CREATED:402,COMMENT_EDITED:403,COMMENT_DELETED:404,COMMENT_REACTION_ADDED:405,COMMENT_REACTION_REMOVED:406,COMMENT_METADATA_UPDATED:409,REJECT_STORAGE_OP:299}),Z=(e=>(e[e.CLOSE_NORMAL=1e3]="CLOSE_NORMAL",e[e.CLOSE_ABNORMAL=1006]="CLOSE_ABNORMAL",e[e.UNEXPECTED_CONDITION=1011]="UNEXPECTED_CONDITION",e[e.TRY_AGAIN_LATER=1013]="TRY_AGAIN_LATER",e[e.INVALID_MESSAGE_FORMAT=4e3]="INVALID_MESSAGE_FORMAT",e[e.NOT_ALLOWED=4001]="NOT_ALLOWED",e[e.MAX_NUMBER_OF_MESSAGES_PER_SECONDS=4002]="MAX_NUMBER_OF_MESSAGES_PER_SECONDS",e[e.MAX_NUMBER_OF_CONCURRENT_CONNECTIONS=4003]="MAX_NUMBER_OF_CONCURRENT_CONNECTIONS",e[e.MAX_NUMBER_OF_MESSAGES_PER_DAY_PER_APP=4004]="MAX_NUMBER_OF_MESSAGES_PER_DAY_PER_APP",e[e.MAX_NUMBER_OF_CONCURRENT_CONNECTIONS_PER_ROOM=4005]="MAX_NUMBER_OF_CONCURRENT_CONNECTIONS_PER_ROOM",e[e.ROOM_ID_UPDATED=4006]="ROOM_ID_UPDATED",e[e.KICKED=4100]="KICKED",e[e.TOKEN_EXPIRED=4109]="TOKEN_EXPIRED",e[e.CLOSE_WITHOUT_RETRY=4999]="CLOSE_WITHOUT_RETRY",e))(Z||{});var It=[250,500,1e3,2e3,4e3,8e3,1e4],Fn=It[0]-1;function Tt(e,t){let n=e===2?Te:e===1?Ie:()=>{};return()=>{n(t)}}var Hn=Tt(1,"Connection to WebSocket closed permanently. Won't retry.");var qn=Symbol(),Wn=Object.freeze({}),Jn=Array.from(new Set("null")),Gn=Array.from(new Set("true")),Yn=Array.from(new Set("false")),Zn=Array.from(new Set("nulltruefalse"));var Xn=Symbol("*");var P=(e=>(e.Read="room:read",e.Write="room:write",e.PresenceWrite="room:presence:write",e.CommentsWrite="comments:write",e.CommentsRead="comments:read",e))(P||{});var Ae=be();process.env.NODE_ENV!=="production"&&typeof window<"u"&&window.addEventListener("message",e=>{e.source===window&&e.data?.source==="liveblocks-devtools-panel"&&Ae.notify(e.data)});var Qn=Ae.observable;var er=Date.now();var tr=Symbol("notification-settings-plain");var _=32,N=126,Ct=N-_+1,At=X(0),kt=X(1),nr=At+X(-1);function X(e){let t=_+(e<0?Ct+e:e);if(t<_||t>N)throw new Error(`Invalid n value: ${e}`);return String.fromCharCode(t)}var Ot=_+1;function Rt(e){if(e==="")return!1;let t=e.length-1,n=e.charCodeAt(t);if(n<Ot||n>N)return!1;for(let r=0;r<t;r++){let o=e.charCodeAt(r);if(o<_||o>N)return!1}return!0}function Nt(e){let t=[];for(let n=0;n<e.length;n++){let r=e.charCodeAt(n);t.push(r<_?_:r>N?N:r)}for(;t.length>0&&t[t.length-1]===_;)t.length--;return t.length>0?String.fromCharCode(...t):kt}function ke(e){return Rt(e)?e:Nt(e)}var rr=Object.freeze({INIT:0,SET_PARENT_KEY:1,CREATE_LIST:2,UPDATE_OBJECT:3,CREATE_OBJECT:4,DELETE_CRDT:5,DELETE_OBJECT_KEY:6,CREATE_MAP:7,CREATE_REGISTER:8});var or=Object.freeze({type:"NoParent"});var E=Object.freeze({OBJECT:0,LIST:1,MAP:2,REGISTER:3});var sr=128*1024;var ir=Object.freeze({UPDATE_PRESENCE:100,BROADCAST_EVENT:103,FETCH_STORAGE:200,UPDATE_STORAGE:201,FETCH_YDOC:300,UPDATE_YDOC:301});var Dt={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"},ar=new RegExp(Object.keys(Dt).map(e=>`\\${e}`).join("|"),"g");var Lt={_:"\\_","*":"\\*","#":"\\#","`":"\\`","~":"\\~","!":"\\!","|":"\\|","(":"\\(",")":"\\)","{":"\\{","}":"\\}","[":"\\[","]":"\\]"},cr=new RegExp(Object.keys(Lt).map(e=>`\\${e}`).join("|"),"g");mt(ve,K,ft);import{DefaultMap as Ln,Room as $n}from"@liveblocks/server";import Pn from"bun";import{mkdirSync as Mn}from"fs";import{ZenRelay as xn}from"@liveblocks/zenrouter";import{ProtocolVersion as De}from"@liveblocks/server";function Oe(e){return btoa(e).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function Re(e){let t=e.replace(/-/g,"+").replace(/_/g,"/"),n=t+"=".repeat((4-t.length%4)%4);return atob(n)}function Ne(e){let t=Math.floor(Date.now()/1e3),n={...e,iat:t,exp:t+60*60,jti:V(12)},r=Oe(JSON.stringify({alg:"none",typ:"JWT"})),o=Oe(JSON.stringify(n));return`${r}.${o}.`}function z(e){let t=e.split(".");if(t.length!==3)return null;let[n,r,o]=t;try{if(JSON.parse(Re(n)).alg!=="none")return null}catch{return null}let i;try{i=JSON.parse(Re(r))}catch{return null}if(o!==""||i.pid!=="localdev")return null;let s=i.exp;return typeof s=="number"&&s<Math.floor(Date.now()/1e3)?null:i}function $t(e,t){if(t[e])return t[e];for(let[n,r]of Object.entries(t))if(n.endsWith("*")){let o=n.slice(0,-1);if(e.startsWith(o))return r}return[]}function Le(e){let t=new URL(e.url),n=t.pathname==="/v7"?De.V7:t.pathname==="/v8"?De.V8:null;if(n===null)return null;let r=t.searchParams.get("roomId");if(!r)return null;let o=t.searchParams.get("tok");if(o!==null){let s=z(o);if(!s)return null;if(s.k==="acc"){let a=$t(r,s.perms);return a.length===0?null:[r,{version:n,id:s.uid,info:s.ui,scopes:a}]}else if(s.k==="id")return[r,{version:n,id:s.uid,info:s.ui,scopes:[P.Write]}];return null}return t.searchParams.get("pubkey")==="pk_localdev"?[r,{version:n,anonymousId:V(),scopes:["room:write"]}]:null}import{makeInMemorySnapshot as Pt,NestedMap as Mt,plainLsonToNodeStream as xt,quote as S}from"@liveblocks/server";import{Database as Ut}from"bun:sqlite";function Q(e){try{return e!==void 0?JSON.parse(e):void 0}catch{return}}function Me(e){if(e>512)throw new Error("More than 512 params not supported");return new Array(e).fill("?").join(",")}function jt(e){return Array.isArray(e)?e:Array.from(e)}function Bt(e){return e.query("SELECT node_id, crdt_json FROM nodes").values().map(([t,n])=>[t,Q(n)])}function M(e,t){let n=e.prepare(`INSERT INTO nodes (node_id, crdt_json)
4
- VALUES (?, ?)
5
- ON CONFLICT (node_id) DO UPDATE SET crdt_json = ?`);e.transaction(o=>{for(let[i,s]of o){let a=JSON.stringify(s);n.run(i,a,a)}})(t)}function xe(e,t){let n=jt(t),r=n.length;e.query(`DELETE FROM nodes WHERE node_id IN (${Me(r)})`).run(...n)}function $e(e){let t=new Mt;for(let[n,r]of e){if(r.parentId===void 0)continue;let o=t.get(r.parentId,r.parentKey);(o===void 0||n>o)&&t.set(r.parentId,r.parentKey,n)}return t}function Kt(e,t){let n=new Map(Bt(e));n.has("root")||n.set("root",{type:E.OBJECT,data:{}});let r=new Set,o=["root"],i=new Map,s=$e(n);for(;o.length>0;){let c=o.pop(),p=Ee(n.get(c));if(p.type===E.OBJECT)for(let g of s.keysAt(c))Object.prototype.hasOwnProperty.call(p.data,g)&&(delete p.data[g],r.add(c),t.warn(`[integrity] Found data key ${S(g)} from ${S(c)} (conflicted with child node)`));if(p.type!==E.REGISTER)o.push(...s.valuesAt(c));else if(n.get(p.parentId)?.type===E.OBJECT)continue;i.set(c,p)}let a=new Set;for(let[c,p]of n)i.has(c)||(p.parentId!==void 0&&i.has(p.parentId)?i.get(p.parentId)?.type===E.REGISTER?t.warn(`[integrity] Found unreachable node ${S(c)} (child of live register)`):t.warn(`[integrity] Found conflicting sibling ${S(c)} (conflicted with ${S(s.get(p.parentId,p.parentKey))} at ${S(p.parentKey)})`):t.warn(`[integrity] Found orphan ${S(c)}`),a.add(c),r.delete(c));if(r.size>0||a.size>0){if(r.size>0){let c=new Map;for(let p of r){let g=i.get(p);g!==void 0&&c.set(p,g)}M(e,c)}a.size>0&&xe(e,a)}let l=a.size===0?s:$e(i);return{nodes:i,revNodes:l}}function Pe(e,t){return e?.type===E.OBJECT&&Object.prototype.hasOwnProperty.call(e.data,t)&&e.data[t]!==void 0}var F=class{db;constructor(t){let n=new Ut(t,{create:!0});n.run("PRAGMA journal_mode = WAL"),n.run("PRAGMA case_sensitive_like = ON"),n.run(`CREATE TABLE IF NOT EXISTS system (
6
- setting TEXT NOT NULL,
7
- jval TEXT NOT NULL,
8
- PRIMARY KEY (setting)
9
- )`),n.run(`CREATE TABLE IF NOT EXISTS nodes (
10
- node_id TEXT NOT NULL,
11
- crdt_json TEXT NOT NULL,
12
- PRIMARY KEY (node_id)
13
- )`),n.run(`CREATE TABLE IF NOT EXISTS metadata (
14
- key TEXT NOT NULL,
15
- jval TEXT NOT NULL,
16
- PRIMARY KEY (key)
17
- )`),n.run(`CREATE TABLE IF NOT EXISTS ydocs (
18
- doc_id TEXT NOT NULL,
19
- key TEXT NOT NULL,
20
- data BLOB NOT NULL,
21
- PRIMARY KEY (doc_id, key)
22
- )`),this.db=n}load_nodes_api(t){let n=this.db,{nodes:r,revNodes:o}=Kt(n,t),i=d=>r.get(d),s=(d,u)=>o.get(d,u),a=(d,u)=>o.has(d,u);function l(d,u){let f;for(let m of o.keysAt(d)){let b=ke(m);b>u&&(f===void 0||b<f)&&(f=b)}return f}function c(d,u,f=!1){let m=i(u.parentId);if(m===void 0)throw new Error(`No such parent ${S(u.parentId)}`);if(u.type===E.REGISTER&&m.type===E.OBJECT)throw new Error("Cannot add register under object");let b=s(u.parentId,u.parentKey);if(b!==d){let R=Pe(m,u.parentKey);if(b!==void 0||R)if(f)h(u.parentId,u.parentKey);else throw new Error(`Key ${S(u.parentKey)} already exists`);o.set(u.parentId,u.parentKey,d)}r.set(d,u),M(n,[[d,u]])}function p(d,u){let f=i(d);if(f?.parentId===void 0)return;if(a(f.parentId,u))throw new Error(`Pos ${S(u)} already taken`);o.delete(f.parentId,f.parentKey);let m={...f,parentKey:u};r.set(d,m),o.set(f.parentId,u,d),M(n,[[d,m]])}function g(d,u,f=!1){let m=i(d);if(m?.type!==E.OBJECT)return;for(let R of Object.keys(u)){let L=s(d,R);if(L!==void 0)if(f)w(L);else throw new Error(`Child node already exists under ${R}`)}let b={...m,data:{...m.data,...u}};r.set(d,b),M(n,[[d,b]])}function w(d){let u=i(d);if(u?.parentId===void 0)return;o.delete(u.parentId,u.parentKey);let f=[],m=[d];for(;m.length>0;){let b=m.pop();m.push(...o.valuesAt(b)),r.delete(b),o.deleteAll(b),f.push(b)}xe(n,f)}function h(d,u){let f=i(d);if(Pe(f,u)){let{[u]:b,...R}=f.data,L={...f,data:R};r.set(d,L),M(n,[[d,L]])}let m=s(d,u);m!==void 0&&w(m)}return{get_node:i,iter_nodes:()=>r.entries(),has_node:d=>r.has(d),get_child_at:s,has_child_at:a,get_next_sibling:l,set_child:c,move_sibling:p,delete_node:w,delete_child_key:h,set_object_data:g,get_snapshot(d){return Pt(r)}}}DANGEROUSLY_reset_nodes(t){let n=this.db.prepare("DELETE FROM nodes"),r=this.db.prepare("INSERT INTO nodes (node_id, crdt_json) VALUES (?, ?)");this.db.transaction(()=>{n.run();for(let[i,s]of xt(t))r.run(i,JSON.stringify(s))})()}raw_iter_nodes(){return this.db.query("SELECT node_id, crdt_json FROM nodes").values().map(([t,n])=>[t,Q(n)])}get_meta(t){let r=this.db.query("SELECT jval FROM metadata WHERE key = ?").get(t)?.jval;if(r!==void 0)return Q(r)??null}put_meta(t,n){let r=JSON.stringify(n);this.db.run(`INSERT INTO metadata (key, jval)
23
- VALUES (?, ?)
24
- ON CONFLICT (key) DO UPDATE SET jval = ?
25
- `,[t,r,r])}delete_meta(t){this.db.query("DELETE FROM metadata WHERE key = ?").run(t)}next_actor(){let t=this.db.query(`INSERT INTO system (setting, jval)
26
- VALUES ('last_actor_id', '0')
27
- ON CONFLICT (setting) DO UPDATE SET jval = json(CAST(jval AS INTEGER) + 1)
28
- RETURNING jval`).get();return JSON.parse(t.jval)}iter_y_updates(t){return this.db.query("SELECT key, data FROM ydocs WHERE doc_id = ?").values(t)}write_y_updates(t,n,r){this.db.query(`INSERT INTO ydocs
29
- VALUES (?, ?, ?)
30
- ON CONFLICT (doc_id, key) DO UPDATE SET data = ?`).run(t,n,r,r)}delete_y_updates(t,n){let r=n.length;this.db.query(`DELETE FROM ydocs WHERE doc_id = ? AND key IN (${Me(r)})`).run(t,...n)}DANGEROUSLY_wipe_all_y_updates(){this.db.query("DELETE FROM ydocs").run()}close(){this.db.close()}};function ee(e){return typeof e=="number"}function Be(e){return typeof e=="string"}function Vt(e){return typeof e=="bigint"}function Ke(e){return!!e&&Object.prototype.toString.call(e)==="[object Date]"&&!isNaN(e)}function zt(e){return typeof e=="object"&&e!==null&&"then"in e&&typeof e.then=="function"}function Ve(e){return e!==null&&typeof e=="object"&&Object.prototype.toString.call(e)==="[object Object]"}var Ft=Symbol.for("decoders.kAnnotationRegistry"),ze=globalThis[Ft]??=new WeakSet;function x(e){return ze.add(e),e}function Fe(e,t){return x({type:"object",fields:e,text:t})}function Ht(e,t){return x({type:"array",items:e,text:t})}function D(e,t){return x({type:"opaque",value:e,text:t})}function qt(e,t){return x({type:"scalar",value:e,text:t})}function He(e,t){return t!==void 0?x({...e,text:t}):e}function qe(e,t){let n=new Map([...e.fields,...t]);return Fe(n,e.text)}function We(e){return ze.has(e)}function Wt(e,t,n){n.add(e);let r=[];for(let o of e)r.push(oe(o,void 0,n));return Ht(r,t)}function Je(e,t,n){n.add(e);let r=new Map;for(let o of Object.keys(e)){let i=e[o];r.set(o,oe(i,void 0,n))}return Fe(r,t)}function oe(e,t,n){return e==null||typeof e=="string"||typeof e=="number"||typeof e=="boolean"||typeof e=="symbol"||typeof e=="bigint"||typeof e.getMonth=="function"?qt(e,t):We(e)?He(e,t):Array.isArray(e)?n.has(e)?D("<circular ref>",t):Wt(e,t,n):Ve(e)?n.has(e)?D("<circular ref>",t):Je(e,t,n):typeof e=="function"?D("<function>",t):zt(e)?D("<Promise>",t):e?.constructor?.name?D(`<${e.constructor.name}>`,t):D("???",t)}function C(e,t){return oe(e,t,new WeakSet)}function Ge(e,t){return Je(e,t,new WeakSet)}var A=" ";function te(e){return e.includes(`
31
- `)}function se(e,t=A){return te(e)?e.split(`
32
- `).map(n=>`${t}${n}`).join(`
33
- `):`${t}${e}`}var Jt=/'/g;function k(e){return typeof e=="string"?"'"+e.replace(Jt,"\\'")+"'":e===void 0?"undefined":JSON.stringify(e)}function H(e,t=[]){let n=[];if(e.type==="array"){let i=e.items,s=0;for(let a of i)for(let l of H(a,[...t,s++]))n.push(l)}else if(e.type==="object"){let i=e.fields;for(let[s,a]of i)for(let l of H(a,[...t,s]))n.push(l)}let r=e.text;if(!r)return n;let o;return t.length===0?o="":t.length===1?o=typeof t[0]=="number"?`Value at index ${t[0]}: `:`Value at key ${k(t[0])}: `:o=`Value at keypath ${k(t.map(String).join("."))}: `,[...n,`${o}${r}`]}function Gt(e,t=80){let n=JSON.stringify(e);if(n.length<=t)return n;let r=`${e.substring(0,t-15)}...`;return n=`${JSON.stringify(r)} [truncated]`,n}function Yt(e,t){let{items:n}=e;if(n.length===0)return"[]";let r=[];for(let o of n){let[i,s]=ie(o,`${t}${A}`);r.push(`${t}${A}${i},`),s!==void 0&&r.push(se(s,`${t}${A}`))}return["[",...r,`${t}]`].join(`
34
- `)}function Zt(e,t){let{fields:n}=e;if(n.size===0)return"{}";let r=[];for(let[o,i]of n){let s=Ye(o),a=`${t}${A}${" ".repeat(s.length+2)}`,[l,c]=ie(i,`${t}${A}`);r.push(`${t}${A}${s}: ${l},`),c!==void 0&&r.push(se(c,a))}return["{",...r,`${t}}`].join(`
35
- `)}function Ye(e){return typeof e=="string"?Gt(e):typeof e=="number"||typeof e=="boolean"||typeof e=="symbol"?e.toString():e===null?"null":e===void 0?"undefined":typeof e=="bigint"?`${e.toString()}n`:Ke(e)?`new Date(${k(e.toISOString())})`:e instanceof Date?"(Invalid Date)":"(unserializable)"}function ie(e,t=""){let n;e.type==="array"?n=Yt(e,t):e.type==="object"?n=Zt(e,t):e.type==="scalar"?n=Ye(e.value):n=e.value;let r=e.text;if(r!==void 0){let o="^".repeat(te(n)?1:n.length);return[n,[o,r].join(te(r)?`
36
- `:" ")]}else return[n,void 0]}function Xt(e){let[t,n]=ie(e);return n!==void 0?`${t}
37
- ${n}`:t}function Qt(e){return H(e,[]).join(`
38
- `)}function*ne(e,t){switch(e.text&&(t.length>0?yield{message:e.text,path:[...t]}:yield{message:e.text}),e.type){case"array":{let n=0;for(let r of e.items)t.push(n++),yield*ne(r,t),t.pop();break}case"object":{for(let[n,r]of e.fields)t.push(n),yield*ne(r,t),t.pop();break}case"scalar":case"opaque":break}}function en(e){return Array.from(ne(e,[]))}function Ze(e){return{ok:!0,value:e,error:void 0}}function Xe(e){return{ok:!1,value:void 0,error:e}}function tn(e){return t=>{try{let n=e(t);return Ze(n)}catch(n){return Xe(C(t,n instanceof Error?n.message:String(n)))}}}function nn(e,t){let n=t(e);if(typeof n=="string"){let r=new Error(`
39
- ${n}`);return r.name="Decoding error",r}else return n}function v(e){function t(h){return e(h,Ze,d=>Xe(We(d)?d:C(h,d)))}function n(h,y=Xt){let d=t(h);if(d.ok)return d.value;throw nn(d.error,y)}function r(h){return t(h).value}function o(h){return a(tn(h))}function i(h,y){return c(d=>h(d)?null:y)}function s(){return w}function a(h){return v((y,d,u)=>{let f=t(y);if(!f.ok)return f;let m=Ue(h)?h:h(f.value,d,u);return Ue(m)?m.decode(f.value):m})}function l(h){return a(h)}function c(h){return a((y,d,u)=>{let f=h(y);return f===null?d(y):u(typeof f=="string"?C(y,f):f)})}function p(h){return v((y,d,u)=>{let f=t(y);return f.ok?f:u(C(f.error,h))})}let w=on({verify:n,value:r,decode:t,transform:o,refine:i,refineType:s,reject:c,describe:p,then:a,pipe:l,"~standard":{version:1,vendor:"decoders",validate:h=>{let y=t(h);return y.ok?{value:y.value}:{issues:en(y.error)}}}});return w}var rn=Symbol.for("decoders.kDecoderRegistry"),Qe=globalThis[rn]??=new WeakSet;function on(e){return Qe.add(e),e}function Ue(e){return Qe.has(e)}var sn=v((e,t,n)=>Array.isArray(e)?t(e):n("Must be an array"));function ae(e){let t=e.decode;return sn.then((n,r,o)=>{let i=[];for(let s=0;s<n.length;++s){let a=n[s],l=t(a);if(l.ok)i.push(l.value);else{i.length=0;let c=l.error,p=n.slice();return p.splice(s,1,C(c,c.text?`${c.text} (at index ${s})`:`index ${s}`)),o(C(p))}}return r(i)})}function an(e){return v((t,n,r)=>t instanceof e?n(t):r(`Must be ${e.name} instance`))}function et(e){return v(t=>e().decode(t))}function cn(e,t){let n=new Set;for(let r of e)t.has(r)||n.add(r);return n}var ce=v((e,t,n)=>Ve(e)?t(e):n("Must be an object"));function de(e){let t=new Set(Object.keys(e));return ce.then((n,r,o)=>{let i=new Set(Object.keys(n)),s=cn(t,i),a={},l=null;for(let c of Object.keys(e)){let p=e[c],g=n[c],w=p.decode(g);if(w.ok){let h=w.value;h!==void 0&&(a[c]=h),s.delete(c)}else{let h=w.error;g===void 0?s.add(c):(l??=new Map,l.set(c,h))}}if(l||s.size>0){let c=Ge(n);if(l&&(c=qe(c,l)),s.size>0){let p=Array.from(s).map(k).join(", "),g=s.size>1?"keys":"key";c=He(c,`Missing ${g}: ${p}`)}return o(c)}return r(a)})}function tt(e){return ce.pipe(t=>{let n=new Set(Object.keys(t));return de(e).transform(r=>{let o=new Set(Object.keys(e));for(let s of o)n.add(s);let i={};for(let s of n)if(o.has(s)){let a=r[s];a!==void 0&&(i[s]=a)}else i[s]=t[s];return i})})}var re=`Either:
40
- `;function dn(e){return`-${se(e).substring(1)}`}function un(e){return e.startsWith(re)?e.substring(re.length):dn(e)}function q(...e){if(e.length===0)throw new Error("Pass at least one decoder to either()");return v((t,n,r)=>{let o=[];for(let s of e){let a=s.decode(t);if(a.ok)return a;o.push(a.error)}let i=re+o.map(s=>un(H(s).join(`
41
- `))).join(`
42
- `);return r(i)})}function je(e){return v((t,n,r)=>{let o=e.indexOf(t);return o!==-1?n(e[o]):r(`Must be one of ${e.map(i=>k(i)).join(", ")}`)})}function nt(e){let t=Object.values(e);if(t.some(ee)){let n=t.filter(ee),r=new Set(n.map(i=>e[i])),o=t.filter(Be).filter(i=>!r.has(i));return je([...n,...o])}else return je(t)}function ln(e){return typeof e=="function"?e():e}var fn=rt(null),hn=rt(void 0),Er=v((e,t,n)=>e==null?t(e):n("Must be undefined or null"));function U(e,t){let n=q(hn,e);return arguments.length>=2?n.transform(r=>r??ln(t)):n}function rt(e){return v((t,n,r)=>t===e?n(e):r(`Must be ${typeof e=="symbol"?String(e):k(e)}`))}var Sr=v((e,t,n)=>t(e));var pn=v((e,t,n)=>typeof e=="boolean"?t(e):n("Must be boolean")),_r=v((e,t,n)=>t(!!e));function ue(e,t){let n=t!==void 0?e:void 0,r=t??e;return ce.then((o,i,s)=>{let a={},l=new Map;for(let c of Object.keys(o)){let p=o[c],g=n?.decode(c);if(g?.ok===!1)return s(C(o,`Invalid key ${k(c)}: ${Qt(g.error)}`));let w=g?.value??c,h=r.decode(p);h.ok?l.size===0&&(a[w]=h.value):(l.set(c,h.error),a={})}return l.size>0?s(qe(Ge(o),l)):i(a)})}var mn=/^([A-Za-z]{2,12}(?:[+][A-Za-z]{2,12})?):\/\/(?:([^@:]*:?(?:[^@]+)?)@)?(?:([A-Za-z0-9.-]+)(?::([0-9]{2,5}))?)(\/(?:[-+~%/.,\w]*)?(?:\?[-+=&;%@.,/\w]*)?(?:#[.,!/\w]*)?)?$/,O=v((e,t,n)=>Be(e)?t(e):n("Must be string")),Ir=I(/\S/,"Must be non-empty string");function I(e,t){return O.refine(n=>e.test(n),t)}var Tr=I(/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,"Must be email"),gn=q(I(mn,"Must be URL").transform(e=>new URL(e)),an(URL)),Cr=gn.refine(e=>e.protocol==="https:","Must be an HTTPS URL"),Ar=I(/^[a-z_][a-z0-9_]*$/i,"Must be valid identifier");var ot=I(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,"Must be uuid"),kr=ot.refine(e=>e[14]==="1","Must be uuidv1"),Or=ot.refine(e=>e[14]==="4","Must be uuidv4"),yn=I(/^[0-9]+$/,"Must only contain digits"),Rr=I(/^[0-9a-f]+$/i,"Must only contain hexadecimal digits"),Nr=yn.transform(Number),vn=/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:[.]\d+)?(?:Z|[+-]\d{2}:?\d{2})$/,bn=v((e,t,n)=>Ke(e)?t(e):n("Must be a Date")),wn=I(vn,"Must be ISO8601 format").refine(e=>!Number.isNaN(new Date(e).getTime()),"Must be valid date/time value"),En=wn.transform(e=>new Date(e)),Dr=q(bn,En).describe("Must be a Date or date string"),Sn=v((e,t,n)=>ee(e)?t(e):n("Must be number")),le=Sn.refine(e=>Number.isFinite(e),"Number must be finite"),_n=le.refine(e=>Number.isInteger(e),"Number must be an integer"),Lr=le.refine(e=>e>=0&&!Object.is(e,-0),"Number must be positive"),$r=_n.refine(e=>e>=0&&!Object.is(e,-0),"Number must be positive"),Pr=v((e,t,n)=>Vt(e)?t(e):n("Must be bigint")),In=et(()=>ue(st)),Tn=et(()=>ae(st)),st=q(fn,O,le,pn,In,Tn).describe("Must be valid JSON value");import{json as fe,ZenRouter as Cn}from"@liveblocks/zenrouter";var it=tt({name:U(O),avatar:U(O)}).refineType();var An=nt(P),W=new Cn({authorize:({req:e})=>{let t=e.headers.get("Authorization");if(t==="Bearer sk_localdev")return!0;if(!t)throw fe({error:"Unauthorized",message:"Missing secret key"},401);if(t.startsWith("Bearer "))throw fe({error:"Forbidden",message:"Invalid secret key. The Liveblocks dev server can only be used with 'sk_localdev' as a secret key"},403);return!1}});W.route("POST /v2/authorize-user",de({userId:O,userInfo:U(it),permissions:ue(ae(An))}),({body:e})=>({token:Ne({k:"acc",pid:"localdev",uid:e.userId,perms:e.permissions,ui:e.userInfo})}));W.route("POST /v2/identify-user",()=>{throw fe({error:"Not supported",message:"ID tokens are not supported with the local dev server. To develop locally, use access tokens instead (via POST /v2/authorize-user)."},404)});import{ZenRouter as kn}from"@liveblocks/zenrouter";var he=new kn({authorize:({req:e})=>{let t=e.headers.get("Authorization");if(!t?.startsWith("Bearer "))return!1;let n=t.slice(7);return z(n)!==null}});he.route("GET /v2/rooms/<roomId>/storage",()=>({root:"Implement me"}));import{abort as ct,html as Rn,ZenRouter as Nn}from"@liveblocks/zenrouter";var at=`<!DOCTYPE html>
43
- <html lang="en">
44
- <head>
45
- <meta charset="UTF-8">
46
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
47
- <title>Liveblocks Dev Server</title>
48
- <style>
49
- * { box-sizing: border-box; }
50
- body {
51
- font-family: system-ui, -apple-system, sans-serif;
52
- background: #0f0f0f;
53
- color: #e5e5e5;
54
- min-height: 100vh;
55
- margin: 0;
56
- padding: 3rem 2rem;
57
- }
58
- main { max-width: 1000px; margin: 0 auto; }
59
- h1 { color: #fff; font-size: 1.5rem; margin: 0 0 2rem; }
60
- h2 { color: #ccc; font-size: 1rem; font-weight: 500; margin: 0 0 1rem; }
61
- .pickers { display: flex; gap: 1rem; margin-bottom: 2rem; }
62
- fieldset {
63
- border: 1px solid #333;
64
- border-radius: 6px;
65
- padding: 1rem 1.25rem;
66
- margin: 0;
67
- flex: 1;
68
- }
69
- legend { color: #888; font-size: 0.75rem; text-transform: uppercase; padding: 0 0.5rem; }
70
- label { display: block; cursor: pointer; padding: 0.25rem 0; }
71
- label.nested { margin-left: 1.5rem; color: #999; }
72
- label.disabled { color: #555; cursor: default; }
73
- input[type="radio"] { margin-right: 0.5rem; accent-color: #7c6aef; }
74
- .columns { display: grid; grid-template-columns: 1fr 1fr; gap: 1.5rem; align-items: start; }
75
- .column h3 { color: #888; font-size: 0.75rem; font-weight: 500; margin: 0 0 0.75rem; text-transform: uppercase; letter-spacing: 0.05em; }
76
- .column pre { margin: 0; min-height: 200px; }
77
- .note { color: #555; font-style: italic; margin: 0; padding: 1rem 0; }
78
- pre {
79
- background: #1a1a1a;
80
- border: 1px solid #333;
81
- border-radius: 8px;
82
- padding: 1.25rem;
83
- overflow-x: auto;
84
- font-size: 0.8rem;
85
- line-height: 1.6;
86
- }
87
- code { font-family: "SF Mono", Consolas, monospace; }
88
- .kw { color: #c586c0; }
89
- .str { color: #ce9178; }
90
- .fn { color: #dcdcaa; }
91
- .prop { color: #9cdcfe; }
92
- .cmt { color: #6a9955; }
93
- .hl {
94
- display: inline-block;
95
- width: calc(100% + 2.5rem);
96
- margin: 0 -1.25rem;
97
- padding: 0 1.25rem;
98
- background: rgba(124, 106, 239, 0.15);
99
- animation: pulse 2s ease-in-out infinite;
100
- }
101
- @keyframes pulse {
102
- 0%, 100% { background: rgba(124, 106, 239, 0.1); }
103
- 50% { background: rgba(124, 106, 239, 0.22); }
104
- }
105
- </style>
106
- </head>
107
- <body>
108
- <main>
109
- <h1>Liveblocks Dev Server</h1>
110
-
111
- <h2>How do you want to use Liveblocks?</h2>
112
- <div class="pickers">
113
- <fieldset>
114
- <legend>Library</legend>
115
- <label><input type="radio" name="library" value="client" checked onchange="update()"> @liveblocks/client</label>
116
- <label><input type="radio" name="library" value="react" onchange="update()"> @liveblocks/react</label>
117
- </fieldset>
118
-
119
- <fieldset>
120
- <legend>Authentication</legend>
121
- <label><input type="radio" name="auth" value="public" checked onchange="update()"> Public key</label>
122
- <label><input type="radio" name="auth" value="secret" onchange="update()"> Secret key <span style="color:#666">(production)</span></label>
123
- <label class="nested disabled" id="label-id"><input type="radio" name="token" value="id" disabled onchange="update()"> ID tokens</label>
124
- <label class="nested disabled" id="label-access"><input type="radio" name="token" value="access" checked disabled onchange="update()"> Access tokens</label>
125
- </fieldset>
126
- </div>
127
-
128
- <div class="columns">
129
- <div class="column">
130
- <h3>Your frontend</h3>
131
- <pre><code id="frontend-snippet"></code></pre>
132
- </div>
133
- <div class="column" id="backend-column">
134
- <h3>Your backend</h3>
135
- <p class="note" id="backend-note">No backend changes required for public key auth.</p>
136
- <pre id="backend-pre"><code id="backend-snippet"></code></pre>
137
- </div>
138
- </div>
139
- </main>
140
-
141
- <script>
142
- function h(cls, text) {
143
- return \`<span class="\${cls}">\${text}</span>\`;
144
- }
145
-
146
- const kw = t => h('kw', t);
147
- const str = t => h('str', \`"\${t}"\`);
148
- const fn = t => h('fn', t);
149
- const prop = t => h('prop', t);
150
- const cmt = t => h('cmt', t);
151
- const hl = t => h('hl', t); // highlighted line
152
-
153
- function update() {
154
- const auth = document.querySelector('input[name="auth"]:checked').value;
155
- const token = document.querySelector('input[name="token"]:checked').value;
156
- const library = document.querySelector('input[name="library"]:checked').value;
157
-
158
- // Enable/disable token options
159
- const isSecret = auth === 'secret';
160
- document.querySelectorAll('input[name="token"]').forEach(el => el.disabled = !isSecret);
161
- document.getElementById('label-id').classList.toggle('disabled', !isSecret);
162
- document.getElementById('label-access').classList.toggle('disabled', !isSecret);
163
-
164
- // Show note or code block for backend
165
- const noteEl = document.getElementById('backend-note');
166
- const preEl = document.getElementById('backend-pre');
167
- if (!isSecret) {
168
- noteEl.style.display = 'block';
169
- noteEl.textContent = 'No backend changes required for public key auth.';
170
- preEl.style.display = 'none';
171
- } else if (token === 'id') {
172
- noteEl.style.display = 'block';
173
- noteEl.textContent = 'ID tokens are not supported with the local dev server. \u{1F622}';
174
- preEl.style.display = 'block';
175
- preEl.style.opacity = '0.5';
176
- preEl.style.filter = 'grayscale(100%)';
177
- } else {
178
- noteEl.style.display = 'none';
179
- preEl.style.display = 'block';
180
- preEl.style.opacity = '1';
181
- preEl.style.filter = 'none';
182
- }
183
-
184
- let frontend = '';
185
- let backend = '';
186
-
187
- if (library === 'client') {
188
- if (auth === 'public') {
189
- frontend = \`\${kw('import')} { \${fn('createClient')} } \${kw('from')} \${str('@liveblocks/client')};
190
-
191
- \${kw('const')} \${prop('client')} = \${fn('createClient')}({
192
- \${hl(\` \${prop('baseUrl')}: \${str('http://localhost:1153')},\`)}
193
- \${hl(\` \${prop('publicApiKey')}: \${str('pk_localdev')},\`)}
194
- });\`;
195
- } else {
196
- frontend = \`\${kw('import')} { \${fn('createClient')} } \${kw('from')} \${str('@liveblocks/client')};
197
-
198
- \${kw('const')} \${prop('client')} = \${fn('createClient')}({
199
- \${hl(\` \${prop('baseUrl')}: \${str('http://localhost:1153')},\`)}
200
- \${hl(\` \${prop('authEndpoint')}: \${str('/api/liveblocks-auth')},\`)}
201
- });\`;
202
- }
203
- } else {
204
- // React
205
- if (auth === 'public') {
206
- frontend = \`\${kw('import')} { \${fn('LiveblocksProvider')}, \${fn('RoomProvider')} } \${kw('from')} \${str('@liveblocks/react')};
207
-
208
- \${kw('function')} \${fn('App')}() {
209
- \${kw('return')} (
210
- &lt;\${fn('LiveblocksProvider')}
211
- \${hl(\` \${prop('baseUrl')}=\${str('http://localhost:1153')}\`)}
212
- \${hl(\` \${prop('publicApiKey')}=\${str('pk_localdev')}\`)}
213
- &gt;
214
- &lt;\${fn('RoomProvider')} \${prop('id')}=\${str('my-room')}&gt;
215
- {\${cmt('/* your app */')}}
216
- &lt;/\${fn('RoomProvider')}&gt;
217
- &lt;/\${fn('LiveblocksProvider')}&gt;
218
- );
219
- }\`;
220
- } else {
221
- frontend = \`\${kw('import')} { \${fn('LiveblocksProvider')}, \${fn('RoomProvider')} } \${kw('from')} \${str('@liveblocks/react')};
222
-
223
- \${kw('function')} \${fn('App')}() {
224
- \${kw('return')} (
225
- &lt;\${fn('LiveblocksProvider')}
226
- \${hl(\` \${prop('baseUrl')}=\${str('http://localhost:1153')}\`)}
227
- \${hl(\` \${prop('authEndpoint')}=\${str('/api/liveblocks-auth')}\`)}
228
- &gt;
229
- &lt;\${fn('RoomProvider')} \${prop('id')}=\${str('my-room')}&gt;
230
- {\${cmt('/* your app */')}}
231
- &lt;/\${fn('RoomProvider')}&gt;
232
- &lt;/\${fn('LiveblocksProvider')}&gt;
233
- );
234
- }\`;
235
- }
236
- }
237
-
238
- // Backend code (only for secret key auth)
239
- if (auth === 'secret') {
240
- if (token === 'access') {
241
- backend = \`\${cmt('// In api/liveblocks-auth/route.ts')}
242
- \${kw('import')} { \${fn('Liveblocks')} } \${kw('from')} \${str('@liveblocks/node')};
243
-
244
- \${kw('const')} \${prop('liveblocks')} = \${kw('new')} \${fn('Liveblocks')}({
245
- \${hl(\` \${prop('secret')}: \${str('sk_localdev')},\`)}
246
- \${hl(\` \${prop('baseUrl')}: \${str('http://localhost:1153')},\`)}
247
- });
248
-
249
- \${kw('export async function')} \${fn('POST')}() {
250
- \${kw('const')} \${prop('user')} = \${cmt('// ... get user from your own session')};
251
-
252
- \${kw('const')} \${prop('session')} = \${prop('liveblocks')}.\${fn('prepareSession')}(
253
- \${prop('user')}.\${prop('id')},
254
- { \${prop('userInfo')}: { \${prop('name')}: \${prop('user')}.\${prop('name')} } }
255
- );
256
- \${prop('session')}.\${fn('allow')}(\${str('*')}, \${prop('session')}.\${prop('FULL_ACCESS')});
257
-
258
- \${kw('const')} { \${prop('status')}, \${prop('body')} } = \${kw('await')} \${prop('session')}.\${fn('authorize')}();
259
- \${kw('return new')} \${fn('Response')}(\${prop('body')}, { \${prop('status')} });
260
- }\`;
261
- } else {
262
- backend = \`\${cmt('// In api/liveblocks-auth/route.ts')}
263
- \${kw('import')} { \${fn('Liveblocks')} } \${kw('from')} \${str('@liveblocks/node')};
264
-
265
- \${kw('const')} \${prop('liveblocks')} = \${kw('new')} \${fn('Liveblocks')}({
266
- \${prop('secret')}: \${str('sk_localdev')},
267
- \${prop('baseUrl')}: \${str('http://localhost:1153')},
268
- });
269
-
270
- \${kw('export async function')} \${fn('POST')}() {
271
- \${kw('const')} \${prop('user')} = \${cmt('// ... get user from your own session')};
272
-
273
- \${kw('const')} { \${prop('status')}, \${prop('body')} } = \${kw('await')} \${prop('liveblocks')}.\${fn('identifyUser')}({
274
- \${prop('userId')}: \${prop('user')}.\${prop('id')},
275
- \${prop('groupIds')}: [],
276
- });
277
-
278
- \${kw('return new')} \${fn('Response')}(\${prop('body')}, { \${prop('status')} });
279
- }\`;
280
- }
281
- }
282
-
283
- document.getElementById('frontend-snippet').innerHTML = frontend;
284
- document.getElementById('backend-snippet').innerHTML = backend;
285
- }
286
-
287
- update();
288
- </script>
289
- </body>
290
- </html>
291
- `;var j=new Nn({authorize:()=>!0});j.route("GET /v7",()=>ct(426));j.route("GET /v8",()=>ct(426));j.route("GET /",()=>Rn(at));var dt=".liveblocks/v1/rooms";function Un(e,t,n,r){if(!e.upgrade(t,{data:{refuseConnection:{code:n,message:r}}}))return new Response("Could not upgrade to WebSocket",{status:426})}var jn=new Ln(e=>{Mn(dt,{recursive:!0});let t=new F(`${dt}/${encodeURIComponent(e)}.db`);return new $n(e,{storage:t})}),J=new xn;J.relay("/v2/authorize-user/*",W);J.relay("/v2/*",he);J.relay("/*",j);var pe=1153,Bn={description:"Start the local Liveblocks dev server",run(e){let t=Dn(e,{string:["port"],boolean:["help"],default:{port:pe},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: ${pe})`),console.log(" -h, --help Show this help message");return}let n=Number(t.port)||pe,r=Pn.serve({hostname:"localhost",port:n,async fetch(o,i){if(o.headers.get("Upgrade")==="websocket"){let s=Le(o);if(!s)return Un(i,o,Z.NOT_ALLOWED,"You have no access to this room");let[a,l]=s,c=jn.getOrCreate(a);await c.load();let p=await c.createTicket(l),g=p.sessionKey;return i.upgrade(o,{data:{room:c,ticket:p,sessionKey:g}})?void 0:new Response("Could not upgrade to WebSocket",{status:426})}return J.fetch(o)},error(o){return console.error(o),new Response("An unknown error occurred",{status:500})},websocket:{open(o){let{refuseConnection:i,room:s,ticket:a}=o.data;if(i){o.close(i.code,i.message);return}s&&a&&s.startBrowserSession(a,o)},async message(o,i){let{room:s,sessionKey:a}=o.data;s&&a&&await s.handleData(a,i)},close(o,i,s){let{room:a,sessionKey:l}=o.data;a&&l&&a.endBrowserSession(l,i,s)}}});console.log(`Liveblocks dev server running at http://${r.hostname}:${r.port}`)}},uo=Bn;export{uo as default};
292
- /*! Bundled license information:
293
-
294
- decoders/dist/index.js:
295
- (* istanbul ignore else -- @preserve *)
296
- */