@dabble/patches 0.6.0 → 0.7.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.
Files changed (133) hide show
  1. package/README.md +221 -208
  2. package/dist/BaseDoc-DkP3tUhT.d.ts +206 -0
  3. package/dist/algorithms/lww/consolidateOps.d.ts +40 -0
  4. package/dist/algorithms/lww/consolidateOps.js +103 -0
  5. package/dist/algorithms/lww/mergeServerWithLocal.d.ts +22 -0
  6. package/dist/algorithms/lww/mergeServerWithLocal.js +32 -0
  7. package/dist/algorithms/{client → ot/client}/applyCommittedChanges.d.ts +10 -3
  8. package/dist/algorithms/{client → ot/client}/applyCommittedChanges.js +7 -4
  9. package/dist/algorithms/{client → ot/client}/createStateFromSnapshot.d.ts +3 -3
  10. package/dist/algorithms/{client → ot/client}/createStateFromSnapshot.js +1 -1
  11. package/dist/algorithms/ot/server/commitChanges.d.ts +43 -0
  12. package/dist/algorithms/{server → ot/server}/commitChanges.js +22 -7
  13. package/dist/algorithms/{server → ot/server}/createVersion.d.ts +5 -5
  14. package/dist/algorithms/{server → ot/server}/createVersion.js +2 -2
  15. package/dist/algorithms/{server → ot/server}/getSnapshotAtRevision.d.ts +5 -5
  16. package/dist/algorithms/{server → ot/server}/getSnapshotAtRevision.js +1 -1
  17. package/dist/algorithms/{server → ot/server}/getStateAtRevision.d.ts +5 -5
  18. package/dist/algorithms/{server → ot/server}/getStateAtRevision.js +1 -1
  19. package/dist/algorithms/{server → ot/server}/handleOfflineSessionsAndBatches.d.ts +5 -5
  20. package/dist/algorithms/{server → ot/server}/handleOfflineSessionsAndBatches.js +3 -3
  21. package/dist/algorithms/{server → ot/server}/transformIncomingChanges.d.ts +3 -3
  22. package/dist/algorithms/{server → ot/server}/transformIncomingChanges.js +3 -3
  23. package/dist/algorithms/{shared → ot/shared}/applyChanges.d.ts +3 -3
  24. package/dist/algorithms/{shared → ot/shared}/applyChanges.js +2 -2
  25. package/dist/algorithms/{shared → ot/shared}/changeBatching.d.ts +3 -3
  26. package/dist/algorithms/{shared → ot/shared}/changeBatching.js +2 -2
  27. package/dist/algorithms/{shared → ot/shared}/rebaseChanges.d.ts +3 -3
  28. package/dist/algorithms/{shared → ot/shared}/rebaseChanges.js +2 -2
  29. package/dist/client/BaseDoc.d.ts +6 -0
  30. package/dist/client/BaseDoc.js +70 -0
  31. package/dist/client/ClientAlgorithm.d.ts +101 -0
  32. package/dist/client/ClientAlgorithm.js +0 -0
  33. package/dist/client/InMemoryStore.d.ts +5 -7
  34. package/dist/client/InMemoryStore.js +7 -36
  35. package/dist/client/IndexedDBStore.d.ts +39 -73
  36. package/dist/client/IndexedDBStore.js +17 -220
  37. package/dist/client/LWWAlgorithm.d.ts +43 -0
  38. package/dist/client/LWWAlgorithm.js +87 -0
  39. package/dist/client/LWWClientStore.d.ts +73 -0
  40. package/dist/client/LWWClientStore.js +0 -0
  41. package/dist/client/LWWDoc.d.ts +56 -0
  42. package/dist/client/LWWDoc.js +84 -0
  43. package/dist/client/LWWInMemoryStore.d.ts +88 -0
  44. package/dist/client/LWWInMemoryStore.js +208 -0
  45. package/dist/client/LWWIndexedDBStore.d.ts +91 -0
  46. package/dist/client/LWWIndexedDBStore.js +275 -0
  47. package/dist/client/OTAlgorithm.d.ts +42 -0
  48. package/dist/client/OTAlgorithm.js +113 -0
  49. package/dist/client/OTClientStore.d.ts +50 -0
  50. package/dist/client/OTClientStore.js +0 -0
  51. package/dist/client/OTDoc.d.ts +6 -0
  52. package/dist/client/OTDoc.js +97 -0
  53. package/dist/client/OTIndexedDBStore.d.ts +84 -0
  54. package/dist/client/OTIndexedDBStore.js +163 -0
  55. package/dist/client/Patches.d.ts +36 -16
  56. package/dist/client/Patches.js +60 -27
  57. package/dist/client/PatchesDoc.d.ts +4 -113
  58. package/dist/client/PatchesDoc.js +3 -153
  59. package/dist/client/PatchesHistoryClient.js +1 -1
  60. package/dist/client/PatchesStore.d.ts +8 -105
  61. package/dist/client/factories.d.ts +72 -0
  62. package/dist/client/factories.js +80 -0
  63. package/dist/client/index.d.ts +14 -5
  64. package/dist/client/index.js +9 -0
  65. package/dist/compression/index.d.ts +2 -2
  66. package/dist/compression/index.js +1 -1
  67. package/dist/{algorithms/shared → compression}/lz.js +1 -1
  68. package/dist/data/change.js +2 -0
  69. package/dist/fractionalIndex.d.ts +67 -0
  70. package/dist/fractionalIndex.js +241 -0
  71. package/dist/index.d.ts +13 -3
  72. package/dist/index.js +1 -0
  73. package/dist/json-patch/types.d.ts +2 -0
  74. package/dist/net/PatchesClient.js +15 -15
  75. package/dist/net/PatchesSync.d.ts +20 -8
  76. package/dist/net/PatchesSync.js +57 -65
  77. package/dist/net/index.d.ts +7 -11
  78. package/dist/net/index.js +6 -1
  79. package/dist/net/protocol/JSONRPCClient.d.ts +4 -4
  80. package/dist/net/protocol/JSONRPCClient.js +6 -4
  81. package/dist/net/protocol/JSONRPCServer.d.ts +45 -9
  82. package/dist/net/protocol/JSONRPCServer.js +63 -8
  83. package/dist/net/serverContext.d.ts +38 -0
  84. package/dist/net/serverContext.js +20 -0
  85. package/dist/net/webrtc/WebRTCTransport.js +1 -1
  86. package/dist/net/websocket/AuthorizationProvider.d.ts +3 -3
  87. package/dist/net/websocket/WebSocketServer.d.ts +29 -20
  88. package/dist/net/websocket/WebSocketServer.js +23 -12
  89. package/dist/server/BranchManager.d.ts +50 -0
  90. package/dist/server/BranchManager.js +0 -0
  91. package/dist/server/CompressedStoreBackend.d.ts +10 -8
  92. package/dist/server/CompressedStoreBackend.js +7 -13
  93. package/dist/server/LWWBranchManager.d.ts +82 -0
  94. package/dist/server/LWWBranchManager.js +99 -0
  95. package/dist/server/LWWMemoryStoreBackend.d.ts +78 -0
  96. package/dist/server/LWWMemoryStoreBackend.js +182 -0
  97. package/dist/server/LWWServer.d.ts +130 -0
  98. package/dist/server/LWWServer.js +214 -0
  99. package/dist/server/{PatchesBranchManager.d.ts → OTBranchManager.d.ts} +32 -12
  100. package/dist/server/{PatchesBranchManager.js → OTBranchManager.js} +27 -42
  101. package/dist/server/OTServer.d.ts +108 -0
  102. package/dist/server/OTServer.js +141 -0
  103. package/dist/server/PatchesHistoryManager.d.ts +21 -16
  104. package/dist/server/PatchesHistoryManager.js +23 -11
  105. package/dist/server/PatchesServer.d.ts +70 -81
  106. package/dist/server/PatchesServer.js +0 -175
  107. package/dist/server/branchUtils.d.ts +82 -0
  108. package/dist/server/branchUtils.js +66 -0
  109. package/dist/server/index.d.ts +18 -7
  110. package/dist/server/index.js +33 -4
  111. package/dist/server/tombstone.d.ts +29 -0
  112. package/dist/server/tombstone.js +32 -0
  113. package/dist/server/types.d.ts +109 -27
  114. package/dist/server/utils.d.ts +12 -0
  115. package/dist/server/utils.js +23 -0
  116. package/dist/solid/context.d.ts +4 -3
  117. package/dist/solid/doc-manager.d.ts +3 -3
  118. package/dist/solid/index.d.ts +4 -3
  119. package/dist/solid/primitives.d.ts +2 -3
  120. package/dist/types.d.ts +4 -2
  121. package/dist/vue/composables.d.ts +2 -3
  122. package/dist/vue/doc-manager.d.ts +3 -3
  123. package/dist/vue/index.d.ts +4 -3
  124. package/dist/vue/provider.d.ts +4 -3
  125. package/package.json +1 -1
  126. package/dist/algorithms/client/collapsePendingChanges.d.ts +0 -30
  127. package/dist/algorithms/client/collapsePendingChanges.js +0 -78
  128. package/dist/algorithms/client/makeChange.d.ts +0 -9
  129. package/dist/algorithms/client/makeChange.js +0 -29
  130. package/dist/algorithms/server/commitChanges.d.ts +0 -19
  131. package/dist/net/websocket/RPCServer.d.ts +0 -141
  132. package/dist/net/websocket/RPCServer.js +0 -204
  133. /package/dist/{algorithms/shared → compression}/lz.d.ts +0 -0
