@dabble/patches 0.2.14 → 0.2.16
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/client/PatchesHistoryClient.d.ts +7 -10
- package/dist/client/PatchesHistoryClient.js +16 -5
- package/dist/net/protocol/JSONRPCClient.d.ts +2 -2
- package/dist/net/protocol/JSONRPCServer.d.ts +79 -0
- package/dist/net/protocol/JSONRPCServer.js +153 -0
- package/dist/net/protocol/types.d.ts +29 -27
- package/dist/net/webrtc/WebRTCTransport.d.ts +4 -4
- package/dist/net/webrtc/WebRTCTransport.js +1 -3
- package/dist/net/websocket/AuthorizationProvider.d.ts +37 -0
- package/dist/net/websocket/AuthorizationProvider.js +7 -0
- package/dist/net/websocket/PatchesWebSocket.d.ts +4 -4
- package/dist/net/websocket/PatchesWebSocket.js +3 -3
- package/dist/net/websocket/WebSocketServer.d.ts +144 -0
- package/dist/net/websocket/WebSocketServer.js +209 -0
- package/dist/net/websocket/WebSocketTransport.d.ts +24 -2
- package/dist/net/websocket/WebSocketTransport.js +31 -3
- package/dist/server/PatchesBranchManager.js +1 -1
- package/dist/server/PatchesHistoryManager.d.ts +26 -7
- package/dist/server/PatchesHistoryManager.js +38 -13
- package/dist/server/PatchesServer.d.ts +22 -46
- package/dist/server/PatchesServer.js +95 -131
- package/package.json +2 -2
- package/dist/net/AbstractTransport.d.ts +0 -47
- package/dist/net/AbstractTransport.js +0 -39
|
@@ -1,18 +1,11 @@
|
|
|
1
|
+
import type { PatchesAPI } from '../net/protocol/types.js';
|
|
1
2
|
import type { Change, ListVersionsOptions, VersionMetadata } from '../types.js';
|
|
2
|
-
/**
|
|
3
|
-
* Transport interface for fetching history data (WebSocket, REST, etc.)
|
|
4
|
-
*/
|
|
5
|
-
export interface HistoryTransport {
|
|
6
|
-
listVersions(docId: string, options?: ListVersionsOptions): Promise<VersionMetadata[]>;
|
|
7
|
-
getVersionState(docId: string, versionId: string): Promise<any>;
|
|
8
|
-
getVersionChanges(docId: string, versionId: string): Promise<Change[]>;
|
|
9
|
-
}
|
|
10
3
|
/**
|
|
11
4
|
* Client-side history/scrubbing interface for a document.
|
|
12
5
|
* Read-only: allows listing versions, loading states/changes, and scrubbing.
|
|
13
6
|
*/
|
|
14
7
|
export declare class PatchesHistoryClient<T = any> {
|
|
15
|
-
private readonly
|
|
8
|
+
private readonly api;
|
|
16
9
|
/** Document ID */
|
|
17
10
|
readonly id: string;
|
|
18
11
|
/** Event signal for versions changes */
|
|
@@ -22,13 +15,17 @@ export declare class PatchesHistoryClient<T = any> {
|
|
|
22
15
|
private _versions;
|
|
23
16
|
private _state;
|
|
24
17
|
private cache;
|
|
25
|
-
constructor(id: string,
|
|
18
|
+
constructor(id: string, api: PatchesAPI);
|
|
26
19
|
/** List of loaded versions */
|
|
27
20
|
get versions(): VersionMetadata[];
|
|
28
21
|
/** Current state (for scrubbing) */
|
|
29
22
|
get state(): any;
|
|
30
23
|
/** List version metadata for this document (with options) */
|
|
31
24
|
listVersions(options?: ListVersionsOptions): Promise<VersionMetadata[]>;
|
|
25
|
+
/** Create a new named version snapshot of the document's current state. */
|
|
26
|
+
createVersion(name: string): Promise<string>;
|
|
27
|
+
/** Update the name of a specific version. */
|
|
28
|
+
updateVersion(versionId: string, updates: Pick<VersionMetadata, 'name'>): Promise<void>;
|
|
32
29
|
/** Load the state for a specific version */
|
|
33
30
|
getStateAtVersion(versionId: string): Promise<any>;
|
|
34
31
|
/** Load the changes for a specific version */
|
|
@@ -36,8 +36,8 @@ class LRUCache {
|
|
|
36
36
|
* Read-only: allows listing versions, loading states/changes, and scrubbing.
|
|
37
37
|
*/
|
|
38
38
|
export class PatchesHistoryClient {
|
|
39
|
-
constructor(id,
|
|
40
|
-
this.
|
|
39
|
+
constructor(id, api) {
|
|
40
|
+
this.api = api;
|
|
41
41
|
/** Event signal for versions changes */
|
|
42
42
|
this.onVersionsChange = signal();
|
|
43
43
|
/** Event signal for state changes */
|
|
@@ -57,15 +57,26 @@ export class PatchesHistoryClient {
|
|
|
57
57
|
}
|
|
58
58
|
/** List version metadata for this document (with options) */
|
|
59
59
|
async listVersions(options) {
|
|
60
|
-
this._versions = await this.
|
|
60
|
+
this._versions = await this.api.listVersions(this.id, options);
|
|
61
61
|
this.onVersionsChange.emit(this._versions);
|
|
62
62
|
return this._versions;
|
|
63
63
|
}
|
|
64
|
+
/** Create a new named version snapshot of the document's current state. */
|
|
65
|
+
async createVersion(name) {
|
|
66
|
+
const versionId = await this.api.createVersion(this.id, name);
|
|
67
|
+
await this.listVersions(); // Refresh the list of versions
|
|
68
|
+
return versionId;
|
|
69
|
+
}
|
|
70
|
+
/** Update the name of a specific version. */
|
|
71
|
+
async updateVersion(versionId, updates) {
|
|
72
|
+
await this.api.updateVersion(this.id, versionId, updates);
|
|
73
|
+
await this.listVersions(); // Refresh the list of versions
|
|
74
|
+
}
|
|
64
75
|
/** Load the state for a specific version */
|
|
65
76
|
async getStateAtVersion(versionId) {
|
|
66
77
|
let data = this.cache.get(versionId);
|
|
67
78
|
if (!data || data.state === undefined) {
|
|
68
|
-
const state = await this.
|
|
79
|
+
const { state } = await this.api.getVersionState(this.id, versionId);
|
|
69
80
|
data = { ...data, state };
|
|
70
81
|
this.cache.set(versionId, data);
|
|
71
82
|
}
|
|
@@ -77,7 +88,7 @@ export class PatchesHistoryClient {
|
|
|
77
88
|
async getChangesForVersion(versionId) {
|
|
78
89
|
let data = this.cache.get(versionId);
|
|
79
90
|
if (!data || data.changes === undefined) {
|
|
80
|
-
const changes = await this.
|
|
91
|
+
const changes = await this.api.getVersionChanges(this.id, versionId);
|
|
81
92
|
data = { ...data, changes };
|
|
82
93
|
this.cache.set(versionId, data);
|
|
83
94
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type SignalSubscriber, type Unsubscriber } from '../../event-signal.js';
|
|
2
|
-
import type {
|
|
2
|
+
import type { ClientTransport } from './types.js';
|
|
3
3
|
/**
|
|
4
4
|
* Implementation of a JSON-RPC 2.0 client that communicates over a provided transport layer.
|
|
5
5
|
* This client handles sending requests, notifications, and processing responses from a server.
|
|
@@ -15,7 +15,7 @@ export declare class JSONRPCClient {
|
|
|
15
15
|
*
|
|
16
16
|
* @param transport - The transport layer implementation that will be used for sending/receiving messages
|
|
17
17
|
*/
|
|
18
|
-
constructor(transport:
|
|
18
|
+
constructor(transport: ClientTransport);
|
|
19
19
|
/**
|
|
20
20
|
* Sends a JSON-RPC request to the server and returns a promise for the response.
|
|
21
21
|
*
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { type Unsubscriber } from '../../event-signal.js';
|
|
2
|
+
import type { Notification, Request, ServerTransport } from './types.js';
|
|
3
|
+
export type ConnectionSignalSubscriber = (connectionId: string, ...args: any[]) => any;
|
|
4
|
+
export type MessageHandler<P = any, R = any> = (connectionId: string, params: P) => Promise<R> | R;
|
|
5
|
+
/**
|
|
6
|
+
* Lightweight JSON-RPC 2.0 server adapter for {@link PatchesServer}.
|
|
7
|
+
*
|
|
8
|
+
* The class is intentionally transport-agnostic: it only needs an object that
|
|
9
|
+
* fulfils the {@link Transport} contract (i.e. something that can exchange
|
|
10
|
+
* string messages and notify when one arrives). This makes it suitable for
|
|
11
|
+
* WebSocket, TCP, or even `postMessage` usage.
|
|
12
|
+
*
|
|
13
|
+
* A new instance is typically created per connected client. You therefore
|
|
14
|
+
* pass in:
|
|
15
|
+
* • the {@link Transport} that represents this client connection
|
|
16
|
+
* • a shared (singleton) {@link PatchesServer}
|
|
17
|
+
* • the unique `clientId` that identifies the connection in subscription
|
|
18
|
+
* calls. How you generate that ID (auth token, random GUID, etc.) is left
|
|
19
|
+
* to the host application.
|
|
20
|
+
*/
|
|
21
|
+
export declare class JSONRPCServer {
|
|
22
|
+
protected transport: ServerTransport;
|
|
23
|
+
/** Map of fully-qualified JSON-RPC method → handler function */
|
|
24
|
+
private readonly handlers;
|
|
25
|
+
/** Allow external callers to emit server-initiated notifications. */
|
|
26
|
+
private readonly notificationSignals;
|
|
27
|
+
constructor(transport: ServerTransport);
|
|
28
|
+
/**
|
|
29
|
+
* Registers a JSON-RPC method.
|
|
30
|
+
*
|
|
31
|
+
* @param method Fully-qualified method name (e.g. "patches.subscribe").
|
|
32
|
+
* @param handler Function that performs the work and returns the result.
|
|
33
|
+
*/
|
|
34
|
+
registerMethod<TParams = any, TResult = any>(method: string, handler: MessageHandler<TParams, TResult>): void;
|
|
35
|
+
/**
|
|
36
|
+
* Subscribes to server-sent notifications for a specific method.
|
|
37
|
+
*
|
|
38
|
+
* @param method - The notification method name to subscribe to
|
|
39
|
+
* @param handler - The callback function that will be invoked when notifications are received
|
|
40
|
+
* @returns A function that can be called to unsubscribe from the notifications
|
|
41
|
+
* @template T - The type of the handler function
|
|
42
|
+
*/
|
|
43
|
+
on<T extends ConnectionSignalSubscriber = ConnectionSignalSubscriber>(method: string, handler: T): Unsubscriber;
|
|
44
|
+
/**
|
|
45
|
+
* Sends a JSON-RPC notification (no `id`, therefore no response expected) to
|
|
46
|
+
* the connected client.
|
|
47
|
+
*/
|
|
48
|
+
notify(connectionIds: string[], method: string, params?: any): void;
|
|
49
|
+
/**
|
|
50
|
+
* Handles incoming messages from the client.
|
|
51
|
+
* @param connectionId - The WebSocket transport object.
|
|
52
|
+
* @param raw - The raw message string.
|
|
53
|
+
*/
|
|
54
|
+
protected _onMessage(connectionId: string, raw: string): Promise<void>;
|
|
55
|
+
/**
|
|
56
|
+
* Handles incoming JSON-RPC requests from the client.
|
|
57
|
+
* @param connectionId - The WebSocket transport object.
|
|
58
|
+
* @param req - The JSON-RPC request object.
|
|
59
|
+
*/
|
|
60
|
+
protected _handleRequest(connectionId: string, req: Request): Promise<void>;
|
|
61
|
+
/**
|
|
62
|
+
* Handles incoming JSON-RPC notifications from the client.
|
|
63
|
+
* @param connectionId - The WebSocket transport object.
|
|
64
|
+
* @param note - The JSON-RPC notification object.
|
|
65
|
+
*/
|
|
66
|
+
protected _handleNotification(connectionId: string, note: Notification): Promise<void>;
|
|
67
|
+
/**
|
|
68
|
+
* Maps JSON-RPC method names to {@link PatchesServer} calls.
|
|
69
|
+
* @param connectionId - The WebSocket transport object.
|
|
70
|
+
* @param method - The JSON-RPC method name.
|
|
71
|
+
* @param params - The JSON-RPC parameters.
|
|
72
|
+
* @returns The result of the {@link PatchesServer} call.
|
|
73
|
+
*/
|
|
74
|
+
protected _dispatch(connectionId: string, method: string, params: any): Promise<any>;
|
|
75
|
+
/**
|
|
76
|
+
* Sends a JSON-RPC error object back to the client.
|
|
77
|
+
*/
|
|
78
|
+
private _sendError;
|
|
79
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { signal } from '../../event-signal.js';
|
|
2
|
+
import { PatchesServer } from '../../server/PatchesServer.js';
|
|
3
|
+
/**
|
|
4
|
+
* Lightweight JSON-RPC 2.0 server adapter for {@link PatchesServer}.
|
|
5
|
+
*
|
|
6
|
+
* The class is intentionally transport-agnostic: it only needs an object that
|
|
7
|
+
* fulfils the {@link Transport} contract (i.e. something that can exchange
|
|
8
|
+
* string messages and notify when one arrives). This makes it suitable for
|
|
9
|
+
* WebSocket, TCP, or even `postMessage` usage.
|
|
10
|
+
*
|
|
11
|
+
* A new instance is typically created per connected client. You therefore
|
|
12
|
+
* pass in:
|
|
13
|
+
* • the {@link Transport} that represents this client connection
|
|
14
|
+
* • a shared (singleton) {@link PatchesServer}
|
|
15
|
+
* • the unique `clientId` that identifies the connection in subscription
|
|
16
|
+
* calls. How you generate that ID (auth token, random GUID, etc.) is left
|
|
17
|
+
* to the host application.
|
|
18
|
+
*/
|
|
19
|
+
export class JSONRPCServer {
|
|
20
|
+
constructor(transport) {
|
|
21
|
+
this.transport = transport;
|
|
22
|
+
/** Map of fully-qualified JSON-RPC method → handler function */
|
|
23
|
+
this.handlers = new Map();
|
|
24
|
+
/** Allow external callers to emit server-initiated notifications. */
|
|
25
|
+
this.notificationSignals = new Map();
|
|
26
|
+
transport.onMessage(this._onMessage.bind(this));
|
|
27
|
+
}
|
|
28
|
+
// -------------------------------------------------------------------------
|
|
29
|
+
// Registration API
|
|
30
|
+
// -------------------------------------------------------------------------
|
|
31
|
+
/**
|
|
32
|
+
* Registers a JSON-RPC method.
|
|
33
|
+
*
|
|
34
|
+
* @param method Fully-qualified method name (e.g. "patches.subscribe").
|
|
35
|
+
* @param handler Function that performs the work and returns the result.
|
|
36
|
+
*/
|
|
37
|
+
registerMethod(method, handler) {
|
|
38
|
+
if (this.handlers.has(method)) {
|
|
39
|
+
throw new Error(`A handler for method '${method}' is already registered.`);
|
|
40
|
+
}
|
|
41
|
+
this.handlers.set(method, handler);
|
|
42
|
+
}
|
|
43
|
+
// -------------------------------------------------------------------------
|
|
44
|
+
// Public helpers
|
|
45
|
+
// -------------------------------------------------------------------------
|
|
46
|
+
/**
|
|
47
|
+
* Subscribes to server-sent notifications for a specific method.
|
|
48
|
+
*
|
|
49
|
+
* @param method - The notification method name to subscribe to
|
|
50
|
+
* @param handler - The callback function that will be invoked when notifications are received
|
|
51
|
+
* @returns A function that can be called to unsubscribe from the notifications
|
|
52
|
+
* @template T - The type of the handler function
|
|
53
|
+
*/
|
|
54
|
+
on(method, handler) {
|
|
55
|
+
let thisSignal = this.notificationSignals.get(method);
|
|
56
|
+
if (!thisSignal) {
|
|
57
|
+
thisSignal = signal();
|
|
58
|
+
this.notificationSignals.set(method, thisSignal);
|
|
59
|
+
}
|
|
60
|
+
return thisSignal(handler);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Sends a JSON-RPC notification (no `id`, therefore no response expected) to
|
|
64
|
+
* the connected client.
|
|
65
|
+
*/
|
|
66
|
+
notify(connectionIds, method, params) {
|
|
67
|
+
const msg = { jsonrpc: '2.0', method, params };
|
|
68
|
+
const msgStr = JSON.stringify(msg);
|
|
69
|
+
connectionIds.forEach(id => this.transport.send(id, msgStr));
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Handles incoming messages from the client.
|
|
73
|
+
* @param connectionId - The WebSocket transport object.
|
|
74
|
+
* @param raw - The raw message string.
|
|
75
|
+
*/
|
|
76
|
+
async _onMessage(connectionId, raw) {
|
|
77
|
+
let message;
|
|
78
|
+
try {
|
|
79
|
+
message = JSON.parse(raw);
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
this._sendError(connectionId, null, -32700, 'Parse error', err);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
if (message && typeof message === 'object' && 'method' in message) {
|
|
86
|
+
// Notification or request either way--delegate.
|
|
87
|
+
if ('id' in message && message.id !== undefined) {
|
|
88
|
+
await this._handleRequest(connectionId, message);
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
await this._handleNotification(connectionId, message);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
// Client sent something we do not understand.
|
|
96
|
+
this._sendError(connectionId, message?.id ?? null, -32600, 'Invalid Request');
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Handles incoming JSON-RPC requests from the client.
|
|
101
|
+
* @param connectionId - The WebSocket transport object.
|
|
102
|
+
* @param req - The JSON-RPC request object.
|
|
103
|
+
*/
|
|
104
|
+
async _handleRequest(connectionId, req) {
|
|
105
|
+
try {
|
|
106
|
+
const result = await this._dispatch(connectionId, req.method, req.params);
|
|
107
|
+
const response = { jsonrpc: '2.0', id: req.id, result };
|
|
108
|
+
this.transport.send(connectionId, JSON.stringify(response));
|
|
109
|
+
}
|
|
110
|
+
catch (err) {
|
|
111
|
+
this._sendError(connectionId, req.id, -32000, err?.message ?? 'Server error', err?.stack);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Handles incoming JSON-RPC notifications from the client.
|
|
116
|
+
* @param connectionId - The WebSocket transport object.
|
|
117
|
+
* @param note - The JSON-RPC notification object.
|
|
118
|
+
*/
|
|
119
|
+
async _handleNotification(connectionId, note) {
|
|
120
|
+
const thisSignal = this.notificationSignals.get(note.method);
|
|
121
|
+
if (thisSignal) {
|
|
122
|
+
thisSignal.emit(connectionId, note.params);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Maps JSON-RPC method names to {@link PatchesServer} calls.
|
|
127
|
+
* @param connectionId - The WebSocket transport object.
|
|
128
|
+
* @param method - The JSON-RPC method name.
|
|
129
|
+
* @param params - The JSON-RPC parameters.
|
|
130
|
+
* @returns The result of the {@link PatchesServer} call.
|
|
131
|
+
*/
|
|
132
|
+
async _dispatch(connectionId, method, params) {
|
|
133
|
+
const handler = this.handlers.get(method);
|
|
134
|
+
if (!handler) {
|
|
135
|
+
throw new Error(`Unknown method '${method}'.`);
|
|
136
|
+
}
|
|
137
|
+
if (!params || typeof params !== 'object' || Array.isArray(params)) {
|
|
138
|
+
throw new Error(`Invalid parameters for method '${method}'.`);
|
|
139
|
+
}
|
|
140
|
+
return handler(connectionId, params);
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Sends a JSON-RPC error object back to the client.
|
|
144
|
+
*/
|
|
145
|
+
_sendError(connectionId, id, code, message, data) {
|
|
146
|
+
const errorObj = {
|
|
147
|
+
jsonrpc: '2.0',
|
|
148
|
+
id: id,
|
|
149
|
+
error: { code, message, data },
|
|
150
|
+
}; // type cast because TS cannot narrow when error present
|
|
151
|
+
this.transport.send(connectionId, JSON.stringify(errorObj));
|
|
152
|
+
}
|
|
153
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Unsubscriber } from '../../event-signal.js';
|
|
2
|
+
import type { Change, ListVersionsOptions, PatchesSnapshot, PatchesState, VersionMetadata } from '../../types';
|
|
2
3
|
/**
|
|
3
4
|
* Represents the possible states of a network transport connection.
|
|
4
5
|
* - 'connecting': Connection is being established
|
|
@@ -8,35 +9,36 @@ import type { Change, PatchesSnapshot, PatchesState, VersionMetadata } from '../
|
|
|
8
9
|
*/
|
|
9
10
|
export type ConnectionState = 'connecting' | 'connected' | 'disconnected' | 'error';
|
|
10
11
|
/**
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
12
|
+
* Minimal contract that the JSON-RPC layer (and therefore Patches core) relies on.
|
|
13
|
+
* A transport only needs the ability to **send** raw strings and **deliver** raw
|
|
14
|
+
* strings it receives from the other side.
|
|
15
|
+
*
|
|
16
|
+
* Anything beyond that (connect/disconnect lifecycle, connection state, etc.) is
|
|
17
|
+
* not needed or handled by Patches and so is not defined here.
|
|
14
18
|
*/
|
|
15
|
-
export interface
|
|
19
|
+
export interface ClientTransport {
|
|
16
20
|
/**
|
|
17
|
-
*
|
|
18
|
-
* @returns A promise that resolves when the connection is established
|
|
21
|
+
* Sends a raw, already-encoded message over the wire.
|
|
19
22
|
*/
|
|
20
|
-
|
|
23
|
+
send(raw: string): void | Promise<void>;
|
|
21
24
|
/**
|
|
22
|
-
*
|
|
25
|
+
* Subscribes to incoming raw messages. Returns an {@link Unsubscriber} so the
|
|
26
|
+
* caller can remove the handler if it no longer cares about incoming data.
|
|
23
27
|
*/
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
*/
|
|
39
|
-
onStateChange(handler: (state: ConnectionState) => void): void;
|
|
28
|
+
onMessage(cb: (raw: string) => void): Unsubscriber;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Minimal contract for server-side transports that can have **multiple** logical peers.
|
|
32
|
+
* Each message must indicate the **from / to** connection. Any additional lifecycle
|
|
33
|
+
* management (upgrade, close, etc.) stays inside the concrete adapter.
|
|
34
|
+
*/
|
|
35
|
+
export interface ServerTransport {
|
|
36
|
+
/** Get a list of all active connection IDs */
|
|
37
|
+
getConnectionIds(): string[];
|
|
38
|
+
/** Send a raw JSON-RPC string to a specific connection */
|
|
39
|
+
send(toConnectionId: string, raw: string): void | Promise<void>;
|
|
40
|
+
/** Subscribe to incoming raw frames from any client */
|
|
41
|
+
onMessage(cb: (fromConnectionId: string, raw: string) => void): Unsubscriber;
|
|
40
42
|
}
|
|
41
43
|
/**
|
|
42
44
|
* Represents a JSON-RPC 2.0 request object.
|
|
@@ -117,13 +119,13 @@ export interface PatchesAPI {
|
|
|
117
119
|
/** Create a new named version snapshot of a document's current state. */
|
|
118
120
|
createVersion(docId: string, name: string): Promise<string>;
|
|
119
121
|
/** List metadata for saved versions of a document. */
|
|
120
|
-
listVersions(docId: string, options
|
|
122
|
+
listVersions(docId: string, options?: ListVersionsOptions): Promise<VersionMetadata[]>;
|
|
121
123
|
/** Get the state snapshot for a specific version ID. */
|
|
122
124
|
getVersionState(docId: string, versionId: string): Promise<PatchesState>;
|
|
123
125
|
/** Get the original Change objects associated with a specific version ID. */
|
|
124
126
|
getVersionChanges(docId: string, versionId: string): Promise<Change[]>;
|
|
125
127
|
/** Update the name of a specific version. */
|
|
126
|
-
updateVersion(docId: string, versionId: string,
|
|
128
|
+
updateVersion(docId: string, versionId: string, updates: Pick<VersionMetadata, 'name'>): Promise<void>;
|
|
127
129
|
}
|
|
128
130
|
export interface PatchesNotificationParams {
|
|
129
131
|
docId: string;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import Peer from 'simple-peer';
|
|
2
|
-
import {
|
|
2
|
+
import type { ClientTransport } from '../protocol/types.js';
|
|
3
3
|
import type { WebSocketTransport } from '../websocket/WebSocketTransport.js';
|
|
4
4
|
/**
|
|
5
5
|
* WebRTC-based transport implementation that enables direct peer-to-peer communication.
|
|
6
6
|
* Uses a WebSocket transport as a signaling channel to establish WebRTC connections.
|
|
7
7
|
* Once connections are established, data flows directly between peers without going through a server.
|
|
8
8
|
*/
|
|
9
|
-
export declare class WebRTCTransport
|
|
9
|
+
export declare class WebRTCTransport implements ClientTransport {
|
|
10
10
|
private transport;
|
|
11
11
|
private rpc;
|
|
12
12
|
private peers;
|
|
@@ -31,7 +31,7 @@ export declare class WebRTCTransport extends AbstractTransport {
|
|
|
31
31
|
* Signal that emits when the underlying signaling transport's state changes.
|
|
32
32
|
* This is delegated directly from the WebSocketTransport.
|
|
33
33
|
*/
|
|
34
|
-
readonly onStateChange: import("../../event-signal.js").Signal<(state: import("../
|
|
34
|
+
readonly onStateChange: import("../../event-signal.js").Signal<(state: import("../protocol/types.js").ConnectionState) => void>;
|
|
35
35
|
/**
|
|
36
36
|
* Creates a new WebRTC transport instance.
|
|
37
37
|
* @param transport - The WebSocket transport to use for signaling
|
|
@@ -46,7 +46,7 @@ export declare class WebRTCTransport extends AbstractTransport {
|
|
|
46
46
|
* Gets the current connection state of the underlying signaling transport.
|
|
47
47
|
* @returns The current connection state
|
|
48
48
|
*/
|
|
49
|
-
get state(): import("../
|
|
49
|
+
get state(): import("../protocol/types.js").ConnectionState;
|
|
50
50
|
/**
|
|
51
51
|
* Establishes the connection by connecting to the signaling server.
|
|
52
52
|
* Peer connections will be established automatically after this.
|
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
import Peer from 'simple-peer';
|
|
2
2
|
import { signal } from '../../event-signal.js';
|
|
3
3
|
import { JSONRPCClient } from '../../net/protocol/JSONRPCClient.js';
|
|
4
|
-
import { AbstractTransport } from '../AbstractTransport.js';
|
|
5
4
|
/**
|
|
6
5
|
* WebRTC-based transport implementation that enables direct peer-to-peer communication.
|
|
7
6
|
* Uses a WebSocket transport as a signaling channel to establish WebRTC connections.
|
|
8
7
|
* Once connections are established, data flows directly between peers without going through a server.
|
|
9
8
|
*/
|
|
10
|
-
export class WebRTCTransport
|
|
9
|
+
export class WebRTCTransport {
|
|
11
10
|
/**
|
|
12
11
|
* Creates a new WebRTC transport instance.
|
|
13
12
|
* @param transport - The WebSocket transport to use for signaling
|
|
14
13
|
*/
|
|
15
14
|
constructor(transport) {
|
|
16
|
-
super();
|
|
17
15
|
this.transport = transport;
|
|
18
16
|
this.peers = new Map();
|
|
19
17
|
/**
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Access level requested for an operation.
|
|
3
|
+
*
|
|
4
|
+
* "read" – non-mutating operations (subscribe, getDoc, listVersions, …)
|
|
5
|
+
* "write" – mutating operations (commitChanges, deleteDoc, …)
|
|
6
|
+
*/
|
|
7
|
+
export type Access = 'read' | 'write';
|
|
8
|
+
/**
|
|
9
|
+
* Allows the host application to decide whether a given connection can perform
|
|
10
|
+
* a certain action on a document. Implementations are entirely application-
|
|
11
|
+
* specific – they may look at a JWT decoded during the WebSocket handshake,
|
|
12
|
+
* consult an ACL service, inspect the actual RPC method, etc.
|
|
13
|
+
*/
|
|
14
|
+
export interface AuthorizationProvider {
|
|
15
|
+
/**
|
|
16
|
+
* General-purpose hook executed for every JSON-RPC call that targets a
|
|
17
|
+
* document. Implementations are free to look only at the first three
|
|
18
|
+
* arguments (connection, docId, kind) or also inspect the method name and
|
|
19
|
+
* original params for fine-grained rules (e.g. suggestion-only commits,
|
|
20
|
+
* owner-only deletes, branch permissions, …).
|
|
21
|
+
*
|
|
22
|
+
* Returning `true` (or a resolved promise with `true`) permits the action.
|
|
23
|
+
* Returning `false` or throwing will cause the RPC to fail with an error.
|
|
24
|
+
*
|
|
25
|
+
* @param connectionId WebSocket connection ID of the caller
|
|
26
|
+
* @param docId Logical document to be accessed (branch IDs count)
|
|
27
|
+
* @param kind High-level access category – `'read' | 'write'`
|
|
28
|
+
* @param method JSON-RPC method name (e.g. `getDoc`, `branch.merge`)
|
|
29
|
+
* @param params The exact parameter object supplied by the client
|
|
30
|
+
*/
|
|
31
|
+
canAccess(connectionId: string, docId: string, kind: Access, method: string, params?: Record<string, any>): boolean | Promise<boolean>;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* A permissive provider that authorises every action. Used as the default so
|
|
35
|
+
* existing callers that don't care about auth continue to work unchanged.
|
|
36
|
+
*/
|
|
37
|
+
export declare const allowAll: AuthorizationProvider;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type Signal } from '../../event-signal.js';
|
|
2
|
-
import type { Change, PatchesSnapshot, VersionMetadata } from '../../types.js';
|
|
3
|
-
import type { ConnectionState,
|
|
2
|
+
import type { Change, ListVersionsOptions, PatchesSnapshot, VersionMetadata } from '../../types.js';
|
|
3
|
+
import type { ConnectionState, PatchesAPI, PatchesNotificationParams } from '../protocol/types.js';
|
|
4
4
|
import { type WebSocketOptions } from './WebSocketTransport.js';
|
|
5
5
|
/**
|
|
6
6
|
* High-level client for the Patches real-time collaboration service.
|
|
@@ -81,7 +81,7 @@ export declare class PatchesWebSocket implements PatchesAPI {
|
|
|
81
81
|
* @param options - Options for filtering or pagination (e.g., limit, offset).
|
|
82
82
|
* @returns A promise resolving with an array of version metadata objects.
|
|
83
83
|
*/
|
|
84
|
-
listVersions(docId: string, options?:
|
|
84
|
+
listVersions(docId: string, options?: ListVersionsOptions): Promise<VersionMetadata[]>;
|
|
85
85
|
/**
|
|
86
86
|
* Gets the document state snapshot corresponding to a specific version ID.
|
|
87
87
|
* @param docId - The ID of the document.
|
|
@@ -103,5 +103,5 @@ export declare class PatchesWebSocket implements PatchesAPI {
|
|
|
103
103
|
* @param name - The new name for the version.
|
|
104
104
|
* @returns A promise resolving when the update is confirmed.
|
|
105
105
|
*/
|
|
106
|
-
updateVersion(docId: string, versionId: string,
|
|
106
|
+
updateVersion(docId: string, versionId: string, updates: Pick<VersionMetadata, 'name'>): Promise<void>;
|
|
107
107
|
}
|
|
@@ -110,7 +110,7 @@ export class PatchesWebSocket {
|
|
|
110
110
|
* @param options - Options for filtering or pagination (e.g., limit, offset).
|
|
111
111
|
* @returns A promise resolving with an array of version metadata objects.
|
|
112
112
|
*/
|
|
113
|
-
async listVersions(docId, options
|
|
113
|
+
async listVersions(docId, options) {
|
|
114
114
|
return this.rpc.request('listVersions', { docId, options });
|
|
115
115
|
}
|
|
116
116
|
/**
|
|
@@ -138,7 +138,7 @@ export class PatchesWebSocket {
|
|
|
138
138
|
* @param name - The new name for the version.
|
|
139
139
|
* @returns A promise resolving when the update is confirmed.
|
|
140
140
|
*/
|
|
141
|
-
async updateVersion(docId, versionId,
|
|
142
|
-
return this.rpc.request('updateVersion', { docId, versionId,
|
|
141
|
+
async updateVersion(docId, versionId, updates) {
|
|
142
|
+
return this.rpc.request('updateVersion', { docId, versionId, updates });
|
|
143
143
|
}
|
|
144
144
|
}
|