@abraca/dabra 1.0.1 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/abracadabra-provider.cjs +1226 -10
- package/dist/abracadabra-provider.cjs.map +1 -1
- package/dist/abracadabra-provider.esm.js +1217 -11
- package/dist/abracadabra-provider.esm.js.map +1 -1
- package/dist/index.d.ts +389 -1
- package/package.json +1 -1
- package/src/AbracadabraClient.ts +67 -0
- package/src/AbracadabraWS.ts +3 -1
- package/src/index.ts +1 -0
- package/src/types.ts +14 -0
- package/src/webrtc/AbracadabraWebRTC.ts +540 -0
- package/src/webrtc/DataChannelRouter.ts +110 -0
- package/src/webrtc/FileTransferChannel.ts +359 -0
- package/src/webrtc/PeerConnection.ts +133 -0
- package/src/webrtc/SignalingSocket.ts +366 -0
- package/src/webrtc/YjsDataChannel.ts +195 -0
- package/src/webrtc/index.ts +20 -0
- package/src/webrtc/types.ts +159 -0
package/dist/index.d.ts
CHANGED
|
@@ -391,6 +391,23 @@ declare class AbracadabraClient {
|
|
|
391
391
|
revokeInvite(code: string): Promise<void>;
|
|
392
392
|
/** Redeem an invite code for the currently authenticated user. */
|
|
393
393
|
redeemInvite(code: string): Promise<void>;
|
|
394
|
+
/** List spaces visible to the caller. No auth required for public spaces. */
|
|
395
|
+
listSpaces(): Promise<SpaceMeta[]>;
|
|
396
|
+
/** Get a single space by ID. */
|
|
397
|
+
getSpace(spaceId: string): Promise<SpaceMeta>;
|
|
398
|
+
/** Get the hub space, or null if none is configured. */
|
|
399
|
+
getHubSpace(): Promise<SpaceMeta | null>;
|
|
400
|
+
/** Create a new space (auth required). */
|
|
401
|
+
createSpace(opts: {
|
|
402
|
+
name: string;
|
|
403
|
+
description?: string;
|
|
404
|
+
visibility?: SpaceMeta["visibility"];
|
|
405
|
+
id?: string;
|
|
406
|
+
}): Promise<SpaceMeta>;
|
|
407
|
+
/** Update an existing space (Owner or admin required). */
|
|
408
|
+
updateSpace(spaceId: string, opts: Partial<Pick<SpaceMeta, "name" | "description" | "visibility" | "is_hub">>): Promise<SpaceMeta>;
|
|
409
|
+
/** Delete a space and its root document (Owner or admin required). */
|
|
410
|
+
deleteSpace(spaceId: string): Promise<void>;
|
|
394
411
|
/** Health check — no auth required. */
|
|
395
412
|
health(): Promise<HealthStatus>;
|
|
396
413
|
/**
|
|
@@ -398,6 +415,12 @@ declare class AbracadabraClient {
|
|
|
398
415
|
* No auth required.
|
|
399
416
|
*/
|
|
400
417
|
serverInfo(): Promise<ServerInfo>;
|
|
418
|
+
/**
|
|
419
|
+
* Fetch ICE server configuration for WebRTC peer connections.
|
|
420
|
+
* Falls back to default Google STUN server if the endpoint is unavailable.
|
|
421
|
+
* No auth required.
|
|
422
|
+
*/
|
|
423
|
+
getIceServers(): Promise<RTCIceServer[]>;
|
|
401
424
|
private request;
|
|
402
425
|
private toError;
|
|
403
426
|
private loadPersistedToken;
|
|
@@ -964,6 +987,17 @@ interface SearchResult {
|
|
|
964
987
|
/** Number of matching trigrams — higher is better. */
|
|
965
988
|
score: number;
|
|
966
989
|
}
|
|
990
|
+
interface SpaceMeta {
|
|
991
|
+
id: string;
|
|
992
|
+
doc_id: string;
|
|
993
|
+
name: string;
|
|
994
|
+
description: string | null;
|
|
995
|
+
visibility: "public" | "private" | "invite";
|
|
996
|
+
is_hub: boolean;
|
|
997
|
+
owner_id: string | null;
|
|
998
|
+
created_at: number;
|
|
999
|
+
updated_at: number;
|
|
1000
|
+
}
|
|
967
1001
|
interface InviteRow {
|
|
968
1002
|
code: string;
|
|
969
1003
|
createdBy: string | null;
|
|
@@ -1563,4 +1597,358 @@ declare class BackgroundSyncManager extends EventEmitter {
|
|
|
1563
1597
|
private _walkXml;
|
|
1564
1598
|
}
|
|
1565
1599
|
//#endregion
|
|
1566
|
-
|
|
1600
|
+
//#region packages/provider/src/webrtc/DataChannelRouter.d.ts
|
|
1601
|
+
declare class DataChannelRouter extends EventEmitter {
|
|
1602
|
+
private connection;
|
|
1603
|
+
private channels;
|
|
1604
|
+
constructor(connection: RTCPeerConnection);
|
|
1605
|
+
/** Create a named data channel (initiator side). */
|
|
1606
|
+
createChannel(name: string, options?: RTCDataChannelInit): RTCDataChannel;
|
|
1607
|
+
/** Create the standard set of channels for Abracadabra WebRTC. */
|
|
1608
|
+
createDefaultChannels(opts: {
|
|
1609
|
+
enableDocSync: boolean;
|
|
1610
|
+
enableAwareness: boolean;
|
|
1611
|
+
enableFileTransfer: boolean;
|
|
1612
|
+
}): void;
|
|
1613
|
+
getChannel(name: string): RTCDataChannel | null;
|
|
1614
|
+
isOpen(name: string): boolean;
|
|
1615
|
+
private registerChannel;
|
|
1616
|
+
close(): void;
|
|
1617
|
+
destroy(): void;
|
|
1618
|
+
}
|
|
1619
|
+
//#endregion
|
|
1620
|
+
//#region packages/provider/src/webrtc/types.d.ts
|
|
1621
|
+
type SignalingIncoming = {
|
|
1622
|
+
type: "join";
|
|
1623
|
+
} | {
|
|
1624
|
+
type: "leave";
|
|
1625
|
+
} | {
|
|
1626
|
+
type: "offer";
|
|
1627
|
+
to: string;
|
|
1628
|
+
sdp: string;
|
|
1629
|
+
} | {
|
|
1630
|
+
type: "answer";
|
|
1631
|
+
to: string;
|
|
1632
|
+
sdp: string;
|
|
1633
|
+
} | {
|
|
1634
|
+
type: "ice";
|
|
1635
|
+
to: string;
|
|
1636
|
+
candidate: string;
|
|
1637
|
+
} | {
|
|
1638
|
+
type: "mute";
|
|
1639
|
+
muted: boolean;
|
|
1640
|
+
} | {
|
|
1641
|
+
type: "media-state";
|
|
1642
|
+
video: boolean;
|
|
1643
|
+
screen: boolean;
|
|
1644
|
+
} | {
|
|
1645
|
+
type: "profile";
|
|
1646
|
+
name: string;
|
|
1647
|
+
color: string;
|
|
1648
|
+
} | {
|
|
1649
|
+
type: "pong";
|
|
1650
|
+
};
|
|
1651
|
+
type SignalingOutgoing = {
|
|
1652
|
+
type: "welcome";
|
|
1653
|
+
peer_id: string;
|
|
1654
|
+
peers: PeerInfo[];
|
|
1655
|
+
} | {
|
|
1656
|
+
type: "joined";
|
|
1657
|
+
peer_id: string;
|
|
1658
|
+
user_id: string;
|
|
1659
|
+
muted: boolean;
|
|
1660
|
+
video: boolean;
|
|
1661
|
+
screen: boolean;
|
|
1662
|
+
name: string | null;
|
|
1663
|
+
color: string | null;
|
|
1664
|
+
} | {
|
|
1665
|
+
type: "left";
|
|
1666
|
+
peer_id: string;
|
|
1667
|
+
} | {
|
|
1668
|
+
type: "offer";
|
|
1669
|
+
from: string;
|
|
1670
|
+
sdp: string;
|
|
1671
|
+
} | {
|
|
1672
|
+
type: "answer";
|
|
1673
|
+
from: string;
|
|
1674
|
+
sdp: string;
|
|
1675
|
+
} | {
|
|
1676
|
+
type: "ice";
|
|
1677
|
+
from: string;
|
|
1678
|
+
candidate: string;
|
|
1679
|
+
} | {
|
|
1680
|
+
type: "mute";
|
|
1681
|
+
peer_id: string;
|
|
1682
|
+
muted: boolean;
|
|
1683
|
+
} | {
|
|
1684
|
+
type: "media-state";
|
|
1685
|
+
peer_id: string;
|
|
1686
|
+
video: boolean;
|
|
1687
|
+
screen: boolean;
|
|
1688
|
+
} | {
|
|
1689
|
+
type: "profile";
|
|
1690
|
+
peer_id: string;
|
|
1691
|
+
name: string;
|
|
1692
|
+
color: string;
|
|
1693
|
+
} | {
|
|
1694
|
+
type: "ping";
|
|
1695
|
+
} | {
|
|
1696
|
+
type: "error";
|
|
1697
|
+
code: string;
|
|
1698
|
+
message: string;
|
|
1699
|
+
};
|
|
1700
|
+
interface PeerInfo {
|
|
1701
|
+
peer_id: string;
|
|
1702
|
+
user_id: string;
|
|
1703
|
+
muted: boolean;
|
|
1704
|
+
video: boolean;
|
|
1705
|
+
screen: boolean;
|
|
1706
|
+
name: string | null;
|
|
1707
|
+
color: string | null;
|
|
1708
|
+
}
|
|
1709
|
+
interface PeerState extends PeerInfo {
|
|
1710
|
+
connectionState: RTCPeerConnectionState | "new";
|
|
1711
|
+
}
|
|
1712
|
+
interface FileTransferMeta {
|
|
1713
|
+
transferId: string;
|
|
1714
|
+
filename: string;
|
|
1715
|
+
mimeType: string;
|
|
1716
|
+
totalSize: number;
|
|
1717
|
+
chunkSize: number;
|
|
1718
|
+
totalChunks: number;
|
|
1719
|
+
}
|
|
1720
|
+
type FileTransferStatus = "pending" | "sending" | "receiving" | "complete" | "cancelled" | "error";
|
|
1721
|
+
declare const CHANNEL_NAMES: {
|
|
1722
|
+
readonly YJS_SYNC: "yjs-sync";
|
|
1723
|
+
readonly AWARENESS: "awareness";
|
|
1724
|
+
readonly FILE_TRANSFER: "file-transfer";
|
|
1725
|
+
readonly CUSTOM: "custom";
|
|
1726
|
+
};
|
|
1727
|
+
interface AbracadabraWebRTCConfiguration {
|
|
1728
|
+
/** Document ID for the signaling room. */
|
|
1729
|
+
docId: string;
|
|
1730
|
+
/** Server base URL (http/https). Signaling URL derived automatically. */
|
|
1731
|
+
url: string;
|
|
1732
|
+
/** JWT token or async token factory. */
|
|
1733
|
+
token: string | (() => string) | (() => Promise<string>);
|
|
1734
|
+
/** Optional Y.Doc to sync over data channels (hybrid mode). */
|
|
1735
|
+
document?: InstanceType<typeof Y.Doc> | null;
|
|
1736
|
+
/** Optional Awareness instance for presence sync over data channels. */
|
|
1737
|
+
awareness?: InstanceType<typeof Awareness> | null;
|
|
1738
|
+
/** ICE server configuration. Defaults to Google STUN. */
|
|
1739
|
+
iceServers?: RTCIceServer[];
|
|
1740
|
+
/** Display name for this peer. */
|
|
1741
|
+
displayName?: string;
|
|
1742
|
+
/** Color identifier for this peer. */
|
|
1743
|
+
color?: string;
|
|
1744
|
+
/** Enable Y.js document sync over data channels. Default: true when document is provided. */
|
|
1745
|
+
enableDocSync?: boolean;
|
|
1746
|
+
/** Enable awareness sync over data channels. Default: true when awareness is provided. */
|
|
1747
|
+
enableAwarenessSync?: boolean;
|
|
1748
|
+
/** Enable file transfer channel. Default: false. */
|
|
1749
|
+
enableFileTransfer?: boolean;
|
|
1750
|
+
/** Max file chunk size in bytes. Default: 16384 (16KB). */
|
|
1751
|
+
fileChunkSize?: number;
|
|
1752
|
+
/** Auto-connect on construction. Default: true. */
|
|
1753
|
+
autoConnect?: boolean;
|
|
1754
|
+
/** WebSocket polyfill for signaling (e.g. for Node.js). */
|
|
1755
|
+
WebSocketPolyfill?: any;
|
|
1756
|
+
}
|
|
1757
|
+
declare const DEFAULT_ICE_SERVERS: RTCIceServer[];
|
|
1758
|
+
declare const DEFAULT_FILE_CHUNK_SIZE = 16384;
|
|
1759
|
+
//#endregion
|
|
1760
|
+
//#region packages/provider/src/webrtc/FileTransferChannel.d.ts
|
|
1761
|
+
/**
|
|
1762
|
+
* Handle for tracking a file transfer in progress.
|
|
1763
|
+
*/
|
|
1764
|
+
declare class FileTransferHandle extends EventEmitter {
|
|
1765
|
+
readonly transferId: string;
|
|
1766
|
+
progress: number;
|
|
1767
|
+
status: FileTransferStatus;
|
|
1768
|
+
private abortController;
|
|
1769
|
+
constructor(transferId: string);
|
|
1770
|
+
cancel(): void;
|
|
1771
|
+
get signal(): AbortSignal;
|
|
1772
|
+
/** @internal */
|
|
1773
|
+
_setProgress(p: number): void;
|
|
1774
|
+
/** @internal */
|
|
1775
|
+
_setStatus(s: FileTransferStatus): void;
|
|
1776
|
+
}
|
|
1777
|
+
/**
|
|
1778
|
+
* Chunked binary file transfer over a dedicated WebRTC data channel.
|
|
1779
|
+
*/
|
|
1780
|
+
declare class FileTransferChannel extends EventEmitter {
|
|
1781
|
+
private readonly router;
|
|
1782
|
+
private receives;
|
|
1783
|
+
private chunkSize;
|
|
1784
|
+
private channelMessageHandler;
|
|
1785
|
+
constructor(router: DataChannelRouter, chunkSize?: number);
|
|
1786
|
+
/** Send a file to a peer. Returns a handle for tracking progress. */
|
|
1787
|
+
send(file: File | Blob, filename: string): Promise<FileTransferHandle>;
|
|
1788
|
+
private handleMessage;
|
|
1789
|
+
private handleStart;
|
|
1790
|
+
private handleChunk;
|
|
1791
|
+
private handleComplete;
|
|
1792
|
+
private handleCancel;
|
|
1793
|
+
destroy(): void;
|
|
1794
|
+
}
|
|
1795
|
+
//#endregion
|
|
1796
|
+
//#region packages/provider/src/webrtc/AbracadabraWebRTC.d.ts
|
|
1797
|
+
/**
|
|
1798
|
+
* Optional WebRTC provider for peer-to-peer Y.js sync, awareness, and file transfer.
|
|
1799
|
+
*
|
|
1800
|
+
* Uses the server's signaling endpoint (`/ws/:doc_id/signaling`) for connection
|
|
1801
|
+
* negotiation, then establishes direct data channels between peers. Designed to
|
|
1802
|
+
* work alongside `AbracadabraProvider` — the server remains the persistence layer,
|
|
1803
|
+
* while WebRTC provides low-latency P2P sync.
|
|
1804
|
+
*
|
|
1805
|
+
* Falls back to a no-op when `RTCPeerConnection` is unavailable (e.g. Node.js).
|
|
1806
|
+
*/
|
|
1807
|
+
declare class AbracadabraWebRTC extends EventEmitter {
|
|
1808
|
+
private signaling;
|
|
1809
|
+
private peerConnections;
|
|
1810
|
+
private yjsChannels;
|
|
1811
|
+
private fileChannels;
|
|
1812
|
+
private readonly config;
|
|
1813
|
+
readonly peers: Map<string, PeerState>;
|
|
1814
|
+
localPeerId: string | null;
|
|
1815
|
+
isConnected: boolean;
|
|
1816
|
+
constructor(configuration: AbracadabraWebRTCConfiguration);
|
|
1817
|
+
/**
|
|
1818
|
+
* Create an AbracadabraWebRTC instance from an existing provider,
|
|
1819
|
+
* reusing its document, awareness, URL, and token.
|
|
1820
|
+
*/
|
|
1821
|
+
static fromProvider(provider: AbracadabraProvider, options?: Partial<AbracadabraWebRTCConfiguration>): AbracadabraWebRTC;
|
|
1822
|
+
connect(): Promise<void>;
|
|
1823
|
+
disconnect(): void;
|
|
1824
|
+
destroy(): void;
|
|
1825
|
+
setMuted(muted: boolean): void;
|
|
1826
|
+
setMediaState(video: boolean, screen: boolean): void;
|
|
1827
|
+
setProfile(name: string, color: string): void;
|
|
1828
|
+
/**
|
|
1829
|
+
* Send a file to a specific peer. Returns a handle for tracking progress.
|
|
1830
|
+
*/
|
|
1831
|
+
sendFile(peerId: string, file: File | Blob, filename: string): Promise<FileTransferHandle | null>;
|
|
1832
|
+
/**
|
|
1833
|
+
* Send a file to all connected peers. Returns an array of handles.
|
|
1834
|
+
*/
|
|
1835
|
+
broadcastFile(file: File | Blob, filename: string): Promise<FileTransferHandle[]>;
|
|
1836
|
+
/**
|
|
1837
|
+
* Send a custom string message to a specific peer via a data channel.
|
|
1838
|
+
*/
|
|
1839
|
+
sendCustomMessage(peerId: string, payload: string): void;
|
|
1840
|
+
/**
|
|
1841
|
+
* Send a custom string message to all connected peers.
|
|
1842
|
+
*/
|
|
1843
|
+
broadcastCustomMessage(payload: string): void;
|
|
1844
|
+
private addPeer;
|
|
1845
|
+
private removePeer;
|
|
1846
|
+
private removeAllPeers;
|
|
1847
|
+
private createPeerConnection;
|
|
1848
|
+
private attachDataHandlers;
|
|
1849
|
+
private initiateConnection;
|
|
1850
|
+
private handleOffer;
|
|
1851
|
+
private buildSignalingUrl;
|
|
1852
|
+
}
|
|
1853
|
+
//#endregion
|
|
1854
|
+
//#region packages/provider/src/webrtc/SignalingSocket.d.ts
|
|
1855
|
+
interface SignalingSocketConfiguration {
|
|
1856
|
+
/** WebSocket URL for the signaling endpoint. */
|
|
1857
|
+
url: string;
|
|
1858
|
+
/** JWT token or async token factory for auth. */
|
|
1859
|
+
token: string | (() => string) | (() => Promise<string>);
|
|
1860
|
+
/** Auto-connect on construction. Default: true. */
|
|
1861
|
+
autoConnect?: boolean;
|
|
1862
|
+
/** WebSocket polyfill (e.g. for Node.js). */
|
|
1863
|
+
WebSocketPolyfill?: any;
|
|
1864
|
+
/** Retry delay in ms. Default: 1000. */
|
|
1865
|
+
delay?: number;
|
|
1866
|
+
/** Retry factor. Default: 2. */
|
|
1867
|
+
factor?: number;
|
|
1868
|
+
/** Min retry delay. Default: 1000. */
|
|
1869
|
+
minDelay?: number;
|
|
1870
|
+
/** Max retry delay. Default: 30000. */
|
|
1871
|
+
maxDelay?: number;
|
|
1872
|
+
/** Randomize delay. Default: true. */
|
|
1873
|
+
jitter?: boolean;
|
|
1874
|
+
/** Max retry attempts (0 = unlimited). Default: 0. */
|
|
1875
|
+
maxAttempts?: number;
|
|
1876
|
+
}
|
|
1877
|
+
declare class SignalingSocket extends EventEmitter {
|
|
1878
|
+
private ws;
|
|
1879
|
+
private wsHandlers;
|
|
1880
|
+
private shouldConnect;
|
|
1881
|
+
private cancelRetry?;
|
|
1882
|
+
private connectionAttempt;
|
|
1883
|
+
private readonly config;
|
|
1884
|
+
localPeerId: string | null;
|
|
1885
|
+
isConnected: boolean;
|
|
1886
|
+
constructor(configuration: SignalingSocketConfiguration);
|
|
1887
|
+
private getToken;
|
|
1888
|
+
connect(): Promise<void>;
|
|
1889
|
+
private createConnection;
|
|
1890
|
+
private handleMessage;
|
|
1891
|
+
private sendRaw;
|
|
1892
|
+
sendOffer(to: string, sdp: string): void;
|
|
1893
|
+
sendAnswer(to: string, sdp: string): void;
|
|
1894
|
+
sendIce(to: string, candidate: string): void;
|
|
1895
|
+
sendMute(muted: boolean): void;
|
|
1896
|
+
sendMediaState(video: boolean, screen: boolean): void;
|
|
1897
|
+
sendProfile(name: string, color: string): void;
|
|
1898
|
+
sendLeave(): void;
|
|
1899
|
+
disconnect(): void;
|
|
1900
|
+
destroy(): void;
|
|
1901
|
+
private cleanup;
|
|
1902
|
+
}
|
|
1903
|
+
//#endregion
|
|
1904
|
+
//#region packages/provider/src/webrtc/PeerConnection.d.ts
|
|
1905
|
+
declare class PeerConnection extends EventEmitter {
|
|
1906
|
+
readonly connection: RTCPeerConnection;
|
|
1907
|
+
readonly router: DataChannelRouter;
|
|
1908
|
+
readonly peerId: string;
|
|
1909
|
+
private pendingCandidates;
|
|
1910
|
+
private hasRemoteDescription;
|
|
1911
|
+
constructor(peerId: string, iceServers: RTCIceServer[]);
|
|
1912
|
+
get connectionState(): RTCPeerConnectionState;
|
|
1913
|
+
get iceConnectionState(): RTCIceConnectionState;
|
|
1914
|
+
/** Create an SDP offer (initiator side). */
|
|
1915
|
+
createOffer(iceRestart?: boolean): Promise<string>;
|
|
1916
|
+
/** Set a remote offer and create an answer (receiver side). Returns the SDP answer. */
|
|
1917
|
+
setRemoteOffer(sdp: string): Promise<string>;
|
|
1918
|
+
/** Set the remote answer (initiator side). */
|
|
1919
|
+
setRemoteAnswer(sdp: string): Promise<void>;
|
|
1920
|
+
/** Add a remote ICE candidate. Queues if remote description not yet set. */
|
|
1921
|
+
addIceCandidate(candidateJson: string): Promise<void>;
|
|
1922
|
+
private flushPendingCandidates;
|
|
1923
|
+
close(): void;
|
|
1924
|
+
destroy(): void;
|
|
1925
|
+
}
|
|
1926
|
+
//#endregion
|
|
1927
|
+
//#region packages/provider/src/webrtc/YjsDataChannel.d.ts
|
|
1928
|
+
/**
|
|
1929
|
+
* Handles Y.js document sync and awareness over WebRTC data channels.
|
|
1930
|
+
*
|
|
1931
|
+
* Uses the same y-protocols/sync encoding as the WebSocket provider but
|
|
1932
|
+
* transported over RTCDataChannel instead. A unique origin is used to
|
|
1933
|
+
* prevent echo loops with the server-based provider.
|
|
1934
|
+
*/
|
|
1935
|
+
declare class YjsDataChannel {
|
|
1936
|
+
private readonly document;
|
|
1937
|
+
private readonly awareness;
|
|
1938
|
+
private readonly router;
|
|
1939
|
+
private docUpdateHandler;
|
|
1940
|
+
private awarenessUpdateHandler;
|
|
1941
|
+
private channelOpenHandler;
|
|
1942
|
+
private channelMessageHandler;
|
|
1943
|
+
constructor(document: Y.Doc, awareness: Awareness | null, router: DataChannelRouter);
|
|
1944
|
+
/** Start listening for Y.js updates and data channel messages. */
|
|
1945
|
+
attach(): void;
|
|
1946
|
+
/** Stop listening and clean up handlers. */
|
|
1947
|
+
detach(): void;
|
|
1948
|
+
destroy(): void;
|
|
1949
|
+
private sendSyncStep1;
|
|
1950
|
+
private handleSyncMessage;
|
|
1951
|
+
private handleAwarenessMessage;
|
|
1952
|
+
}
|
|
1953
|
+
//#endregion
|
|
1954
|
+
export { AbracadabraBaseProvider, AbracadabraBaseProviderConfiguration, AbracadabraClient, AbracadabraClientConfig, AbracadabraOutgoingMessageArguments, AbracadabraProvider, AbracadabraProviderConfiguration, AbracadabraWS, AbracadabraWSConfiguration, AbracadabraWebRTC, type AbracadabraWebRTCConfiguration, AbracadabraWebSocketConn, AuthMessageType, AuthorizedScope, AwarenessError, BackgroundSyncManager, type BackgroundSyncManagerOptions, BackgroundSyncPersistence, CHANNEL_NAMES, CloseEvent, CompleteAbracadabraBaseProviderConfiguration, CompleteAbracadabraWSConfiguration, CompleteHocuspocusProviderConfiguration, CompleteHocuspocusProviderWebsocketConfiguration, ConnectionTimeout, Constructable, ConstructableOutgoingMessage, CryptoIdentity, CryptoIdentityKeystore, DEFAULT_FILE_CHUNK_SIZE, DEFAULT_ICE_SERVERS, DataChannelRouter, type DocEncryptionInfo, DocKeyManager, type DocSyncState, DocumentCache, type DocumentCacheOptions, DocumentMeta, E2EAbracadabraProvider, E2EOfflineStore, EffectiveRole, EncryptedYMap, EncryptedYText, FileBlobStore, FileTransferChannel, FileTransferHandle, type FileTransferMeta, type FileTransferStatus, Forbidden, HealthStatus, HocusPocusWebSocket, HocuspocusProvider, HocuspocusProviderConfiguration, HocuspocusProviderWebsocket, HocuspocusProviderWebsocketConfiguration, HocuspocusWebSocket, InviteRow, MessageTooBig, MessageType, OfflineStore, OutgoingMessageArguments, OutgoingMessageInterface, PeerConnection, type PeerInfo, type PeerState, PendingSubdoc, PermissionEntry, PublicKeyInfo, ResetConnection, SearchIndex, SearchResult, ServerInfo, type SignalingIncoming, type SignalingOutgoing, SignalingSocket, SpaceMeta, StatesArray, SubdocMessage, SubdocRegisteredEvent, Unauthorized, UploadInfo, UploadMeta, UploadQueueEntry, UploadQueueStatus, UserProfile, WebSocketStatus, WsReadyStates, YjsDataChannel, attachUpdatedAtObserver, awarenessStatesToArray, decryptField, encryptField, makeEncryptedYMap, makeEncryptedYText, onAuthenticatedParameters, onAuthenticationFailedParameters, onAwarenessChangeParameters, onAwarenessUpdateParameters, onCloseParameters, onDisconnectParameters, onMessageParameters, onOpenParameters, onOutgoingMessageParameters, onStatelessParameters, onStatusParameters, onSubdocLoadedParameters, onSubdocRegisteredParameters, onSyncedParameters, onUnsyncedChangesParameters, readAuthMessage, writeAuthenticated, writeAuthentication, writePermissionDenied, writeTokenSyncRequest };
|
package/package.json
CHANGED
package/src/AbracadabraClient.ts
CHANGED
|
@@ -8,6 +8,7 @@ import type {
|
|
|
8
8
|
HealthStatus,
|
|
9
9
|
ServerInfo,
|
|
10
10
|
InviteRow,
|
|
11
|
+
SpaceMeta,
|
|
11
12
|
} from "./types.ts";
|
|
12
13
|
import type { DocEncryptionInfo } from "./types.ts";
|
|
13
14
|
import type { DocumentCache } from "./DocumentCache.ts";
|
|
@@ -472,6 +473,54 @@ export class AbracadabraClient {
|
|
|
472
473
|
await this.request("POST", "/invites/redeem", { body: { code } });
|
|
473
474
|
}
|
|
474
475
|
|
|
476
|
+
// ── Spaces ───────────────────────────────────────────────────────────────
|
|
477
|
+
|
|
478
|
+
/** List spaces visible to the caller. No auth required for public spaces. */
|
|
479
|
+
async listSpaces(): Promise<SpaceMeta[]> {
|
|
480
|
+
const res = await this.request<{ spaces: SpaceMeta[] }>("GET", "/spaces", { auth: false });
|
|
481
|
+
return res.spaces;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/** Get a single space by ID. */
|
|
485
|
+
async getSpace(spaceId: string): Promise<SpaceMeta> {
|
|
486
|
+
return this.request<SpaceMeta>("GET", `/spaces/${encodeURIComponent(spaceId)}`, { auth: false });
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/** Get the hub space, or null if none is configured. */
|
|
490
|
+
async getHubSpace(): Promise<SpaceMeta | null> {
|
|
491
|
+
try {
|
|
492
|
+
return await this.request<SpaceMeta>("GET", "/spaces/hub", { auth: false });
|
|
493
|
+
} catch (e: unknown) {
|
|
494
|
+
if (typeof e === "object" && e !== null && "status" in e && (e as { status: number }).status === 404) {
|
|
495
|
+
return null;
|
|
496
|
+
}
|
|
497
|
+
throw e;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
/** Create a new space (auth required). */
|
|
502
|
+
async createSpace(opts: {
|
|
503
|
+
name: string;
|
|
504
|
+
description?: string;
|
|
505
|
+
visibility?: SpaceMeta["visibility"];
|
|
506
|
+
id?: string;
|
|
507
|
+
}): Promise<SpaceMeta> {
|
|
508
|
+
return this.request<SpaceMeta>("POST", "/spaces", { body: opts });
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
/** Update an existing space (Owner or admin required). */
|
|
512
|
+
async updateSpace(
|
|
513
|
+
spaceId: string,
|
|
514
|
+
opts: Partial<Pick<SpaceMeta, "name" | "description" | "visibility" | "is_hub">>,
|
|
515
|
+
): Promise<SpaceMeta> {
|
|
516
|
+
return this.request<SpaceMeta>("PATCH", `/spaces/${encodeURIComponent(spaceId)}`, { body: opts });
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
/** Delete a space and its root document (Owner or admin required). */
|
|
520
|
+
async deleteSpace(spaceId: string): Promise<void> {
|
|
521
|
+
await this.request("DELETE", `/spaces/${encodeURIComponent(spaceId)}`);
|
|
522
|
+
}
|
|
523
|
+
|
|
475
524
|
// ── System ───────────────────────────────────────────────────────────────
|
|
476
525
|
|
|
477
526
|
/** Health check — no auth required. */
|
|
@@ -487,6 +536,24 @@ export class AbracadabraClient {
|
|
|
487
536
|
return this.request<ServerInfo>("GET", "/info", { auth: false });
|
|
488
537
|
}
|
|
489
538
|
|
|
539
|
+
/**
|
|
540
|
+
* Fetch ICE server configuration for WebRTC peer connections.
|
|
541
|
+
* Falls back to default Google STUN server if the endpoint is unavailable.
|
|
542
|
+
* No auth required.
|
|
543
|
+
*/
|
|
544
|
+
async getIceServers(): Promise<RTCIceServer[]> {
|
|
545
|
+
try {
|
|
546
|
+
const res = await this.request<{ iceServers: RTCIceServer[] }>(
|
|
547
|
+
"GET",
|
|
548
|
+
"/ice-servers",
|
|
549
|
+
{ auth: false },
|
|
550
|
+
);
|
|
551
|
+
return res.iceServers;
|
|
552
|
+
} catch {
|
|
553
|
+
return [{ urls: "stun:stun.l.google.com:19302" }];
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
|
|
490
557
|
// ── Internals ────────────────────────────────────────────────────────────
|
|
491
558
|
|
|
492
559
|
private async request<T = void>(
|
package/src/AbracadabraWS.ts
CHANGED
|
@@ -511,7 +511,9 @@ export class AbracadabraWS extends EventEmitter {
|
|
|
511
511
|
this.emit("status", { status: WebSocketStatus.Disconnected });
|
|
512
512
|
|
|
513
513
|
// Detect server-side rate-limit close (code 4429).
|
|
514
|
-
|
|
514
|
+
// `event` may be a CloseEvent (browser) with `.code`, or a raw number (ws/Node.js).
|
|
515
|
+
console.log('[DEBUG] onClose event:', typeof event, JSON.stringify(event), 'code:', (event as any)?.code);
|
|
516
|
+
const isRateLimited = (event as any)?.code === 4429 || event === 4429;
|
|
515
517
|
this.emit("disconnect", { event });
|
|
516
518
|
if (isRateLimited) {
|
|
517
519
|
this.emit("rateLimited");
|
package/src/index.ts
CHANGED
|
@@ -23,3 +23,4 @@ export { BackgroundSyncManager } from "./BackgroundSyncManager.ts";
|
|
|
23
23
|
export type { BackgroundSyncManagerOptions } from "./BackgroundSyncManager.ts";
|
|
24
24
|
export { BackgroundSyncPersistence } from "./BackgroundSyncPersistence.ts";
|
|
25
25
|
export type { DocSyncState } from "./BackgroundSyncPersistence.ts";
|
|
26
|
+
export * from "./webrtc/index.ts";
|
package/src/types.ts
CHANGED
|
@@ -223,6 +223,20 @@ export interface SearchResult {
|
|
|
223
223
|
score: number;
|
|
224
224
|
}
|
|
225
225
|
|
|
226
|
+
// ── Spaces ───────────────────────────────────────────────────────────────────
|
|
227
|
+
|
|
228
|
+
export interface SpaceMeta {
|
|
229
|
+
id: string;
|
|
230
|
+
doc_id: string;
|
|
231
|
+
name: string;
|
|
232
|
+
description: string | null;
|
|
233
|
+
visibility: "public" | "private" | "invite";
|
|
234
|
+
is_hub: boolean;
|
|
235
|
+
owner_id: string | null;
|
|
236
|
+
created_at: number;
|
|
237
|
+
updated_at: number;
|
|
238
|
+
}
|
|
239
|
+
|
|
226
240
|
// ── Invites ──────────────────────────────────────────────────────────────────
|
|
227
241
|
|
|
228
242
|
export interface InviteRow {
|