@fluid-experimental/tree 0.59.1000 → 0.59.2000-63294

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 (68) hide show
  1. package/api-extractor.json +5 -9
  2. package/dist/Checkout.js +1 -1
  3. package/dist/Checkout.js.map +1 -1
  4. package/dist/SharedTree.d.ts +16 -10
  5. package/dist/SharedTree.d.ts.map +1 -1
  6. package/dist/SharedTree.js +22 -10
  7. package/dist/SharedTree.js.map +1 -1
  8. package/dist/SummaryTestUtilities.js.map +1 -1
  9. package/dist/Transaction.d.ts +22 -2
  10. package/dist/Transaction.d.ts.map +1 -1
  11. package/dist/Transaction.js +26 -6
  12. package/dist/Transaction.js.map +1 -1
  13. package/dist/index.d.ts +2 -2
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +2 -1
  16. package/dist/index.js.map +1 -1
  17. package/dist/persisted-types/0.1.1.d.ts +9 -0
  18. package/dist/persisted-types/0.1.1.d.ts.map +1 -1
  19. package/dist/persisted-types/0.1.1.js.map +1 -1
  20. package/lib/Checkout.js +1 -1
  21. package/lib/Checkout.js.map +1 -1
  22. package/lib/SharedTree.d.ts +16 -10
  23. package/lib/SharedTree.d.ts.map +1 -1
  24. package/lib/SharedTree.js +22 -10
  25. package/lib/SharedTree.js.map +1 -1
  26. package/lib/SummaryTestUtilities.js.map +1 -1
  27. package/lib/Transaction.d.ts +22 -2
  28. package/lib/Transaction.d.ts.map +1 -1
  29. package/lib/Transaction.js +25 -5
  30. package/lib/Transaction.js.map +1 -1
  31. package/lib/index.d.ts +2 -2
  32. package/lib/index.d.ts.map +1 -1
  33. package/lib/index.js +1 -1
  34. package/lib/index.js.map +1 -1
  35. package/lib/persisted-types/0.1.1.d.ts +9 -0
  36. package/lib/persisted-types/0.1.1.d.ts.map +1 -1
  37. package/lib/persisted-types/0.1.1.js.map +1 -1
  38. package/lib/test/Forest.perf.tests.js +5 -3
  39. package/lib/test/Forest.perf.tests.js.map +1 -1
  40. package/lib/test/IdCompressor.tests.js +1 -1
  41. package/lib/test/IdCompressor.tests.js.map +1 -1
  42. package/lib/test/Summary.tests.d.ts.map +1 -1
  43. package/lib/test/Summary.tests.js +3 -3
  44. package/lib/test/Summary.tests.js.map +1 -1
  45. package/lib/test/Transaction.tests.js +36 -2
  46. package/lib/test/Transaction.tests.js.map +1 -1
  47. package/lib/test/fuzz/SharedTreeFuzzTests.js +2 -2
  48. package/lib/test/fuzz/SharedTreeFuzzTests.js.map +1 -1
  49. package/lib/test/utilities/PendingLocalStateTests.d.ts.map +1 -1
  50. package/lib/test/utilities/PendingLocalStateTests.js +4 -4
  51. package/lib/test/utilities/PendingLocalStateTests.js.map +1 -1
  52. package/lib/test/utilities/SharedTreeTests.d.ts.map +1 -1
  53. package/lib/test/utilities/SharedTreeTests.js +59 -7
  54. package/lib/test/utilities/SharedTreeTests.js.map +1 -1
  55. package/lib/test/utilities/SharedTreeVersioningTests.js +1 -1
  56. package/lib/test/utilities/SharedTreeVersioningTests.js.map +1 -1
  57. package/lib/test/utilities/SummarySizeTests.js.map +1 -1
  58. package/lib/test/utilities/TestUtilities.d.ts +2 -0
  59. package/lib/test/utilities/TestUtilities.d.ts.map +1 -1
  60. package/lib/test/utilities/TestUtilities.js +9 -1
  61. package/lib/test/utilities/TestUtilities.js.map +1 -1
  62. package/package.json +21 -18
  63. package/src/Checkout.ts +1 -1
  64. package/src/SharedTree.ts +39 -19
  65. package/src/SummaryTestUtilities.ts +1 -1
  66. package/src/Transaction.ts +38 -6
  67. package/src/index.ts +2 -1
  68. package/src/persisted-types/0.1.1.ts +11 -0
package/src/SharedTree.ts CHANGED
@@ -66,6 +66,7 @@ import {
66
66
  ghostSessionId,
67
67
  WriteFormat,
68
68
  TreeNodeSequence,
69
+ InternalizedChange,
69
70
  } from './persisted-types';
