liveblocks 0.0.4 → 0.0.5

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,294 @@
1
+ import{parse as Jt}from"@bomb.sh/args";import{WebsocketCloseCodes as Ft}from"@liveblocks/core";import{DefaultMap as Kt,Room as Bt}from"@liveblocks/server";import Wt from"bun";import{mkdirSync as qt}from"fs";import{ZenRelay as Yt}from"zenrouter";import{nanoid as xe,Permission as Ue}from"@liveblocks/core";import{ProtocolVersion as ie}from"@liveblocks/server";import{nanoid as Pe}from"@liveblocks/core";function re(e){return btoa(e).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function oe(e){let t=e.replace(/-/g,"+").replace(/_/g,"/"),n=t+"=".repeat((4-t.length%4)%4);return atob(n)}function se(e){let t=Math.floor(Date.now()/1e3),n={...e,iat:t,exp:t+60*60,jti:Pe(12)},r=re(JSON.stringify({alg:"none",typ:"JWT"})),o=re(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(oe(n)).alg!=="none")return null}catch{return null}let i;try{i=JSON.parse(oe(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 Je(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 ae(e){let t=new URL(e.url),n=t.pathname==="/v7"?ie.V7:t.pathname==="/v8"?ie.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=Je(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:[Ue.Write]}];return null}return t.searchParams.get("pubkey")==="pk_localdev"?[r,{version:n,anonymousId:xe(),scopes:["room:write"]}]:null}import{asPos as Fe,CrdtType as w,nn as Ke}from"@liveblocks/core";import{makeInMemorySnapshot as Be,NestedMap as We,plainLsonToNodeStream as qe,quote as $}from"@liveblocks/server";import{Database as Ye}from"bun:sqlite";function J(e){try{return e!==void 0?JSON.parse(e):void 0}catch{return}}function ue(e){if(e>512)throw new Error("More than 512 params not supported");return new Array(e).fill("?").join(",")}function Ge(e){return Array.isArray(e)?e:Array.from(e)}function Ve(e){return e.query("SELECT node_id, crdt_json FROM nodes").values().map(([t,n])=>[t,J(n)])}function j(e,t){let n=e.prepare(`INSERT INTO nodes (node_id, crdt_json)
2
+ VALUES (?, ?)
3
+ 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 de(e,t){let n=Ge(t),r=n.length;e.query(`DELETE FROM nodes WHERE node_id IN (${ue(r)})`).run(...n)}function ce(e){let t=new We;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 Xe(e,t){let n=new Map(Ve(e));n.has("root")||n.set("root",{type:w.OBJECT,data:{}});let r=new Set,o=["root"],i=new Map,s=ce(n);for(;o.length>0;){let c=o.pop(),p=Ke(n.get(c));if(p.type===w.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 ${$(g)} from ${$(c)} (conflicted with child node)`));if(p.type!==w.REGISTER)o.push(...s.valuesAt(c));else if(n.get(p.parentId)?.type===w.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===w.REGISTER?t.warn(`[integrity] Found unreachable node ${$(c)} (child of live register)`):t.warn(`[integrity] Found conflicting sibling ${$(c)} (conflicted with ${$(s.get(p.parentId,p.parentKey))} at ${$(p.parentKey)})`):t.warn(`[integrity] Found orphan ${$(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)}j(e,c)}a.size>0&&de(e,a)}let m=a.size===0?s:ce(i);return{nodes:i,revNodes:m}}function le(e,t){return e?.type===w.OBJECT&&Object.prototype.hasOwnProperty.call(e.data,t)&&e.data[t]!==void 0}var C=class{db;constructor(t){let n=new Ye(t,{create:!0});n.run("PRAGMA journal_mode = WAL"),n.run("PRAGMA case_sensitive_like = ON"),n.run(`CREATE TABLE IF NOT EXISTS system (
4
+ setting TEXT NOT NULL,
5
+ jval TEXT NOT NULL,
6
+ PRIMARY KEY (setting)
7
+ )`),n.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
+ )`),n.run(`CREATE TABLE IF NOT EXISTS metadata (
12
+ key TEXT NOT NULL,
13
+ jval TEXT NOT NULL,
14
+ PRIMARY KEY (key)
15
+ )`),n.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=n}load_nodes_api(t){let n=this.db,{nodes:r,revNodes:o}=Xe(n,t),i=l=>r.get(l),s=(l,u)=>o.get(l,u),a=(l,u)=>o.has(l,u);function m(l,u){let d;for(let y of o.keysAt(l)){let k=Fe(y);k>u&&(d===void 0||k<d)&&(d=k)}return d}function c(l,u,d=!1){let y=i(u.parentId);if(y===void 0)throw new Error(`No such parent ${$(u.parentId)}`);if(u.type===w.REGISTER&&y.type===w.OBJECT)throw new Error("Cannot add register under object");let k=s(u.parentId,u.parentKey);if(k!==l){let N=le(y,u.parentKey);if(k!==void 0||N)if(d)f(u.parentId,u.parentKey);else throw new Error(`Key ${$(u.parentKey)} already exists`);o.set(u.parentId,u.parentKey,l)}r.set(l,u),j(n,[[l,u]])}function p(l,u){let d=i(l);if(d?.parentId===void 0)return;if(a(d.parentId,u))throw new Error(`Pos ${$(u)} already taken`);o.delete(d.parentId,d.parentKey);let y={...d,parentKey:u};r.set(l,y),o.set(d.parentId,u,l),j(n,[[l,y]])}function g(l,u,d=!1){let y=i(l);if(y?.type!==w.OBJECT)return;for(let N of Object.keys(u)){let _=s(l,N);if(_!==void 0)if(d)v(_);else throw new Error(`Child node already exists under ${N}`)}let k={...y,data:{...y.data,...u}};r.set(l,k),j(n,[[l,k]])}function v(l){let u=i(l);if(u?.parentId===void 0)return;o.delete(u.parentId,u.parentKey);let d=[],y=[l];for(;y.length>0;){let k=y.pop();y.push(...o.valuesAt(k)),r.delete(k),o.deleteAll(k),d.push(k)}de(n,d)}function f(l,u){let d=i(l);if(le(d,u)){let{[u]:k,...N}=d.data,_={...d,data:N};r.set(l,_),j(n,[[l,_]])}let y=s(l,u);y!==void 0&&v(y)}return{get_node:i,iter_nodes:()=>r.entries(),has_node:l=>r.has(l),get_child_at:s,has_child_at:a,get_next_sibling:m,set_child:c,move_sibling:p,delete_node:v,delete_child_key:f,set_object_data:g,get_snapshot(l){return Be(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 qe(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,J(n)])}get_meta(t){let r=this.db.query("SELECT jval FROM metadata WHERE key = ?").get(t)?.jval;if(r!==void 0)return J(r)??null}put_meta(t,n){let r=JSON.stringify(n);this.db.run(`INSERT INTO metadata (key, jval)
21
+ VALUES (?, ?)
22
+ ON CONFLICT (key) DO UPDATE SET jval = ?
23
+ `,[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)
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(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
27
+ VALUES (?, ?, ?)
28
+ 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 (${ue(r)})`).run(t,...n)}DANGEROUSLY_wipe_all_y_updates(){this.db.query("DELETE FROM ydocs").run()}close(){this.db.close()}};import{Permission as Mt}from"@liveblocks/core";function F(e){return typeof e=="number"}function me(e){return typeof e=="string"}function He(e){return typeof e=="bigint"}function ye(e){return!!e&&Object.prototype.toString.call(e)==="[object Date]"&&!isNaN(e)}function Ze(e){return typeof e=="object"&&e!==null&&"then"in e&&typeof e.then=="function"}function ge(e){return e!==null&&typeof e=="object"&&Object.prototype.toString.call(e)==="[object Object]"}var Qe=Symbol.for("decoders.kAnnotationRegistry"),be=globalThis[Qe]??=new WeakSet;function L(e){return be.add(e),e}function he(e,t){return L({type:"object",fields:e,text:t})}function et(e,t){return L({type:"array",items:e,text:t})}function R(e,t){return L({type:"opaque",value:e,text:t})}function tt(e,t){return L({type:"scalar",value:e,text:t})}function ke(e,t){return t!==void 0?L({...e,text:t}):e}function ve(e,t){let n=new Map([...e.fields,...t]);return he(n,e.text)}function $e(e){return be.has(e)}function nt(e,t,n){n.add(e);let r=[];for(let o of e)r.push(q(o,void 0,n));return et(r,t)}function we(e,t,n){n.add(e);let r=new Map;for(let o of Object.keys(e)){let i=e[o];r.set(o,q(i,void 0,n))}return he(r,t)}function q(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"?tt(e,t):$e(e)?ke(e,t):Array.isArray(e)?n.has(e)?R("<circular ref>",t):nt(e,t,n):ge(e)?n.has(e)?R("<circular ref>",t):we(e,t,n):typeof e=="function"?R("<function>",t):Ze(e)?R("<Promise>",t):e?.constructor?.name?R(`<${e.constructor.name}>`,t):R("???",t)}function E(e,t){return q(e,t,new WeakSet)}function Se(e,t){return we(e,t,new WeakSet)}var T=" ";function K(e){return e.includes(`
29
+ `)}function Y(e,t=T){return K(e)?e.split(`
30
+ `).map(n=>`${t}${n}`).join(`
31
+ `):`${t}${e}`}var rt=/'/g;function I(e){return typeof e=="string"?"'"+e.replace(rt,"\\'")+"'":e===void 0?"undefined":JSON.stringify(e)}function D(e,t=[]){let n=[];if(e.type==="array"){let i=e.items,s=0;for(let a of i)for(let m of D(a,[...t,s++]))n.push(m)}else if(e.type==="object"){let i=e.fields;for(let[s,a]of i)for(let m of D(a,[...t,s]))n.push(m)}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 ${I(t[0])}: `:o=`Value at keypath ${I(t.map(String).join("."))}: `,[...n,`${o}${r}`]}function ot(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 st(e,t){let{items:n}=e;if(n.length===0)return"[]";let r=[];for(let o of n){let[i,s]=G(o,`${t}${T}`);r.push(`${t}${T}${i},`),s!==void 0&&r.push(Y(s,`${t}${T}`))}return["[",...r,`${t}]`].join(`
32
+ `)}function it(e,t){let{fields:n}=e;if(n.size===0)return"{}";let r=[];for(let[o,i]of n){let s=Ee(o),a=`${t}${T}${" ".repeat(s.length+2)}`,[m,c]=G(i,`${t}${T}`);r.push(`${t}${T}${s}: ${m},`),c!==void 0&&r.push(Y(c,a))}return["{",...r,`${t}}`].join(`
33
+ `)}function Ee(e){return typeof e=="string"?ot(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`:ye(e)?`new Date(${I(e.toISOString())})`:e instanceof Date?"(Invalid Date)":"(unserializable)"}function G(e,t=""){let n;e.type==="array"?n=st(e,t):e.type==="object"?n=it(e,t):e.type==="scalar"?n=Ee(e.value):n=e.value;let r=e.text;if(r!==void 0){let o="^".repeat(K(n)?1:n.length);return[n,[o,r].join(K(r)?`
34
+ `:" ")]}else return[n,void 0]}function at(e){let[t,n]=G(e);return n!==void 0?`${t}
35
+ ${n}`:t}function ct(e){return D(e,[]).join(`
36
+ `)}function*B(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*B(r,t),t.pop();break}case"object":{for(let[n,r]of e.fields)t.push(n),yield*B(r,t),t.pop();break}case"scalar":case"opaque":break}}function lt(e){return Array.from(B(e,[]))}function Te(e){return{ok:!0,value:e,error:void 0}}function Ie(e){return{ok:!1,value:void 0,error:e}}function ut(e){return t=>{try{let n=e(t);return Te(n)}catch(n){return Ie(E(t,n instanceof Error?n.message:String(n)))}}}function dt(e,t){let n=t(e);if(typeof n=="string"){let r=new Error(`
37
+ ${n}`);return r.name="Decoding error",r}else return n}function h(e){function t(f){return e(f,Te,l=>Ie($e(l)?l:E(f,l)))}function n(f,b=at){let l=t(f);if(l.ok)return l.value;throw dt(l.error,b)}function r(f){return t(f).value}function o(f){return a(ut(f))}function i(f,b){return c(l=>f(l)?null:b)}function s(){return v}function a(f){return h((b,l,u)=>{let d=t(b);if(!d.ok)return d;let y=fe(f)?f:f(d.value,l,u);return fe(y)?y.decode(d.value):y})}function m(f){return a(f)}function c(f){return a((b,l,u)=>{let d=f(b);return d===null?l(b):u(typeof d=="string"?E(b,d):d)})}function p(f){return h((b,l,u)=>{let d=t(b);return d.ok?d:u(E(d.error,f))})}let v=pt({verify:n,value:r,decode:t,transform:o,refine:i,refineType:s,reject:c,describe:p,then:a,pipe:m,"~standard":{version:1,vendor:"decoders",validate:f=>{let b=t(f);return b.ok?{value:b.value}:{issues:lt(b.error)}}}});return v}var ft=Symbol.for("decoders.kDecoderRegistry"),Oe=globalThis[ft]??=new WeakSet;function pt(e){return Oe.add(e),e}function fe(e){return Oe.has(e)}var mt=h((e,t,n)=>Array.isArray(e)?t(e):n("Must be an array"));function V(e){let t=e.decode;return mt.then((n,r,o)=>{let i=[];for(let s=0;s<n.length;++s){let a=n[s],m=t(a);if(m.ok)i.push(m.value);else{i.length=0;let c=m.error,p=n.slice();return p.splice(s,1,E(c,c.text?`${c.text} (at index ${s})`:`index ${s}`)),o(E(p))}}return r(i)})}function yt(e){return h((t,n,r)=>t instanceof e?n(t):r(`Must be ${e.name} instance`))}function Ne(e){return h(t=>e().decode(t))}function gt(e,t){let n=new Set;for(let r of e)t.has(r)||n.add(r);return n}var X=h((e,t,n)=>ge(e)?t(e):n("Must be an object"));function H(e){let t=new Set(Object.keys(e));return X.then((n,r,o)=>{let i=new Set(Object.keys(n)),s=gt(t,i),a={},m=null;for(let c of Object.keys(e)){let p=e[c],g=n[c],v=p.decode(g);if(v.ok){let f=v.value;f!==void 0&&(a[c]=f),s.delete(c)}else{let f=v.error;g===void 0?s.add(c):(m??=new Map,m.set(c,f))}}if(m||s.size>0){let c=Se(n);if(m&&(c=ve(c,m)),s.size>0){let p=Array.from(s).map(I).join(", "),g=s.size>1?"keys":"key";c=ke(c,`Missing ${g}: ${p}`)}return o(c)}return r(a)})}function Re(e){return X.pipe(t=>{let n=new Set(Object.keys(t));return H(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 W=`Either:
38
+ `;function bt(e){return`-${Y(e).substring(1)}`}function ht(e){return e.startsWith(W)?e.substring(W.length):bt(e)}function P(...e){if(e.length===0)throw new Error("Pass at least one decoder to either()");return h((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=W+o.map(s=>ht(D(s).join(`
39
+ `))).join(`
40
+ `);return r(i)})}function pe(e){return h((t,n,r)=>{let o=e.indexOf(t);return o!==-1?n(e[o]):r(`Must be one of ${e.map(i=>I(i)).join(", ")}`)})}function _e(e){let t=Object.values(e);if(t.some(F)){let n=t.filter(F),r=new Set(n.map(i=>e[i])),o=t.filter(me).filter(i=>!r.has(i));return pe([...n,...o])}else return pe(t)}function kt(e){return typeof e=="function"?e():e}var vt=je(null),$t=je(void 0),cn=h((e,t,n)=>e==null?t(e):n("Must be undefined or null"));function A(e,t){let n=P($t,e);return arguments.length>=2?n.transform(r=>r??kt(t)):n}function je(e){return h((t,n,r)=>t===e?n(e):r(`Must be ${typeof e=="symbol"?String(e):I(e)}`))}var ln=h((e,t,n)=>t(e));var wt=h((e,t,n)=>typeof e=="boolean"?t(e):n("Must be boolean")),un=h((e,t,n)=>t(!!e));function Z(e,t){let n=t!==void 0?e:void 0,r=t??e;return X.then((o,i,s)=>{let a={},m=new Map;for(let c of Object.keys(o)){let p=o[c],g=n?.decode(c);if(g?.ok===!1)return s(E(o,`Invalid key ${I(c)}: ${ct(g.error)}`));let v=g?.value??c,f=r.decode(p);f.ok?m.size===0&&(a[v]=f.value):(m.set(c,f.error),a={})}return m.size>0?s(ve(Se(o),m)):i(a)})}var St=/^([A-Za-z]{2,12}(?:[+][A-Za-z]{2,12})?):\/\/(?:([^@:]*:?(?:[^@]+)?)@)?(?:([A-Za-z0-9.-]+)(?::([0-9]{2,5}))?)(\/(?:[-+~%/.,\w]*)?(?:\?[-+=&;%@.,/\w]*)?(?:#[.,!/\w]*)?)?$/,O=h((e,t,n)=>me(e)?t(e):n("Must be string")),dn=S(/\S/,"Must be non-empty string");function S(e,t){return O.refine(n=>e.test(n),t)}var fn=S(/^(([^<>()[\]\\.,;:\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"),Et=P(S(St,"Must be URL").transform(e=>new URL(e)),yt(URL)),pn=Et.refine(e=>e.protocol==="https:","Must be an HTTPS URL"),mn=S(/^[a-z_][a-z0-9_]*$/i,"Must be valid identifier");var Le=S(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,"Must be uuid"),yn=Le.refine(e=>e[14]==="1","Must be uuidv1"),gn=Le.refine(e=>e[14]==="4","Must be uuidv4"),Tt=S(/^[0-9]+$/,"Must only contain digits"),bn=S(/^[0-9a-f]+$/i,"Must only contain hexadecimal digits"),hn=Tt.transform(Number),It=/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:[.]\d+)?(?:Z|[+-]\d{2}:?\d{2})$/,Ot=h((e,t,n)=>ye(e)?t(e):n("Must be a Date")),Nt=S(It,"Must be ISO8601 format").refine(e=>!Number.isNaN(new Date(e).getTime()),"Must be valid date/time value"),Rt=Nt.transform(e=>new Date(e)),kn=P(Ot,Rt).describe("Must be a Date or date string"),_t=h((e,t,n)=>F(e)?t(e):n("Must be number")),Q=_t.refine(e=>Number.isFinite(e),"Number must be finite"),jt=Q.refine(e=>Number.isInteger(e),"Number must be an integer"),vn=Q.refine(e=>e>=0&&!Object.is(e,-0),"Number must be positive"),$n=jt.refine(e=>e>=0&&!Object.is(e,-0),"Number must be positive"),wn=h((e,t,n)=>He(e)?t(e):n("Must be bigint")),Lt=Ne(()=>Z(Ae)),At=Ne(()=>V(Ae)),Ae=P(vt,O,Q,wt,Lt,At).describe("Must be valid JSON value");import{json as ee,ZenRouter as zt}from"zenrouter";var Me=Re({name:A(O),avatar:A(O)}).refineType();var Ct=_e(Mt),x=new zt({authorize:({req:e})=>{let t=e.headers.get("Authorization");if(t==="Bearer sk_localdev")return!0;if(!t)throw ee({error:"Unauthorized",message:"Missing secret key"},401);if(t.startsWith("Bearer "))throw ee({error:"Forbidden",message:"Invalid secret key. The Liveblocks dev server can only be used with 'sk_localdev' as a secret key"},403);return!1}});x.route("POST /v2/authorize-user",H({userId:O,userInfo:A(Me),permissions:Z(V(Ct))}),({body:e})=>({token:se({k:"acc",pid:"localdev",uid:e.userId,perms:e.permissions,ui:e.userInfo})}));x.route("POST /v2/identify-user",()=>{throw ee({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 Dt}from"zenrouter";var te=new Dt({authorize:({req:e})=>{let t=e.headers.get("Authorization");if(!t?.startsWith("Bearer "))return!1;let n=t.slice(7);return z(n)!==null}});te.route("GET /v2/rooms/<roomId>/storage",()=>({root:"Implement me"}));import{abort as Ce,html as xt,ZenRouter as Ut}from"zenrouter";var ze=`<!DOCTYPE html>
41
+ <html lang="en">
42
+ <head>
43
+ <meta charset="UTF-8">
44
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
45
+ <title>Liveblocks Dev Server</title>
46
+ <style>
47
+ * { box-sizing: border-box; }
48
+ body {
49
+ font-family: system-ui, -apple-system, sans-serif;
50
+ background: #0f0f0f;
51
+ color: #e5e5e5;
52
+ min-height: 100vh;
53
+ margin: 0;
54
+ padding: 3rem 2rem;
55
+ }
56
+ main { max-width: 1000px; margin: 0 auto; }
57
+ h1 { color: #fff; font-size: 1.5rem; margin: 0 0 2rem; }
58
+ h2 { color: #ccc; font-size: 1rem; font-weight: 500; margin: 0 0 1rem; }
59
+ .pickers { display: flex; gap: 1rem; margin-bottom: 2rem; }
60
+ fieldset {
61
+ border: 1px solid #333;
62
+ border-radius: 6px;
63
+ padding: 1rem 1.25rem;
64
+ margin: 0;
65
+ flex: 1;
66
+ }
67
+ legend { color: #888; font-size: 0.75rem; text-transform: uppercase; padding: 0 0.5rem; }
68
+ label { display: block; cursor: pointer; padding: 0.25rem 0; }
69
+ label.nested { margin-left: 1.5rem; color: #999; }
70
+ label.disabled { color: #555; cursor: default; }
71
+ input[type="radio"] { margin-right: 0.5rem; accent-color: #7c6aef; }
72
+ .columns { display: grid; grid-template-columns: 1fr 1fr; gap: 1.5rem; align-items: start; }
73
+ .column h3 { color: #888; font-size: 0.75rem; font-weight: 500; margin: 0 0 0.75rem; text-transform: uppercase; letter-spacing: 0.05em; }
74
+ .column pre { margin: 0; min-height: 200px; }
75
+ .note { color: #555; font-style: italic; margin: 0; padding: 1rem 0; }
76
+ pre {
77
+ background: #1a1a1a;
78
+ border: 1px solid #333;
79
+ border-radius: 8px;
80
+ padding: 1.25rem;
81
+ overflow-x: auto;
82
+ font-size: 0.8rem;
83
+ line-height: 1.6;
84
+ }
85
+ code { font-family: "SF Mono", Consolas, monospace; }
86
+ .kw { color: #c586c0; }
87
+ .str { color: #ce9178; }
88
+ .fn { color: #dcdcaa; }
89
+ .prop { color: #9cdcfe; }
90
+ .cmt { color: #6a9955; }
91
+ .hl {
92
+ display: inline-block;
93
+ width: calc(100% + 2.5rem);
94
+ margin: 0 -1.25rem;
95
+ padding: 0 1.25rem;
96
+ background: rgba(124, 106, 239, 0.15);
97
+ animation: pulse 2s ease-in-out infinite;
98
+ }
99
+ @keyframes pulse {
100
+ 0%, 100% { background: rgba(124, 106, 239, 0.1); }
101
+ 50% { background: rgba(124, 106, 239, 0.22); }
102
+ }
103
+ </style>
104
+ </head>
105
+ <body>
106
+ <main>
107
+ <h1>Liveblocks Dev Server</h1>
108
+
109
+ <h2>How do you want to use Liveblocks?</h2>
110
+ <div class="pickers">
111
+ <fieldset>
112
+ <legend>Library</legend>
113
+ <label><input type="radio" name="library" value="client" checked onchange="update()"> @liveblocks/client</label>
114
+ <label><input type="radio" name="library" value="react" onchange="update()"> @liveblocks/react</label>
115
+ </fieldset>
116
+
117
+ <fieldset>
118
+ <legend>Authentication</legend>
119
+ <label><input type="radio" name="auth" value="public" checked onchange="update()"> Public key</label>
120
+ <label><input type="radio" name="auth" value="secret" onchange="update()"> Secret key <span style="color:#666">(production)</span></label>
121
+ <label class="nested disabled" id="label-id"><input type="radio" name="token" value="id" disabled onchange="update()"> ID tokens</label>
122
+ <label class="nested disabled" id="label-access"><input type="radio" name="token" value="access" checked disabled onchange="update()"> Access tokens</label>
123
+ </fieldset>
124
+ </div>
125
+
126
+ <div class="columns">
127
+ <div class="column">
128
+ <h3>Your frontend</h3>
129
+ <pre><code id="frontend-snippet"></code></pre>
130
+ </div>
131
+ <div class="column" id="backend-column">
132
+ <h3>Your backend</h3>
133
+ <p class="note" id="backend-note">No backend changes required for public key auth.</p>
134
+ <pre id="backend-pre"><code id="backend-snippet"></code></pre>
135
+ </div>
136
+ </div>
137
+ </main>
138
+
139
+ <script>
140
+ function h(cls, text) {
141
+ return \`<span class="\${cls}">\${text}</span>\`;
142
+ }
143
+
144
+ const kw = t => h('kw', t);
145
+ const str = t => h('str', \`"\${t}"\`);
146
+ const fn = t => h('fn', t);
147
+ const prop = t => h('prop', t);
148
+ const cmt = t => h('cmt', t);
149
+ const hl = t => h('hl', t); // highlighted line
150
+
151
+ function update() {
152
+ const auth = document.querySelector('input[name="auth"]:checked').value;
153
+ const token = document.querySelector('input[name="token"]:checked').value;
154
+ const library = document.querySelector('input[name="library"]:checked').value;
155
+
156
+ // Enable/disable token options
157
+ const isSecret = auth === 'secret';
158
+ document.querySelectorAll('input[name="token"]').forEach(el => el.disabled = !isSecret);
159
+ document.getElementById('label-id').classList.toggle('disabled', !isSecret);
160
+ document.getElementById('label-access').classList.toggle('disabled', !isSecret);
161
+
162
+ // Show note or code block for backend
163
+ const noteEl = document.getElementById('backend-note');
164
+ const preEl = document.getElementById('backend-pre');
165
+ if (!isSecret) {
166
+ noteEl.style.display = 'block';
167
+ noteEl.textContent = 'No backend changes required for public key auth.';
168
+ preEl.style.display = 'none';
169
+ } else if (token === 'id') {
170
+ noteEl.style.display = 'block';
171
+ noteEl.textContent = 'ID tokens are not supported with the local dev server. \u{1F622}';
172
+ preEl.style.display = 'block';
173
+ preEl.style.opacity = '0.5';
174
+ preEl.style.filter = 'grayscale(100%)';
175
+ } else {
176
+ noteEl.style.display = 'none';
177
+ preEl.style.display = 'block';
178
+ preEl.style.opacity = '1';
179
+ preEl.style.filter = 'none';
180
+ }
181
+
182
+ let frontend = '';
183
+ let backend = '';
184
+
185
+ if (library === 'client') {
186
+ if (auth === 'public') {
187
+ frontend = \`\${kw('import')} { \${fn('createClient')} } \${kw('from')} \${str('@liveblocks/client')};
188
+
189
+ \${kw('const')} \${prop('client')} = \${fn('createClient')}({
190
+ \${hl(\` \${prop('baseUrl')}: \${str('http://localhost:1153')},\`)}
191
+ \${hl(\` \${prop('publicApiKey')}: \${str('pk_localdev')},\`)}
192
+ });\`;
193
+ } else {
194
+ frontend = \`\${kw('import')} { \${fn('createClient')} } \${kw('from')} \${str('@liveblocks/client')};
195
+
196
+ \${kw('const')} \${prop('client')} = \${fn('createClient')}({
197
+ \${hl(\` \${prop('baseUrl')}: \${str('http://localhost:1153')},\`)}
198
+ \${hl(\` \${prop('authEndpoint')}: \${str('/api/liveblocks-auth')},\`)}
199
+ });\`;
200
+ }
201
+ } else {
202
+ // React
203
+ if (auth === 'public') {
204
+ frontend = \`\${kw('import')} { \${fn('LiveblocksProvider')}, \${fn('RoomProvider')} } \${kw('from')} \${str('@liveblocks/react')};
205
+
206
+ \${kw('function')} \${fn('App')}() {
207
+ \${kw('return')} (
208
+ &lt;\${fn('LiveblocksProvider')}
209
+ \${hl(\` \${prop('baseUrl')}=\${str('http://localhost:1153')}\`)}
210
+ \${hl(\` \${prop('publicApiKey')}=\${str('pk_localdev')}\`)}
211
+ &gt;
212
+ &lt;\${fn('RoomProvider')} \${prop('id')}=\${str('my-room')}&gt;
213
+ {\${cmt('/* your app */')}}
214
+ &lt;/\${fn('RoomProvider')}&gt;
215
+ &lt;/\${fn('LiveblocksProvider')}&gt;
216
+ );
217
+ }\`;
218
+ } else {
219
+ frontend = \`\${kw('import')} { \${fn('LiveblocksProvider')}, \${fn('RoomProvider')} } \${kw('from')} \${str('@liveblocks/react')};
220
+
221
+ \${kw('function')} \${fn('App')}() {
222
+ \${kw('return')} (
223
+ &lt;\${fn('LiveblocksProvider')}
224
+ \${hl(\` \${prop('baseUrl')}=\${str('http://localhost:1153')}\`)}
225
+ \${hl(\` \${prop('authEndpoint')}=\${str('/api/liveblocks-auth')}\`)}
226
+ &gt;
227
+ &lt;\${fn('RoomProvider')} \${prop('id')}=\${str('my-room')}&gt;
228
+ {\${cmt('/* your app */')}}
229
+ &lt;/\${fn('RoomProvider')}&gt;
230
+ &lt;/\${fn('LiveblocksProvider')}&gt;
231
+ );
232
+ }\`;
233
+ }
234
+ }
235
+
236
+ // Backend code (only for secret key auth)
237
+ if (auth === 'secret') {
238
+ if (token === 'access') {
239
+ backend = \`\${cmt('// In api/liveblocks-auth/route.ts')}
240
+ \${kw('import')} { \${fn('Liveblocks')} } \${kw('from')} \${str('@liveblocks/node')};
241
+
242
+ \${kw('const')} \${prop('liveblocks')} = \${kw('new')} \${fn('Liveblocks')}({
243
+ \${hl(\` \${prop('secret')}: \${str('sk_localdev')},\`)}
244
+ \${hl(\` \${prop('baseUrl')}: \${str('http://localhost:1153')},\`)}
245
+ });
246
+
247
+ \${kw('export async function')} \${fn('POST')}() {
248
+ \${kw('const')} \${prop('user')} = \${cmt('// ... get user from your own session')};
249
+
250
+ \${kw('const')} \${prop('session')} = \${prop('liveblocks')}.\${fn('prepareSession')}(
251
+ \${prop('user')}.\${prop('id')},
252
+ { \${prop('userInfo')}: { \${prop('name')}: \${prop('user')}.\${prop('name')} } }
253
+ );
254
+ \${prop('session')}.\${fn('allow')}(\${str('*')}, \${prop('session')}.\${prop('FULL_ACCESS')});
255
+
256
+ \${kw('const')} { \${prop('status')}, \${prop('body')} } = \${kw('await')} \${prop('session')}.\${fn('authorize')}();
257
+ \${kw('return new')} \${fn('Response')}(\${prop('body')}, { \${prop('status')} });
258
+ }\`;
259
+ } else {
260
+ backend = \`\${cmt('// In api/liveblocks-auth/route.ts')}
261
+ \${kw('import')} { \${fn('Liveblocks')} } \${kw('from')} \${str('@liveblocks/node')};
262
+
263
+ \${kw('const')} \${prop('liveblocks')} = \${kw('new')} \${fn('Liveblocks')}({
264
+ \${prop('secret')}: \${str('sk_localdev')},
265
+ \${prop('baseUrl')}: \${str('http://localhost:1153')},
266
+ });
267
+
268
+ \${kw('export async function')} \${fn('POST')}() {
269
+ \${kw('const')} \${prop('user')} = \${cmt('// ... get user from your own session')};
270
+
271
+ \${kw('const')} { \${prop('status')}, \${prop('body')} } = \${kw('await')} \${prop('liveblocks')}.\${fn('identifyUser')}({
272
+ \${prop('userId')}: \${prop('user')}.\${prop('id')},
273
+ \${prop('groupIds')}: [],
274
+ });
275
+
276
+ \${kw('return new')} \${fn('Response')}(\${prop('body')}, { \${prop('status')} });
277
+ }\`;
278
+ }
279
+ }
280
+
281
+ document.getElementById('frontend-snippet').innerHTML = frontend;
282
+ document.getElementById('backend-snippet').innerHTML = backend;
283
+ }
284
+
285
+ update();
286
+ </script>
287
+ </body>
288
+ </html>
289
+ `;var M=new Ut({authorize:()=>!0});M.route("GET /v7",()=>Ce(426));M.route("GET /v8",()=>Ce(426));M.route("GET /",()=>xt(ze));var De=".liveblocks/v1/rooms";function Gt(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 Vt=new Kt(e=>{qt(De,{recursive:!0});let t=new C(`${De}/${encodeURIComponent(e)}.db`);return new Bt(e,{storage:t})}),U=new Yt;U.relay("/v2/authorize-user/*",x);U.relay("/v2/*",te);U.relay("/*",M);var ne=1153,Xt={description:"Start the local Liveblocks dev server",run(e){let t=Jt(e,{string:["port"],boolean:["help"],default:{port:ne},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(" -h, --help Show this help message");return}let n=Number(t.port)||ne,r=Wt.serve({hostname:"localhost",port:n,async fetch(o,i){if(o.headers.get("Upgrade")==="websocket"){let s=ae(o);if(!s)return Gt(i,o,Ft.NOT_ALLOWED,"You have no access to this room");let[a,m]=s,c=Vt.getOrCreate(a);await c.load();let p=await c.createTicket(m),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 U.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:m}=o.data;a&&m&&a.endBrowserSession(m,i,s)}}});console.log(`Liveblocks dev server running at http://${r.hostname}:${r.port}`)}},Xn=Xt;export{Xn as default};
290
+ /*! Bundled license information:
291
+
292
+ decoders/dist/index.js:
293
+ (* istanbul ignore else -- @preserve *)
294
+ */
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{parse as i}from"@bomb.sh/args";function c(){return typeof Bun<"u"}var s=process.argv.slice(2),e=s.findIndex(o=>!o.startsWith("-")),r=i(e>=0?s.slice(0,e):s,{boolean:["help","version"],alias:{h:"help",v:"version"}}),n=e>=0?s[e]:void 0,t=e>=0?s.slice(e+1):[];async function a(o){switch(o){case"dev":return c()||(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)),(await import("./dev-server-R3HQI4EJ.js")).default;case"upgrade":return(await import("./upgrade-7ASDJF6I.js")).default;default:return}}var d={dev:"Start the local Liveblocks dev server",upgrade:"Upgrade all Liveblocks packages"};function m(){console.log("liveblocks v0.0.4"),console.log(),console.log("Usage: liveblocks <command> [options]"),console.log(),console.log("SubCommands:");for(let[o,l]of Object.entries(d))console.log(` ${o.padEnd(12)} ${l}`);console.log(),console.log("Options:"),console.log(" -h, --help Show this help message"),console.log(" -v, --version Show version number")}async function u(){r.version&&(console.log("0.0.4"),process.exit(0)),(r.help||!n)&&(m(),process.exit(n?0:1));let o=await a(n);o?await o.run(t):(console.error(`Unknown command: ${n}`),console.error('Run "liveblocks --help" for usage.'),process.exit(1))}u();
2
+ import{parse as i}from"@bomb.sh/args";function c(){return typeof Bun<"u"}var s=process.argv.slice(2),e=s.findIndex(o=>!o.startsWith("-")),r=i(e>=0?s.slice(0,e):s,{boolean:["help","version"],alias:{h:"help",v:"version"}}),n=e>=0?s[e]:void 0,t=e>=0?s.slice(e+1):[];async function a(o){switch(o){case"dev":return c()||(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)),(await import("./dev-server-A47GRSRM.js")).default;case"upgrade":return(await import("./upgrade-7ASDJF6I.js")).default;default:return}}var d={dev:"Start the local Liveblocks dev server",upgrade:"Upgrade all Liveblocks packages"};function m(){console.log("liveblocks v0.0.5"),console.log(),console.log("Usage: liveblocks <command> [options]"),console.log(),console.log("SubCommands:");for(let[o,l]of Object.entries(d))console.log(` ${o.padEnd(12)} ${l}`);console.log(),console.log("Options:"),console.log(" -h, --help Show this help message"),console.log(" -v, --version Show version number")}async function u(){r.version&&(console.log("0.0.5"),process.exit(0)),(r.help||!n)&&(m(),process.exit(n?0:1));let o=await a(n);o?await o.run(t):(console.error(`Unknown command: ${n}`),console.error('Run "liveblocks --help" for usage.'),process.exit(1))}u();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "liveblocks",
3
- "version": "0.0.4",
3
+ "version": "0.0.5",
4
4
  "description": "Liveblocks command line interface",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,294 +0,0 @@
1
- import{parse as Ut}from"@bomb.sh/args";import{DefaultMap as Jt,Room as Ft}from"@liveblocks/server";import Kt from"bun";import{mkdirSync as Bt}from"fs";import{ZenRelay as Wt}from"zenrouter";import{nanoid as xe}from"@liveblocks/core";import{ProtocolVersion as ie}from"@liveblocks/server";import{nanoid as Ce}from"@liveblocks/core";function ne(e){return btoa(e).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function oe(e){let t=e.replace(/-/g,"+").replace(/_/g,"/"),r=t+"=".repeat((4-t.length%4)%4);return atob(r)}function se(e){let t=Math.floor(Date.now()/1e3),r={...e,iat:t,exp:t+60*60,jti:Ce(12)},n=ne(JSON.stringify({alg:"none",typ:"JWT"})),o=ne(JSON.stringify(r));return`${n}.${o}.`}function z(e){let t=e.split(".");if(t.length!==3)return null;let[r,n,o]=t;try{if(JSON.parse(oe(r)).alg!=="none")return null}catch{return null}let i;try{i=JSON.parse(oe(n))}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 Ue(e,t){if(t[e])return t[e];for(let[r,n]of Object.entries(t))if(r.endsWith("*")){let o=r.slice(0,-1);if(e.startsWith(o))return n}return[]}function ae(e){let t=new URL(e.url),r=t.pathname==="/v7"?ie.V7:t.pathname==="/v8"?ie.V8:null;if(r===null)return null;let n=t.searchParams.get("roomId");if(!n)return null;let o=t.searchParams.get("tok");if(o!==null){let s=z(o);return s?[n,{version:r,id:s.uid,info:s.ui,scopes:Ue(n,s.perms)}]:null}return t.searchParams.get("pubkey")==="pk_localdev"?[n,{version:r,anonymousId:xe(),scopes:["room:write"]}]:null}import{asPos as Je,CrdtType as w,nn as Fe}from"@liveblocks/core";import{makeInMemorySnapshot as Ke,NestedMap as Be,plainLsonToNodeStream as We,quote as $}from"@liveblocks/server";import{Database as qe}from"bun:sqlite";function J(e){try{return e!==void 0?JSON.parse(e):void 0}catch{return}}function ue(e){if(e>512)throw new Error("More than 512 params not supported");return new Array(e).fill("?").join(",")}function Ye(e){return Array.isArray(e)?e:Array.from(e)}function Ge(e){return e.query("SELECT node_id, crdt_json FROM nodes").values().map(([t,r])=>[t,J(r)])}function j(e,t){let r=e.prepare(`INSERT INTO nodes (node_id, crdt_json)
2
- VALUES (?, ?)
3
- ON CONFLICT (node_id) DO UPDATE SET crdt_json = ?`);e.transaction(o=>{for(let[i,s]of o){let l=JSON.stringify(s);r.run(i,l,l)}})(t)}function de(e,t){let r=Ye(t),n=r.length;e.query(`DELETE FROM nodes WHERE node_id IN (${ue(n)})`).run(...r)}function ce(e){let t=new Be;for(let[r,n]of e){if(n.parentId===void 0)continue;let o=t.get(n.parentId,n.parentKey);(o===void 0||r>o)&&t.set(n.parentId,n.parentKey,r)}return t}function Ve(e,t){let r=new Map(Ge(e));r.has("root")||r.set("root",{type:w.OBJECT,data:{}});let n=new Set,o=["root"],i=new Map,s=ce(r);for(;o.length>0;){let a=o.pop(),p=Fe(r.get(a));if(p.type===w.OBJECT)for(let g of s.keysAt(a))Object.prototype.hasOwnProperty.call(p.data,g)&&(delete p.data[g],n.add(a),t.warn(`[integrity] Found data key ${$(g)} from ${$(a)} (conflicted with child node)`));if(p.type!==w.REGISTER)o.push(...s.valuesAt(a));else if(r.get(p.parentId)?.type===w.OBJECT)continue;i.set(a,p)}let l=new Set;for(let[a,p]of r)i.has(a)||(p.parentId!==void 0&&i.has(p.parentId)?i.get(p.parentId)?.type===w.REGISTER?t.warn(`[integrity] Found unreachable node ${$(a)} (child of live register)`):t.warn(`[integrity] Found conflicting sibling ${$(a)} (conflicted with ${$(s.get(p.parentId,p.parentKey))} at ${$(p.parentKey)})`):t.warn(`[integrity] Found orphan ${$(a)}`),l.add(a),n.delete(a));if(n.size>0||l.size>0){if(n.size>0){let a=new Map;for(let p of n){let g=i.get(p);g!==void 0&&a.set(p,g)}j(e,a)}l.size>0&&de(e,l)}let m=l.size===0?s:ce(i);return{nodes:i,revNodes:m}}function le(e,t){return e?.type===w.OBJECT&&Object.prototype.hasOwnProperty.call(e.data,t)&&e.data[t]!==void 0}var D=class{db;constructor(t){let r=new qe(t,{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(t){let r=this.db,{nodes:n,revNodes:o}=Ve(r,t),i=c=>n.get(c),s=(c,u)=>o.get(c,u),l=(c,u)=>o.has(c,u);function m(c,u){let d;for(let y of o.keysAt(c)){let k=Je(y);k>u&&(d===void 0||k<d)&&(d=k)}return d}function a(c,u,d=!1){let y=i(u.parentId);if(y===void 0)throw new Error(`No such parent ${$(u.parentId)}`);if(u.type===w.REGISTER&&y.type===w.OBJECT)throw new Error("Cannot add register under object");let k=s(u.parentId,u.parentKey);if(k!==c){let N=le(y,u.parentKey);if(k!==void 0||N)if(d)f(u.parentId,u.parentKey);else throw new Error(`Key ${$(u.parentKey)} already exists`);o.set(u.parentId,u.parentKey,c)}n.set(c,u),j(r,[[c,u]])}function p(c,u){let d=i(c);if(d?.parentId===void 0)return;if(l(d.parentId,u))throw new Error(`Pos ${$(u)} already taken`);o.delete(d.parentId,d.parentKey);let y={...d,parentKey:u};n.set(c,y),o.set(d.parentId,u,c),j(r,[[c,y]])}function g(c,u,d=!1){let y=i(c);if(y?.type!==w.OBJECT)return;for(let N of Object.keys(u)){let _=s(c,N);if(_!==void 0)if(d)v(_);else throw new Error(`Child node already exists under ${N}`)}let k={...y,data:{...y.data,...u}};n.set(c,k),j(r,[[c,k]])}function v(c){let u=i(c);if(u?.parentId===void 0)return;o.delete(u.parentId,u.parentKey);let d=[],y=[c];for(;y.length>0;){let k=y.pop();y.push(...o.valuesAt(k)),n.delete(k),o.deleteAll(k),d.push(k)}de(r,d)}function f(c,u){let d=i(c);if(le(d,u)){let{[u]:k,...N}=d.data,_={...d,data:N};n.set(c,_),j(r,[[c,_]])}let y=s(c,u);y!==void 0&&v(y)}return{get_node:i,iter_nodes:()=>n.entries(),has_node:c=>n.has(c),get_child_at:s,has_child_at:l,get_next_sibling:m,set_child:a,move_sibling:p,delete_node:v,delete_child_key:f,set_object_data:g,get_snapshot(c){return Ke(n)}}}DANGEROUSLY_reset_nodes(t){let r=this.db.prepare("DELETE FROM nodes"),n=this.db.prepare("INSERT INTO nodes (node_id, crdt_json) VALUES (?, ?)");this.db.transaction(()=>{r.run();for(let[i,s]of We(t))n.run(i,JSON.stringify(s))})()}raw_iter_nodes(){return this.db.query("SELECT node_id, crdt_json FROM nodes").values().map(([t,r])=>[t,J(r)])}get_meta(t){let n=this.db.query("SELECT jval FROM metadata WHERE key = ?").get(t)?.jval;if(n!==void 0)return J(n)??null}put_meta(t,r){let n=JSON.stringify(r);this.db.run(`INSERT INTO metadata (key, jval)
21
- VALUES (?, ?)
22
- ON CONFLICT (key) DO UPDATE SET jval = ?
23
- `,[t,n,n])}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)
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(t.jval)}iter_y_updates(t){return this.db.query("SELECT key, data FROM ydocs WHERE doc_id = ?").values(t)}write_y_updates(t,r,n){this.db.query(`INSERT INTO ydocs
27
- VALUES (?, ?, ?)
28
- ON CONFLICT (doc_id, key) DO UPDATE SET data = ?`).run(t,r,n,n)}delete_y_updates(t,r){let n=r.length;this.db.query(`DELETE FROM ydocs WHERE doc_id = ? AND key IN (${ue(n)})`).run(t,...r)}DANGEROUSLY_wipe_all_y_updates(){this.db.query("DELETE FROM ydocs").run()}close(){this.db.close()}};import{Permission as Lt}from"@liveblocks/core";function F(e){return typeof e=="number"}function me(e){return typeof e=="string"}function Xe(e){return typeof e=="bigint"}function ye(e){return!!e&&Object.prototype.toString.call(e)==="[object Date]"&&!isNaN(e)}function He(e){return typeof e=="object"&&e!==null&&"then"in e&&typeof e.then=="function"}function ge(e){return e!==null&&typeof e=="object"&&Object.prototype.toString.call(e)==="[object Object]"}var Ze=Symbol.for("decoders.kAnnotationRegistry"),be=globalThis[Ze]??=new WeakSet;function A(e){return be.add(e),e}function he(e,t){return A({type:"object",fields:e,text:t})}function Qe(e,t){return A({type:"array",items:e,text:t})}function R(e,t){return A({type:"opaque",value:e,text:t})}function et(e,t){return A({type:"scalar",value:e,text:t})}function ke(e,t){return t!==void 0?A({...e,text:t}):e}function ve(e,t){let r=new Map([...e.fields,...t]);return he(r,e.text)}function $e(e){return be.has(e)}function tt(e,t,r){r.add(e);let n=[];for(let o of e)n.push(q(o,void 0,r));return Qe(n,t)}function we(e,t,r){r.add(e);let n=new Map;for(let o of Object.keys(e)){let i=e[o];n.set(o,q(i,void 0,r))}return he(n,t)}function q(e,t,r){return e==null||typeof e=="string"||typeof e=="number"||typeof e=="boolean"||typeof e=="symbol"||typeof e=="bigint"||typeof e.getMonth=="function"?et(e,t):$e(e)?ke(e,t):Array.isArray(e)?r.has(e)?R("<circular ref>",t):tt(e,t,r):ge(e)?r.has(e)?R("<circular ref>",t):we(e,t,r):typeof e=="function"?R("<function>",t):He(e)?R("<Promise>",t):e?.constructor?.name?R(`<${e.constructor.name}>`,t):R("???",t)}function E(e,t){return q(e,t,new WeakSet)}function Se(e,t){return we(e,t,new WeakSet)}var T=" ";function K(e){return e.includes(`
29
- `)}function Y(e,t=T){return K(e)?e.split(`
30
- `).map(r=>`${t}${r}`).join(`
31
- `):`${t}${e}`}var rt=/'/g;function I(e){return typeof e=="string"?"'"+e.replace(rt,"\\'")+"'":e===void 0?"undefined":JSON.stringify(e)}function P(e,t=[]){let r=[];if(e.type==="array"){let i=e.items,s=0;for(let l of i)for(let m of P(l,[...t,s++]))r.push(m)}else if(e.type==="object"){let i=e.fields;for(let[s,l]of i)for(let m of P(l,[...t,s]))r.push(m)}let n=e.text;if(!n)return r;let o;return t.length===0?o="":t.length===1?o=typeof t[0]=="number"?`Value at index ${t[0]}: `:`Value at key ${I(t[0])}: `:o=`Value at keypath ${I(t.map(String).join("."))}: `,[...r,`${o}${n}`]}function nt(e,t=80){let r=JSON.stringify(e);if(r.length<=t)return r;let n=`${e.substring(0,t-15)}...`;return r=`${JSON.stringify(n)} [truncated]`,r}function ot(e,t){let{items:r}=e;if(r.length===0)return"[]";let n=[];for(let o of r){let[i,s]=G(o,`${t}${T}`);n.push(`${t}${T}${i},`),s!==void 0&&n.push(Y(s,`${t}${T}`))}return["[",...n,`${t}]`].join(`
32
- `)}function st(e,t){let{fields:r}=e;if(r.size===0)return"{}";let n=[];for(let[o,i]of r){let s=Ee(o),l=`${t}${T}${" ".repeat(s.length+2)}`,[m,a]=G(i,`${t}${T}`);n.push(`${t}${T}${s}: ${m},`),a!==void 0&&n.push(Y(a,l))}return["{",...n,`${t}}`].join(`
33
- `)}function Ee(e){return typeof e=="string"?nt(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`:ye(e)?`new Date(${I(e.toISOString())})`:e instanceof Date?"(Invalid Date)":"(unserializable)"}function G(e,t=""){let r;e.type==="array"?r=ot(e,t):e.type==="object"?r=st(e,t):e.type==="scalar"?r=Ee(e.value):r=e.value;let n=e.text;if(n!==void 0){let o="^".repeat(K(r)?1:r.length);return[r,[o,n].join(K(n)?`
34
- `:" ")]}else return[r,void 0]}function it(e){let[t,r]=G(e);return r!==void 0?`${t}
35
- ${r}`:t}function at(e){return P(e,[]).join(`
36
- `)}function*B(e,t){switch(e.text&&(t.length>0?yield{message:e.text,path:[...t]}:yield{message:e.text}),e.type){case"array":{let r=0;for(let n of e.items)t.push(r++),yield*B(n,t),t.pop();break}case"object":{for(let[r,n]of e.fields)t.push(r),yield*B(n,t),t.pop();break}case"scalar":case"opaque":break}}function ct(e){return Array.from(B(e,[]))}function Te(e){return{ok:!0,value:e,error:void 0}}function Ie(e){return{ok:!1,value:void 0,error:e}}function lt(e){return t=>{try{let r=e(t);return Te(r)}catch(r){return Ie(E(t,r instanceof Error?r.message:String(r)))}}}function ut(e,t){let r=t(e);if(typeof r=="string"){let n=new Error(`
37
- ${r}`);return n.name="Decoding error",n}else return r}function h(e){function t(f){return e(f,Te,c=>Ie($e(c)?c:E(f,c)))}function r(f,b=it){let c=t(f);if(c.ok)return c.value;throw ut(c.error,b)}function n(f){return t(f).value}function o(f){return l(lt(f))}function i(f,b){return a(c=>f(c)?null:b)}function s(){return v}function l(f){return h((b,c,u)=>{let d=t(b);if(!d.ok)return d;let y=fe(f)?f:f(d.value,c,u);return fe(y)?y.decode(d.value):y})}function m(f){return l(f)}function a(f){return l((b,c,u)=>{let d=f(b);return d===null?c(b):u(typeof d=="string"?E(b,d):d)})}function p(f){return h((b,c,u)=>{let d=t(b);return d.ok?d:u(E(d.error,f))})}let v=ft({verify:r,value:n,decode:t,transform:o,refine:i,refineType:s,reject:a,describe:p,then:l,pipe:m,"~standard":{version:1,vendor:"decoders",validate:f=>{let b=t(f);return b.ok?{value:b.value}:{issues:ct(b.error)}}}});return v}var dt=Symbol.for("decoders.kDecoderRegistry"),Oe=globalThis[dt]??=new WeakSet;function ft(e){return Oe.add(e),e}function fe(e){return Oe.has(e)}var pt=h((e,t,r)=>Array.isArray(e)?t(e):r("Must be an array"));function V(e){let t=e.decode;return pt.then((r,n,o)=>{let i=[];for(let s=0;s<r.length;++s){let l=r[s],m=t(l);if(m.ok)i.push(m.value);else{i.length=0;let a=m.error,p=r.slice();return p.splice(s,1,E(a,a.text?`${a.text} (at index ${s})`:`index ${s}`)),o(E(p))}}return n(i)})}function mt(e){return h((t,r,n)=>t instanceof e?r(t):n(`Must be ${e.name} instance`))}function Ne(e){return h(t=>e().decode(t))}function yt(e,t){let r=new Set;for(let n of e)t.has(n)||r.add(n);return r}var X=h((e,t,r)=>ge(e)?t(e):r("Must be an object"));function H(e){let t=new Set(Object.keys(e));return X.then((r,n,o)=>{let i=new Set(Object.keys(r)),s=yt(t,i),l={},m=null;for(let a of Object.keys(e)){let p=e[a],g=r[a],v=p.decode(g);if(v.ok){let f=v.value;f!==void 0&&(l[a]=f),s.delete(a)}else{let f=v.error;g===void 0?s.add(a):(m??=new Map,m.set(a,f))}}if(m||s.size>0){let a=Se(r);if(m&&(a=ve(a,m)),s.size>0){let p=Array.from(s).map(I).join(", "),g=s.size>1?"keys":"key";a=ke(a,`Missing ${g}: ${p}`)}return o(a)}return n(l)})}function Re(e){return X.pipe(t=>{let r=new Set(Object.keys(t));return H(e).transform(n=>{let o=new Set(Object.keys(e));for(let s of o)r.add(s);let i={};for(let s of r)if(o.has(s)){let l=n[s];l!==void 0&&(i[s]=l)}else i[s]=t[s];return i})})}var W=`Either:
38
- `;function gt(e){return`-${Y(e).substring(1)}`}function bt(e){return e.startsWith(W)?e.substring(W.length):gt(e)}function C(...e){if(e.length===0)throw new Error("Pass at least one decoder to either()");return h((t,r,n)=>{let o=[];for(let s of e){let l=s.decode(t);if(l.ok)return l;o.push(l.error)}let i=W+o.map(s=>bt(P(s).join(`
39
- `))).join(`
40
- `);return n(i)})}function pe(e){return h((t,r,n)=>{let o=e.indexOf(t);return o!==-1?r(e[o]):n(`Must be one of ${e.map(i=>I(i)).join(", ")}`)})}function _e(e){let t=Object.values(e);if(t.some(F)){let r=t.filter(F),n=new Set(r.map(i=>e[i])),o=t.filter(me).filter(i=>!n.has(i));return pe([...r,...o])}else return pe(t)}function ht(e){return typeof e=="function"?e():e}var kt=je(null),vt=je(void 0),or=h((e,t,r)=>e==null?t(e):r("Must be undefined or null"));function L(e,t){let r=C(vt,e);return arguments.length>=2?r.transform(n=>n??ht(t)):r}function je(e){return h((t,r,n)=>t===e?r(e):n(`Must be ${typeof e=="symbol"?String(e):I(e)}`))}var sr=h((e,t,r)=>t(e));var $t=h((e,t,r)=>typeof e=="boolean"?t(e):r("Must be boolean")),ir=h((e,t,r)=>t(!!e));function Z(e,t){let r=t!==void 0?e:void 0,n=t??e;return X.then((o,i,s)=>{let l={},m=new Map;for(let a of Object.keys(o)){let p=o[a],g=r?.decode(a);if(g?.ok===!1)return s(E(o,`Invalid key ${I(a)}: ${at(g.error)}`));let v=g?.value??a,f=n.decode(p);f.ok?m.size===0&&(l[v]=f.value):(m.set(a,f.error),l={})}return m.size>0?s(ve(Se(o),m)):i(l)})}var wt=/^([A-Za-z]{2,12}(?:[+][A-Za-z]{2,12})?):\/\/(?:([^@:]*:?(?:[^@]+)?)@)?(?:([A-Za-z0-9.-]+)(?::([0-9]{2,5}))?)(\/(?:[-+~%/.,\w]*)?(?:\?[-+=&;%@.,/\w]*)?(?:#[.,!/\w]*)?)?$/,O=h((e,t,r)=>me(e)?t(e):r("Must be string")),ar=S(/\S/,"Must be non-empty string");function S(e,t){return O.refine(r=>e.test(r),t)}var cr=S(/^(([^<>()[\]\\.,;:\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"),St=C(S(wt,"Must be URL").transform(e=>new URL(e)),mt(URL)),lr=St.refine(e=>e.protocol==="https:","Must be an HTTPS URL"),ur=S(/^[a-z_][a-z0-9_]*$/i,"Must be valid identifier");var Ae=S(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,"Must be uuid"),dr=Ae.refine(e=>e[14]==="1","Must be uuidv1"),fr=Ae.refine(e=>e[14]==="4","Must be uuidv4"),Et=S(/^[0-9]+$/,"Must only contain digits"),pr=S(/^[0-9a-f]+$/i,"Must only contain hexadecimal digits"),mr=Et.transform(Number),Tt=/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:[.]\d+)?(?:Z|[+-]\d{2}:?\d{2})$/,It=h((e,t,r)=>ye(e)?t(e):r("Must be a Date")),Ot=S(Tt,"Must be ISO8601 format").refine(e=>!Number.isNaN(new Date(e).getTime()),"Must be valid date/time value"),Nt=Ot.transform(e=>new Date(e)),yr=C(It,Nt).describe("Must be a Date or date string"),Rt=h((e,t,r)=>F(e)?t(e):r("Must be number")),Q=Rt.refine(e=>Number.isFinite(e),"Number must be finite"),_t=Q.refine(e=>Number.isInteger(e),"Number must be an integer"),gr=Q.refine(e=>e>=0&&!Object.is(e,-0),"Number must be positive"),br=_t.refine(e=>e>=0&&!Object.is(e,-0),"Number must be positive"),hr=h((e,t,r)=>Xe(e)?t(e):r("Must be bigint")),jt=Ne(()=>Z(Le)),At=Ne(()=>V(Le)),Le=C(kt,O,Q,$t,jt,At).describe("Must be valid JSON value");import{json as ee,ZenRouter as Mt}from"zenrouter";var Me=Re({name:L(O),avatar:L(O)}).refineType();var zt=_e(Lt),x=new Mt({authorize:({req:e})=>{let t=e.headers.get("Authorization");if(t==="Bearer sk_localdev")return!0;if(!t)throw ee({error:"Unauthorized",message:"Missing secret key"},401);if(t.startsWith("Bearer "))throw ee({error:"Forbidden",message:"Invalid secret key. The Liveblocks dev server can only be used with 'sk_localdev' as a secret key"},403);return!1}});x.route("POST /v2/authorize-user",H({userId:O,userInfo:L(Me),permissions:Z(V(zt))}),({body:e})=>({token:se({k:"acc",pid:"localdev",uid:e.userId,perms:e.permissions,ui:e.userInfo})}));x.route("POST /v2/identify-user",()=>{throw ee({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 Dt}from"zenrouter";var te=new Dt({authorize:({req:e})=>{let t=e.headers.get("Authorization");if(!t?.startsWith("Bearer "))return!1;let r=t.slice(7);return z(r)!==null}});te.route("GET /v2/rooms/<roomId>/storage",()=>({root:"Implement me"}));import{abort as De,html as Ct,ZenRouter as xt}from"zenrouter";var ze=`<!DOCTYPE html>
41
- <html lang="en">
42
- <head>
43
- <meta charset="UTF-8">
44
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
45
- <title>Liveblocks Dev Server</title>
46
- <style>
47
- * { box-sizing: border-box; }
48
- body {
49
- font-family: system-ui, -apple-system, sans-serif;
50
- background: #0f0f0f;
51
- color: #e5e5e5;
52
- min-height: 100vh;
53
- margin: 0;
54
- padding: 3rem 2rem;
55
- }
56
- main { max-width: 1000px; margin: 0 auto; }
57
- h1 { color: #fff; font-size: 1.5rem; margin: 0 0 2rem; }
58
- h2 { color: #ccc; font-size: 1rem; font-weight: 500; margin: 0 0 1rem; }
59
- .pickers { display: flex; gap: 1rem; margin-bottom: 2rem; }
60
- fieldset {
61
- border: 1px solid #333;
62
- border-radius: 6px;
63
- padding: 1rem 1.25rem;
64
- margin: 0;
65
- flex: 1;
66
- }
67
- legend { color: #888; font-size: 0.75rem; text-transform: uppercase; padding: 0 0.5rem; }
68
- label { display: block; cursor: pointer; padding: 0.25rem 0; }
69
- label.nested { margin-left: 1.5rem; color: #999; }
70
- label.disabled { color: #555; cursor: default; }
71
- input[type="radio"] { margin-right: 0.5rem; accent-color: #7c6aef; }
72
- .columns { display: grid; grid-template-columns: 1fr 1fr; gap: 1.5rem; align-items: start; }
73
- .column h3 { color: #888; font-size: 0.75rem; font-weight: 500; margin: 0 0 0.75rem; text-transform: uppercase; letter-spacing: 0.05em; }
74
- .column pre { margin: 0; min-height: 200px; }
75
- .note { color: #555; font-style: italic; margin: 0; padding: 1rem 0; }
76
- pre {
77
- background: #1a1a1a;
78
- border: 1px solid #333;
79
- border-radius: 8px;
80
- padding: 1.25rem;
81
- overflow-x: auto;
82
- font-size: 0.8rem;
83
- line-height: 1.6;
84
- }
85
- code { font-family: "SF Mono", Consolas, monospace; }
86
- .kw { color: #c586c0; }
87
- .str { color: #ce9178; }
88
- .fn { color: #dcdcaa; }
89
- .prop { color: #9cdcfe; }
90
- .cmt { color: #6a9955; }
91
- .hl {
92
- display: inline-block;
93
- width: calc(100% + 2.5rem);
94
- margin: 0 -1.25rem;
95
- padding: 0 1.25rem;
96
- background: rgba(124, 106, 239, 0.15);
97
- animation: pulse 2s ease-in-out infinite;
98
- }
99
- @keyframes pulse {
100
- 0%, 100% { background: rgba(124, 106, 239, 0.1); }
101
- 50% { background: rgba(124, 106, 239, 0.22); }
102
- }
103
- </style>
104
- </head>
105
- <body>
106
- <main>
107
- <h1>Liveblocks Dev Server</h1>
108
-
109
- <h2>How do you want to use Liveblocks?</h2>
110
- <div class="pickers">
111
- <fieldset>
112
- <legend>Library</legend>
113
- <label><input type="radio" name="library" value="client" checked onchange="update()"> @liveblocks/client</label>
114
- <label><input type="radio" name="library" value="react" onchange="update()"> @liveblocks/react</label>
115
- </fieldset>
116
-
117
- <fieldset>
118
- <legend>Authentication</legend>
119
- <label><input type="radio" name="auth" value="public" checked onchange="update()"> Public key</label>
120
- <label><input type="radio" name="auth" value="secret" onchange="update()"> Secret key <span style="color:#666">(production)</span></label>
121
- <label class="nested disabled" id="label-id"><input type="radio" name="token" value="id" disabled onchange="update()"> ID tokens</label>
122
- <label class="nested disabled" id="label-access"><input type="radio" name="token" value="access" checked disabled onchange="update()"> Access tokens</label>
123
- </fieldset>
124
- </div>
125
-
126
- <div class="columns">
127
- <div class="column">
128
- <h3>Your frontend</h3>
129
- <pre><code id="frontend-snippet"></code></pre>
130
- </div>
131
- <div class="column" id="backend-column">
132
- <h3>Your backend</h3>
133
- <p class="note" id="backend-note">No backend changes required for public key auth.</p>
134
- <pre id="backend-pre"><code id="backend-snippet"></code></pre>
135
- </div>
136
- </div>
137
- </main>
138
-
139
- <script>
140
- function h(cls, text) {
141
- return \`<span class="\${cls}">\${text}</span>\`;
142
- }
143
-
144
- const kw = t => h('kw', t);
145
- const str = t => h('str', \`"\${t}"\`);
146
- const fn = t => h('fn', t);
147
- const prop = t => h('prop', t);
148
- const cmt = t => h('cmt', t);
149
- const hl = t => h('hl', t); // highlighted line
150
-
151
- function update() {
152
- const auth = document.querySelector('input[name="auth"]:checked').value;
153
- const token = document.querySelector('input[name="token"]:checked').value;
154
- const library = document.querySelector('input[name="library"]:checked').value;
155
-
156
- // Enable/disable token options
157
- const isSecret = auth === 'secret';
158
- document.querySelectorAll('input[name="token"]').forEach(el => el.disabled = !isSecret);
159
- document.getElementById('label-id').classList.toggle('disabled', !isSecret);
160
- document.getElementById('label-access').classList.toggle('disabled', !isSecret);
161
-
162
- // Show note or code block for backend
163
- const noteEl = document.getElementById('backend-note');
164
- const preEl = document.getElementById('backend-pre');
165
- if (!isSecret) {
166
- noteEl.style.display = 'block';
167
- noteEl.textContent = 'No backend changes required for public key auth.';
168
- preEl.style.display = 'none';
169
- } else if (token === 'id') {
170
- noteEl.style.display = 'block';
171
- noteEl.textContent = 'ID tokens are not supported with the local dev server. \u{1F622}';
172
- preEl.style.display = 'block';
173
- preEl.style.opacity = '0.5';
174
- preEl.style.filter = 'grayscale(100%)';
175
- } else {
176
- noteEl.style.display = 'none';
177
- preEl.style.display = 'block';
178
- preEl.style.opacity = '1';
179
- preEl.style.filter = 'none';
180
- }
181
-
182
- let frontend = '';
183
- let backend = '';
184
-
185
- if (library === 'client') {
186
- if (auth === 'public') {
187
- frontend = \`\${kw('import')} { \${fn('createClient')} } \${kw('from')} \${str('@liveblocks/client')};
188
-
189
- \${kw('const')} \${prop('client')} = \${fn('createClient')}({
190
- \${hl(\` \${prop('baseUrl')}: \${str('http://localhost:1153')},\`)}
191
- \${hl(\` \${prop('publicApiKey')}: \${str('pk_localdev')},\`)}
192
- });\`;
193
- } else {
194
- frontend = \`\${kw('import')} { \${fn('createClient')} } \${kw('from')} \${str('@liveblocks/client')};
195
-
196
- \${kw('const')} \${prop('client')} = \${fn('createClient')}({
197
- \${hl(\` \${prop('baseUrl')}: \${str('http://localhost:1153')},\`)}
198
- \${hl(\` \${prop('authEndpoint')}: \${str('/api/liveblocks-auth')},\`)}
199
- });\`;
200
- }
201
- } else {
202
- // React
203
- if (auth === 'public') {
204
- frontend = \`\${kw('import')} { \${fn('LiveblocksProvider')}, \${fn('RoomProvider')} } \${kw('from')} \${str('@liveblocks/react')};
205
-
206
- \${kw('function')} \${fn('App')}() {
207
- \${kw('return')} (
208
- &lt;\${fn('LiveblocksProvider')}
209
- \${hl(\` \${prop('baseUrl')}=\${str('http://localhost:1153')}\`)}
210
- \${hl(\` \${prop('publicApiKey')}=\${str('pk_localdev')}\`)}
211
- &gt;
212
- &lt;\${fn('RoomProvider')} \${prop('id')}=\${str('my-room')}&gt;
213
- {\${cmt('/* your app */')}}
214
- &lt;/\${fn('RoomProvider')}&gt;
215
- &lt;/\${fn('LiveblocksProvider')}&gt;
216
- );
217
- }\`;
218
- } else {
219
- frontend = \`\${kw('import')} { \${fn('LiveblocksProvider')}, \${fn('RoomProvider')} } \${kw('from')} \${str('@liveblocks/react')};
220
-
221
- \${kw('function')} \${fn('App')}() {
222
- \${kw('return')} (
223
- &lt;\${fn('LiveblocksProvider')}
224
- \${hl(\` \${prop('baseUrl')}=\${str('http://localhost:1153')}\`)}
225
- \${hl(\` \${prop('authEndpoint')}=\${str('/api/liveblocks-auth')}\`)}
226
- &gt;
227
- &lt;\${fn('RoomProvider')} \${prop('id')}=\${str('my-room')}&gt;
228
- {\${cmt('/* your app */')}}
229
- &lt;/\${fn('RoomProvider')}&gt;
230
- &lt;/\${fn('LiveblocksProvider')}&gt;
231
- );
232
- }\`;
233
- }
234
- }
235
-
236
- // Backend code (only for secret key auth)
237
- if (auth === 'secret') {
238
- if (token === 'access') {
239
- backend = \`\${cmt('// In api/liveblocks-auth/route.ts')}
240
- \${kw('import')} { \${fn('Liveblocks')} } \${kw('from')} \${str('@liveblocks/node')};
241
-
242
- \${kw('const')} \${prop('liveblocks')} = \${kw('new')} \${fn('Liveblocks')}({
243
- \${hl(\` \${prop('secret')}: \${str('sk_localdev')},\`)}
244
- \${hl(\` \${prop('baseUrl')}: \${str('http://localhost:1153')},\`)}
245
- });
246
-
247
- \${kw('export async function')} \${fn('POST')}() {
248
- \${kw('const')} \${prop('user')} = \${cmt('// ... get user from your own session')};
249
-
250
- \${kw('const')} \${prop('session')} = \${prop('liveblocks')}.\${fn('prepareSession')}(
251
- \${prop('user')}.\${prop('id')},
252
- { \${prop('userInfo')}: { \${prop('name')}: \${prop('user')}.\${prop('name')} } }
253
- );
254
- \${prop('session')}.\${fn('allow')}(\${str('*')}, \${prop('session')}.\${prop('FULL_ACCESS')});
255
-
256
- \${kw('const')} { \${prop('status')}, \${prop('body')} } = \${kw('await')} \${prop('session')}.\${fn('authorize')}();
257
- \${kw('return new')} \${fn('Response')}(\${prop('body')}, { \${prop('status')} });
258
- }\`;
259
- } else {
260
- backend = \`\${cmt('// In api/liveblocks-auth/route.ts')}
261
- \${kw('import')} { \${fn('Liveblocks')} } \${kw('from')} \${str('@liveblocks/node')};
262
-
263
- \${kw('const')} \${prop('liveblocks')} = \${kw('new')} \${fn('Liveblocks')}({
264
- \${prop('secret')}: \${str('sk_localdev')},
265
- \${prop('baseUrl')}: \${str('http://localhost:1153')},
266
- });
267
-
268
- \${kw('export async function')} \${fn('POST')}() {
269
- \${kw('const')} \${prop('user')} = \${cmt('// ... get user from your own session')};
270
-
271
- \${kw('const')} { \${prop('status')}, \${prop('body')} } = \${kw('await')} \${prop('liveblocks')}.\${fn('identifyUser')}({
272
- \${prop('userId')}: \${prop('user')}.\${prop('id')},
273
- \${prop('groupIds')}: [],
274
- });
275
-
276
- \${kw('return new')} \${fn('Response')}(\${prop('body')}, { \${prop('status')} });
277
- }\`;
278
- }
279
- }
280
-
281
- document.getElementById('frontend-snippet').innerHTML = frontend;
282
- document.getElementById('backend-snippet').innerHTML = backend;
283
- }
284
-
285
- update();
286
- </script>
287
- </body>
288
- </html>
289
- `;var M=new xt({authorize:()=>!0});M.route("GET /v7",()=>De(426));M.route("GET /v8",()=>De(426));M.route("GET /",()=>Ct(ze));var Pe=".liveblocks/v1/rooms",qt=new Jt(e=>{Bt(Pe,{recursive:!0});let t=new D(`${Pe}/${encodeURIComponent(e)}.db`);return new Ft(e,{storage:t})}),U=new Wt;U.relay("/v2/authorize-user/*",x);U.relay("/v2/*",te);U.relay("/*",M);var re=1153,Yt={description:"Start the local Liveblocks dev server",run(e){let t=Ut(e,{string:["port"],boolean:["help"],default:{port:re},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: ${re})`),console.log(" -h, --help Show this help message");return}let r=Number(t.port)||re,n=Kt.serve({hostname:"localhost",port:r,async fetch(o,i){if(o.headers.get("Upgrade")==="websocket"){let s=ae(o);if(!s)return new Response("Unauthorized",{status:403});let[l,m]=s,a=qt.getOrCreate(l);await a.load();let p=await a.createTicket(m),g=p.sessionKey;return i.upgrade(o,{data:{room:a,ticket:p,sessionKey:g}})?void 0:new Response("Could not upgrade to WebSocket",{status:426})}return U.fetch(o)},error(o){return console.error(o),new Response("An unknown error occurred",{status:500})},websocket:{open(o){let{room:i,ticket:s}=o.data;i.startBrowserSession(s,o)},async message(o,i){let{room:s,sessionKey:l}=o.data;await s.handleData(l,i)},close(o,i,s){let{room:l,sessionKey:m}=o.data;l.endBrowserSession(m,i,s)}}});console.log(`Liveblocks dev server running at http://${n.hostname}:${n.port}`)}},Wr=Yt;export{Wr as default};
290
- /*! Bundled license information:
291
-
292
- decoders/dist/index.js:
293
- (* istanbul ignore else -- @preserve *)
294
- */