@@ -1,14 +1,24 @@
1
1
  import "../chunk-IZ2YBCUP.js";
2
- import { createId } from "crypto-id";
3
- import { breakChanges } from "../algorithms/shared/changeBatching.js";
2
+ import { getStateAtRevision } from "../algorithms/ot/server/getStateAtRevision.js";
3
+ import { breakChanges } from "../algorithms/ot/shared/changeBatching.js";
4
4
  import { createChange } from "../data/change.js";
5
5
  import { createVersionMetadata } from "../data/version.js";
6
- class PatchesBranchManager {
6
+ import {
7
+ assertBranchMetadata,
8
+ assertBranchOpenForMerge,
9
+ assertNotABranch,
10
+ branchManagerApi,
11
+ createBranchRecord,
12
+ generateBranchId,
13
+ wrapMergeCommit
14
+ } from "./branchUtils.js";
15
+ class OTBranchManager {
7
16
  constructor(store, patchesServer, maxPayloadBytes) {
8
17
  this.store = store;
9
18
  this.patchesServer = patchesServer;
10
19
  this.maxPayloadBytes = maxPayloadBytes;
11
20
  }
21
+ static api = branchManagerApi;
12
22
  /**
13
23
  * Lists all open branches for a document.
14
24
  * @param docId - The ID of the document.
@@ -26,12 +36,9 @@ class PatchesBranchManager {
26
36
  * @returns The ID of the new branch document.
27
37
  */
28
38
  async createBranch(docId, rev, metadata) {
29
- const maybeBranch = await this.store.loadBranch(docId);
30
- if (maybeBranch) {
31
- throw new Error("Cannot create a branch from another branch.");
32
- }
33
- const stateAtRev = (await this.patchesServer.getStateAtRevision(docId, rev)).state;
34
- const branchDocId = this.store.createBranchId ? await Promise.resolve(this.store.createBranchId(docId)) : createId(22);
39
+ await assertNotABranch(this.store, docId);
40
+ const { state: stateAtRev } = await getStateAtRevision(this.store, docId, rev);
41
+ const branchDocId = await generateBranchId(this.store, docId);
35
42
  const now = Date.now();
36
43
  const initialVersionMetadata = createVersionMetadata({
37
44
  origin: "main",
@@ -45,14 +52,7 @@ class PatchesBranchManager {
45
52
  branchName: metadata?.name
46
53
  });
47
54
  await this.store.createVersion(branchDocId, initialVersionMetadata, stateAtRev, []);
48
- const branch = {
49
- ...metadata,
50
- id: branchDocId,
51
- docId,
52
- branchedAtRev: rev,
53
- createdAt: now,
54
- status: "open"
55
- };
55
+ const branch = createBranchRecord(branchDocId, docId, rev, metadata);
56
56
  await this.store.createBranch(branch);
57
57
  return branchDocId;
58
58
  }
@@ -81,12 +81,7 @@ class PatchesBranchManager {
81
81
  */
82
82
  async mergeBranch(branchId) {
83
83
  const branch = await this.store.loadBranch(branchId);
84
- if (!branch) {
85
- throw new Error(`Branch with ID ${branchId} not found.`);
86
- }
87
- if (branch.status !== "open") {
88
- throw new Error(`Branch ${branchId} is not open (status: ${branch.status}). Cannot merge.`);
89
- }
84
+ assertBranchOpenForMerge(branch, branchId);
90
85
  const sourceDocId = branch.docId;
91
86
  const branchStartRevOnSource = branch.branchedAtRev;
92
87
  const branchChanges = await this.store.listChanges(branchId, {});
@@ -113,12 +108,11 @@ class PatchesBranchManager {
113
108
  parentId: lastVersionId
114
109
  });
115
110
  const state = await this.store.loadVersionState(branchId, v.id);
116
- const changes = await this.store.loadVersionChanges(branchId, v.id);
111
+ const changes = await this.store.loadVersionChanges?.(branchId, v.id);
117
112
  await this.store.createVersion(sourceDocId, newVersionMetadata, state, changes);
118
113
  lastVersionId = newVersionMetadata.id;
119
114
  }
120
- let committedMergeChanges = [];
121
- try {
115
+ const committedMergeChanges = await wrapMergeCommit(branchId, sourceDocId, async () => {
122
116
  if (canFastForward) {
123
117
  const adjustedChanges = branchChanges.map((c) => ({
124
118
  ...c,
@@ -126,7 +120,7 @@ class PatchesBranchManager {
126
120
  rev: void 0
127
121
  // Let commitChanges assign sequential revs
128
122
  }));
129
- [, committedMergeChanges] = await this.patchesServer.commitChanges(sourceDocId, adjustedChanges);
123
+ return this.patchesServer.commitChanges(sourceDocId, adjustedChanges);
130
124
  } else {
131
125
  const rev = branchStartRevOnSource + branchChanges.length;
132
126
  const flattenedChange = createChange(
@@ -138,26 +132,17 @@ class PatchesBranchManager {
138
132
  if (this.maxPayloadBytes) {
139
133
  changesToCommit = breakChanges(changesToCommit, this.maxPayloadBytes);
140
134
  }
141
- [, committedMergeChanges] = await this.patchesServer.commitChanges(sourceDocId, changesToCommit);
135
+ return this.patchesServer.commitChanges(sourceDocId, changesToCommit);
142
136
  }
143
- } catch (error) {
144
- console.error(`Failed to merge branch ${branchId} into ${sourceDocId}:`, error);
145
- throw new Error(`Merge failed: ${error instanceof Error ? error.message : String(error)}`);
146
- }
137
+ });
147
138
  await this.closeBranch(branchId, "merged");
148
139
  return committedMergeChanges;
149
140
  }
150
141
  }
151
- const nonModifiableMetadataFields = /* @__PURE__ */ new Set(["id", "docId", "branchedAtRev", "createdAt", "status"]);
152
- function assertBranchMetadata(metadata) {
153
- if (!metadata) return;
154
- for (const key in metadata) {
155
- if (nonModifiableMetadataFields.has(key)) {
156
- throw new Error(`Cannot modify branch field ${key}`);
157
- }
158
- }
159
- }
142
+ import { assertBranchMetadata as assertBranchMetadata2 } from "./branchUtils.js";
143
+ const PatchesBranchManager = OTBranchManager;
160
144
  export {
145
+ OTBranchManager,
161
146
  PatchesBranchManager,
162
- assertBranchMetadata
147
+ assertBranchMetadata2 as assertBranchMetadata
163
148
  };
@@ -0,0 +1,108 @@
1
+ import { Signal } from '../event-signal.js';
2
+ import { PatchesServer } from './PatchesServer.js';
3
+ import { OTStoreBackend } from './types.js';
4
+ import { Change, DeleteDocOptions, PatchesState, ChangeInput, CommitChangesOptions, ChangeMutator, EditableVersionMetadata } from '../types.js';
5
+ import { ApiDefinition } from '../net/protocol/JSONRPCServer.js';
6
+ import '../json-patch/JSONPatch.js';
7
+ import '@dabble/delta';
8
+ import '../json-patch/types.js';
9
+ import '../net/websocket/AuthorizationProvider.js';
10
+ import '../net/protocol/types.js';
11
+
12
+ /**
13
+ * Configuration options for the OTServer.
14
+ */
15
+ interface OTServerOptions {
16
+ /**
17
+ * The maximum time difference in minutes between consecutive changes
18
+ * to be considered part of the same editing session for versioning.
19
+ * Defaults to 30 minutes.
20
+ */
21
+ sessionTimeoutMinutes?: number;
22
+ /**
23
+ * Maximum size in bytes for a single change's storage representation.
24
+ * Useful for databases with row size limits.
25
+ */
26
+ maxStorageBytes?: number;
27
+ }
28
+ /**
29
+ * Handles the server-side Operational Transformation (OT) logic,
30
+ * coordinating batches of changes, managing versioning based on sessions (including offline),
31
+ * and persisting data using a backend store.
32
+ *
33
+ * For compression support, wrap your store with CompressedStoreBackend before passing it:
34
+ * @example
35
+ * import { OTServer, CompressedStoreBackend } from '@dabble/patches/server';
36
+ * import { base64Compressor } from '@dabble/patches/compression';
37
+ *
38
+ * const compressedStore = new CompressedStoreBackend(store, base64Compressor);
39
+ * const server = new OTServer(compressedStore);
40
+ */
41
+ declare class OTServer implements PatchesServer {
42
+ /**
43
+ * Static API definition for use with JSONRPCServer.register().
44
+ * Maps method names to required access levels.
45
+ */
46
+ static api: ApiDefinition;
47
+ private readonly sessionTimeoutMillis;
48
+ private readonly maxStorageBytes?;
49
+ readonly store: OTStoreBackend;
50
+ /** Notifies listeners whenever a batch of changes is *successfully* committed. */
51
+ readonly onChangesCommitted: Signal<(docId: string, changes: Change[], originClientId?: string) => void>;
52
+ /** Notifies listeners when a document is deleted. */
53
+ readonly onDocDeleted: Signal<(docId: string, options?: DeleteDocOptions, originClientId?: string) => void>;
54
+ constructor(store: OTStoreBackend, options?: OTServerOptions);
55
+ /**
56
+ * Get the current state of a document.
57
+ * @param docId - The ID of the document.
58
+ * @returns The current state of the document.
59
+ */
60
+ getDoc(docId: string): Promise<PatchesState>;
61
+ /**
62
+ * Get changes that occurred after a specific revision.
63
+ * @param docId - The ID of the document.
64
+ * @param rev - The revision number.
65
+ * @returns The changes that occurred after the specified revision.
66
+ */
67
+ getChangesSince(docId: string, rev: number): Promise<Change[]>;
68
+ /**
69
+ * Commits a set of changes to a document, applying operational transformation as needed.
70
+ *
71
+ * Returns all changes the client needs to apply: both catchup changes (from other
72
+ * clients) and the client's own transformed changes. Only the new changes are
73
+ * broadcast to other clients.
74
+ *
75
+ * @param docId - The ID of the document.
76
+ * @param changes - The changes to commit.
77
+ * @param options - Optional commit settings (e.g., forceCommit for migrations).
78
+ * @returns Combined array of catchup changes followed by the client's committed changes.
79
+ */
80
+ commitChanges(docId: string, changes: ChangeInput[], options?: CommitChangesOptions): Promise<Change[]>;
81
+ /**
82
+ * Make a server-side change to a document.
83
+ * @param mutator
84
+ * @returns
85
+ */
86
+ change<T = Record<string, any>>(docId: string, mutator: ChangeMutator<T>, metadata?: Record<string, any>): Promise<Change | null>;
87
+ /**
88
+ * Deletes a document.
89
+ * @param docId The document ID.
90
+ * @param options - Optional deletion settings (e.g., skipTombstone for testing).
91
+ */
92
+ deleteDoc(docId: string, options?: DeleteDocOptions): Promise<void>;
93
+ /**
94
+ * Removes the tombstone for a deleted document, allowing it to be recreated.
95
+ * @param docId The document ID.
96
+ * @returns True if tombstone was found and removed, false if no tombstone existed.
97
+ */
98
+ undeleteDoc(docId: string): Promise<boolean>;
99
+ /**
100
+ * Captures the current state of a document as a new version.
101
+ * @param docId The document ID.
102
+ * @param metadata Optional metadata for the version.
103
+ * @returns The ID of the created version.
104
+ */
105
+ captureCurrentVersion(docId: string, metadata?: EditableVersionMetadata): Promise<string | null>;
106
+ }
107
+
108
+ export { CommitChangesOptions, OTServer, type OTServerOptions };
@@ -0,0 +1,141 @@
1
+ import "../chunk-IZ2YBCUP.js";
2
+ import { commitChanges } from "../algorithms/ot/server/commitChanges.js";
3
+ import { createVersion } from "../algorithms/ot/server/createVersion.js";
4
+ import { getSnapshotAtRevision } from "../algorithms/ot/server/getSnapshotAtRevision.js";
5
+ import { getStateAtRevision } from "../algorithms/ot/server/getStateAtRevision.js";
6
+ import { applyChanges } from "../algorithms/ot/shared/applyChanges.js";
7
+ import { createChange } from "../data/change.js";
8
+ import { signal } from "../event-signal.js";
9
+ import { createJSONPatch } from "../json-patch/createJSONPatch.js";
10
+ import { getClientId } from "../net/serverContext.js";
11
+ import { createTombstoneIfSupported, removeTombstoneIfExists } from "./tombstone.js";
12
+ import { assertVersionMetadata } from "./utils.js";
13
+ class OTServer {
14
+ /**
15
+ * Static API definition for use with JSONRPCServer.register().
16
+ * Maps method names to required access levels.
17
+ */
18
+ static api = {
19
+ getDoc: "read",
20
+ getChangesSince: "read",
21
+ commitChanges: "write",
22
+ deleteDoc: "write",
23
+ undeleteDoc: "write"
24
+ };
25
+ sessionTimeoutMillis;
26
+ maxStorageBytes;
27
+ store;
28
+ /** Notifies listeners whenever a batch of changes is *successfully* committed. */
29
+ onChangesCommitted = signal();
30
+ /** Notifies listeners when a document is deleted. */
31
+ onDocDeleted = signal();
32
+ constructor(store, options = {}) {
33
+ this.sessionTimeoutMillis = (options.sessionTimeoutMinutes ?? 30) * 60 * 1e3;
34
+ this.maxStorageBytes = options.maxStorageBytes;
35
+ this.store = store;
36
+ }
37
+ /**
38
+ * Get the current state of a document.
39
+ * @param docId - The ID of the document.
40
+ * @returns The current state of the document.
41
+ */
42
+ async getDoc(docId) {
43
+ return getStateAtRevision(this.store, docId);
44
+ }
45
+ /**
46
+ * Get changes that occurred after a specific revision.
47
+ * @param docId - The ID of the document.
48
+ * @param rev - The revision number.
49
+ * @returns The changes that occurred after the specified revision.
50
+ */
51
+ getChangesSince(docId, rev) {
52
+ return this.store.listChanges(docId, { startAfter: rev });
53
+ }
54
+ /**
55
+ * Commits a set of changes to a document, applying operational transformation as needed.
56
+ *
57
+ * Returns all changes the client needs to apply: both catchup changes (from other
58
+ * clients) and the client's own transformed changes. Only the new changes are
59
+ * broadcast to other clients.
60
+ *
61
+ * @param docId - The ID of the document.
62
+ * @param changes - The changes to commit.
63
+ * @param options - Optional commit settings (e.g., forceCommit for migrations).
64
+ * @returns Combined array of catchup changes followed by the client's committed changes.
65
+ */
66
+ async commitChanges(docId, changes, options) {
67
+ const { catchupChanges, newChanges } = await commitChanges(
68
+ this.store,
69
+ docId,
70
+ changes,
71
+ this.sessionTimeoutMillis,
72
+ options,
73
+ this.maxStorageBytes
74
+ );
75
+ if (newChanges.length > 0) {
76
+ try {
77
+ await this.onChangesCommitted.emit(docId, newChanges, getClientId());
78
+ } catch (error) {
79
+ console.error(`Failed to notify clients about committed changes for doc ${docId}:`, error);
80
+ }
81
+ }
82
+ return [...catchupChanges, ...newChanges];
83
+ }
84
+ /**
85
+ * Make a server-side change to a document.
86
+ * @param mutator
87
+ * @returns
88
+ */
89
+ async change(docId, mutator, metadata) {
90
+ const { state, rev } = await this.getDoc(docId);
91
+ const patch = createJSONPatch(mutator);
92
+ if (patch.ops.length === 0) {
93
+ return null;
94
+ }
95
+ const change = createChange(rev, rev + 1, patch.ops, metadata);
96
+ patch.apply(state);
97
+ await this.commitChanges(docId, [change]);
98
+ return change;
99
+ }
100
+ /**
101
+ * Deletes a document.
102
+ * @param docId The document ID.
103
+ * @param options - Optional deletion settings (e.g., skipTombstone for testing).
104
+ */
105
+ async deleteDoc(docId, options) {
106
+ const clientId = getClientId();
107
+ const { rev } = await this.getDoc(docId);
108
+ await createTombstoneIfSupported(this.store, docId, rev, clientId, options?.skipTombstone);
109
+ await this.store.deleteDoc(docId);
110
+ await this.onDocDeleted.emit(docId, options, clientId);
111
+ }
112
+ /**
113
+ * Removes the tombstone for a deleted document, allowing it to be recreated.
114
+ * @param docId The document ID.
115
+ * @returns True if tombstone was found and removed, false if no tombstone existed.
116
+ */
117
+ async undeleteDoc(docId) {
118
+ return removeTombstoneIfExists(this.store, docId);
119
+ }
120
+ // === Version Operations ===
121
+ /**
122
+ * Captures the current state of a document as a new version.
123
+ * @param docId The document ID.
124
+ * @param metadata Optional metadata for the version.
125
+ * @returns The ID of the created version.
126
+ */
127
+ async captureCurrentVersion(docId, metadata) {
128
+ assertVersionMetadata(metadata);
129
+ const { state: initialState, changes } = await getSnapshotAtRevision(this.store, docId);
130
+ let state = initialState;
131
+ state = applyChanges(state, changes);
132
+ const version = await createVersion(this.store, docId, state, changes, metadata);
133
+ if (!version) {
134
+ return null;
135
+ }
136
+ return version.id;
137
+ }
138
+ }
139
+ export {
140
+ OTServer
141
+ };
@@ -1,21 +1,26 @@
1
- import { ListVersionsOptions, VersionMetadata, EditableVersionMetadata, Change, ListChangesOptions } from '../types.js';
1
+ import { ApiDefinition } from '../net/protocol/JSONRPCServer.js';
2
+ import { ListVersionsOptions, VersionMetadata, EditableVersionMetadata, Change } from '../types.js';
2
3
  import { PatchesServer } from './PatchesServer.js';
4
+ import { VersioningStoreBackend } from './types.js';
5
+ import '../event-signal.js';
6
+ import '../net/websocket/AuthorizationProvider.js';
7
+ import '../json-patch/types.js';
3
8
  import '../json-patch/JSONPatch.js';
4
9
  import '@dabble/delta';
5
- import '../json-patch/types.js';
6
- import '../event-signal.js';
7
- import './types.js';
8
- import '../compression/index.js';
9
- import '../algorithms/shared/lz.js';
10
+ import '../net/protocol/types.js';
10
11
 
11
12
  /**
12
13
  * Helps retrieve historical information (versions, changes) for a document
13
- * using the new versioning model based on IDs and metadata.
14
+ * using the versioning model based on IDs and metadata.
15
+ *
16
+ * Works with any PatchesServer that implements captureCurrentVersion
17
+ * (both OTServer and LWWServer with VersioningStoreBackend).
14
18
  */
15
19
  declare class PatchesHistoryManager {
16
- private readonly patches;
17
- private readonly store;
18
- constructor(patches: PatchesServer);
20
+ protected readonly patches: PatchesServer;
21
+ static api: ApiDefinition;
22
+ protected readonly store: VersioningStoreBackend;
23
+ constructor(patches: PatchesServer, store: VersioningStoreBackend);
19
24
  /**
20
25
  * Lists version metadata for the document, supporting various filters.
21
26
  * @param docId - The ID of the document.
@@ -55,13 +60,13 @@ declare class PatchesHistoryManager {
55
60
  */
56
61
  getChangesForVersion(docId: string, versionId: string): Promise<Change[]>;
57
62
  /**
58
- * Lists committed server changes for the document, typically used for server-side processing
59
- * or deep history analysis based on raw revisions.
60
- * @param docId - The ID of the document.
61
- * @param options - Options like start/end revision, limit.
62
- * @returns The list of committed Change objects.
63
+ * Alias for getStateAtVersion for RPC API compatibility.
64
+ */
65
+ getVersionState(docId: string, versionId: string): Promise<any>;
66
+ /**
67
+ * Alias for getChangesForVersion for RPC API compatibility.
63
68
  */
64
- listServerChanges(docId: string, options?: ListChangesOptions): Promise<Change[]>;
69
+ getVersionChanges(docId: string, versionId: string): Promise<Change[]>;
65
70
  }
66
71
 
67
72
  export { PatchesHistoryManager };
@@ -1,10 +1,17 @@
1
1
  import "../chunk-IZ2YBCUP.js";
2
- import { assertVersionMetadata } from "./PatchesServer.js";
2
+ import { assertVersionMetadata } from "./utils.js";
3
3
  class PatchesHistoryManager {
4
- constructor(patches) {
4
+ constructor(patches, store) {
5
5
  this.patches = patches;
6
- this.store = patches.store;
6
+ this.store = store;
7
7
  }
8
+ static api = {
9
+ listVersions: "read",
10
+ createVersion: "write",
11
+ updateVersion: "write",
12
+ getVersionState: "read",
13
+ getVersionChanges: "read"
14
+ };
8
15
  store;
9
16
  /**
10
17
  * Lists version metadata for the document, supporting various filters.
@@ -63,21 +70,26 @@ class PatchesHistoryManager {
63
70
  */
64
71
  async getChangesForVersion(docId, versionId) {
65
72
  try {
66
- return await this.store.loadVersionChanges(docId, versionId);
73
+ return await this.store.loadVersionChanges?.(docId, versionId) ?? [];
67
74
  } catch (error) {
68
75
  console.error(`Failed to load changes for version ${versionId} of doc ${docId}.`, error);
69
76
  throw new Error(`Could not load changes for version ${versionId}.`);
70
77
  }
71
78
  }
79
+ // ---------------------------------------------------------------------------
80
+ // Alias methods for RPC API compatibility
81
+ // ---------------------------------------------------------------------------
72
82
  /**
73
- * Lists committed server changes for the document, typically used for server-side processing
74
- * or deep history analysis based on raw revisions.
75
- * @param docId - The ID of the document.
76
- * @param options - Options like start/end revision, limit.
77
- * @returns The list of committed Change objects.
83
+ * Alias for getStateAtVersion for RPC API compatibility.
84
+ */
85
+ async getVersionState(docId, versionId) {
86
+ return this.getStateAtVersion(docId, versionId);
87
+ }
88
+ /**
89
+ * Alias for getChangesForVersion for RPC API compatibility.
78
90
  */
79
- async listServerChanges(docId, options = {}) {
80
- return await this.store.listChanges(docId, options);
91
+ async getVersionChanges(docId, versionId) {
92
+ return this.getChangesForVersion(docId, versionId);
81
93
  }
82
94
  }
83
95
  export {