@pointyink/rogue 0.1.2 → 0.2.1

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 @@
1
+ import*as r from"@pointyink/serde/node_id_v0";function o(e,n){if(n.byteLength<r.SIZE)return e;const t=new DataView(n.buffer,n.byteOffset,n.byteLength),c=r.decode(t,0),i=e[c.replica_id]??-1;return c.sequence>i&&(e[c.replica_id]=c.sequence),e}function u(e,n,t){return e(t),o(n,t)}function f(e,n){const t=JSON.stringify(n);if(t==="{}")return e;const c=e.includes("?")?"&":"?";return`${e}${c}since=${encodeURIComponent(t)}`}export{o as advanceWatermark,f as appendSince,u as mergeAndAdvance};
@@ -0,0 +1 @@
1
+ function N(s,o){const e=o>0?s.getAtVisIx(o-1):null;if(!e)return null;if(e.node.kind==="text"){const i=e.node.domNode;return{node:i,offset:i.data.length}}const d=e.node.domNode,r=d.parentNode;return r?{node:r,offset:Array.from(r.childNodes).indexOf(d)+1}:null}function g(s,o,e,d){const r=s.visIxOf(e),i=s.visIxOf(d);if(r===null||i===null||r>i)return null;const u=o.visIxToDomPos(r);if(!u)return null;const a=o.visIxToDomPos(i+1)??N(o,s.visLen());return a?{startNode:u.node,startOffset:u.offset,endNode:a.node,endOffset:a.offset}:null}function p(s,o,e){const d=o.domPosToVisIx(e.startNode,e.startOffset),r=o.domPosToVisIx(e.endNode,e.endOffset);if(d===null||r===null)return null;const i=r-1;if(d>i)return null;const u=s.idAtVisIx(d),a=s.idAtVisIx(i);return!u||!a?null:{start_id:u,end_id:a}}function P({crdt:s,rope:o,baseUrl:e,fetchFn:d,rerender:r,onError:i,getSelectionRange:u}){const a=d??((t,n)=>globalThis.fetch(t,n));let x=[],y=!1;async function l(t,n){const f=await a(e+t,{credentials:"same-origin",headers:{"Content-Type":"application/json"},...n});if(!f.ok)throw new Error(`comments request failed: ${f.status} ${t}`);return f.status===204?null:f.json()}function O(t){return{...t,range:g(s,o,t.start_id,t.end_id)}}function m(){return x.map(O)}function I(){y||r?.(m())}async function h(){x=await l("")??[];const t=m();return I(),t}async function c(t){try{await t(),await h()}catch(n){throw i?.(n),n}}return{load:h,threads:()=>m(),create:t=>{const n=u?.();if(!n)return Promise.reject(new Error("no text selected"));const f=p(s,o,n);return f?c(()=>l("",{method:"POST",body:JSON.stringify({...f,message:t})})):Promise.reject(new Error("empty selection cannot be anchored"))},reply:(t,n)=>c(()=>l(`/${t}/replies`,{method:"POST",body:JSON.stringify({message:n})})),update:(t,n)=>c(()=>l(`/${t}`,{method:"PATCH",body:JSON.stringify(n)})),remove:t=>c(()=>l(`/${t}`,{method:"DELETE"})),handleDocumentChange:t=>{I()},handleRemoteChange:()=>h(),destroy:()=>{y=!0}}}export{p as anchorForRange,P as createComments,N as endOfDocPos,g as resolveAnchor};
@@ -0,0 +1 @@
1
+ const t=Object.freeze({out_of_memory:1,invalid_group:2,out_of_bounds:3,parse_error:4,render_overflow:5,unknown_parent:6,unknown_target:7,unknown_id:8}),n=Object.freeze(Object.fromEntries(Object.entries(t).map(([e,r])=>[r,e])));function o(e){try{const r=JSON.parse(e);return typeof r?.type!="number"||typeof r?.name!="string"?null:{type:r.type,name:r.name,desc:r.desc}}catch{return null}}export{t as CrdtErrorKind,n as CrdtErrorNames,o as parseCrdtError};
@@ -0,0 +1 @@
1
+ function c({docId:a,onOp:s}){const e=new BroadcastChannel(`rogue:doc:${a}`);return e.onmessage=o=>{const t=o.data;if(t?.type==="op")try{s(t.payload)}catch{}},{broadcastOp(o){e.postMessage({type:"op",payload:o})},destroy(){e.close()}}}export{c as createDocBus};
@@ -0,0 +1 @@
1
+ import*as b from"@pointyink/serde/node_id_v0";const p="rogue-doc-cache",w=1,c="ops";let i=null;function u(){return i||(i=new Promise((n,t)=>{const e=indexedDB.open(p,w);e.onupgradeneeded=()=>{const o=e.result;o.objectStoreNames.contains(c)||o.createObjectStore(c,{keyPath:["docId","seq","replica"]})},e.onsuccess=()=>n(e.result),e.onerror=()=>t(e.error)}),i)}async function l(n,t){if(t.byteLength<b.SIZE)return;const e=new DataView(t.buffer,t.byteOffset,t.byteLength),o=b.decode(e,0),r=await u();await new Promise((d,s)=>{const a=r.transaction(c,"readwrite");a.objectStore(c).put({docId:n,seq:o.sequence,replica:o.replica_id,payload:t}),a.oncomplete=()=>d(void 0),a.onerror=()=>s(a.error)})}async function m(n){const t=await u();return new Promise((e,o)=>{const r=t.transaction(c,"readonly"),d=IDBKeyRange.bound([n],[n,[]]),s=r.objectStore(c).getAll(d);s.onsuccess=()=>{const a=s.result||[];e(a.map(f=>f.payload))},s.onerror=()=>o(s.error)})}async function g(n){const t=await u();await new Promise((e,o)=>{const r=t.transaction(c,"readwrite");r.objectStore(c).delete(IDBKeyRange.bound([n],[n,[]])),r.oncomplete=()=>e(void 0),r.onerror=()=>o(r.error)})}export{l as cacheOp,m as cachedOps,g as clearDoc};
@@ -0,0 +1 @@
1
+ import{createRogueNode as Q}from"./sync.js";import{createComments as V}from"./comments.js";import{createSuggestions as X}from"./suggestions.js";import{resolveReplicaId as Y,syncBaseUrlFromWsUrl as z}from"./replica.js";import{createSyncClient as Z}from"./sync-client.js";import{createDocBus as _}from"./doc-bus.js";async function ie({element:l,doc:h,win:u,serializeNode:r,omitNode:m,unwrapNode:t,setMode:f,getCollapsedSelection:n,getSelectionRange:s,onClick:H,paste:A=!1,wsUrl:d,docId:c,getToken:a,isNewDoc:L,readOnly:B,onStatus:O,onProgress:P,onReady:x,onCommit:U,onOpTooLarge:j,onDocTooLarge:q,onError:G,onDocDeleted:J,comments:S,suggestions:v}){let w=null,p=null,C=null,M=null,$=!1,b=!B;const K=()=>f($&&b?"design":"readonly");let D;d&&c&&a&&(D=await Y({docId:c,wsUrl:d,getToken:a,isNewDoc:L,readOnly:B}));const i=await Q({element:l,doc:h,win:u,serializeNode:r,omitNode:m,unwrapNode:t,replicaId:D,paste:A,onOpTooLarge:j,onDocTooLarge:q,onError:G,onChange:e=>{p?.handleDocumentChange(e),C?.handleDocumentChange(e)},onCommit:e=>{b&&(M?.broadcastOp(e),w?.send(e)),U?.(e)}});let y=new Map;const F=d&&c?`${z(d)}/docs/${c}/comments`:void 0;if(S&&F){const e=async(o,g={})=>{if(!a)throw new Error("[rogue] comments need a token source");const E=await a();return fetch(o,{...g,headers:{...g.headers||{},Authorization:`Bearer ${E}`}})};p=V({crdt:i.crdt,rope:i.rope,baseUrl:F,fetchFn:a?e:void 0,getSelectionRange:s,rerender:o=>{y=I(u,h,o),S.render?.(o)},onError:o=>console.error("[rogue] comments:",o)})}let R=new Map;const N=d&&c?`${z(d)}/docs/${c}/suggestions`:void 0;if(v&&N){const e=async(o,g={})=>{if(!a)throw new Error("[rogue] suggestions need a token source");const E=await a();return fetch(o,{...g,headers:{...g.headers||{},Authorization:`Bearer ${E}`}})};C=X({crdt:i.crdt,rope:i.rope,baseUrl:N,fetchFn:a?e:void 0,markUserEdit:()=>i.markUserEdit(),getSelectionRange:s,rerender:o=>{R=T(u,h,o),v.render?.(o)},onError:o=>console.error("[rogue] suggestions:",o)})}return S?.onClick&&H(()=>{if(y.size===0)return;const e=n();if(e)for(const[o,g]of y)try{if(g.isPointInRange(e.node,e.offset)){S.onClick?.(o);return}}catch{}}),v?.onClick&&H(()=>{if(R.size===0)return;const e=n();if(e)for(const[o,g]of R)try{if(g.isPointInRange(e.node,e.offset)){v.onClick?.(o);return}}catch{}}),d&&c&&a&&(i.beginLoad(),f("readonly"),w=Z({wsUrl:d,docId:c,mergeOp:i.mergeOp,getToken:a,onStatus:O,onReady:()=>{i.endLoad(),$=!0,K(),p?.load(),C?.load(),x?.()},onSyncStart:e=>i.reserve(e>>3),onProgress:P,onCommentChanged:()=>p?.handleRemoteChange(),onSuggestionChanged:()=>C?.handleRemoteChange(),onDocDeleted:J}),typeof BroadcastChannel<"u"&&(M=_({docId:c,onOp:e=>i.mergeOp(e)}))),{node:i,comments:p,suggestions:C,deleteDoc:()=>w?.sendDelete()}}function I(l,h,u){const r=l,m=new Map;if(!r.CSS?.highlights||typeof r.Highlight!="function")return m;let t=W.get(r);t||(t={active:new r.Highlight,resolved:new r.Highlight},W.set(r,t),r.CSS.highlights.set("rogue-comment",t.active),r.CSS.highlights.set("rogue-comment-resolved",t.resolved)),t.active.clear(),t.resolved.clear();const f=new Map;for(const n of u){if(!n.range)continue;const s=h.createRange();s.setStart(n.range.startNode,n.range.startOffset),s.setEnd(n.range.endNode,n.range.endOffset),(n.resolved?t.resolved:t.active).add(s),f.set(n.id,s)}return f}const W=new WeakMap;function T(l,h,u){const r=l,m=new Map;if(!r.CSS?.highlights||typeof r.Highlight!="function")return m;let t=k.get(r);t||(t={active:new r.Highlight},k.set(r,t),r.CSS.highlights.set("rogue-suggestion",t.active)),t.active.clear();const f=new Map;for(const n of u){if(n.status!=="open"||!n.range)continue;const s=h.createRange();s.setStart(n.range.startNode,n.range.startOffset),s.setEnd(n.range.endNode,n.range.endOffset),t.active.add(s),f.set(n.id,s)}return f}const k=new WeakMap;export{ie as createEditorSession};
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import{DomRope as r,buildRope as p}from"./dom_rope.js";import{createRogueNode as m}from"./sync.js";import{RogueText as R}from"./rogue_text.js";export{r as DomRope,R as RogueText,p as buildRope,m as createRogueNode};
1
+ import{DomRope as o,buildRope as t}from"./dom_rope.js";import{createRogueNode as m}from"./sync.js";import{createComments as d}from"./comments.js";import{createSuggestions as x}from"./suggestions.js";import{createEditorSession as i}from"./editor-session.js";import{RogueText as l}from"./rogue_text.js";import{CrdtErrorKind as n,CrdtErrorNames as g,parseCrdtError as u}from"./crdt_error.js";import{resolveReplicaId as E,syncBaseUrlFromWsUrl as I,persistedReplicaId as N}from"./replica.js";export{n as CrdtErrorKind,g as CrdtErrorNames,o as DomRope,l as RogueText,t as buildRope,d as createComments,i as createEditorSession,m as createRogueNode,x as createSuggestions,u as parseCrdtError,N as persistedReplicaId,E as resolveReplicaId,I as syncBaseUrlFromWsUrl};
package/dist/oplog.js CHANGED
@@ -1 +1 @@
1
- const w="rogue-oplog",b=1,c="pending_ops";let d=null;function u(){return d||(d=new Promise((n,t)=>{const e=indexedDB.open(w,1);e.onupgradeneeded=()=>{const r=e.result;r.objectStoreNames.contains(c)||r.createObjectStore(c,{keyPath:["docId","id"]})},e.onsuccess=()=>n(e.result),e.onerror=()=>t(e.error)}),d)}async function f(n,t,e){const r=await u();await new Promise((a,s)=>{const o=r.transaction(c,"readwrite");o.objectStore(c).put({docId:n,id:t,payload:e,ts:Date.now()}),o.oncomplete=()=>a(void 0),o.onerror=()=>s(o.error)})}async function g(n,t){if(t.length===0)return;const e=await u();await new Promise((r,a)=>{const s=e.transaction(c,"readwrite"),o=s.objectStore(c);for(const i of t)o.delete([n,i]);s.oncomplete=()=>r(void 0),s.onerror=()=>a(s.error)})}async function m(n){const t=await u();return new Promise((e,r)=>{const a=t.transaction(c,"readonly"),s=IDBKeyRange.bound([n],[n,[]]),o=a.objectStore(c).getAll(s);o.onsuccess=()=>{const i=o.result||[];i.sort((l,p)=>l.ts-p.ts),e(i)},o.onerror=()=>r(o.error)})}export{g as ackMany,f as enqueue,m as pendingFor};
1
+ const p="rogue-oplog",b=1,c="pending_ops";let d=null;function u(){return d||(d=new Promise((o,t)=>{const e=indexedDB.open(p,1);e.onupgradeneeded=()=>{const r=e.result;r.objectStoreNames.contains(c)||r.createObjectStore(c,{keyPath:["docId","id"]})},e.onsuccess=()=>o(e.result),e.onerror=()=>t(e.error)}),d)}async function f(o,t,e){const r=await u();await new Promise((s,a)=>{const n=r.transaction(c,"readwrite");n.objectStore(c).put({docId:o,id:t,payload:e,ts:Date.now()}),n.oncomplete=()=>s(void 0),n.onerror=()=>a(n.error)})}async function m(o,t){if(t.length===0)return;const e=await u();await new Promise((r,s)=>{const a=e.transaction(c,"readwrite"),n=a.objectStore(c);for(const i of t)n.delete([o,i]);a.oncomplete=()=>r(void 0),a.onerror=()=>s(a.error)})}async function g(o){const t=await u();await new Promise((e,r)=>{const s=t.transaction(c,"readwrite");s.objectStore(c).delete(IDBKeyRange.bound([o],[o,[]])),s.oncomplete=()=>e(void 0),s.onerror=()=>r(s.error)})}async function x(o){const t=await u();return new Promise((e,r)=>{const s=t.transaction(c,"readonly"),a=IDBKeyRange.bound([o],[o,[]]),n=s.objectStore(c).getAll(a);n.onsuccess=()=>{const i=n.result||[];i.sort((l,w)=>l.ts-w.ts),e(i)},n.onerror=()=>r(n.error)})}export{m as ackMany,g as clearDoc,f as enqueue,x as pendingFor};
@@ -0,0 +1 @@
1
+ const a="rogue.replicaId.";function p(t){const e=new URL(t);return`${e.protocol==="wss:"?"https:":"http:"}//${e.host}`}function f(t){const e=localStorage.getItem(a+t);if(e===null)return null;const r=Number(e);return Number.isInteger(r)&&r>=1&&r<=65534?r:null}function i(t,e){localStorage.setItem(a+t,String(e))}async function h({docId:t,wsUrl:e,getToken:r,isNewDoc:l=!1,readOnly:c=!1}){if(c)return Math.floor(Math.random()*65534)+1;const o=f(t);if(o!==null)return o;if(l)return i(t,1),1;const u=await r(),n=await fetch(`${p(e)}/docs/${t}/replica`,{method:"POST",headers:{Authorization:`Bearer ${u}`}});if(!n.ok)throw new Error(`replica allocation failed: ${n.status}`);const{replica_id:s}=await n.json();return i(t,s),s}export{f as persistedReplicaId,h as resolveReplicaId,p as syncBaseUrlFromWsUrl};