@dabble/patches 0.2.32 → 0.3.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.
- package/dist/algorithms/client/applyCommittedChanges.d.ts +8 -0
- package/dist/algorithms/client/applyCommittedChanges.js +40 -0
- package/dist/{utils → algorithms/client}/batching.d.ts +1 -1
- package/dist/{utils → algorithms/client}/batching.js +2 -2
- package/dist/{utils → algorithms/client}/breakChange.d.ts +2 -3
- package/dist/algorithms/client/breakChange.js +258 -0
- package/dist/algorithms/client/createStateFromSnapshot.d.ts +7 -0
- package/dist/algorithms/client/createStateFromSnapshot.js +9 -0
- package/dist/algorithms/client/getJSONByteSize.js +12 -0
- package/dist/algorithms/client/makeChange.d.ts +3 -0
- package/dist/algorithms/client/makeChange.js +37 -0
- package/dist/algorithms/server/commitChanges.d.ts +12 -0
- package/dist/algorithms/server/commitChanges.js +80 -0
- package/dist/algorithms/server/createVersion.d.ts +12 -0
- package/dist/algorithms/server/createVersion.js +28 -0
- package/dist/algorithms/server/getSnapshotAtRevision.d.ts +10 -0
- package/dist/algorithms/server/getSnapshotAtRevision.js +29 -0
- package/dist/algorithms/server/getStateAtRevision.d.ts +9 -0
- package/dist/algorithms/server/getStateAtRevision.js +18 -0
- package/dist/algorithms/server/handleOfflineSessionsAndBatches.d.ts +12 -0
- package/dist/algorithms/server/handleOfflineSessionsAndBatches.js +80 -0
- package/dist/algorithms/server/transformIncomingChanges.d.ts +11 -0
- package/dist/algorithms/server/transformIncomingChanges.js +40 -0
- package/dist/algorithms/shared/applyChanges.d.ts +10 -0
- package/dist/algorithms/shared/applyChanges.js +17 -0
- package/dist/{utils.d.ts → algorithms/shared/rebaseChanges.d.ts} +1 -11
- package/dist/{utils.js → algorithms/shared/rebaseChanges.js} +3 -43
- package/dist/client/InMemoryStore.d.ts +2 -1
- package/dist/client/InMemoryStore.js +9 -3
- package/dist/client/IndexedDBStore.d.ts +34 -2
- package/dist/client/IndexedDBStore.js +399 -282
- package/dist/client/Patches.d.ts +11 -41
- package/dist/client/Patches.js +197 -208
- package/dist/client/PatchesDoc.d.ts +24 -41
- package/dist/client/PatchesDoc.js +57 -214
- package/dist/client/PatchesHistoryClient.js +1 -1
- package/dist/client/PatchesStore.d.ts +186 -9
- package/dist/data/change.d.ts +3 -0
- package/dist/data/change.js +20 -0
- package/dist/data/version.d.ts +12 -0
- package/dist/data/version.js +17 -0
- package/dist/json-patch/ops/add.js +1 -1
- package/dist/json-patch/ops/move.js +1 -1
- package/dist/json-patch/ops/remove.js +1 -1
- package/dist/json-patch/ops/replace.js +1 -1
- package/dist/json-patch/utils/get.js +0 -1
- package/dist/json-patch/utils/log.d.ts +4 -1
- package/dist/json-patch/utils/log.js +2 -5
- package/dist/json-patch/utils/ops.d.ts +1 -1
- package/dist/json-patch/utils/ops.js +4 -1
- package/dist/json-patch/utils/paths.js +2 -2
- package/dist/json-patch/utils/toArrayIndex.js +1 -1
- package/dist/net/PatchesSync.d.ts +55 -24
- package/dist/net/PatchesSync.js +336 -258
- package/dist/net/protocol/types.d.ts +1 -1
- package/dist/net/websocket/AuthorizationProvider.d.ts +9 -2
- package/dist/net/websocket/AuthorizationProvider.js +14 -2
- package/dist/net/websocket/PatchesWebSocket.d.ts +2 -2
- package/dist/net/websocket/PatchesWebSocket.js +3 -2
- package/dist/net/websocket/RPCServer.d.ts +2 -2
- package/dist/net/websocket/RPCServer.js +3 -3
- package/dist/net/websocket/SignalingService.js +1 -1
- package/dist/net/websocket/WebSocketServer.d.ts +1 -1
- package/dist/net/websocket/WebSocketServer.js +2 -2
- package/dist/net/websocket/WebSocketTransport.js +1 -1
- package/dist/net/websocket/onlineState.d.ts +2 -2
- package/dist/net/websocket/onlineState.js +9 -3
- package/dist/server/PatchesBranchManager.js +9 -16
- package/dist/server/PatchesHistoryManager.js +1 -1
- package/dist/server/PatchesServer.d.ts +11 -38
- package/dist/server/PatchesServer.js +32 -255
- package/dist/server/index.d.ts +4 -4
- package/dist/server/index.js +3 -3
- package/dist/server/types.d.ts +1 -1
- package/dist/types.d.ts +8 -6
- package/dist/utils/concurrency.d.ts +26 -0
- package/dist/utils/concurrency.js +60 -0
- package/dist/utils/deferred.d.ts +7 -0
- package/dist/utils/deferred.js +23 -0
- package/package.json +11 -5
- package/dist/utils/breakChange.js +0 -302
- package/dist/utils/getJSONByteSize.js +0 -12
- /package/dist/{utils → algorithms/client}/getJSONByteSize.d.ts +0 -0
|
@@ -40,7 +40,14 @@ export interface AuthorizationProvider<T extends AuthContext = AuthContext> {
|
|
|
40
40
|
canAccess(ctx: T | undefined, docId: string, kind: Access, method: string, params?: Record<string, any>): boolean | Promise<boolean>;
|
|
41
41
|
}
|
|
42
42
|
/**
|
|
43
|
-
* A permissive provider that authorises every action.
|
|
44
|
-
*
|
|
43
|
+
* A permissive provider that authorises every action.
|
|
44
|
+
* WARNING: This should only be used for development/testing purposes.
|
|
45
|
+
* Never use this in production as it allows unrestricted access.
|
|
45
46
|
*/
|
|
46
47
|
export declare const allowAll: AuthorizationProvider;
|
|
48
|
+
/**
|
|
49
|
+
* A secure default provider that denies all access.
|
|
50
|
+
* This forces developers to explicitly implement proper authorization.
|
|
51
|
+
* Use this as the default to ensure security by default.
|
|
52
|
+
*/
|
|
53
|
+
export declare const denyAll: AuthorizationProvider;
|
|
@@ -1,7 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* A permissive provider that authorises every action.
|
|
3
|
-
*
|
|
2
|
+
* A permissive provider that authorises every action.
|
|
3
|
+
* WARNING: This should only be used for development/testing purposes.
|
|
4
|
+
* Never use this in production as it allows unrestricted access.
|
|
4
5
|
*/
|
|
5
6
|
export const allowAll = {
|
|
6
7
|
canAccess: () => true,
|
|
7
8
|
};
|
|
9
|
+
/**
|
|
10
|
+
* A secure default provider that denies all access.
|
|
11
|
+
* This forces developers to explicitly implement proper authorization.
|
|
12
|
+
* Use this as the default to ensure security by default.
|
|
13
|
+
*/
|
|
14
|
+
export const denyAll = {
|
|
15
|
+
canAccess: () => {
|
|
16
|
+
console.warn('Authorization check failed: No authorization provider configured. Access denied.');
|
|
17
|
+
return false;
|
|
18
|
+
},
|
|
19
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type Signal } from '../../event-signal.js';
|
|
2
2
|
import type { Change, EditableVersionMetadata, ListVersionsOptions, PatchesSnapshot, PatchesState, VersionMetadata } from '../../types.js';
|
|
3
|
-
import type { ConnectionState, PatchesAPI
|
|
3
|
+
import type { ConnectionState, PatchesAPI } 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.
|
|
@@ -14,7 +14,7 @@ export declare class PatchesWebSocket implements PatchesAPI {
|
|
|
14
14
|
/** Signal emitted when the underlying WebSocket connection state changes. */
|
|
15
15
|
readonly onStateChange: Signal<(state: ConnectionState) => void>;
|
|
16
16
|
/** Signal emitted when the server pushes document changes. */
|
|
17
|
-
readonly onChangesCommitted: Signal<(
|
|
17
|
+
readonly onChangesCommitted: Signal<(docId: string, changes: Change[]) => void>;
|
|
18
18
|
/**
|
|
19
19
|
* Creates a new Patches WebSocket client instance.
|
|
20
20
|
* @param url - The WebSocket server URL to connect to
|
|
@@ -21,8 +21,9 @@ export class PatchesWebSocket {
|
|
|
21
21
|
this.onStateChange = this.transport.onStateChange;
|
|
22
22
|
// Register handlers for server-sent notifications
|
|
23
23
|
// Note: Type assertions might be needed if rpc.on doesn't infer strongly enough
|
|
24
|
-
this.rpc.on('changesCommitted', (params
|
|
25
|
-
|
|
24
|
+
this.rpc.on('changesCommitted', (params) => {
|
|
25
|
+
const { docId, changes } = params;
|
|
26
|
+
this.onChangesCommitted.emit(docId, changes);
|
|
26
27
|
});
|
|
27
28
|
}
|
|
28
29
|
// --- Connection Management ---
|
|
@@ -3,7 +3,7 @@ import type { PatchesHistoryManager } from '../../server/PatchesHistoryManager.j
|
|
|
3
3
|
import type { PatchesServer } from '../../server/PatchesServer.js';
|
|
4
4
|
import type { Change, EditableVersionMetadata, ListChangesOptions, ListVersionsOptions } from '../../types.js';
|
|
5
5
|
import { JSONRPCServer } from '../protocol/JSONRPCServer.js';
|
|
6
|
-
import type
|
|
6
|
+
import { type AuthContext, type AuthorizationProvider } from './AuthorizationProvider.js';
|
|
7
7
|
/**
|
|
8
8
|
* High-level client for the Patches real-time collaboration service.
|
|
9
9
|
* This class provides document subscription, patch notification handling,
|
|
@@ -26,7 +26,7 @@ export declare class RPCServer {
|
|
|
26
26
|
* @param patches - The patches server instance to handle document operations
|
|
27
27
|
* @param history - (Optional) History manager instance to handle versioning operations
|
|
28
28
|
* @param branches - (Optional) Branch manager instance to handle branching operations
|
|
29
|
-
* @param auth - (Optional) Authorization provider implementation. Defaults to
|
|
29
|
+
* @param auth - (Optional) Authorization provider implementation. Defaults to deny-all for security.
|
|
30
30
|
*/
|
|
31
31
|
constructor({ patches, history, branches, auth }: RPCServerOptions);
|
|
32
32
|
/**
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { StatusError } from '../error.js';
|
|
2
2
|
import { JSONRPCServer } from '../protocol/JSONRPCServer.js';
|
|
3
|
-
import {
|
|
3
|
+
import { denyAll } from './AuthorizationProvider.js';
|
|
4
4
|
export class RPCServer {
|
|
5
5
|
/**
|
|
6
6
|
* Creates a new Patches WebSocket client instance.
|
|
7
7
|
* @param patches - The patches server instance to handle document operations
|
|
8
8
|
* @param history - (Optional) History manager instance to handle versioning operations
|
|
9
9
|
* @param branches - (Optional) Branch manager instance to handle branching operations
|
|
10
|
-
* @param auth - (Optional) Authorization provider implementation. Defaults to
|
|
10
|
+
* @param auth - (Optional) Authorization provider implementation. Defaults to deny-all for security.
|
|
11
11
|
*/
|
|
12
|
-
constructor({ patches, history, branches, auth =
|
|
12
|
+
constructor({ patches, history, branches, auth = denyAll }) {
|
|
13
13
|
this.rpc = new JSONRPCServer();
|
|
14
14
|
this.patches = patches;
|
|
15
15
|
this.history = history;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ServerTransport } from '../protocol/types.js';
|
|
2
|
-
import type
|
|
2
|
+
import { type AuthContext, type AuthorizationProvider } from './AuthorizationProvider.js';
|
|
3
3
|
import type { RPCServer } from './RPCServer.js';
|
|
4
4
|
/**
|
|
5
5
|
* High-level client for the Patches real-time collaboration service.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { denyAll } from './AuthorizationProvider.js';
|
|
2
2
|
/**
|
|
3
3
|
* High-level client for the Patches real-time collaboration service.
|
|
4
4
|
* This class provides document subscription, patch notification handling,
|
|
@@ -13,7 +13,7 @@ export class WebSocketServer {
|
|
|
13
13
|
constructor(transport, rpcServer) {
|
|
14
14
|
this.transport = transport;
|
|
15
15
|
const { rpc, auth } = rpcServer;
|
|
16
|
-
this.auth = auth ||
|
|
16
|
+
this.auth = auth || denyAll;
|
|
17
17
|
// Subscription operations
|
|
18
18
|
rpc.registerMethod('subscribe', this.subscribe.bind(this));
|
|
19
19
|
rpc.registerMethod('unsubscribe', this.unsubscribe.bind(this));
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { signal } from '../../event-signal.js';
|
|
2
|
-
import { deferred } from '../../utils.js';
|
|
2
|
+
import { deferred } from '../../utils/deferred.js';
|
|
3
3
|
import { onlineState } from './onlineState.js';
|
|
4
4
|
/**
|
|
5
5
|
* WebSocket-based transport implementation that provides communication over the WebSocket protocol.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
declare class OnlineState {
|
|
2
|
-
onOnlineChange: import("../../event-signal").Signal<(isOnline: boolean) => void>;
|
|
3
|
-
_isOnline: boolean;
|
|
2
|
+
onOnlineChange: import("../../event-signal.js").Signal<(isOnline: boolean) => void>;
|
|
3
|
+
protected _isOnline: boolean;
|
|
4
4
|
constructor();
|
|
5
5
|
get isOnline(): boolean;
|
|
6
6
|
get isOffline(): boolean;
|
|
@@ -1,11 +1,17 @@
|
|
|
1
|
-
import { signal } from '../../event-signal';
|
|
1
|
+
import { signal } from '../../event-signal.js';
|
|
2
2
|
class OnlineState {
|
|
3
3
|
constructor() {
|
|
4
4
|
this.onOnlineChange = signal();
|
|
5
5
|
this._isOnline = typeof navigator !== 'undefined' && navigator.onLine;
|
|
6
6
|
if (typeof addEventListener === 'function') {
|
|
7
|
-
addEventListener('online', () =>
|
|
8
|
-
|
|
7
|
+
addEventListener('online', () => {
|
|
8
|
+
this._isOnline = true;
|
|
9
|
+
this.onOnlineChange.emit(true);
|
|
10
|
+
});
|
|
11
|
+
addEventListener('offline', () => {
|
|
12
|
+
this._isOnline = false;
|
|
13
|
+
this.onOnlineChange.emit(false);
|
|
14
|
+
});
|
|
9
15
|
}
|
|
10
16
|
}
|
|
11
17
|
get isOnline() {
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { createId } from 'crypto-id';
|
|
2
|
+
import { createChange } from '../data/change.js';
|
|
3
|
+
import { createVersionMetadata } from '../data/version.js';
|
|
2
4
|
/**
|
|
3
5
|
* Helps manage branches for a document. A branch is a document that is branched from another document. Its first
|
|
4
6
|
* version will be the point-in-time of the original document at the time of the branch. Branches allow for parallel
|
|
@@ -36,8 +38,7 @@ export class PatchesBranchManager {
|
|
|
36
38
|
const branchDocId = createId();
|
|
37
39
|
const now = Date.now();
|
|
38
40
|
// Create an initial version at the branch point rev (for snapshotting/large docs)
|
|
39
|
-
const initialVersionMetadata = {
|
|
40
|
-
id: createId(),
|
|
41
|
+
const initialVersionMetadata = createVersionMetadata({
|
|
41
42
|
origin: 'main', // Branch doc versions are 'main' until merged
|
|
42
43
|
startDate: now,
|
|
43
44
|
endDate: now,
|
|
@@ -46,7 +47,7 @@ export class PatchesBranchManager {
|
|
|
46
47
|
name: metadata?.name,
|
|
47
48
|
groupId: branchDocId,
|
|
48
49
|
branchName: metadata?.name,
|
|
49
|
-
};
|
|
50
|
+
});
|
|
50
51
|
await this.store.createVersion(branchDocId, initialVersionMetadata, stateAtRev, []);
|
|
51
52
|
// 2. Create the branch metadata record
|
|
52
53
|
const branch = {
|
|
@@ -106,30 +107,22 @@ export class PatchesBranchManager {
|
|
|
106
107
|
// 4. For each version, create a corresponding version in the main doc with updated fields
|
|
107
108
|
let lastVersionId;
|
|
108
109
|
for (const v of branchVersions) {
|
|
109
|
-
const
|
|
110
|
-
const newVersionMetadata = {
|
|
110
|
+
const newVersionMetadata = createVersionMetadata({
|
|
111
111
|
...v,
|
|
112
|
-
id: newVersionId,
|
|
113
112
|
origin: 'branch',
|
|
114
113
|
baseRev: branchStartRevOnSource,
|
|
115
114
|
groupId: branchId,
|
|
116
115
|
branchName: branch.name,
|
|
117
116
|
parentId: lastVersionId,
|
|
118
|
-
};
|
|
117
|
+
});
|
|
119
118
|
const state = await this.store.loadVersionState(branchId, v.id);
|
|
120
119
|
const changes = await this.store.loadVersionChanges(branchId, v.id);
|
|
121
120
|
await this.store.createVersion(sourceDocId, newVersionMetadata, state, changes);
|
|
122
|
-
lastVersionId =
|
|
121
|
+
lastVersionId = newVersionMetadata.id;
|
|
123
122
|
}
|
|
124
123
|
// 5. Flatten all branch changes into a single change for the main doc
|
|
125
|
-
const
|
|
126
|
-
const flattenedChange =
|
|
127
|
-
id: createId(12),
|
|
128
|
-
ops: branchChanges.flatMap(c => c.ops),
|
|
129
|
-
rev: branchStartRevOnSource + branchChanges.length,
|
|
130
|
-
baseRev: branchStartRevOnSource,
|
|
131
|
-
created: now,
|
|
132
|
-
};
|
|
124
|
+
const rev = branchStartRevOnSource + branchChanges.length;
|
|
125
|
+
const flattenedChange = createChange(branchStartRevOnSource, rev, branchChanges.flatMap(c => c.ops));
|
|
133
126
|
// 6. Commit the flattened change to the main doc
|
|
134
127
|
let committedMergeChanges = [];
|
|
135
128
|
try {
|
|
@@ -28,7 +28,7 @@ export class PatchesHistoryManager {
|
|
|
28
28
|
*/
|
|
29
29
|
async createVersion(docId, metadata) {
|
|
30
30
|
assertVersionMetadata(metadata);
|
|
31
|
-
return await this.patches.
|
|
31
|
+
return await this.patches.captureCurrentVersion(docId, metadata);
|
|
32
32
|
}
|
|
33
33
|
/**
|
|
34
34
|
* Updates the name of a specific version.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { JSONPatch } from '../json-patch/JSONPatch.js';
|
|
2
|
-
import type { Change, EditableVersionMetadata,
|
|
2
|
+
import type { Change, EditableVersionMetadata, PatchesState } from '../types.js';
|
|
3
3
|
import type { PatchesStoreBackend } from './types.js';
|
|
4
4
|
/**
|
|
5
5
|
* Configuration options for the PatchesServer.
|
|
@@ -32,6 +32,13 @@ export declare class PatchesServer {
|
|
|
32
32
|
* @returns The state of the document at the specified revision.
|
|
33
33
|
*/
|
|
34
34
|
getDoc(docId: string, atRev?: number): Promise<PatchesState>;
|
|
35
|
+
/**
|
|
36
|
+
* Get the state of a document at a specific revision.
|
|
37
|
+
* @param docId - The ID of the document.
|
|
38
|
+
* @param rev - The revision number.
|
|
39
|
+
* @returns The state of the document at the specified revision.
|
|
40
|
+
*/
|
|
41
|
+
getStateAtRevision(docId: string, atRev?: number): Promise<PatchesState>;
|
|
35
42
|
/**
|
|
36
43
|
* Get changes that occurred after a specific revision.
|
|
37
44
|
* @param docId - The ID of the document.
|
|
@@ -62,45 +69,11 @@ export declare class PatchesServer {
|
|
|
62
69
|
*/
|
|
63
70
|
deleteDoc(docId: string, originClientId?: string): Promise<void>;
|
|
64
71
|
/**
|
|
65
|
-
*
|
|
72
|
+
* Captures the current state of a document as a new version.
|
|
66
73
|
* @param docId The document ID.
|
|
67
|
-
* @param
|
|
74
|
+
* @param metadata Optional metadata for the version.
|
|
68
75
|
* @returns The ID of the created version.
|
|
69
76
|
*/
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Gets the state at a specific revision.
|
|
73
|
-
* @param docId The document ID.
|
|
74
|
-
* @param rev The revision number. If not provided, the latest state and its revision is returned.
|
|
75
|
-
* @returns The state at the specified revision *and* its revision number.
|
|
76
|
-
*/
|
|
77
|
-
getStateAtRevision(docId: string, rev?: number): Promise<PatchesState>;
|
|
78
|
-
/**
|
|
79
|
-
* Retrieves the document state of the version before the given revision and changes after up to that revision or all
|
|
80
|
-
* changes since that version.
|
|
81
|
-
* @param docId The document ID.
|
|
82
|
-
* @param rev The revision number. If not provided, the latest state, its revision, and all changes since are returned.
|
|
83
|
-
* @returns The document state at the last version before the revision, its revision number, and all changes up to the specified revision (or all changes if no revision is provided).
|
|
84
|
-
*/
|
|
85
|
-
protected _getSnapshotAtRevision(docId: string, rev?: number): Promise<PatchesSnapshot>;
|
|
86
|
-
/**
|
|
87
|
-
* Creates a new version snapshot of a document's current state.
|
|
88
|
-
* @param docId The document ID.
|
|
89
|
-
* @param state The document state at the time of the version.
|
|
90
|
-
* @param changes The changes since the last version that created the state (the last change's rev is the state's rev and will be the version's rev).
|
|
91
|
-
* @param metadata The metadata of the version.
|
|
92
|
-
* @returns The ID of the created version.
|
|
93
|
-
*/
|
|
94
|
-
protected _createVersion(docId: string, state: any, changes: Change[], metadata?: EditableVersionMetadata): Promise<VersionMetadata | undefined>;
|
|
95
|
-
/**
|
|
96
|
-
* Handles offline/large batch versioning logic for multi-batch uploads.
|
|
97
|
-
* Groups changes into sessions, merges with previous batch if needed, and creates/extends versions.
|
|
98
|
-
* @param docId Document ID
|
|
99
|
-
* @param changes The incoming changes (all with the same batchId)
|
|
100
|
-
* @param baseRev The base revision for the batch
|
|
101
|
-
* @param batchId The batch identifier
|
|
102
|
-
* @returns The collapsed changes for transformation
|
|
103
|
-
*/
|
|
104
|
-
protected _handleOfflineBatches(docId: string, changes: Change[], baseRev: number, batchId?: string): Promise<Change[]>;
|
|
77
|
+
captureCurrentVersion(docId: string, metadata?: EditableVersionMetadata): Promise<string>;
|
|
105
78
|
}
|
|
106
79
|
export declare function assertVersionMetadata(metadata?: EditableVersionMetadata): void;
|