@dabble/patches 0.2.18 → 0.2.20
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/net/protocol/types.d.ts +4 -4
- package/dist/net/websocket/PatchesWebSocket.d.ts +29 -3
- package/dist/net/websocket/PatchesWebSocket.js +39 -4
- package/dist/net/websocket/WebSocketServer.d.ts +7 -7
- package/dist/net/websocket/WebSocketServer.js +27 -7
- package/dist/server/PatchesBranchManager.d.ts +9 -2
- package/dist/server/PatchesBranchManager.js +23 -5
- package/dist/server/PatchesHistoryManager.d.ts +5 -10
- package/dist/server/PatchesHistoryManager.js +7 -5
- package/dist/server/PatchesServer.d.ts +5 -4
- package/dist/server/PatchesServer.js +26 -5
- package/dist/server/types.d.ts +2 -2
- package/dist/types.d.ts +6 -0
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Unsubscriber } from '../../event-signal.js';
|
|
2
|
-
import type { Change, ListVersionsOptions, PatchesSnapshot, PatchesState, VersionMetadata } from '../../types';
|
|
2
|
+
import type { Change, EditableVersionMetadata, ListVersionsOptions, PatchesSnapshot, PatchesState, VersionMetadata } from '../../types';
|
|
3
3
|
/**
|
|
4
4
|
* Represents the possible states of a network transport connection.
|
|
5
5
|
* - 'connecting': Connection is being established
|
|
@@ -117,15 +117,15 @@ export interface PatchesAPI {
|
|
|
117
117
|
/** Delete a document. */
|
|
118
118
|
deleteDoc(docId: string): Promise<void>;
|
|
119
119
|
/** Create a new named version snapshot of a document's current state. */
|
|
120
|
-
createVersion(docId: string,
|
|
120
|
+
createVersion(docId: string, metadata: EditableVersionMetadata): Promise<string>;
|
|
121
121
|
/** List metadata for saved versions of a document. */
|
|
122
122
|
listVersions(docId: string, options?: ListVersionsOptions): Promise<VersionMetadata[]>;
|
|
123
123
|
/** Get the state snapshot for a specific version ID. */
|
|
124
124
|
getVersionState(docId: string, versionId: string): Promise<PatchesState>;
|
|
125
125
|
/** Get the original Change objects associated with a specific version ID. */
|
|
126
126
|
getVersionChanges(docId: string, versionId: string): Promise<Change[]>;
|
|
127
|
-
/** Update the name of a specific version. */
|
|
128
|
-
updateVersion(docId: string, versionId: string,
|
|
127
|
+
/** Update the name and other metadata of a specific version. */
|
|
128
|
+
updateVersion(docId: string, versionId: string, metadata: EditableVersionMetadata): Promise<void>;
|
|
129
129
|
}
|
|
130
130
|
export interface PatchesNotificationParams {
|
|
131
131
|
docId: string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type Signal } from '../../event-signal.js';
|
|
2
|
-
import type { Change, ListVersionsOptions, PatchesSnapshot, VersionMetadata } from '../../types.js';
|
|
2
|
+
import type { Change, EditableVersionMetadata, ListVersionsOptions, PatchesSnapshot, VersionMetadata } from '../../types.js';
|
|
3
3
|
import type { ConnectionState, PatchesAPI, PatchesNotificationParams } from '../protocol/types.js';
|
|
4
4
|
import { type WebSocketOptions } from './WebSocketTransport.js';
|
|
5
5
|
/**
|
|
@@ -74,7 +74,7 @@ export declare class PatchesWebSocket implements PatchesAPI {
|
|
|
74
74
|
* @param name - A descriptive name for the version.
|
|
75
75
|
* @returns A promise resolving with the unique ID of the newly created version.
|
|
76
76
|
*/
|
|
77
|
-
createVersion(docId: string,
|
|
77
|
+
createVersion(docId: string, metadata: EditableVersionMetadata): Promise<string>;
|
|
78
78
|
/**
|
|
79
79
|
* Lists metadata for saved versions of a document.
|
|
80
80
|
* @param docId - The ID of the document.
|
|
@@ -103,5 +103,31 @@ 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, metadata: EditableVersionMetadata): Promise<void>;
|
|
107
|
+
/**
|
|
108
|
+
* Lists all branches for a document.
|
|
109
|
+
* @param docId - The ID of the document.
|
|
110
|
+
* @returns A promise resolving with an array of branch metadata objects.
|
|
111
|
+
*/
|
|
112
|
+
listBranches(docId: string): Promise<VersionMetadata[]>;
|
|
113
|
+
/**
|
|
114
|
+
* Creates a new branch for a document.
|
|
115
|
+
* @param docId - The ID of the document.
|
|
116
|
+
* @param rev - The revision number to base the new branch on.
|
|
117
|
+
* @param metadata - Optional metadata for the new branch.
|
|
118
|
+
* @returns A promise resolving with the unique ID of the newly created branch.
|
|
119
|
+
*/
|
|
120
|
+
createBranch(docId: string, rev: number, metadata?: EditableVersionMetadata): Promise<string>;
|
|
121
|
+
/**
|
|
122
|
+
* Closes a branch on the server.
|
|
123
|
+
* @param branchId - The ID of the branch to close.
|
|
124
|
+
* @returns A promise resolving when the branch is closed.
|
|
125
|
+
*/
|
|
126
|
+
closeBranch(branchId: string): Promise<void>;
|
|
127
|
+
/**
|
|
128
|
+
* Merges a branch on the server.
|
|
129
|
+
* @param branchId - The ID of the branch to merge.
|
|
130
|
+
* @returns A promise resolving when the merge is confirmed.
|
|
131
|
+
*/
|
|
132
|
+
mergeBranch(branchId: string): Promise<void>;
|
|
107
133
|
}
|
|
@@ -101,8 +101,8 @@ export class PatchesWebSocket {
|
|
|
101
101
|
* @param name - A descriptive name for the version.
|
|
102
102
|
* @returns A promise resolving with the unique ID of the newly created version.
|
|
103
103
|
*/
|
|
104
|
-
async createVersion(docId,
|
|
105
|
-
return this.rpc.request('createVersion', { docId,
|
|
104
|
+
async createVersion(docId, metadata) {
|
|
105
|
+
return this.rpc.request('createVersion', { docId, metadata });
|
|
106
106
|
}
|
|
107
107
|
/**
|
|
108
108
|
* Lists metadata for saved versions of a document.
|
|
@@ -138,7 +138,42 @@ 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, metadata) {
|
|
142
|
+
return this.rpc.request('updateVersion', { docId, versionId, metadata });
|
|
143
|
+
}
|
|
144
|
+
// === Branch Operations ===
|
|
145
|
+
/**
|
|
146
|
+
* Lists all branches for a document.
|
|
147
|
+
* @param docId - The ID of the document.
|
|
148
|
+
* @returns A promise resolving with an array of branch metadata objects.
|
|
149
|
+
*/
|
|
150
|
+
async listBranches(docId) {
|
|
151
|
+
return this.rpc.request('listBranches', { docId });
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Creates a new branch for a document.
|
|
155
|
+
* @param docId - The ID of the document.
|
|
156
|
+
* @param rev - The revision number to base the new branch on.
|
|
157
|
+
* @param metadata - Optional metadata for the new branch.
|
|
158
|
+
* @returns A promise resolving with the unique ID of the newly created branch.
|
|
159
|
+
*/
|
|
160
|
+
async createBranch(docId, rev, metadata) {
|
|
161
|
+
return this.rpc.request('createBranch', { docId, rev, metadata });
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Closes a branch on the server.
|
|
165
|
+
* @param branchId - The ID of the branch to close.
|
|
166
|
+
* @returns A promise resolving when the branch is closed.
|
|
167
|
+
*/
|
|
168
|
+
async closeBranch(branchId) {
|
|
169
|
+
return this.rpc.request('closeBranch', { branchId });
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Merges a branch on the server.
|
|
173
|
+
* @param branchId - The ID of the branch to merge.
|
|
174
|
+
* @returns A promise resolving when the merge is confirmed.
|
|
175
|
+
*/
|
|
176
|
+
async mergeBranch(branchId) {
|
|
177
|
+
return this.rpc.request('mergeBranch', { branchId });
|
|
143
178
|
}
|
|
144
179
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { PatchesBranchManager } from '../../server/PatchesBranchManager.js';
|
|
2
2
|
import type { PatchesHistoryManager } from '../../server/PatchesHistoryManager.js';
|
|
3
3
|
import type { PatchesServer } from '../../server/PatchesServer.js';
|
|
4
|
-
import type { Change,
|
|
4
|
+
import type { Change, EditableVersionMetadata, ListVersionsOptions } from '../../types.js';
|
|
5
5
|
import { JSONRPCServer } from '../protocol/JSONRPCServer.js';
|
|
6
6
|
import type { ServerTransport } from '../protocol/types.js';
|
|
7
7
|
import type { AuthorizationProvider } from './AuthorizationProvider.js';
|
|
@@ -35,6 +35,8 @@ export declare class WebSocketServer {
|
|
|
35
35
|
protected assertAccess(connectionId: string, docId: string, kind: 'read' | 'write', method: string, params?: Record<string, any>): Promise<void>;
|
|
36
36
|
protected assertRead(connectionId: string, docId: string, method: string, params?: Record<string, any>): Promise<void>;
|
|
37
37
|
protected assertWrite(connectionId: string, docId: string, method: string, params?: Record<string, any>): Promise<void>;
|
|
38
|
+
protected assertHistoryEnabled(): void;
|
|
39
|
+
protected assertBranchingEnabled(): void;
|
|
38
40
|
/**
|
|
39
41
|
* Subscribes the client to one or more documents to receive real-time updates.
|
|
40
42
|
* @param connectionId - The ID of the connection making the request
|
|
@@ -98,15 +100,15 @@ export declare class WebSocketServer {
|
|
|
98
100
|
listVersions(connectionId: string, params: {
|
|
99
101
|
docId: string;
|
|
100
102
|
options?: ListVersionsOptions;
|
|
101
|
-
}): Promise<VersionMetadata[]>;
|
|
103
|
+
}): Promise<import("../../types.js").VersionMetadata[]>;
|
|
102
104
|
createVersion(connectionId: string, params: {
|
|
103
105
|
docId: string;
|
|
104
|
-
|
|
106
|
+
metadata: EditableVersionMetadata;
|
|
105
107
|
}): Promise<string>;
|
|
106
108
|
updateVersion(connectionId: string, params: {
|
|
107
109
|
docId: string;
|
|
108
110
|
versionId: string;
|
|
109
|
-
|
|
111
|
+
metadata: EditableVersionMetadata;
|
|
110
112
|
}): Promise<void>;
|
|
111
113
|
getStateAtVersion(connectionId: string, params: {
|
|
112
114
|
docId: string;
|
|
@@ -131,12 +133,10 @@ export declare class WebSocketServer {
|
|
|
131
133
|
createBranch(connectionId: string, params: {
|
|
132
134
|
docId: string;
|
|
133
135
|
rev: number;
|
|
134
|
-
|
|
135
|
-
metadata?: Record<string, any>;
|
|
136
|
+
metadata?: EditableVersionMetadata;
|
|
136
137
|
}): Promise<string>;
|
|
137
138
|
closeBranch(connectionId: string, params: {
|
|
138
139
|
branchId: string;
|
|
139
|
-
status?: 'merged' | 'closed';
|
|
140
140
|
}): Promise<void>;
|
|
141
141
|
mergeBranch(connectionId: string, params: {
|
|
142
142
|
branchId: string;
|
|
@@ -54,6 +54,16 @@ export class WebSocketServer {
|
|
|
54
54
|
assertWrite(connectionId, docId, method, params) {
|
|
55
55
|
return this.assertAccess(connectionId, docId, 'write', method, params);
|
|
56
56
|
}
|
|
57
|
+
assertHistoryEnabled() {
|
|
58
|
+
if (!this.history) {
|
|
59
|
+
throw new Error('History is not enabled');
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
assertBranchingEnabled() {
|
|
63
|
+
if (!this.branches) {
|
|
64
|
+
throw new Error('Branching is not enabled');
|
|
65
|
+
}
|
|
66
|
+
}
|
|
57
67
|
// --- Patches API Methods ---
|
|
58
68
|
// === Subscription Operations ===
|
|
59
69
|
/**
|
|
@@ -154,31 +164,37 @@ export class WebSocketServer {
|
|
|
154
164
|
// History Manager wrappers
|
|
155
165
|
// ---------------------------------------------------------------------------
|
|
156
166
|
async listVersions(connectionId, params) {
|
|
167
|
+
this.assertHistoryEnabled();
|
|
157
168
|
const { docId, options } = params;
|
|
158
169
|
await this.assertRead(connectionId, docId, 'listVersions', params);
|
|
159
170
|
return this.history.listVersions(docId, options ?? {});
|
|
160
171
|
}
|
|
161
172
|
async createVersion(connectionId, params) {
|
|
162
|
-
|
|
173
|
+
this.assertHistoryEnabled();
|
|
174
|
+
const { docId, metadata } = params;
|
|
163
175
|
await this.assertWrite(connectionId, docId, 'createVersion', params);
|
|
164
|
-
return this.history.createVersion(docId,
|
|
176
|
+
return this.history.createVersion(docId, metadata);
|
|
165
177
|
}
|
|
166
178
|
async updateVersion(connectionId, params) {
|
|
167
|
-
|
|
179
|
+
this.assertHistoryEnabled();
|
|
180
|
+
const { docId, versionId, metadata } = params;
|
|
168
181
|
await this.assertWrite(connectionId, docId, 'updateVersion', params);
|
|
169
|
-
return this.history.updateVersion(docId, versionId,
|
|
182
|
+
return this.history.updateVersion(docId, versionId, metadata);
|
|
170
183
|
}
|
|
171
184
|
async getStateAtVersion(connectionId, params) {
|
|
185
|
+
this.assertHistoryEnabled();
|
|
172
186
|
const { docId, versionId } = params;
|
|
173
187
|
await this.assertRead(connectionId, docId, 'getStateAtVersion', params);
|
|
174
188
|
return this.history.getStateAtVersion(docId, versionId);
|
|
175
189
|
}
|
|
176
190
|
async getChangesForVersion(connectionId, params) {
|
|
191
|
+
this.assertHistoryEnabled();
|
|
177
192
|
const { docId, versionId } = params;
|
|
178
193
|
await this.assertRead(connectionId, docId, 'getChangesForVersion', params);
|
|
179
194
|
return this.history.getChangesForVersion(docId, versionId);
|
|
180
195
|
}
|
|
181
196
|
async listServerChanges(connectionId, params) {
|
|
197
|
+
this.assertHistoryEnabled();
|
|
182
198
|
const { docId, options } = params;
|
|
183
199
|
await this.assertRead(connectionId, docId, 'listServerChanges', params);
|
|
184
200
|
return this.history.listServerChanges(docId, options ?? {});
|
|
@@ -187,21 +203,25 @@ export class WebSocketServer {
|
|
|
187
203
|
// Branch Manager wrappers
|
|
188
204
|
// ---------------------------------------------------------------------------
|
|
189
205
|
async listBranches(connectionId, params) {
|
|
206
|
+
this.assertBranchingEnabled();
|
|
190
207
|
const { docId } = params;
|
|
191
208
|
await this.assertRead(connectionId, docId, 'listBranches', params);
|
|
192
209
|
return this.branches.listBranches(docId);
|
|
193
210
|
}
|
|
194
211
|
async createBranch(connectionId, params) {
|
|
195
|
-
|
|
212
|
+
this.assertBranchingEnabled();
|
|
213
|
+
const { docId, rev, metadata } = params;
|
|
196
214
|
await this.assertWrite(connectionId, docId, 'createBranch', params);
|
|
197
|
-
return this.branches.createBranch(docId,
|
|
215
|
+
return this.branches.createBranch(docId, rev, metadata);
|
|
198
216
|
}
|
|
199
217
|
async closeBranch(connectionId, params) {
|
|
218
|
+
this.assertBranchingEnabled();
|
|
200
219
|
const { branchId } = params;
|
|
201
220
|
await this.assertWrite(connectionId, branchId, 'closeBranch', params);
|
|
202
|
-
return this.branches.closeBranch(branchId,
|
|
221
|
+
return this.branches.closeBranch(branchId, 'closed');
|
|
203
222
|
}
|
|
204
223
|
async mergeBranch(connectionId, params) {
|
|
224
|
+
this.assertBranchingEnabled();
|
|
205
225
|
const { branchId } = params;
|
|
206
226
|
await this.assertWrite(connectionId, branchId, 'mergeBranch', params);
|
|
207
227
|
return this.branches.mergeBranch(branchId);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Branch, BranchStatus, Change } from '../types.js';
|
|
1
|
+
import type { Branch, BranchStatus, Change, EditableBranchMetadata } from '../types.js';
|
|
2
2
|
import type { PatchesServer } from './PatchesServer.js';
|
|
3
3
|
import type { BranchingStoreBackend } from './types.js';
|
|
4
4
|
/**
|
|
@@ -24,7 +24,13 @@ export declare class PatchesBranchManager {
|
|
|
24
24
|
* @param metadata - Additional optional metadata to store with the branch.
|
|
25
25
|
* @returns The ID of the new branch document.
|
|
26
26
|
*/
|
|
27
|
-
createBranch(docId: string, rev: number,
|
|
27
|
+
createBranch(docId: string, rev: number, metadata?: EditableBranchMetadata): Promise<string>;
|
|
28
|
+
/**
|
|
29
|
+
* Updates a branch's metadata.
|
|
30
|
+
* @param branchId - The ID of the branch to update.
|
|
31
|
+
* @param metadata - The metadata to update.
|
|
32
|
+
*/
|
|
33
|
+
updateBranch(branchId: string, metadata: EditableBranchMetadata): Promise<void>;
|
|
28
34
|
/**
|
|
29
35
|
* Closes a branch, marking it as merged or deleted.
|
|
30
36
|
* @param branchId - The ID of the branch to close.
|
|
@@ -39,3 +45,4 @@ export declare class PatchesBranchManager {
|
|
|
39
45
|
*/
|
|
40
46
|
mergeBranch(branchId: string): Promise<Change[]>;
|
|
41
47
|
}
|
|
48
|
+
export declare function assertBranchMetadata(metadata?: EditableBranchMetadata): void;
|
|
@@ -25,7 +25,7 @@ export class PatchesBranchManager {
|
|
|
25
25
|
* @param metadata - Additional optional metadata to store with the branch.
|
|
26
26
|
* @returns The ID of the new branch document.
|
|
27
27
|
*/
|
|
28
|
-
async createBranch(docId, rev,
|
|
28
|
+
async createBranch(docId, rev, metadata) {
|
|
29
29
|
// Prevent branching off a branch
|
|
30
30
|
const maybeBranch = await this.store.loadBranch(docId);
|
|
31
31
|
if (maybeBranch) {
|
|
@@ -43,24 +43,32 @@ export class PatchesBranchManager {
|
|
|
43
43
|
endDate: now,
|
|
44
44
|
rev,
|
|
45
45
|
baseRev: rev,
|
|
46
|
-
name:
|
|
46
|
+
name: metadata?.name,
|
|
47
47
|
groupId: branchDocId,
|
|
48
|
-
branchName,
|
|
48
|
+
branchName: metadata?.name,
|
|
49
49
|
};
|
|
50
50
|
await this.store.createVersion(branchDocId, initialVersionMetadata, stateAtRev, []);
|
|
51
51
|
// 2. Create the branch metadata record
|
|
52
52
|
const branch = {
|
|
53
|
+
...metadata,
|
|
53
54
|
id: branchDocId,
|
|
54
55
|
branchedFromId: docId,
|
|
55
56
|
branchedRev: rev,
|
|
56
57
|
created: now,
|
|
57
|
-
name: branchName,
|
|
58
58
|
status: 'open',
|
|
59
|
-
...(metadata && { metadata }),
|
|
60
59
|
};
|
|
61
60
|
await this.store.createBranch(branch);
|
|
62
61
|
return branchDocId;
|
|
63
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* Updates a branch's metadata.
|
|
65
|
+
* @param branchId - The ID of the branch to update.
|
|
66
|
+
* @param metadata - The metadata to update.
|
|
67
|
+
*/
|
|
68
|
+
async updateBranch(branchId, metadata) {
|
|
69
|
+
assertBranchMetadata(metadata);
|
|
70
|
+
await this.store.updateBranch(branchId, metadata);
|
|
71
|
+
}
|
|
64
72
|
/**
|
|
65
73
|
* Closes a branch, marking it as merged or deleted.
|
|
66
74
|
* @param branchId - The ID of the branch to close.
|
|
@@ -136,3 +144,13 @@ export class PatchesBranchManager {
|
|
|
136
144
|
return committedMergeChanges;
|
|
137
145
|
}
|
|
138
146
|
}
|
|
147
|
+
const nonModifiableMetadataFields = new Set(['id', 'branchedFromId', 'branchedRev', 'created', 'status']);
|
|
148
|
+
export function assertBranchMetadata(metadata) {
|
|
149
|
+
if (!metadata)
|
|
150
|
+
return;
|
|
151
|
+
for (const key in metadata) {
|
|
152
|
+
if (nonModifiableMetadataFields.has(key)) {
|
|
153
|
+
throw new Error(`Cannot modify branch field ${key}`);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Change, ListVersionsOptions, VersionMetadata } from '../types.js';
|
|
2
|
-
import type
|
|
1
|
+
import type { Change, EditableVersionMetadata, ListChangesOptions, ListVersionsOptions, VersionMetadata } from '../types.js';
|
|
2
|
+
import { type PatchesServer } from './PatchesServer.js';
|
|
3
3
|
/**
|
|
4
4
|
* Helps retrieve historical information (versions, changes) for a document
|
|
5
5
|
* using the new versioning model based on IDs and metadata.
|
|
@@ -21,14 +21,14 @@ export declare class PatchesHistoryManager {
|
|
|
21
21
|
* @param name The name of the version.
|
|
22
22
|
* @returns The ID of the created version.
|
|
23
23
|
*/
|
|
24
|
-
createVersion(docId: string,
|
|
24
|
+
createVersion(docId: string, metadata?: EditableVersionMetadata): Promise<string>;
|
|
25
25
|
/**
|
|
26
26
|
* Updates the name of a specific version.
|
|
27
27
|
* @param docId - The ID of the document.
|
|
28
28
|
* @param versionId - The ID of the version to update.
|
|
29
29
|
* @param name - The new name for the version.
|
|
30
30
|
*/
|
|
31
|
-
updateVersion(docId: string, versionId: string,
|
|
31
|
+
updateVersion(docId: string, versionId: string, metadata: EditableVersionMetadata): Promise<void>;
|
|
32
32
|
/**
|
|
33
33
|
* Loads the full document state snapshot for a specific version by its ID.
|
|
34
34
|
* @param docId - The ID of the document.
|
|
@@ -53,10 +53,5 @@ export declare class PatchesHistoryManager {
|
|
|
53
53
|
* @param options - Options like start/end revision, limit.
|
|
54
54
|
* @returns The list of committed Change objects.
|
|
55
55
|
*/
|
|
56
|
-
listServerChanges(docId: string, options?:
|
|
57
|
-
limit?: number;
|
|
58
|
-
startAfterRev?: number;
|
|
59
|
-
endBeforeRev?: number;
|
|
60
|
-
reverse?: boolean;
|
|
61
|
-
}): Promise<Change[]>;
|
|
56
|
+
listServerChanges(docId: string, options?: ListChangesOptions): Promise<Change[]>;
|
|
62
57
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { assertVersionMetadata } from './PatchesServer.js';
|
|
1
2
|
/**
|
|
2
3
|
* Helps retrieve historical information (versions, changes) for a document
|
|
3
4
|
* using the new versioning model based on IDs and metadata.
|
|
@@ -25,8 +26,9 @@ export class PatchesHistoryManager {
|
|
|
25
26
|
* @param name The name of the version.
|
|
26
27
|
* @returns The ID of the created version.
|
|
27
28
|
*/
|
|
28
|
-
async createVersion(docId,
|
|
29
|
-
|
|
29
|
+
async createVersion(docId, metadata) {
|
|
30
|
+
assertVersionMetadata(metadata);
|
|
31
|
+
return await this.patches.createVersion(docId, metadata);
|
|
30
32
|
}
|
|
31
33
|
/**
|
|
32
34
|
* Updates the name of a specific version.
|
|
@@ -34,8 +36,9 @@ export class PatchesHistoryManager {
|
|
|
34
36
|
* @param versionId - The ID of the version to update.
|
|
35
37
|
* @param name - The new name for the version.
|
|
36
38
|
*/
|
|
37
|
-
async updateVersion(docId, versionId,
|
|
38
|
-
|
|
39
|
+
async updateVersion(docId, versionId, metadata) {
|
|
40
|
+
assertVersionMetadata(metadata);
|
|
41
|
+
return this.store.updateVersion(docId, versionId, metadata);
|
|
39
42
|
}
|
|
40
43
|
/**
|
|
41
44
|
* Loads the full document state snapshot for a specific version by its ID.
|
|
@@ -78,7 +81,6 @@ export class PatchesHistoryManager {
|
|
|
78
81
|
* @returns The list of committed Change objects.
|
|
79
82
|
*/
|
|
80
83
|
async listServerChanges(docId, options = {}) {
|
|
81
|
-
// Added return type
|
|
82
84
|
return await this.store.listChanges(docId, options);
|
|
83
85
|
}
|
|
84
86
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Change, PatchesSnapshot, PatchesState, VersionMetadata } from '../types.js';
|
|
1
|
+
import type { Change, EditableVersionMetadata, PatchesSnapshot, PatchesState, VersionMetadata } from '../types.js';
|
|
2
2
|
import type { PatchesStoreBackend } from './types.js';
|
|
3
3
|
/**
|
|
4
4
|
* Configuration options for the PatchesServer.
|
|
@@ -67,7 +67,7 @@ export declare class PatchesServer {
|
|
|
67
67
|
* @param name The name of the version.
|
|
68
68
|
* @returns The ID of the created version.
|
|
69
69
|
*/
|
|
70
|
-
createVersion(docId: string,
|
|
70
|
+
createVersion(docId: string, metadata?: EditableVersionMetadata): Promise<string>;
|
|
71
71
|
/**
|
|
72
72
|
* Gets the state at a specific revision.
|
|
73
73
|
* @param docId The document ID.
|
|
@@ -88,10 +88,10 @@ export declare class PatchesServer {
|
|
|
88
88
|
* @param docId The document ID.
|
|
89
89
|
* @param state The document state at the time of the version.
|
|
90
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
|
|
91
|
+
* @param metadata The metadata of the version.
|
|
92
92
|
* @returns The ID of the created version.
|
|
93
93
|
*/
|
|
94
|
-
protected _createVersion(docId: string, state: any, changes: Change[],
|
|
94
|
+
protected _createVersion(docId: string, state: any, changes: Change[], metadata?: EditableVersionMetadata): Promise<VersionMetadata | undefined>;
|
|
95
95
|
/**
|
|
96
96
|
* Handles offline/large batch versioning logic for multi-batch uploads.
|
|
97
97
|
* Groups changes into sessions, merges with previous batch if needed, and creates/extends versions.
|
|
@@ -103,3 +103,4 @@ export declare class PatchesServer {
|
|
|
103
103
|
*/
|
|
104
104
|
protected _handleOfflineBatches(docId: string, changes: Change[], baseRev: number, batchId?: string): Promise<Change[]>;
|
|
105
105
|
}
|
|
106
|
+
export declare function assertVersionMetadata(metadata?: EditableVersionMetadata): void;
|
|
@@ -159,10 +159,11 @@ export class PatchesServer {
|
|
|
159
159
|
* @param name The name of the version.
|
|
160
160
|
* @returns The ID of the created version.
|
|
161
161
|
*/
|
|
162
|
-
async createVersion(docId,
|
|
162
|
+
async createVersion(docId, metadata) {
|
|
163
|
+
assertVersionMetadata(metadata);
|
|
163
164
|
let { state, changes } = await this._getSnapshotAtRevision(docId);
|
|
164
165
|
state = applyChanges(state, changes);
|
|
165
|
-
const version = await this._createVersion(docId, state, changes,
|
|
166
|
+
const version = await this._createVersion(docId, state, changes, metadata);
|
|
166
167
|
if (!version) {
|
|
167
168
|
throw new Error(`No changes to create a version for doc ${docId}.`);
|
|
168
169
|
}
|
|
@@ -218,10 +219,10 @@ export class PatchesServer {
|
|
|
218
219
|
* @param docId The document ID.
|
|
219
220
|
* @param state The document state at the time of the version.
|
|
220
221
|
* @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).
|
|
221
|
-
* @param
|
|
222
|
+
* @param metadata The metadata of the version.
|
|
222
223
|
* @returns The ID of the created version.
|
|
223
224
|
*/
|
|
224
|
-
async _createVersion(docId, state, changes,
|
|
225
|
+
async _createVersion(docId, state, changes, metadata) {
|
|
225
226
|
if (changes.length === 0)
|
|
226
227
|
return;
|
|
227
228
|
const baseRev = changes[0].baseRev;
|
|
@@ -231,12 +232,12 @@ export class PatchesServer {
|
|
|
231
232
|
const versionId = createId();
|
|
232
233
|
const sessionMetadata = {
|
|
233
234
|
id: versionId,
|
|
234
|
-
name,
|
|
235
235
|
origin: 'main',
|
|
236
236
|
startDate: changes[0].created,
|
|
237
237
|
endDate: changes[changes.length - 1].created,
|
|
238
238
|
rev: changes[changes.length - 1].rev,
|
|
239
239
|
baseRev,
|
|
240
|
+
...metadata,
|
|
240
241
|
};
|
|
241
242
|
await this.store.createVersion(docId, sessionMetadata, state, changes);
|
|
242
243
|
return sessionMetadata;
|
|
@@ -320,3 +321,23 @@ export class PatchesServer {
|
|
|
320
321
|
];
|
|
321
322
|
}
|
|
322
323
|
}
|
|
324
|
+
const nonModifiableMetadataFields = new Set([
|
|
325
|
+
'id',
|
|
326
|
+
'parentId',
|
|
327
|
+
'groupId',
|
|
328
|
+
'origin',
|
|
329
|
+
'branchName',
|
|
330
|
+
'startDate',
|
|
331
|
+
'endDate',
|
|
332
|
+
'rev',
|
|
333
|
+
'baseRev',
|
|
334
|
+
]);
|
|
335
|
+
export function assertVersionMetadata(metadata) {
|
|
336
|
+
if (!metadata)
|
|
337
|
+
return;
|
|
338
|
+
for (const key in metadata) {
|
|
339
|
+
if (nonModifiableMetadataFields.has(key)) {
|
|
340
|
+
throw new Error(`Cannot modify version field ${key}`);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
package/dist/server/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Branch, Change, ListChangesOptions, ListVersionsOptions, VersionMetadata } from '../types';
|
|
1
|
+
import type { Branch, Change, EditableVersionMetadata, ListChangesOptions, ListVersionsOptions, VersionMetadata } from '../types';
|
|
2
2
|
/**
|
|
3
3
|
* Interface for a backend storage system for patch synchronization.
|
|
4
4
|
* Defines methods needed by PatchesServer, PatchesHistoryManager, etc.
|
|
@@ -18,7 +18,7 @@ export interface PatchesStoreBackend {
|
|
|
18
18
|
*/
|
|
19
19
|
createVersion(docId: string, metadata: VersionMetadata, state: any, changes: Change[]): Promise<void>;
|
|
20
20
|
/** Update a version's metadata. */
|
|
21
|
-
updateVersion(docId: string, versionId: string, metadata:
|
|
21
|
+
updateVersion(docId: string, versionId: string, metadata: EditableVersionMetadata): Promise<void>;
|
|
22
22
|
/** Lists version metadata based on filtering/sorting options. */
|
|
23
23
|
listVersions(docId: string, options: ListVersionsOptions): Promise<VersionMetadata[]>;
|
|
24
24
|
/** Loads the state snapshot for a specific version ID. */
|
package/dist/types.d.ts
CHANGED
|
@@ -51,6 +51,7 @@ export interface Branch {
|
|
|
51
51
|
/** Optional arbitrary metadata associated with the branch record. */
|
|
52
52
|
[metadata: string]: any;
|
|
53
53
|
}
|
|
54
|
+
export type EditableBranchMetadata = Disallowed<Branch, 'id' | 'branchedFromId' | 'branchedRev' | 'created' | 'status'>;
|
|
54
55
|
/**
|
|
55
56
|
* Metadata, state snapshot, and included changes for a specific version.
|
|
56
57
|
*/
|
|
@@ -77,6 +78,10 @@ export interface VersionMetadata {
|
|
|
77
78
|
/** Optional arbitrary metadata associated with the version. */
|
|
78
79
|
[metadata: string]: any;
|
|
79
80
|
}
|
|
81
|
+
type Disallowed<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>> & {
|
|
82
|
+
[P in K]?: never;
|
|
83
|
+
};
|
|
84
|
+
export type EditableVersionMetadata = Disallowed<VersionMetadata, 'id' | 'parentId' | 'groupId' | 'origin' | 'branchName' | 'startDate' | 'endDate' | 'rev' | 'baseRev'>;
|
|
80
85
|
/**
|
|
81
86
|
* Options for listing committed server changes. *Always* ordered by revision number.
|
|
82
87
|
*/
|
|
@@ -117,3 +122,4 @@ export interface Deferred<T = void> {
|
|
|
117
122
|
reject: (reason?: any) => void;
|
|
118
123
|
status: 'pending' | 'fulfilled' | 'rejected';
|
|
119
124
|
}
|
|
125
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dabble/patches",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.20",
|
|
4
4
|
"description": "Immutable JSON Patch implementation based on RFC 6902 supporting operational transformation and last-writer-wins",
|
|
5
5
|
"author": "Jacob Wright <jacwright@gmail.com>",
|
|
6
6
|
"bugs": {
|