@dabble/patches 0.2.18 → 0.2.19

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.
@@ -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, branchName?: string, metadata?: Record<string, any>): Promise<string>;
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, branchName, metadata) {
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: branchName,
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 { PatchesServer } from './PatchesServer.js';
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, name: string): Promise<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, updates: Pick<VersionMetadata, 'name'>): Promise<void>;
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, name) {
29
- return await this.patches.createVersion(docId, name);
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, updates) {
38
- return this.store.updateVersion(docId, versionId, { name: updates.name });
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, name: string): Promise<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 name The name of the version.
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[], name?: string): Promise<VersionMetadata | undefined>;
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, name) {
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, name);
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 name The name of the version.
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, name) {
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
+ }
@@ -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: Partial<VersionMetadata>): Promise<void>;
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.18",
3
+ "version": "0.2.19",
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": {