70
71
  import { serialize, SummaryContents } from './Summary';
71
72
  import {
@@ -558,16 +559,8 @@ export class SharedTree extends SharedObject<ISharedTreeEvents> implements NodeI
558
559
  * @returns the edit history of the tree.
559
560
  * @public
560
561
  */
561
- public get edits(): OrderedEditSet {
562
- return this.editLog;
563
- }
564
-
565
- /**
566
- * @returns the edit history of the tree. The format of the contents of edits are subject to change and should not be relied upon.
567
- * @internal
568
- */
569
- public get editsInternal(): OrderedEditSet<ChangeInternal> {
570
- return this.editLog;
562
+ public get edits(): OrderedEditSet<InternalizedChange> {
563
+ return this.editLog as unknown as OrderedEditSet<InternalizedChange>;
571
564
  }
572
565
 
573
566
  private deserializeHandle(serializedHandle: string): IFluidHandle<ArrayBufferLike> {
@@ -1133,7 +1126,7 @@ export class SharedTree extends SharedObject<ISharedTreeEvents> implements NodeI
1133
1126
 
1134
1127
  const unifyHistoricalIds = (context: NodeIdContext): void => {
1135
1128
  for (let i = 0; i < this.editLog.numberOfSequencedEdits; i++) {
1136
- const edit = this.editsInternal.getEditInSessionAtIndex(i);
1129
+ const edit = this.editLog.getEditInSessionAtIndex(i);
1137
1130
  convertEditIds(edit, (id) => context.generateNodeId(this.convertToStableNodeId(id)));
1138
1131
  }
1139
1132
  };
@@ -1146,7 +1139,7 @@ export class SharedTree extends SharedObject<ISharedTreeEvents> implements NodeI
1146
1139
  unifyHistoricalIds(ghostContext);
1147
1140
  // The same logic applies to string interning, so intern all the strings in the history (superset of those in the current view)
1148
1141
  for (let i = 0; i < this.editLog.numberOfSequencedEdits; i++) {
1149
- this.internStringsFromEdit(this.editsInternal.getEditInSessionAtIndex(i));
1142
+ this.internStringsFromEdit(this.editLog.getEditInSessionAtIndex(i));
1150
1143
  }
1151
1144
  } else {
1152
1145
  // Clients do not have the full history, but all share the same current view (sequenced). They can all finalize the same final
@@ -1174,9 +1167,9 @@ export class SharedTree extends SharedObject<ISharedTreeEvents> implements NodeI
1174
1167
  * should be used instead.
1175
1168
  * @public
1176
1169
  */
1177
- public applyEdit(...changes: Change[]): Edit<unknown>;
1178
- public applyEdit(changes: Change[]): Edit<unknown>;
1179
- public applyEdit(headOrChanges: Change | Change[], ...tail: Change[]): Edit<unknown> {
1170
+ public applyEdit(...changes: Change[]): Edit<InternalizedChange>;
1171
+ public applyEdit(changes: Change[]): Edit<InternalizedChange>;
1172
+ public applyEdit(headOrChanges: Change | Change[], ...tail: Change[]): Edit<InternalizedChange> {
1180
1173
  const changes = Array.isArray(headOrChanges) ? headOrChanges : [headOrChanges, ...tail];
1181
1174
  const id = newEditId();
1182
1175
  const internalEdit: Edit<ChangeInternal> = {
@@ -1185,7 +1178,34 @@ export class SharedTree extends SharedObject<ISharedTreeEvents> implements NodeI
1185
1178
  };
1186
1179
  this.submitEditOp(internalEdit);
1187
1180
  this.applyEditLocally(internalEdit, undefined);
1188
- return internalEdit;
1181
+ return internalEdit as unknown as Edit<InternalizedChange>;
1182
+ }
1183
+
1184
+ /**
1185
+ * Merges `edits` from `other` into this SharedTree.
1186
+ * @param other - Tree containing the edits that should be applied to this one.
1187
+ * @param edits - Iterable of edits from `other` to apply.
1188
+ * @param stableIdRemapper - Optional remapper to translate stable identities from `other` into stable identities on this tree.
1189
+ * Any references that `other` contains to a stable id `foo` will be replaced with references to the id `stableIdRemapper(foo)`.
1190
+ *
1191
+ * Payloads on the edits are left intact.
1192
+ * @returns a list containing `EditId`s for all applied edits.
1193
+ */
1194
+ public mergeEditsFrom(
1195
+ other: SharedTree,
1196
+ edits: Iterable<Edit<InternalizedChange>>,
1197
+ stableIdRemapper?: (id: StableNodeId) => StableNodeId
1198
+ ): EditId[] {
1199
+ const idConverter = (id: NodeId) => {
1200
+ const stableId = other.convertToStableNodeId(id);
1201
+ const convertedStableId = stableIdRemapper?.(stableId) ?? stableId;
1202
+ return this.generateNodeId(convertedStableId);
1203
+ };
1204
+
1205
+ return Array.from(
1206
+ edits as unknown as Iterable<Edit<ChangeInternal>>,
1207
+ (edit) => this.applyEditInternal(convertEditIds(edit, (id) => idConverter(id))).id
1208
+ );
1189
1209
  }
1190
1210
 
1191
1211
  /**
@@ -1305,7 +1325,7 @@ export class SharedTree extends SharedObject<ISharedTreeEvents> implements NodeI
1305
1325
  */
1306
1326
  public revert(editId: EditId): EditId | undefined {
1307
1327
  const index = this.edits.getIndexOfId(editId);
1308
- const edit = this.editLog.getEditInSessionAtIndex(index);
1328
+ const edit = this.edits.getEditInSessionAtIndex(index);
1309
1329
  const before = this.logViewer.getRevisionViewInSession(index);
1310
1330
  const changes = this.revertChanges(edit.changes, before);
1311
1331
  if (changes === undefined) {
@@ -1322,8 +1342,8 @@ export class SharedTree extends SharedObject<ISharedTreeEvents> implements NodeI
1322
1342
  * @returns the inverse of `changes` or undefined if the changes could not be inverted for the given tree state.
1323
1343
  * @internal
1324
1344
  */
1325
- public revertChanges(changes: readonly ChangeInternal[], before: RevisionView): ChangeInternal[] | undefined {
1326
- return revert(changes, before);
1345
+ public revertChanges(changes: readonly InternalizedChange[], before: RevisionView): ChangeInternal[] | undefined {
1346
+ return revert(changes as unknown as readonly ChangeInternal[], before);
1327
1347
  }
1328
1348
 
1329
1349
  /**
@@ -29,7 +29,7 @@ export interface UploadedEditChunkContents {
29
29
  */
30
30
  export async function getUploadedEditChunkContents(sharedTree: SharedTree): Promise<UploadedEditChunkContents[]> {
31
31
  const editChunks: UploadedEditChunkContents[] = [];
32
- const { editChunks: editsOrHandles } = (sharedTree.edits as EditLog<ChangeInternal>).getEditLogSummary();
32
+ const { editChunks: editsOrHandles } = (sharedTree.edits as unknown as EditLog<ChangeInternal>).getEditLogSummary();
33
33
  for (const { chunk } of editsOrHandles) {
34
34
  if (!Array.isArray(chunk)) {
35
35
  const handle = chunk as FluidEditHandle;
@@ -3,6 +3,8 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
+ import { IErrorEvent } from '@fluidframework/common-definitions';
7
+ import { TypedEventEmitter } from '@fluidframework/common-utils';
6
8
  import { ChangeInternal, Edit, EditStatus } from './persisted-types';
7
9
  import { newEditId } from './EditUtilities';
8
10
  import { TreeView } from './TreeView';
@@ -11,11 +13,30 @@ import { SharedTree } from './SharedTree';
11
13
  import { GenericTransaction, TransactionInternal } from './TransactionInternal';
12
14
  import { CachingLogViewer } from './LogViewer';
13
15
 
16
+ /**
17
+ * An event emitted by a `Transaction` to indicate a state change. See {@link TransactionEvents} for event argument information.
18
+ * @public
19
+ */
20
+ export enum TransactionEvent {
21
+ /**
22
+ * `currentView` has changed from `before` to `after`
23
+ */
24
+ ViewChange = 'viewChange',
25
+ }
26
+
27
+ /**
28
+ * Events which may be emitted by `Transaction`
29
+ * @public
30
+ */
31
+ export interface TransactionEvents extends IErrorEvent {
32
+ (event: TransactionEvent.ViewChange, listener: (before: TreeView, after: TreeView) => void);
33
+ }
34
+
14
35
  /**
15
36
  * Buffers changes to be applied to an isolated view of a `SharedTree` over time before applying them directly to the tree itself as a
16
37
  * single edit
17
38
  */
18
- export class Transaction {
39
+ export class Transaction extends TypedEventEmitter<TransactionEvents> {
19
40
  /** The view of the tree when this transaction was created */
20
41
  public readonly startingView: TreeView;
21
42
  private readonly transaction: GenericTransaction;
@@ -26,6 +47,7 @@ export class Transaction {
26
47
  * @param tree - the `SharedTree` that this transaction applies changes to
27
48
  */
28
49
  public constructor(public readonly tree: SharedTree) {
50
+ super();
29
51
  const { currentView } = tree;
30
52
  this.transaction = new GenericTransaction(currentView, new TransactionInternal.Policy());
31
53
  this.startingView = currentView;
@@ -57,7 +79,8 @@ export class Transaction {
57
79
  /**
58
80
  * Attempt to apply a sequence of changes in this transaction. The `currentView` will be updated to reflect the new tree state after all
59
81
  * applied changes. If any change fails to apply, the remaining changes will be ignored and this transaction will be automatically
60
- * closed (see `isOpen`). If this transaction is already closed, this method has no effect.
82
+ * closed (see `isOpen`). If this transaction is already closed, this method has no effect. This method will emit a
83
+ * `TransactionEvent.ViewChange` event at most once per call.
61
84
  * @param changes - the changes to apply
62
85
  * @returns either the `EditStatus` of the given changes or the `EditStatus` of the last change before the transaction was closed
63
86
  */
@@ -66,7 +89,16 @@ export class Transaction {
66
89
  public apply(headOrChanges: Change | Change[], ...tail: Change[]): EditStatus {
67
90
  if (this.isOpen) {
68
91
  const changes = Array.isArray(headOrChanges) ? headOrChanges : [headOrChanges, ...tail];
69
- this.transaction.applyChanges(changes.map((c) => this.tree.internalizeChange(c)));
92
+ if (changes.length > 0) {
93
+ const previousView = this.currentView;
94
+ this.transaction.applyChanges(changes.map((c) => this.tree.internalizeChange(c)));
95
+ if (
96
+ this.listenerCount(TransactionEvent.ViewChange) > 0 &&
97
+ !previousView.hasEqualForest(this.currentView)
98
+ ) {
99
+ this.emit(TransactionEvent.ViewChange, previousView, this.currentView);
100
+ }
101
+ }
70
102
  }
71
103
  return this.status;
72
104
  }
@@ -77,10 +109,10 @@ export class Transaction {
77
109
  public closeAndCommit(): void {
78
110
  if (this.isOpen) {
79
111
  if (this.transaction.changes.length > 0) {
80
- const edit: Edit<ChangeInternal> = { id: newEditId(), changes: this.transaction.changes };
81
112
  const result = this.transaction.close();
82
- if (this.tree.editsInternal instanceof CachingLogViewer) {
83
- this.tree.editsInternal.setKnownEditingResult(edit, result);
113
+ const edit: Edit<ChangeInternal> = { id: newEditId(), changes: result.changes };
114
+ if (this.tree.edits instanceof CachingLogViewer) {
115
+ this.tree.edits.setKnownEditingResult(edit, result);
84
116
  }
85
117
  this.tree.applyEditInternal(edit);
86
118
  }
package/src/index.ts CHANGED
@@ -56,6 +56,7 @@ export {
56
56
  ConstraintEffect,
57
57
  Edit,
58
58
  ChangeInternal,
59
+ InternalizedChange,
59
60
  ChangeNode,
60
61
  ChangeNode_0_0_2,
61
62
  EditLogSummary,
@@ -135,7 +136,7 @@ export {
135
136
  } from './TreeView';
136
137
  export { RevisionView, TransactionView } from './RevisionView';
137
138
  export { NodeIdContext, NodeIdGenerator, NodeIdConverter } from './NodeIdUtilities';
138
- export { Transaction } from './Transaction';
139
+ export { Transaction, TransactionEvent, TransactionEvents } from './Transaction';
139
140
 
140
141
  /**
141
142
  * TODO:#61413: Publish test utilities from a separate test package
@@ -204,6 +204,17 @@ export interface CompressedBuildInternal<TId extends OpSpaceNodeId> {
204
204
  */
205
205
  export type CompressedBuildNode<TId extends OpSpaceNodeId> = CompressedPlaceholderTree<TId, DetachedSequenceId>;
206
206
 
207
+ // TODO: `ChangeInternal`s should be assignable to this type without casting; this will require some test refactoring.
208
+ /**
209
+ * This type should be used as an opaque handle in the public API for `ChangeInternal` objects.
210
+ * This is useful for supporting public APIs which involve working with a tree's edit history,
211
+ * which will involve changes that have already been internalized.
212
+ * @public
213
+ */
214
+ export interface InternalizedChange {
215
+ InternalChangeBrand: '2cae1045-61cf-4ef7-a6a3-8ad920cb7ab3';
216
+ }
217
+
207
218
  /**
208
219
  * {@inheritdoc (Change:type)}
209
220
  * @public