@fluidframework/tree 2.50.0-345060 → 2.50.0

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 (46) hide show
  1. package/CHANGELOG.md +100 -0
  2. package/dist/packageVersion.d.ts +1 -1
  3. package/dist/packageVersion.d.ts.map +1 -1
  4. package/dist/packageVersion.js +1 -1
  5. package/dist/packageVersion.js.map +1 -1
  6. package/dist/shared-tree-core/defaultResubmitMachine.d.ts +8 -12
  7. package/dist/shared-tree-core/defaultResubmitMachine.d.ts.map +1 -1
  8. package/dist/shared-tree-core/defaultResubmitMachine.js +54 -46
  9. package/dist/shared-tree-core/defaultResubmitMachine.js.map +1 -1
  10. package/dist/shared-tree-core/resubmitMachine.d.ts +6 -0
  11. package/dist/shared-tree-core/resubmitMachine.d.ts.map +1 -1
  12. package/dist/shared-tree-core/resubmitMachine.js.map +1 -1
  13. package/dist/shared-tree-core/sharedTreeCore.d.ts +1 -0
  14. package/dist/shared-tree-core/sharedTreeCore.d.ts.map +1 -1
  15. package/dist/shared-tree-core/sharedTreeCore.js +16 -3
  16. package/dist/shared-tree-core/sharedTreeCore.js.map +1 -1
  17. package/dist/simple-tree/api/configuration.js +4 -4
  18. package/dist/simple-tree/api/configuration.js.map +1 -1
  19. package/dist/simple-tree/unhydratedFlexTreeFromInsertable.js +1 -1
  20. package/dist/simple-tree/unhydratedFlexTreeFromInsertable.js.map +1 -1
  21. package/lib/packageVersion.d.ts +1 -1
  22. package/lib/packageVersion.d.ts.map +1 -1
  23. package/lib/packageVersion.js +1 -1
  24. package/lib/packageVersion.js.map +1 -1
  25. package/lib/shared-tree-core/defaultResubmitMachine.d.ts +8 -12
  26. package/lib/shared-tree-core/defaultResubmitMachine.d.ts.map +1 -1
  27. package/lib/shared-tree-core/defaultResubmitMachine.js +55 -47
  28. package/lib/shared-tree-core/defaultResubmitMachine.js.map +1 -1
  29. package/lib/shared-tree-core/resubmitMachine.d.ts +6 -0
  30. package/lib/shared-tree-core/resubmitMachine.d.ts.map +1 -1
  31. package/lib/shared-tree-core/resubmitMachine.js.map +1 -1
  32. package/lib/shared-tree-core/sharedTreeCore.d.ts +1 -0
  33. package/lib/shared-tree-core/sharedTreeCore.d.ts.map +1 -1
  34. package/lib/shared-tree-core/sharedTreeCore.js +16 -3
  35. package/lib/shared-tree-core/sharedTreeCore.js.map +1 -1
  36. package/lib/simple-tree/api/configuration.js +4 -4
  37. package/lib/simple-tree/api/configuration.js.map +1 -1
  38. package/lib/simple-tree/unhydratedFlexTreeFromInsertable.js +1 -1
  39. package/lib/simple-tree/unhydratedFlexTreeFromInsertable.js.map +1 -1
  40. package/package.json +20 -20
  41. package/src/packageVersion.ts +1 -1
  42. package/src/shared-tree-core/defaultResubmitMachine.ts +99 -52
  43. package/src/shared-tree-core/resubmitMachine.ts +7 -0
  44. package/src/shared-tree-core/sharedTreeCore.ts +18 -6
  45. package/src/simple-tree/api/configuration.ts +4 -4
  46. package/src/simple-tree/unhydratedFlexTreeFromInsertable.ts +1 -1
@@ -2,7 +2,7 @@
2
2
  * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
3
  * Licensed under the MIT License.
4
4
  */
5
- import { assert, oob } from "@fluidframework/core-utils/internal";
5
+ import { assert, DoublyLinkedList, oob, } from "@fluidframework/core-utils/internal";
6
6
  import { disposeSymbol, hasSome } from "../util/index.js";
7
7
  /**
8
8
  * Default implementation of {@link ResubmitMachine}.
@@ -23,39 +23,49 @@ export class DefaultResubmitMachine {
23
23
  /**
24
24
  * The list of commits (from oldest to most recent) that have been submitted but not sequenced.
25
25
  */
26
- this.inFlightQueue = [];
26
+ this.inFlightQueue = new DoublyLinkedList();
27
27
  /**
28
- * The list of commits (from oldest to most recent) that should be resubmitted.
28
+ * The current enrichment version for in-flight commits.
29
+ * Incremented when a peer commit is sequenced.
29
30
  */
30
- this.resubmitQueue = [];
31
- /**
32
- * Represents the index in the `inFlightQueue` array of the most recent in flight commit that has
33
- * undergone rebasing but whose enrichments have not been updated.
34
- * All in-flight commits with an index inferior or equal to this number have stale enrichments.
35
- *
36
- * Is -1 when *any* of the following is true:
37
- * - There are no in-flight commits (i.e., no local commits have been made or they have all been sequenced)
38
- * - None of the in-flight commits have been rebased
39
- * - In-flight commits that have been rebased have all had their enrichments updated
40
- */
41
- this.latestInFlightCommitWithStaleEnrichments = -1;
31
+ this.currentEnrichment = 0;
42
32
  }
43
33
  onCommitSubmitted(commit) {
44
- if (this.isInResubmitPhase) {
45
- const toResubmit = this.resubmitQueue.shift();
46
- assert(toResubmit === commit, 0x981 /* Unexpected commit submitted during resubmit phase */);
34
+ if (this.pendingResubmitRange !== undefined) {
35
+ const toResubmit = this.pendingResubmitRange?.first;
36
+ assert(toResubmit?.data.commit === commit, 0x981 /* Unexpected commit submitted during resubmit phase */);
37
+ // If we are not at the last commit to resubmit, advance the range to the next node.
38
+ // Otherwise, clear the resubmit range as we are done resubmitting.
39
+ if (toResubmit !== this.pendingResubmitRange.last) {
40
+ assert(toResubmit.next !== undefined, 0xbd6 /* must be more in the list */);
41
+ this.pendingResubmitRange.first = toResubmit.next;
42
+ }
43
+ else {
44
+ this.pendingResubmitRange = undefined;
45
+ }
46
+ toResubmit.remove();
47
47
  }
48
- this.inFlightQueue.push(commit);
48
+ this.inFlightQueue.push({ commit, lastEnrichment: this.currentEnrichment });
49
+ }
50
+ onCommitRollback(commit) {
51
+ assert(commit.revision === this.inFlightQueue.last?.data.commit.revision, 0xbd7 /* must rollback latest commit in the in flight queue */);
52
+ this.inFlightQueue.pop();
49
53
  }
50
54
  prepareForResubmit(toResubmit) {
51
55
  assert(!this.isInResubmitPhase, 0x957 /* Invalid resubmit phase start during incomplete resubmit phase */);
52
- assert(toResubmit.length === this.inFlightQueue.length, 0x958 /* Unexpected resubmit of more or fewer commits than are in flight */);
53
- if (this.latestInFlightCommitWithStaleEnrichments === -1) {
54
- // No in-flight commits have stale enrichments, so we can resubmit them as is
55
- this.resubmitQueue = this.inFlightQueue;
56
- this.inFlightQueue = [];
56
+ if (!hasSome(toResubmit)) {
57
+ return;
57
58
  }
58
- else {
59
+ assert(toResubmit.length <= this.inFlightQueue.length, 0xbd8 /* Unexpected resubmit of more commits than are in flight */);
60
+ // Find the first in-flight commit to resubmit.
61
+ const first = this.inFlightQueue.find((v) => v.data.commit.revision === toResubmit[0].revision);
62
+ // Always resubmit to the end of all outstanding ops, but the list may grow during resubmit,
63
+ // so we must track the current end at the start of the phase.
64
+ const last = this.inFlightQueue.last;
65
+ assert(first !== undefined && last !== undefined, 0xbd9 /* there must be inflight commits to resubmit */);
66
+ this.pendingResubmitRange = { first, last };
67
+ // If any in-flight commits have stale enrichments, recompute them.
68
+ if (first.data.lastEnrichment < this.currentEnrichment) {
59
69
  const checkout = this.tip.fork();
60
70
  // Roll back the checkout to the state before the oldest commit
61
71
  for (let iCommit = toResubmit.length - 1; iCommit >= 0; iCommit -= 1) {
@@ -66,46 +76,44 @@ export class DefaultResubmitMachine {
66
76
  // forwards from an earlier fork instead of backwards.
67
77
  checkout.applyTipChange(rollback);
68
78
  }
69
- // Update the enrichments of the stale commits
70
- for (let iCommit = 0; iCommit <= this.latestInFlightCommitWithStaleEnrichments; iCommit += 1) {
71
- const commit = toResubmit[iCommit] ?? oob();
72
- const enrichedChange = checkout.updateChangeEnrichments(commit.change, commit.revision);
73
- const enrichedCommit = { ...commit, change: enrichedChange };
74
- this.resubmitQueue.push(enrichedCommit);
75
- if (iCommit < this.latestInFlightCommitWithStaleEnrichments) {
76
- checkout.applyTipChange(enrichedChange, commit.revision);
79
+ // Update the enrichments of the stale commits in the in-flight queue.
80
+ let current = first;
81
+ for (const commit of toResubmit) {
82
+ assert(current !== undefined, 0xbda /* there must be an inflight commit for each resubmit commit */);
83
+ current.data.commit = commit;
84
+ if (current.data.lastEnrichment < this.currentEnrichment) {
85
+ const enrichedChange = checkout.updateChangeEnrichments(commit.change, commit.revision);
86
+ const enrichedCommit = { ...commit, change: enrichedChange };
87
+ // Optimization: only apply the enriched change if the next commit also needs enrichment.
88
+ if (current.next !== undefined &&
89
+ current.next.data.lastEnrichment < this.currentEnrichment) {
90
+ checkout.applyTipChange(enrichedChange, commit.revision);
91
+ }
92
+ current.data.commit = enrichedCommit;
93
+ current.data.lastEnrichment = this.currentEnrichment;
77
94
  }
78
- this.inFlightQueue.shift();
95
+ current = current.next;
79
96
  }
80
97
  checkout[disposeSymbol]();
81
- // Whatever commits are left do not have stale enrichments
82
- for (const commit of this.inFlightQueue) {
83
- this.resubmitQueue.push(commit);
84
- }
85
- this.inFlightQueue.length = 0;
86
98
  }
87
- this.latestInFlightCommitWithStaleEnrichments = -1;
88
99
  }
89
100
  peekNextCommit() {
90
101
  assert(this.isInResubmitPhase, 0x982 /* No available commit to resubmit outside of resubmit phase */);
91
- assert(hasSome(this.resubmitQueue), 0xa87 /* Expected resubmit queue to be non-empty */);
92
- return this.resubmitQueue[0];
102
+ assert(this.pendingResubmitRange !== undefined, 0xa87 /* Expected resubmit queue to be non-empty */);
103
+ return this.pendingResubmitRange.first.data.commit;
93
104
  }
94
105
  get isInResubmitPhase() {
95
- return this.resubmitQueue.length !== 0;
106
+ return this.pendingResubmitRange !== undefined;
96
107
  }
97
108
  onSequencedCommitApplied(isLocal) {
98
109
  if (isLocal) {
99
110
  // The oldest in-flight commit has been sequenced
100
111
  assert(this.inFlightQueue.length > 0, 0x959 /* Sequencing of unknown local commit */);
101
112
  this.inFlightQueue.shift();
102
- if (this.latestInFlightCommitWithStaleEnrichments >= 0) {
103
- this.latestInFlightCommitWithStaleEnrichments -= 1;
104
- }
105
113
  }
106
114
  else {
107
115
  // A peer commit has been sequenced
108
- this.latestInFlightCommitWithStaleEnrichments = this.inFlightQueue.length - 1;
116
+ this.currentEnrichment++;
109
117
  }
110
118
  }
111
119
  }
@@ -1 +1 @@
1
- {"version":3,"file":"defaultResubmitMachine.js","sourceRoot":"","sources":["../../src/shared-tree-core/defaultResubmitMachine.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { assert, oob } from \"@fluidframework/core-utils/internal\";\n\nimport type { GraphCommit, TaggedChange } from \"../core/index.js\";\nimport { disposeSymbol, hasSome } from \"../util/index.js\";\n\nimport type { ChangeEnricherReadonlyCheckout, ResubmitMachine } from \"./index.js\";\n\n/**\n * Default implementation of {@link ResubmitMachine}.\n */\nexport class DefaultResubmitMachine<TChange> implements ResubmitMachine<TChange> {\n\t/**\n\t * The list of commits (from oldest to most recent) that have been submitted but not sequenced.\n\t */\n\tprivate inFlightQueue: GraphCommit<TChange>[] = [];\n\n\t/**\n\t * The list of commits (from oldest to most recent) that should be resubmitted.\n\t */\n\tprivate resubmitQueue: GraphCommit<TChange>[] = [];\n\n\t/**\n\t * Represents the index in the `inFlightQueue` array of the most recent in flight commit that has\n\t * undergone rebasing but whose enrichments have not been updated.\n\t * All in-flight commits with an index inferior or equal to this number have stale enrichments.\n\t *\n\t * Is -1 when *any* of the following is true:\n\t * - There are no in-flight commits (i.e., no local commits have been made or they have all been sequenced)\n\t * - None of the in-flight commits have been rebased\n\t * - In-flight commits that have been rebased have all had their enrichments updated\n\t */\n\tprivate latestInFlightCommitWithStaleEnrichments: number = -1;\n\n\tpublic constructor(\n\t\t/**\n\t\t * A function that can create a rollback for a given change.\n\t\t */\n\t\tprivate readonly makeRollback: (change: TaggedChange<TChange>) => TChange,\n\t\t/**\n\t\t * Change enricher that represent the tip of the top-level local branch (i.e., the branch on which in-flight\n\t\t * commits are applied and automatically rebased).\n\t\t */\n\t\tprivate readonly tip: ChangeEnricherReadonlyCheckout<TChange>,\n\t) {}\n\n\tpublic onCommitSubmitted(commit: GraphCommit<TChange>): void {\n\t\tif (this.isInResubmitPhase) {\n\t\t\tconst toResubmit = this.resubmitQueue.shift();\n\t\t\tassert(\n\t\t\t\ttoResubmit === commit,\n\t\t\t\t0x981 /* Unexpected commit submitted during resubmit phase */,\n\t\t\t);\n\t\t}\n\t\tthis.inFlightQueue.push(commit);\n\t}\n\n\tpublic prepareForResubmit(toResubmit: readonly GraphCommit<TChange>[]): void {\n\t\tassert(\n\t\t\t!this.isInResubmitPhase,\n\t\t\t0x957 /* Invalid resubmit phase start during incomplete resubmit phase */,\n\t\t);\n\t\tassert(\n\t\t\ttoResubmit.length === this.inFlightQueue.length,\n\t\t\t0x958 /* Unexpected resubmit of more or fewer commits than are in flight */,\n\t\t);\n\t\tif (this.latestInFlightCommitWithStaleEnrichments === -1) {\n\t\t\t// No in-flight commits have stale enrichments, so we can resubmit them as is\n\t\t\tthis.resubmitQueue = this.inFlightQueue;\n\t\t\tthis.inFlightQueue = [];\n\t\t} else {\n\t\t\tconst checkout = this.tip.fork();\n\t\t\t// Roll back the checkout to the state before the oldest commit\n\t\t\tfor (let iCommit = toResubmit.length - 1; iCommit >= 0; iCommit -= 1) {\n\t\t\t\tconst commit = toResubmit[iCommit] ?? oob();\n\t\t\t\tconst rollback = this.makeRollback(commit);\n\t\t\t\t// WARNING: it's not currently possible to roll back past a schema change (see AB#7265).\n\t\t\t\t// Either we have to make it possible to do so, or this logic will have to change to work\n\t\t\t\t// forwards from an earlier fork instead of backwards.\n\t\t\t\tcheckout.applyTipChange(rollback);\n\t\t\t}\n\t\t\t// Update the enrichments of the stale commits\n\t\t\tfor (\n\t\t\t\tlet iCommit = 0;\n\t\t\t\tiCommit <= this.latestInFlightCommitWithStaleEnrichments;\n\t\t\t\tiCommit += 1\n\t\t\t) {\n\t\t\t\tconst commit = toResubmit[iCommit] ?? oob();\n\t\t\t\tconst enrichedChange = checkout.updateChangeEnrichments(\n\t\t\t\t\tcommit.change,\n\t\t\t\t\tcommit.revision,\n\t\t\t\t);\n\t\t\t\tconst enrichedCommit = { ...commit, change: enrichedChange };\n\t\t\t\tthis.resubmitQueue.push(enrichedCommit);\n\t\t\t\tif (iCommit < this.latestInFlightCommitWithStaleEnrichments) {\n\t\t\t\t\tcheckout.applyTipChange(enrichedChange, commit.revision);\n\t\t\t\t}\n\t\t\t\tthis.inFlightQueue.shift();\n\t\t\t}\n\t\t\tcheckout[disposeSymbol]();\n\t\t\t// Whatever commits are left do not have stale enrichments\n\t\t\tfor (const commit of this.inFlightQueue) {\n\t\t\t\tthis.resubmitQueue.push(commit);\n\t\t\t}\n\t\t\tthis.inFlightQueue.length = 0;\n\t\t}\n\t\tthis.latestInFlightCommitWithStaleEnrichments = -1;\n\t}\n\n\tpublic peekNextCommit(): GraphCommit<TChange> {\n\t\tassert(\n\t\t\tthis.isInResubmitPhase,\n\t\t\t0x982 /* No available commit to resubmit outside of resubmit phase */,\n\t\t);\n\t\tassert(hasSome(this.resubmitQueue), 0xa87 /* Expected resubmit queue to be non-empty */);\n\t\treturn this.resubmitQueue[0];\n\t}\n\n\tpublic get isInResubmitPhase(): boolean {\n\t\treturn this.resubmitQueue.length !== 0;\n\t}\n\n\tpublic onSequencedCommitApplied(isLocal: boolean): void {\n\t\tif (isLocal) {\n\t\t\t// The oldest in-flight commit has been sequenced\n\t\t\tassert(this.inFlightQueue.length > 0, 0x959 /* Sequencing of unknown local commit */);\n\t\t\tthis.inFlightQueue.shift();\n\t\t\tif (this.latestInFlightCommitWithStaleEnrichments >= 0) {\n\t\t\t\tthis.latestInFlightCommitWithStaleEnrichments -= 1;\n\t\t\t}\n\t\t} else {\n\t\t\t// A peer commit has been sequenced\n\t\t\tthis.latestInFlightCommitWithStaleEnrichments = this.inFlightQueue.length - 1;\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"defaultResubmitMachine.js","sourceRoot":"","sources":["../../src/shared-tree-core/defaultResubmitMachine.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport {\n\tassert,\n\tDoublyLinkedList,\n\toob,\n\ttype ListNode,\n\ttype ListNodeRange,\n} from \"@fluidframework/core-utils/internal\";\n\nimport type { GraphCommit, TaggedChange } from \"../core/index.js\";\nimport { disposeSymbol, hasSome } from \"../util/index.js\";\n\nimport type { ChangeEnricherReadonlyCheckout, ResubmitMachine } from \"./index.js\";\n\ninterface PendingChange<TChange> {\n\tcommit: GraphCommit<TChange>;\n\tlastEnrichment: number;\n}\ntype PendingChangeNode<TChange> = ListNode<PendingChange<TChange>>;\n\n/**\n * Default implementation of {@link ResubmitMachine}.\n */\nexport class DefaultResubmitMachine<TChange> implements ResubmitMachine<TChange> {\n\t/**\n\t * The list of commits (from oldest to most recent) that have been submitted but not sequenced.\n\t */\n\tprivate readonly inFlightQueue: DoublyLinkedList<PendingChange<TChange>> =\n\t\tnew DoublyLinkedList();\n\n\t/**\n\t * The range of in-flight commits that are currently being resubmitted.\n\t * Defined only during the resubmit phase.\n\t */\n\tprivate pendingResubmitRange: ListNodeRange<PendingChange<TChange>> | undefined;\n\n\t/**\n\t * The current enrichment version for in-flight commits.\n\t * Incremented when a peer commit is sequenced.\n\t */\n\tprivate currentEnrichment: number = 0;\n\n\tpublic constructor(\n\t\t/**\n\t\t * A function that can create a rollback for a given change.\n\t\t */\n\t\tprivate readonly makeRollback: (change: TaggedChange<TChange>) => TChange,\n\t\t/**\n\t\t * Change enricher that represent the tip of the top-level local branch (i.e., the branch on which in-flight\n\t\t * commits are applied and automatically rebased).\n\t\t */\n\t\tprivate readonly tip: ChangeEnricherReadonlyCheckout<TChange>,\n\t) {}\n\n\tpublic onCommitSubmitted(commit: GraphCommit<TChange>): void {\n\t\tif (this.pendingResubmitRange !== undefined) {\n\t\t\tconst toResubmit = this.pendingResubmitRange?.first;\n\t\t\tassert(\n\t\t\t\ttoResubmit?.data.commit === commit,\n\t\t\t\t0x981 /* Unexpected commit submitted during resubmit phase */,\n\t\t\t);\n\t\t\t// If we are not at the last commit to resubmit, advance the range to the next node.\n\t\t\t// Otherwise, clear the resubmit range as we are done resubmitting.\n\t\t\tif (toResubmit !== this.pendingResubmitRange.last) {\n\t\t\t\tassert(toResubmit.next !== undefined, 0xbd6 /* must be more in the list */);\n\t\t\t\tthis.pendingResubmitRange.first = toResubmit.next;\n\t\t\t} else {\n\t\t\t\tthis.pendingResubmitRange = undefined;\n\t\t\t}\n\t\t\ttoResubmit.remove();\n\t\t}\n\t\tthis.inFlightQueue.push({ commit, lastEnrichment: this.currentEnrichment });\n\t}\n\n\tpublic onCommitRollback(commit: GraphCommit<TChange>): void {\n\t\tassert(\n\t\t\tcommit.revision === this.inFlightQueue.last?.data.commit.revision,\n\t\t\t0xbd7 /* must rollback latest commit in the in flight queue */,\n\t\t);\n\t\tthis.inFlightQueue.pop();\n\t}\n\n\tpublic prepareForResubmit(toResubmit: readonly GraphCommit<TChange>[]): void {\n\t\tassert(\n\t\t\t!this.isInResubmitPhase,\n\t\t\t0x957 /* Invalid resubmit phase start during incomplete resubmit phase */,\n\t\t);\n\n\t\tif (!hasSome(toResubmit)) {\n\t\t\treturn;\n\t\t}\n\n\t\tassert(\n\t\t\ttoResubmit.length <= this.inFlightQueue.length,\n\t\t\t0xbd8 /* Unexpected resubmit of more commits than are in flight */,\n\t\t);\n\n\t\t// Find the first in-flight commit to resubmit.\n\t\tconst first = this.inFlightQueue.find(\n\t\t\t(v) => v.data.commit.revision === toResubmit[0].revision,\n\t\t);\n\t\t// Always resubmit to the end of all outstanding ops, but the list may grow during resubmit,\n\t\t// so we must track the current end at the start of the phase.\n\t\tconst last = this.inFlightQueue.last;\n\t\tassert(\n\t\t\tfirst !== undefined && last !== undefined,\n\t\t\t0xbd9 /* there must be inflight commits to resubmit */,\n\t\t);\n\n\t\tthis.pendingResubmitRange = { first, last };\n\t\t// If any in-flight commits have stale enrichments, recompute them.\n\t\tif (first.data.lastEnrichment < this.currentEnrichment) {\n\t\t\tconst checkout = this.tip.fork();\n\n\t\t\t// Roll back the checkout to the state before the oldest commit\n\t\t\tfor (let iCommit = toResubmit.length - 1; iCommit >= 0; iCommit -= 1) {\n\t\t\t\tconst commit = toResubmit[iCommit] ?? oob();\n\t\t\t\tconst rollback = this.makeRollback(commit);\n\t\t\t\t// WARNING: it's not currently possible to roll back past a schema change (see AB#7265).\n\t\t\t\t// Either we have to make it possible to do so, or this logic will have to change to work\n\t\t\t\t// forwards from an earlier fork instead of backwards.\n\t\t\t\tcheckout.applyTipChange(rollback);\n\t\t\t}\n\n\t\t\t// Update the enrichments of the stale commits in the in-flight queue.\n\t\t\tlet current: PendingChangeNode<TChange> | undefined = first;\n\t\t\tfor (const commit of toResubmit) {\n\t\t\t\tassert(\n\t\t\t\t\tcurrent !== undefined,\n\t\t\t\t\t0xbda /* there must be an inflight commit for each resubmit commit */,\n\t\t\t\t);\n\t\t\t\tcurrent.data.commit = commit;\n\t\t\t\tif (current.data.lastEnrichment < this.currentEnrichment) {\n\t\t\t\t\tconst enrichedChange = checkout.updateChangeEnrichments(\n\t\t\t\t\t\tcommit.change,\n\t\t\t\t\t\tcommit.revision,\n\t\t\t\t\t);\n\t\t\t\t\tconst enrichedCommit = { ...commit, change: enrichedChange };\n\n\t\t\t\t\t// Optimization: only apply the enriched change if the next commit also needs enrichment.\n\t\t\t\t\tif (\n\t\t\t\t\t\tcurrent.next !== undefined &&\n\t\t\t\t\t\tcurrent.next.data.lastEnrichment < this.currentEnrichment\n\t\t\t\t\t) {\n\t\t\t\t\t\tcheckout.applyTipChange(enrichedChange, commit.revision);\n\t\t\t\t\t}\n\n\t\t\t\t\tcurrent.data.commit = enrichedCommit;\n\t\t\t\t\tcurrent.data.lastEnrichment = this.currentEnrichment;\n\t\t\t\t}\n\t\t\t\tcurrent = current.next;\n\t\t\t}\n\t\t\tcheckout[disposeSymbol]();\n\t\t}\n\t}\n\n\tpublic peekNextCommit(): GraphCommit<TChange> {\n\t\tassert(\n\t\t\tthis.isInResubmitPhase,\n\t\t\t0x982 /* No available commit to resubmit outside of resubmit phase */,\n\t\t);\n\t\tassert(\n\t\t\tthis.pendingResubmitRange !== undefined,\n\t\t\t0xa87 /* Expected resubmit queue to be non-empty */,\n\t\t);\n\t\treturn this.pendingResubmitRange.first.data.commit;\n\t}\n\n\tpublic get isInResubmitPhase(): boolean {\n\t\treturn this.pendingResubmitRange !== undefined;\n\t}\n\n\tpublic onSequencedCommitApplied(isLocal: boolean): void {\n\t\tif (isLocal) {\n\t\t\t// The oldest in-flight commit has been sequenced\n\t\t\tassert(this.inFlightQueue.length > 0, 0x959 /* Sequencing of unknown local commit */);\n\t\t\tthis.inFlightQueue.shift();\n\t\t} else {\n\t\t\t// A peer commit has been sequenced\n\t\t\tthis.currentEnrichment++;\n\t\t}\n\t}\n}\n"]}
@@ -31,6 +31,12 @@ export interface ResubmitMachine<TChange> {
31
31
  * @param commit - the (enriched) commit (re)submitted. Not mutated.
32
32
  */
33
33
  onCommitSubmitted(commit: GraphCommit<TChange>): void;
34
+ /**
35
+ * Must be called on a commit after rollback, so it can be removed
36
+ * as it will never be (re)submitted.
37
+ * @param commit - The commit that was rolled back
38
+ */
39
+ onCommitRollback(commit: GraphCommit<TChange>): void;
34
40
  /**
35
41
  * Must be called after a sequenced commit is applied.
36
42
  * Note that this may be called multiples times in a row after a number of sequenced commits have been applied
@@ -1 +1 @@
1
- {"version":3,"file":"resubmitMachine.d.ts","sourceRoot":"","sources":["../../src/shared-tree-core/resubmitMachine.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD;;;GAGG;AACH,MAAM,WAAW,eAAe,CAAC,OAAO;IACvC;;;;;;OAMG;IACH,kBAAkB,CAAC,UAAU,EAAE,SAAS,WAAW,CAAC,OAAO,CAAC,EAAE,GAAG,IAAI,CAAC;IAEtE;;;;OAIG;IACH,cAAc,IAAI,WAAW,CAAC,OAAO,CAAC,CAAC;IAEvC;;OAEG;IACH,QAAQ,CAAC,iBAAiB,EAAE,OAAO,CAAC;IAEpC;;;OAGG;IACH,iBAAiB,CAAC,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;IAEtD;;;;;OAKG;IACH,wBAAwB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;CACjD"}
1
+ {"version":3,"file":"resubmitMachine.d.ts","sourceRoot":"","sources":["../../src/shared-tree-core/resubmitMachine.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD;;;GAGG;AACH,MAAM,WAAW,eAAe,CAAC,OAAO;IACvC;;;;;;OAMG;IACH,kBAAkB,CAAC,UAAU,EAAE,SAAS,WAAW,CAAC,OAAO,CAAC,EAAE,GAAG,IAAI,CAAC;IAEtE;;;;OAIG;IACH,cAAc,IAAI,WAAW,CAAC,OAAO,CAAC,CAAC;IAEvC;;OAEG;IACH,QAAQ,CAAC,iBAAiB,EAAE,OAAO,CAAC;IAEpC;;;OAGG;IACH,iBAAiB,CAAC,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;IAEtD;;;;OAIG;IACH,gBAAgB,CAAC,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;IAErD;;;;;OAKG;IACH,wBAAwB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;CACjD"}
@@ -1 +1 @@
1
- {"version":3,"file":"resubmitMachine.js","sourceRoot":"","sources":["../../src/shared-tree-core/resubmitMachine.ts"],"names":[],"mappings":"AAAA;;;GAGG","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport type { GraphCommit } from \"../core/index.js\";\n\n/**\n * Encapsulates a state machine that can be used by a {@link SharedTreeCore} manage resubmit phases,\n * especially dealing with the proper updating of enrichments on resubmitted commits.\n */\nexport interface ResubmitMachine<TChange> {\n\t/**\n\t * Must be called before calling `enrichCommit` as part of a resubmit phase.\n\t * @param toResubmit - the commits that will be resubmitted (from oldest to newest).\n\t * This must be the most rebased version of these commits (i.e., rebased over all known concurrent edits)\n\t * as opposed to the version which was last submitted.\n\t * `toResubmit` can be safely mutated by the caller after this call returns.\n\t */\n\tprepareForResubmit(toResubmit: readonly GraphCommit<TChange>[]): void;\n\n\t/**\n\t * @returns the next commit that should be resubmitted.\n\t *\n\t * Throws when invoked outside of a resubmit phase.\n\t */\n\tpeekNextCommit(): GraphCommit<TChange>;\n\n\t/**\n\t * Is true iff the commit enricher is currently in a resubmit phase.\n\t */\n\treadonly isInResubmitPhase: boolean;\n\n\t/**\n\t * Must be when a commit is submitted or resubmitted.\n\t * @param commit - the (enriched) commit (re)submitted. Not mutated.\n\t */\n\tonCommitSubmitted(commit: GraphCommit<TChange>): void;\n\n\t/**\n\t * Must be called after a sequenced commit is applied.\n\t * Note that this may be called multiples times in a row after a number of sequenced commits have been applied\n\t * (as opposed to always being called before the next sequenced commit is applied).\n\t * @param isLocal - whether the sequenced commit was generated by the local session.\n\t */\n\tonSequencedCommitApplied(isLocal: boolean): void;\n}\n"]}
1
+ {"version":3,"file":"resubmitMachine.js","sourceRoot":"","sources":["../../src/shared-tree-core/resubmitMachine.ts"],"names":[],"mappings":"AAAA;;;GAGG","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport type { GraphCommit } from \"../core/index.js\";\n\n/**\n * Encapsulates a state machine that can be used by a {@link SharedTreeCore} manage resubmit phases,\n * especially dealing with the proper updating of enrichments on resubmitted commits.\n */\nexport interface ResubmitMachine<TChange> {\n\t/**\n\t * Must be called before calling `enrichCommit` as part of a resubmit phase.\n\t * @param toResubmit - the commits that will be resubmitted (from oldest to newest).\n\t * This must be the most rebased version of these commits (i.e., rebased over all known concurrent edits)\n\t * as opposed to the version which was last submitted.\n\t * `toResubmit` can be safely mutated by the caller after this call returns.\n\t */\n\tprepareForResubmit(toResubmit: readonly GraphCommit<TChange>[]): void;\n\n\t/**\n\t * @returns the next commit that should be resubmitted.\n\t *\n\t * Throws when invoked outside of a resubmit phase.\n\t */\n\tpeekNextCommit(): GraphCommit<TChange>;\n\n\t/**\n\t * Is true iff the commit enricher is currently in a resubmit phase.\n\t */\n\treadonly isInResubmitPhase: boolean;\n\n\t/**\n\t * Must be when a commit is submitted or resubmitted.\n\t * @param commit - the (enriched) commit (re)submitted. Not mutated.\n\t */\n\tonCommitSubmitted(commit: GraphCommit<TChange>): void;\n\n\t/**\n\t * Must be called on a commit after rollback, so it can be removed\n\t * as it will never be (re)submitted.\n\t * @param commit - The commit that was rolled back\n\t */\n\tonCommitRollback(commit: GraphCommit<TChange>): void;\n\n\t/**\n\t * Must be called after a sequenced commit is applied.\n\t * Note that this may be called multiples times in a row after a number of sequenced commits have been applied\n\t * (as opposed to always being called before the next sequenced commit is applied).\n\t * @param isLocal - whether the sequenced commit was generated by the local session.\n\t */\n\tonSequencedCommitApplied(isLocal: boolean): void;\n}\n"]}
@@ -79,6 +79,7 @@ export declare class SharedTreeCore<TEditor extends ChangeFamilyEditor, TChange>
79
79
  getLocalBranch(): SharedTreeBranch<TEditor, TChange>;
80
80
  didAttach(): void;
81
81
  reSubmitCore(content: JsonCompatibleReadOnly, localOpMetadata: unknown): void;
82
+ rollback(content: JsonCompatibleReadOnly, localOpMetadata: unknown): void;
82
83
  applyStashedOp(content: JsonCompatibleReadOnly): void;
83
84
  }
84
85
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"sharedTreeCore.d.ts","sourceRoot":"","sources":["../../src/shared-tree-core/sharedTreeCore.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sharedTreeCore.d.ts","sourceRoot":"","sources":["../../src/shared-tree-core/sharedTreeCore.ts"],"names":[],"mappings":"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"}
@@ -255,11 +255,12 @@ let SharedTreeCore = (() => {
255
255
  const { commit: { revision }, } = this.messageCodec.decode(this.serializer.decode(content), {
256
256
  idCompressor: this.idCompressor,
257
257
  });
258
- const [commit] = this.editManager.findLocalCommit(revision);
259
258
  // If a resubmit phase is not already in progress, then this must be the first commit of a new resubmit phase.
260
259
  if (this.resubmitMachine.isInResubmitPhase === false) {
261
- const toResubmit = this.editManager.getLocalCommits();
262
- assert(commit === toResubmit[0], 0x95d /* Resubmit phase should start with the oldest local commit */);
260
+ const localCommits = this.editManager.getLocalCommits();
261
+ const revisionIndex = localCommits.findIndex((c) => c.revision === revision);
262
+ assert(revisionIndex >= 0, 0xbdb /* revision must exist in local commits */);
263
+ const toResubmit = localCommits.slice(revisionIndex);
263
264
  this.resubmitMachine.prepareForResubmit(toResubmit);
264
265
  }
265
266
  assert(isClonableSchemaPolicy(localOpMetadata), 0x95e /* Local metadata must contain schema and policy. */);
@@ -267,6 +268,18 @@ let SharedTreeCore = (() => {
267
268
  const enrichedCommit = this.resubmitMachine.peekNextCommit();
268
269
  this.submitCommit(enrichedCommit, localOpMetadata, true);
269
270
  }
271
+ rollback(content, localOpMetadata) {
272
+ // Empty context object is passed in, as our decode function is schema-agnostic.
273
+ const { commit: { revision }, } = this.messageCodec.decode(this.serializer.decode(content), {
274
+ idCompressor: this.idCompressor,
275
+ });
276
+ const [commit] = this.editManager.findLocalCommit(revision);
277
+ const { parent } = commit;
278
+ assert(parent !== undefined, 0xbdc /* must have parent */);
279
+ const [precedingCommit] = this.editManager.findLocalCommit(parent.revision);
280
+ this.editManager.localBranch.removeAfter(precedingCommit);
281
+ this.resubmitMachine.onCommitRollback(commit);
282
+ }
270
283
  applyStashedOp(content) {
271
284
  // Empty context object is passed in, as our decode function is schema-agnostic.
272
285
  const { commit: { revision, change }, } = this.messageCodec.decode(content, { idCompressor: this.idCompressor });
@@ -1 +1 @@
1
- {"version":3,"file":"sharedTreeCore.js","sourceRoot":"","sources":["../../src/shared-tree-core/sharedTreeCore.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport type { IFluidLoadable, ITelemetryBaseLogger } from \"@fluidframework/core-interfaces\";\nimport { assert } from \"@fluidframework/core-utils/internal\";\nimport type { IChannelStorageService } from \"@fluidframework/datastore-definitions/internal\";\nimport type { IIdCompressor, SessionId } from \"@fluidframework/id-compressor\";\nimport type {\n\tIExperimentalIncrementalSummaryContext,\n\tIRuntimeMessageCollection,\n\tISummaryTreeWithStats,\n\tITelemetryContext,\n} from \"@fluidframework/runtime-definitions/internal\";\nimport { SummaryTreeBuilder } from \"@fluidframework/runtime-utils/internal\";\nimport type {\n\tIChannelView,\n\tIFluidSerializer,\n} from \"@fluidframework/shared-object-base/internal\";\nimport { createChildLogger } from \"@fluidframework/telemetry-utils/internal\";\n\nimport type { ICodecOptions, IJsonCodec } from \"../codec/index.js\";\nimport {\n\ttype ChangeFamily,\n\ttype ChangeFamilyEditor,\n\tfindAncestor,\n\ttype GraphCommit,\n\ttype RevisionTag,\n\tRevisionTagCodec,\n\ttype SchemaAndPolicy,\n\ttype SchemaPolicy,\n\ttype TaggedChange,\n\ttype TreeStoredSchemaRepository,\n} from \"../core/index.js\";\nimport {\n\ttype JsonCompatibleReadOnly,\n\tbrand,\n\ttype Breakable,\n\ttype WithBreakable,\n\tthrowIfBroken,\n\tbreakingClass,\n} from \"../util/index.js\";\n\nimport type { SharedTreeBranch } from \"./branch.js\";\nimport { BranchCommitEnricher } from \"./branchCommitEnricher.js\";\nimport { type ChangeEnricherReadonlyCheckout, NoOpChangeEnricher } from \"./changeEnricher.js\";\nimport { DefaultResubmitMachine } from \"./defaultResubmitMachine.js\";\nimport { EditManager, minimumPossibleSequenceNumber } from \"./editManager.js\";\nimport { makeEditManagerCodec } from \"./editManagerCodecs.js\";\nimport type { SeqNumber } from \"./editManagerFormat.js\";\nimport { EditManagerSummarizer } from \"./editManagerSummarizer.js\";\nimport { type MessageEncodingContext, makeMessageCodec } from \"./messageCodecs.js\";\nimport type { DecodedMessage } from \"./messageTypes.js\";\nimport type { ResubmitMachine } from \"./resubmitMachine.js\";\n\n// TODO: Organize this to be adjacent to persisted types.\nconst summarizablesTreeKey = \"indexes\";\n\nexport interface ExplicitCoreCodecVersions {\n\teditManager: number;\n\tmessage: number;\n}\n\nexport interface ClonableSchemaAndPolicy extends SchemaAndPolicy {\n\tschema: TreeStoredSchemaRepository;\n}\n\n/**\n * Generic shared tree, which needs to be configured with indexes, field kinds and other configuration.\n */\n@breakingClass\nexport class SharedTreeCore<TEditor extends ChangeFamilyEditor, TChange>\n\timplements WithBreakable\n{\n\tprivate readonly editManager: EditManager<TEditor, TChange, ChangeFamily<TEditor, TChange>>;\n\tprivate readonly summarizables: readonly [EditManagerSummarizer<TChange>, ...Summarizable[]];\n\t/**\n\t * The sequence number that this instance is at.\n\t * This number is artificial in that it is made up by this instance as opposed to being provided by the runtime.\n\t * Is `undefined` after (and only after) this instance is attached.\n\t */\n\tprivate detachedRevision: SeqNumber | undefined = minimumPossibleSequenceNumber;\n\n\t/**\n\t * Used to encode/decode messages sent to/received from the Fluid runtime.\n\t *\n\t * @remarks Since there is currently only one format, this can just be cached on the class.\n\t * With more write formats active, it may make sense to keep around the \"usual\" format codec\n\t * (the one for the current persisted configuration) and resolve codecs for different versions\n\t * as necessary (e.g. an upgrade op came in, or the configuration changed within the collab window\n\t * and an op needs to be interpreted which isn't written with the current configuration).\n\t */\n\tprivate readonly messageCodec: IJsonCodec<\n\t\tDecodedMessage<TChange>,\n\t\tunknown,\n\t\tunknown,\n\t\tMessageEncodingContext\n\t>;\n\n\tprivate readonly resubmitMachine: ResubmitMachine<TChange>;\n\tpublic readonly commitEnricher: BranchCommitEnricher<TChange>;\n\n\tpublic readonly mintRevisionTag: () => RevisionTag;\n\n\tprivate readonly schemaAndPolicy: ClonableSchemaAndPolicy;\n\n\t/**\n\t * @param summarizables - Summarizers for all indexes used by this tree\n\t * @param changeFamily - The change family\n\t * @param editManager - The edit manager\n\t * @param runtime - The IFluidDataStoreRuntime which contains the shared object\n\t * @param editor - Used to edit the state of the tree. Edits will be immediately applied locally to the tree.\n\t * If there is no transaction currently ongoing, then the edits will be submitted to Fluid immediately as well.\n\t */\n\tpublic constructor(\n\t\tpublic readonly breaker: Breakable,\n\t\tpublic readonly sharedObject: IChannelView & IFluidLoadable,\n\t\tpublic readonly serializer: IFluidSerializer,\n\t\tpublic readonly submitLocalMessage: (content: unknown, localOpMetadata?: unknown) => void,\n\t\tlogger: ITelemetryBaseLogger | undefined,\n\t\tsummarizables: readonly Summarizable[],\n\t\tchangeFamily: ChangeFamily<TEditor, TChange>,\n\t\toptions: ICodecOptions,\n\t\tformatOptions: ExplicitCoreCodecVersions,\n\t\tprivate readonly idCompressor: IIdCompressor,\n\t\tschema: TreeStoredSchemaRepository,\n\t\tschemaPolicy: SchemaPolicy,\n\t\tresubmitMachine?: ResubmitMachine<TChange>,\n\t\tenricher?: ChangeEnricherReadonlyCheckout<TChange>,\n\t\tpublic readonly getEditor: () => TEditor = () => this.getLocalBranch().editor,\n\t) {\n\t\tthis.schemaAndPolicy = {\n\t\t\tschema,\n\t\t\tpolicy: schemaPolicy,\n\t\t};\n\n\t\tconst rebaseLogger = createChildLogger({\n\t\t\tlogger,\n\t\t\tnamespace: \"Rebase\",\n\t\t});\n\n\t\tthis.mintRevisionTag = () => this.idCompressor.generateCompressedId();\n\t\t/**\n\t\t * A random ID that uniquely identifies this client in the collab session.\n\t\t * This is sent alongside every op to identify which client the op originated from.\n\t\t * This is used rather than the Fluid client ID because the Fluid client ID is not stable across reconnections.\n\t\t */\n\t\tconst localSessionId = idCompressor.localSessionId;\n\t\tthis.editManager = new EditManager(\n\t\t\tchangeFamily,\n\t\t\tlocalSessionId,\n\t\t\tthis.mintRevisionTag,\n\t\t\trebaseLogger,\n\t\t);\n\n\t\tthis.editManager.localBranch.events.on(\"beforeChange\", (change) => {\n\t\t\tif (this.detachedRevision === undefined) {\n\t\t\t\t// Commit enrichment is only necessary for changes that will be submitted as ops, and changes issued while detached are not submitted.\n\t\t\t\tthis.commitEnricher.processChange(change);\n\t\t\t}\n\t\t\tif (change.type === \"append\") {\n\t\t\t\tfor (const commit of change.newCommits) {\n\t\t\t\t\tthis.submitCommit(commit, this.schemaAndPolicy, false);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tconst revisionTagCodec = new RevisionTagCodec(idCompressor);\n\t\tconst editManagerCodec = makeEditManagerCodec(\n\t\t\tthis.editManager.changeFamily.codecs,\n\t\t\trevisionTagCodec,\n\t\t\toptions,\n\t\t\tformatOptions.editManager,\n\t\t);\n\t\tthis.summarizables = [\n\t\t\tnew EditManagerSummarizer(\n\t\t\t\tthis.editManager,\n\t\t\t\teditManagerCodec,\n\t\t\t\tthis.idCompressor,\n\t\t\t\tthis.schemaAndPolicy,\n\t\t\t),\n\t\t\t...summarizables,\n\t\t];\n\t\tassert(\n\t\t\tnew Set(this.summarizables.map((e) => e.key)).size === this.summarizables.length,\n\t\t\t0x350 /* Index summary element keys must be unique */,\n\t\t);\n\n\t\tthis.messageCodec = makeMessageCodec(\n\t\t\tchangeFamily.codecs,\n\t\t\tnew RevisionTagCodec(idCompressor),\n\t\t\toptions,\n\t\t\tformatOptions.message,\n\t\t);\n\n\t\tconst changeEnricher = enricher ?? new NoOpChangeEnricher();\n\t\tthis.resubmitMachine =\n\t\t\tresubmitMachine ??\n\t\t\tnew DefaultResubmitMachine(\n\t\t\t\t(change: TaggedChange<TChange>) =>\n\t\t\t\t\tchangeFamily.rebaser.invert(change, true, this.mintRevisionTag()),\n\t\t\t\tchangeEnricher,\n\t\t\t);\n\t\tthis.commitEnricher = new BranchCommitEnricher(changeFamily.rebaser, changeEnricher);\n\t}\n\n\t// TODO: SharedObject's merging of the two summary methods into summarizeCore is not what we want here:\n\t// We might want to not subclass it, or override/reimplement most of its functionality.\n\t@throwIfBroken\n\tpublic summarizeCore(\n\t\tserializer: IFluidSerializer,\n\t\ttelemetryContext?: ITelemetryContext,\n\t\tincrementalSummaryContext?: IExperimentalIncrementalSummaryContext,\n\t\tfullTree?: boolean,\n\t): ISummaryTreeWithStats {\n\t\tconst builder = new SummaryTreeBuilder();\n\t\tconst summarizableBuilder = new SummaryTreeBuilder();\n\t\t// Merge the summaries of all summarizables together under a single ISummaryTree\n\t\tfor (const s of this.summarizables) {\n\t\t\t// Add the summarizable's path in the summary tree to the incremental summary context's\n\t\t\t// summary path, so that the summarizable can use it to generate incremental summaries.\n\t\t\tconst childIncrementalSummaryContext =\n\t\t\t\tincrementalSummaryContext === undefined\n\t\t\t\t\t? undefined\n\t\t\t\t\t: {\n\t\t\t\t\t\t\t...incrementalSummaryContext,\n\t\t\t\t\t\t\tsummaryPath: `${incrementalSummaryContext.summaryPath}/${summarizablesTreeKey}/${s.key}`,\n\t\t\t\t\t\t};\n\t\t\tsummarizableBuilder.addWithStats(\n\t\t\t\ts.key,\n\t\t\t\ts.summarize({\n\t\t\t\t\tstringify: (contents: unknown) =>\n\t\t\t\t\t\tserializer.stringify(contents, this.sharedObject.handle),\n\t\t\t\t\tfullTree,\n\t\t\t\t\ttelemetryContext,\n\t\t\t\t\tincrementalSummaryContext: childIncrementalSummaryContext,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\tbuilder.addWithStats(summarizablesTreeKey, summarizableBuilder.getSummaryTree());\n\t\treturn builder.getSummaryTree();\n\t}\n\n\tpublic async loadCore(services: IChannelStorageService): Promise<void> {\n\t\tassert(\n\t\t\tthis.editManager.localBranch.getHead() === this.editManager.getTrunkHead(),\n\t\t\t0xaaa /* All local changes should be applied to the trunk before loading from summary */,\n\t\t);\n\t\tconst [editManagerSummarizer, ...summarizables] = this.summarizables;\n\t\tconst loadEditManager = this.loadSummarizable(editManagerSummarizer, services);\n\t\tconst loadSummarizables = summarizables.map(async (s) =>\n\t\t\tthis.loadSummarizable(s, services),\n\t\t);\n\n\t\tif (this.detachedRevision !== undefined) {\n\t\t\t// If we are detached but loading from a summary, then we need to update our detached revision to ensure that it is ahead of all detached revisions in the summary.\n\t\t\t// First, finish loading the edit manager so that we can inspect the sequence numbers of the commits on the trunk.\n\t\t\tawait loadEditManager;\n\t\t\t// Find the most recent detached revision in the summary trunk...\n\t\t\tlet latestDetachedSequenceNumber: SeqNumber | undefined;\n\t\t\tfindAncestor(this.editManager.getTrunkHead(), (c) => {\n\t\t\t\tconst sequenceNumber = this.editManager.getSequenceNumber(c);\n\t\t\t\tif (sequenceNumber !== undefined && sequenceNumber < 0) {\n\t\t\t\t\tlatestDetachedSequenceNumber = sequenceNumber;\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t});\n\t\t\t// ...and set our detached revision to be as it would be if we had been already created that revision.\n\t\t\tthis.detachedRevision = latestDetachedSequenceNumber ?? this.detachedRevision;\n\t\t\tawait Promise.all(loadSummarizables);\n\t\t} else {\n\t\t\tawait Promise.all([loadEditManager, ...loadSummarizables]);\n\t\t}\n\t}\n\n\tprivate async loadSummarizable(\n\t\tsummarizable: Summarizable,\n\t\tservices: IChannelStorageService,\n\t): Promise<void> {\n\t\treturn summarizable.load(\n\t\t\tscopeStorageService(services, summarizablesTreeKey, summarizable.key),\n\t\t\t(contents) => this.serializer.parse(contents),\n\t\t);\n\t}\n\n\t/**\n\t * Submits an op to the Fluid runtime containing the given commit\n\t * @param commit - the commit to submit\n\t * @returns the submitted commit. This is undefined if the underlying `SharedObject` is not attached,\n\t * and may differ from `commit` due to enrichments like detached tree refreshers.\n\t */\n\tprotected submitCommit(\n\t\tcommit: GraphCommit<TChange>,\n\t\tschemaAndPolicy: ClonableSchemaAndPolicy,\n\t\tisResubmit: boolean,\n\t): void {\n\t\tassert(\n\t\t\tthis.sharedObject.isAttached() === (this.detachedRevision === undefined),\n\t\t\t0x95a /* Detached revision should only be set when not attached */,\n\t\t);\n\n\t\tconst enrichedCommit =\n\t\t\tthis.detachedRevision === undefined && !isResubmit\n\t\t\t\t? this.commitEnricher.enrich(commit)\n\t\t\t\t: commit;\n\n\t\t// Edits submitted before the first attach are treated as sequenced because they will be included\n\t\t// in the attach summary that is uploaded to the service.\n\t\t// Until this attach workflow happens, this instance essentially behaves as a centralized data structure.\n\t\tif (this.detachedRevision !== undefined) {\n\t\t\tconst newRevision: SeqNumber = brand((this.detachedRevision as number) + 1);\n\t\t\tthis.detachedRevision = newRevision;\n\t\t\tthis.editManager.addSequencedChanges(\n\t\t\t\t[enrichedCommit],\n\t\t\t\tthis.editManager.localSessionId,\n\t\t\t\tnewRevision,\n\t\t\t\tthis.detachedRevision,\n\t\t\t);\n\t\t\tthis.editManager.advanceMinimumSequenceNumber(newRevision, false);\n\t\t\treturn undefined;\n\t\t}\n\t\tconst message = this.messageCodec.encode(\n\t\t\t{\n\t\t\t\tcommit: enrichedCommit,\n\t\t\t\tsessionId: this.editManager.localSessionId,\n\t\t\t},\n\t\t\t{\n\t\t\t\tidCompressor: this.idCompressor,\n\t\t\t\tschema: schemaAndPolicy,\n\t\t\t},\n\t\t);\n\t\tthis.submitLocalMessage(message, {\n\t\t\t// Clone the schema to ensure that during resubmit the schema has not been mutated by later changes\n\t\t\tschema: schemaAndPolicy.schema.clone(),\n\t\t\tpolicy: schemaAndPolicy.policy,\n\t\t});\n\t\tthis.resubmitMachine.onCommitSubmitted(enrichedCommit);\n\t}\n\n\t/**\n\t * Process a bunch of messages from the runtime. SharedObject will call this method with a bunch of messages.\n\t */\n\tpublic processMessagesCore(messagesCollection: IRuntimeMessageCollection): void {\n\t\tconst { envelope, local, messagesContent } = messagesCollection;\n\t\tconst commits: GraphCommit<TChange>[] = [];\n\t\tlet messagesSessionId: SessionId | undefined;\n\n\t\t// Get a list of all the commits from the messages.\n\t\tfor (const messageContent of messagesContent) {\n\t\t\t// Empty context object is passed in, as our decode function is schema-agnostic.\n\t\t\tconst { commit, sessionId } = this.messageCodec.decode(messageContent.contents, {\n\t\t\t\tidCompressor: this.idCompressor,\n\t\t\t});\n\t\t\tcommits.push(commit);\n\n\t\t\tif (messagesSessionId !== undefined) {\n\t\t\t\tassert(\n\t\t\t\t\tmessagesSessionId === sessionId,\n\t\t\t\t\t0xad9 /* All messages in a bunch must have the same session ID */,\n\t\t\t\t);\n\t\t\t}\n\t\t\tmessagesSessionId = sessionId;\n\t\t}\n\n\t\tassert(messagesSessionId !== undefined, 0xada /* Messages must have a session ID */);\n\n\t\tthis.editManager.addSequencedChanges(\n\t\t\tcommits,\n\t\t\tmessagesSessionId,\n\t\t\tbrand(envelope.sequenceNumber),\n\t\t\tbrand(envelope.referenceSequenceNumber),\n\t\t);\n\n\t\t// Update the resubmit machine for each commit applied.\n\t\tfor (const _ of messagesContent) {\n\t\t\tthis.resubmitMachine.onSequencedCommitApplied(local);\n\t\t}\n\n\t\tthis.editManager.advanceMinimumSequenceNumber(brand(envelope.minimumSequenceNumber));\n\t}\n\n\tpublic getLocalBranch(): SharedTreeBranch<TEditor, TChange> {\n\t\treturn this.editManager.localBranch;\n\t}\n\n\tpublic didAttach(): void {\n\t\tthis.detachedRevision = undefined;\n\t}\n\n\tpublic reSubmitCore(content: JsonCompatibleReadOnly, localOpMetadata: unknown): void {\n\t\t// Empty context object is passed in, as our decode function is schema-agnostic.\n\t\tconst {\n\t\t\tcommit: { revision },\n\t\t} = this.messageCodec.decode(this.serializer.decode(content), {\n\t\t\tidCompressor: this.idCompressor,\n\t\t});\n\t\tconst [commit] = this.editManager.findLocalCommit(revision);\n\t\t// If a resubmit phase is not already in progress, then this must be the first commit of a new resubmit phase.\n\t\tif (this.resubmitMachine.isInResubmitPhase === false) {\n\t\t\tconst toResubmit = this.editManager.getLocalCommits();\n\t\t\tassert(\n\t\t\t\tcommit === toResubmit[0],\n\t\t\t\t0x95d /* Resubmit phase should start with the oldest local commit */,\n\t\t\t);\n\t\t\tthis.resubmitMachine.prepareForResubmit(toResubmit);\n\t\t}\n\t\tassert(\n\t\t\tisClonableSchemaPolicy(localOpMetadata),\n\t\t\t0x95e /* Local metadata must contain schema and policy. */,\n\t\t);\n\t\tassert(\n\t\t\tthis.resubmitMachine.isInResubmitPhase !== false,\n\t\t\t0x984 /* Invalid resubmit outside of resubmit phase */,\n\t\t);\n\t\tconst enrichedCommit = this.resubmitMachine.peekNextCommit();\n\t\tthis.submitCommit(enrichedCommit, localOpMetadata, true);\n\t}\n\n\tpublic applyStashedOp(content: JsonCompatibleReadOnly): void {\n\t\t// Empty context object is passed in, as our decode function is schema-agnostic.\n\t\tconst {\n\t\t\tcommit: { revision, change },\n\t\t} = this.messageCodec.decode(content, { idCompressor: this.idCompressor });\n\t\tthis.editManager.localBranch.apply({ change, revision });\n\t}\n}\n\nfunction isClonableSchemaPolicy(\n\tmaybeSchemaPolicy: unknown,\n): maybeSchemaPolicy is ClonableSchemaAndPolicy {\n\tconst schemaAndPolicy = maybeSchemaPolicy as ClonableSchemaAndPolicy;\n\treturn schemaAndPolicy.schema !== undefined && schemaAndPolicy.policy !== undefined;\n}\n\n/**\n * Specifies the behavior of a component that puts data in a summary.\n */\nexport interface Summarizable {\n\t/**\n\t * Field name in summary json under which this element stores its data.\n\t */\n\treadonly key: string;\n\n\t/**\n\t * {@inheritDoc @fluidframework/datastore-definitions#(IChannel:interface).summarize}\n\t * @param stringify - Serializes the contents of the component (including {@link (IFluidHandle:interface)}s) for storage.\n\t * @param fullTree - A flag indicating whether the attempt should generate a full\n\t * summary tree without any handles for unchanged subtrees. It should only be set to true when generating\n\t * a summary from the entire container. The default value is false.\n\t * @param trackState - An optimization for tracking state of objects across summaries. If the state\n\t * of an object did not change since last successful summary, an\n\t * {@link @fluidframework/protocol-definitions#ISummaryHandle} can be used\n\t * instead of re-summarizing it. If this is `false`, the expectation is that you should never\n\t * send an `ISummaryHandle`, since you are not expected to track state. The default value is true.\n\t * @param telemetryContext - See {@link @fluidframework/runtime-definitions#ITelemetryContext}.\n\t * @param incrementalSummaryContext - See {@link @fluidframework/runtime-definitions#IExperimentalIncrementalSummaryContext}.\n\t */\n\tsummarize(props: {\n\t\tstringify: SummaryElementStringifier;\n\t\tfullTree?: boolean;\n\t\ttrackState?: boolean;\n\t\ttelemetryContext?: ITelemetryContext;\n\t\tincrementalSummaryContext?: IExperimentalIncrementalSummaryContext;\n\t}): ISummaryTreeWithStats;\n\n\t/**\n\t * Allows the component to perform custom loading. The storage service is scoped to this component and therefore\n\t * paths in this component will not collide with those in other components, even if they are the same string.\n\t * @param service - Storage used by the component\n\t * @param parse - Parses serialized data from storage into runtime objects for the component\n\t */\n\tload(service: IChannelStorageService, parse: SummaryElementParser): Promise<void>;\n}\n\n/**\n * Serializes the given contents into a string acceptable for storing in summaries, i.e. all\n * Fluid handles have been replaced appropriately by an IFluidSerializer\n */\nexport type SummaryElementStringifier = (contents: unknown) => string;\n\n/**\n * Parses a serialized/summarized string into an object, rehydrating any Fluid handles as necessary\n */\nexport type SummaryElementParser = (contents: string) => unknown;\n\n/**\n * Compose an {@link IChannelStorageService} which prefixes all paths before forwarding them to the original service\n */\nfunction scopeStorageService(\n\tservice: IChannelStorageService,\n\t...pathElements: string[]\n): IChannelStorageService {\n\tconst scope = `${pathElements.join(\"/\")}/`;\n\n\treturn {\n\t\tasync readBlob(path: string): Promise<ArrayBufferLike> {\n\t\t\treturn service.readBlob(`${scope}${path}`);\n\t\t},\n\t\tasync contains(path) {\n\t\t\treturn service.contains(`${scope}${path}`);\n\t\t},\n\t\tasync list(path) {\n\t\t\treturn service.list(`${scope}${path}`);\n\t\t},\n\t};\n}\n"]}
1
+ {"version":3,"file":"sharedTreeCore.js","sourceRoot":"","sources":["../../src/shared-tree-core/sharedTreeCore.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport type { IFluidLoadable, ITelemetryBaseLogger } from \"@fluidframework/core-interfaces\";\nimport { assert } from \"@fluidframework/core-utils/internal\";\nimport type { IChannelStorageService } from \"@fluidframework/datastore-definitions/internal\";\nimport type { IIdCompressor, SessionId } from \"@fluidframework/id-compressor\";\nimport type {\n\tIExperimentalIncrementalSummaryContext,\n\tIRuntimeMessageCollection,\n\tISummaryTreeWithStats,\n\tITelemetryContext,\n} from \"@fluidframework/runtime-definitions/internal\";\nimport { SummaryTreeBuilder } from \"@fluidframework/runtime-utils/internal\";\nimport type {\n\tIChannelView,\n\tIFluidSerializer,\n} from \"@fluidframework/shared-object-base/internal\";\nimport { createChildLogger } from \"@fluidframework/telemetry-utils/internal\";\n\nimport type { ICodecOptions, IJsonCodec } from \"../codec/index.js\";\nimport {\n\ttype ChangeFamily,\n\ttype ChangeFamilyEditor,\n\tfindAncestor,\n\ttype GraphCommit,\n\ttype RevisionTag,\n\tRevisionTagCodec,\n\ttype SchemaAndPolicy,\n\ttype SchemaPolicy,\n\ttype TaggedChange,\n\ttype TreeStoredSchemaRepository,\n} from \"../core/index.js\";\nimport {\n\ttype JsonCompatibleReadOnly,\n\tbrand,\n\ttype Breakable,\n\ttype WithBreakable,\n\tthrowIfBroken,\n\tbreakingClass,\n} from \"../util/index.js\";\n\nimport type { SharedTreeBranch } from \"./branch.js\";\nimport { BranchCommitEnricher } from \"./branchCommitEnricher.js\";\nimport { type ChangeEnricherReadonlyCheckout, NoOpChangeEnricher } from \"./changeEnricher.js\";\nimport { DefaultResubmitMachine } from \"./defaultResubmitMachine.js\";\nimport { EditManager, minimumPossibleSequenceNumber } from \"./editManager.js\";\nimport { makeEditManagerCodec } from \"./editManagerCodecs.js\";\nimport type { SeqNumber } from \"./editManagerFormat.js\";\nimport { EditManagerSummarizer } from \"./editManagerSummarizer.js\";\nimport { type MessageEncodingContext, makeMessageCodec } from \"./messageCodecs.js\";\nimport type { DecodedMessage } from \"./messageTypes.js\";\nimport type { ResubmitMachine } from \"./resubmitMachine.js\";\n\n// TODO: Organize this to be adjacent to persisted types.\nconst summarizablesTreeKey = \"indexes\";\n\nexport interface ExplicitCoreCodecVersions {\n\teditManager: number;\n\tmessage: number;\n}\n\nexport interface ClonableSchemaAndPolicy extends SchemaAndPolicy {\n\tschema: TreeStoredSchemaRepository;\n}\n\n/**\n * Generic shared tree, which needs to be configured with indexes, field kinds and other configuration.\n */\n@breakingClass\nexport class SharedTreeCore<TEditor extends ChangeFamilyEditor, TChange>\n\timplements WithBreakable\n{\n\tprivate readonly editManager: EditManager<TEditor, TChange, ChangeFamily<TEditor, TChange>>;\n\tprivate readonly summarizables: readonly [EditManagerSummarizer<TChange>, ...Summarizable[]];\n\t/**\n\t * The sequence number that this instance is at.\n\t * This number is artificial in that it is made up by this instance as opposed to being provided by the runtime.\n\t * Is `undefined` after (and only after) this instance is attached.\n\t */\n\tprivate detachedRevision: SeqNumber | undefined = minimumPossibleSequenceNumber;\n\n\t/**\n\t * Used to encode/decode messages sent to/received from the Fluid runtime.\n\t *\n\t * @remarks Since there is currently only one format, this can just be cached on the class.\n\t * With more write formats active, it may make sense to keep around the \"usual\" format codec\n\t * (the one for the current persisted configuration) and resolve codecs for different versions\n\t * as necessary (e.g. an upgrade op came in, or the configuration changed within the collab window\n\t * and an op needs to be interpreted which isn't written with the current configuration).\n\t */\n\tprivate readonly messageCodec: IJsonCodec<\n\t\tDecodedMessage<TChange>,\n\t\tunknown,\n\t\tunknown,\n\t\tMessageEncodingContext\n\t>;\n\n\tprivate readonly resubmitMachine: ResubmitMachine<TChange>;\n\tpublic readonly commitEnricher: BranchCommitEnricher<TChange>;\n\n\tpublic readonly mintRevisionTag: () => RevisionTag;\n\n\tprivate readonly schemaAndPolicy: ClonableSchemaAndPolicy;\n\n\t/**\n\t * @param summarizables - Summarizers for all indexes used by this tree\n\t * @param changeFamily - The change family\n\t * @param editManager - The edit manager\n\t * @param runtime - The IFluidDataStoreRuntime which contains the shared object\n\t * @param editor - Used to edit the state of the tree. Edits will be immediately applied locally to the tree.\n\t * If there is no transaction currently ongoing, then the edits will be submitted to Fluid immediately as well.\n\t */\n\tpublic constructor(\n\t\tpublic readonly breaker: Breakable,\n\t\tpublic readonly sharedObject: IChannelView & IFluidLoadable,\n\t\tpublic readonly serializer: IFluidSerializer,\n\t\tpublic readonly submitLocalMessage: (content: unknown, localOpMetadata?: unknown) => void,\n\t\tlogger: ITelemetryBaseLogger | undefined,\n\t\tsummarizables: readonly Summarizable[],\n\t\tchangeFamily: ChangeFamily<TEditor, TChange>,\n\t\toptions: ICodecOptions,\n\t\tformatOptions: ExplicitCoreCodecVersions,\n\t\tprivate readonly idCompressor: IIdCompressor,\n\t\tschema: TreeStoredSchemaRepository,\n\t\tschemaPolicy: SchemaPolicy,\n\t\tresubmitMachine?: ResubmitMachine<TChange>,\n\t\tenricher?: ChangeEnricherReadonlyCheckout<TChange>,\n\t\tpublic readonly getEditor: () => TEditor = () => this.getLocalBranch().editor,\n\t) {\n\t\tthis.schemaAndPolicy = {\n\t\t\tschema,\n\t\t\tpolicy: schemaPolicy,\n\t\t};\n\n\t\tconst rebaseLogger = createChildLogger({\n\t\t\tlogger,\n\t\t\tnamespace: \"Rebase\",\n\t\t});\n\n\t\tthis.mintRevisionTag = () => this.idCompressor.generateCompressedId();\n\t\t/**\n\t\t * A random ID that uniquely identifies this client in the collab session.\n\t\t * This is sent alongside every op to identify which client the op originated from.\n\t\t * This is used rather than the Fluid client ID because the Fluid client ID is not stable across reconnections.\n\t\t */\n\t\tconst localSessionId = idCompressor.localSessionId;\n\t\tthis.editManager = new EditManager(\n\t\t\tchangeFamily,\n\t\t\tlocalSessionId,\n\t\t\tthis.mintRevisionTag,\n\t\t\trebaseLogger,\n\t\t);\n\n\t\tthis.editManager.localBranch.events.on(\"beforeChange\", (change) => {\n\t\t\tif (this.detachedRevision === undefined) {\n\t\t\t\t// Commit enrichment is only necessary for changes that will be submitted as ops, and changes issued while detached are not submitted.\n\t\t\t\tthis.commitEnricher.processChange(change);\n\t\t\t}\n\t\t\tif (change.type === \"append\") {\n\t\t\t\tfor (const commit of change.newCommits) {\n\t\t\t\t\tthis.submitCommit(commit, this.schemaAndPolicy, false);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tconst revisionTagCodec = new RevisionTagCodec(idCompressor);\n\t\tconst editManagerCodec = makeEditManagerCodec(\n\t\t\tthis.editManager.changeFamily.codecs,\n\t\t\trevisionTagCodec,\n\t\t\toptions,\n\t\t\tformatOptions.editManager,\n\t\t);\n\t\tthis.summarizables = [\n\t\t\tnew EditManagerSummarizer(\n\t\t\t\tthis.editManager,\n\t\t\t\teditManagerCodec,\n\t\t\t\tthis.idCompressor,\n\t\t\t\tthis.schemaAndPolicy,\n\t\t\t),\n\t\t\t...summarizables,\n\t\t];\n\t\tassert(\n\t\t\tnew Set(this.summarizables.map((e) => e.key)).size === this.summarizables.length,\n\t\t\t0x350 /* Index summary element keys must be unique */,\n\t\t);\n\n\t\tthis.messageCodec = makeMessageCodec(\n\t\t\tchangeFamily.codecs,\n\t\t\tnew RevisionTagCodec(idCompressor),\n\t\t\toptions,\n\t\t\tformatOptions.message,\n\t\t);\n\n\t\tconst changeEnricher = enricher ?? new NoOpChangeEnricher();\n\t\tthis.resubmitMachine =\n\t\t\tresubmitMachine ??\n\t\t\tnew DefaultResubmitMachine(\n\t\t\t\t(change: TaggedChange<TChange>) =>\n\t\t\t\t\tchangeFamily.rebaser.invert(change, true, this.mintRevisionTag()),\n\t\t\t\tchangeEnricher,\n\t\t\t);\n\t\tthis.commitEnricher = new BranchCommitEnricher(changeFamily.rebaser, changeEnricher);\n\t}\n\n\t// TODO: SharedObject's merging of the two summary methods into summarizeCore is not what we want here:\n\t// We might want to not subclass it, or override/reimplement most of its functionality.\n\t@throwIfBroken\n\tpublic summarizeCore(\n\t\tserializer: IFluidSerializer,\n\t\ttelemetryContext?: ITelemetryContext,\n\t\tincrementalSummaryContext?: IExperimentalIncrementalSummaryContext,\n\t\tfullTree?: boolean,\n\t): ISummaryTreeWithStats {\n\t\tconst builder = new SummaryTreeBuilder();\n\t\tconst summarizableBuilder = new SummaryTreeBuilder();\n\t\t// Merge the summaries of all summarizables together under a single ISummaryTree\n\t\tfor (const s of this.summarizables) {\n\t\t\t// Add the summarizable's path in the summary tree to the incremental summary context's\n\t\t\t// summary path, so that the summarizable can use it to generate incremental summaries.\n\t\t\tconst childIncrementalSummaryContext =\n\t\t\t\tincrementalSummaryContext === undefined\n\t\t\t\t\t? undefined\n\t\t\t\t\t: {\n\t\t\t\t\t\t\t...incrementalSummaryContext,\n\t\t\t\t\t\t\tsummaryPath: `${incrementalSummaryContext.summaryPath}/${summarizablesTreeKey}/${s.key}`,\n\t\t\t\t\t\t};\n\t\t\tsummarizableBuilder.addWithStats(\n\t\t\t\ts.key,\n\t\t\t\ts.summarize({\n\t\t\t\t\tstringify: (contents: unknown) =>\n\t\t\t\t\t\tserializer.stringify(contents, this.sharedObject.handle),\n\t\t\t\t\tfullTree,\n\t\t\t\t\ttelemetryContext,\n\t\t\t\t\tincrementalSummaryContext: childIncrementalSummaryContext,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\tbuilder.addWithStats(summarizablesTreeKey, summarizableBuilder.getSummaryTree());\n\t\treturn builder.getSummaryTree();\n\t}\n\n\tpublic async loadCore(services: IChannelStorageService): Promise<void> {\n\t\tassert(\n\t\t\tthis.editManager.localBranch.getHead() === this.editManager.getTrunkHead(),\n\t\t\t0xaaa /* All local changes should be applied to the trunk before loading from summary */,\n\t\t);\n\t\tconst [editManagerSummarizer, ...summarizables] = this.summarizables;\n\t\tconst loadEditManager = this.loadSummarizable(editManagerSummarizer, services);\n\t\tconst loadSummarizables = summarizables.map(async (s) =>\n\t\t\tthis.loadSummarizable(s, services),\n\t\t);\n\n\t\tif (this.detachedRevision !== undefined) {\n\t\t\t// If we are detached but loading from a summary, then we need to update our detached revision to ensure that it is ahead of all detached revisions in the summary.\n\t\t\t// First, finish loading the edit manager so that we can inspect the sequence numbers of the commits on the trunk.\n\t\t\tawait loadEditManager;\n\t\t\t// Find the most recent detached revision in the summary trunk...\n\t\t\tlet latestDetachedSequenceNumber: SeqNumber | undefined;\n\t\t\tfindAncestor(this.editManager.getTrunkHead(), (c) => {\n\t\t\t\tconst sequenceNumber = this.editManager.getSequenceNumber(c);\n\t\t\t\tif (sequenceNumber !== undefined && sequenceNumber < 0) {\n\t\t\t\t\tlatestDetachedSequenceNumber = sequenceNumber;\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t});\n\t\t\t// ...and set our detached revision to be as it would be if we had been already created that revision.\n\t\t\tthis.detachedRevision = latestDetachedSequenceNumber ?? this.detachedRevision;\n\t\t\tawait Promise.all(loadSummarizables);\n\t\t} else {\n\t\t\tawait Promise.all([loadEditManager, ...loadSummarizables]);\n\t\t}\n\t}\n\n\tprivate async loadSummarizable(\n\t\tsummarizable: Summarizable,\n\t\tservices: IChannelStorageService,\n\t): Promise<void> {\n\t\treturn summarizable.load(\n\t\t\tscopeStorageService(services, summarizablesTreeKey, summarizable.key),\n\t\t\t(contents) => this.serializer.parse(contents),\n\t\t);\n\t}\n\n\t/**\n\t * Submits an op to the Fluid runtime containing the given commit\n\t * @param commit - the commit to submit\n\t * @returns the submitted commit. This is undefined if the underlying `SharedObject` is not attached,\n\t * and may differ from `commit` due to enrichments like detached tree refreshers.\n\t */\n\tprotected submitCommit(\n\t\tcommit: GraphCommit<TChange>,\n\t\tschemaAndPolicy: ClonableSchemaAndPolicy,\n\t\tisResubmit: boolean,\n\t): void {\n\t\tassert(\n\t\t\tthis.sharedObject.isAttached() === (this.detachedRevision === undefined),\n\t\t\t0x95a /* Detached revision should only be set when not attached */,\n\t\t);\n\n\t\tconst enrichedCommit =\n\t\t\tthis.detachedRevision === undefined && !isResubmit\n\t\t\t\t? this.commitEnricher.enrich(commit)\n\t\t\t\t: commit;\n\n\t\t// Edits submitted before the first attach are treated as sequenced because they will be included\n\t\t// in the attach summary that is uploaded to the service.\n\t\t// Until this attach workflow happens, this instance essentially behaves as a centralized data structure.\n\t\tif (this.detachedRevision !== undefined) {\n\t\t\tconst newRevision: SeqNumber = brand((this.detachedRevision as number) + 1);\n\t\t\tthis.detachedRevision = newRevision;\n\t\t\tthis.editManager.addSequencedChanges(\n\t\t\t\t[enrichedCommit],\n\t\t\t\tthis.editManager.localSessionId,\n\t\t\t\tnewRevision,\n\t\t\t\tthis.detachedRevision,\n\t\t\t);\n\t\t\tthis.editManager.advanceMinimumSequenceNumber(newRevision, false);\n\t\t\treturn undefined;\n\t\t}\n\t\tconst message = this.messageCodec.encode(\n\t\t\t{\n\t\t\t\tcommit: enrichedCommit,\n\t\t\t\tsessionId: this.editManager.localSessionId,\n\t\t\t},\n\t\t\t{\n\t\t\t\tidCompressor: this.idCompressor,\n\t\t\t\tschema: schemaAndPolicy,\n\t\t\t},\n\t\t);\n\t\tthis.submitLocalMessage(message, {\n\t\t\t// Clone the schema to ensure that during resubmit the schema has not been mutated by later changes\n\t\t\tschema: schemaAndPolicy.schema.clone(),\n\t\t\tpolicy: schemaAndPolicy.policy,\n\t\t});\n\t\tthis.resubmitMachine.onCommitSubmitted(enrichedCommit);\n\t}\n\n\t/**\n\t * Process a bunch of messages from the runtime. SharedObject will call this method with a bunch of messages.\n\t */\n\tpublic processMessagesCore(messagesCollection: IRuntimeMessageCollection): void {\n\t\tconst { envelope, local, messagesContent } = messagesCollection;\n\t\tconst commits: GraphCommit<TChange>[] = [];\n\t\tlet messagesSessionId: SessionId | undefined;\n\n\t\t// Get a list of all the commits from the messages.\n\t\tfor (const messageContent of messagesContent) {\n\t\t\t// Empty context object is passed in, as our decode function is schema-agnostic.\n\t\t\tconst { commit, sessionId } = this.messageCodec.decode(messageContent.contents, {\n\t\t\t\tidCompressor: this.idCompressor,\n\t\t\t});\n\t\t\tcommits.push(commit);\n\n\t\t\tif (messagesSessionId !== undefined) {\n\t\t\t\tassert(\n\t\t\t\t\tmessagesSessionId === sessionId,\n\t\t\t\t\t0xad9 /* All messages in a bunch must have the same session ID */,\n\t\t\t\t);\n\t\t\t}\n\t\t\tmessagesSessionId = sessionId;\n\t\t}\n\n\t\tassert(messagesSessionId !== undefined, 0xada /* Messages must have a session ID */);\n\n\t\tthis.editManager.addSequencedChanges(\n\t\t\tcommits,\n\t\t\tmessagesSessionId,\n\t\t\tbrand(envelope.sequenceNumber),\n\t\t\tbrand(envelope.referenceSequenceNumber),\n\t\t);\n\n\t\t// Update the resubmit machine for each commit applied.\n\t\tfor (const _ of messagesContent) {\n\t\t\tthis.resubmitMachine.onSequencedCommitApplied(local);\n\t\t}\n\n\t\tthis.editManager.advanceMinimumSequenceNumber(brand(envelope.minimumSequenceNumber));\n\t}\n\n\tpublic getLocalBranch(): SharedTreeBranch<TEditor, TChange> {\n\t\treturn this.editManager.localBranch;\n\t}\n\n\tpublic didAttach(): void {\n\t\tthis.detachedRevision = undefined;\n\t}\n\n\tpublic reSubmitCore(content: JsonCompatibleReadOnly, localOpMetadata: unknown): void {\n\t\t// Empty context object is passed in, as our decode function is schema-agnostic.\n\t\tconst {\n\t\t\tcommit: { revision },\n\t\t} = this.messageCodec.decode(this.serializer.decode(content), {\n\t\t\tidCompressor: this.idCompressor,\n\t\t});\n\t\t// If a resubmit phase is not already in progress, then this must be the first commit of a new resubmit phase.\n\t\tif (this.resubmitMachine.isInResubmitPhase === false) {\n\t\t\tconst localCommits = this.editManager.getLocalCommits();\n\t\t\tconst revisionIndex = localCommits.findIndex((c) => c.revision === revision);\n\t\t\tassert(revisionIndex >= 0, 0xbdb /* revision must exist in local commits */);\n\t\t\tconst toResubmit = localCommits.slice(revisionIndex);\n\t\t\tthis.resubmitMachine.prepareForResubmit(toResubmit);\n\t\t}\n\t\tassert(\n\t\t\tisClonableSchemaPolicy(localOpMetadata),\n\t\t\t0x95e /* Local metadata must contain schema and policy. */,\n\t\t);\n\t\tassert(\n\t\t\tthis.resubmitMachine.isInResubmitPhase !== false,\n\t\t\t0x984 /* Invalid resubmit outside of resubmit phase */,\n\t\t);\n\t\tconst enrichedCommit = this.resubmitMachine.peekNextCommit();\n\t\tthis.submitCommit(enrichedCommit, localOpMetadata, true);\n\t}\n\tpublic rollback(content: JsonCompatibleReadOnly, localOpMetadata: unknown): void {\n\t\t// Empty context object is passed in, as our decode function is schema-agnostic.\n\t\tconst {\n\t\t\tcommit: { revision },\n\t\t} = this.messageCodec.decode(this.serializer.decode(content), {\n\t\t\tidCompressor: this.idCompressor,\n\t\t});\n\t\tconst [commit] = this.editManager.findLocalCommit(revision);\n\t\tconst { parent } = commit;\n\t\tassert(parent !== undefined, 0xbdc /* must have parent */);\n\t\tconst [precedingCommit] = this.editManager.findLocalCommit(parent.revision);\n\t\tthis.editManager.localBranch.removeAfter(precedingCommit);\n\t\tthis.resubmitMachine.onCommitRollback(commit);\n\t}\n\n\tpublic applyStashedOp(content: JsonCompatibleReadOnly): void {\n\t\t// Empty context object is passed in, as our decode function is schema-agnostic.\n\t\tconst {\n\t\t\tcommit: { revision, change },\n\t\t} = this.messageCodec.decode(content, { idCompressor: this.idCompressor });\n\t\tthis.editManager.localBranch.apply({ change, revision });\n\t}\n}\n\nfunction isClonableSchemaPolicy(\n\tmaybeSchemaPolicy: unknown,\n): maybeSchemaPolicy is ClonableSchemaAndPolicy {\n\tconst schemaAndPolicy = maybeSchemaPolicy as ClonableSchemaAndPolicy;\n\treturn schemaAndPolicy.schema !== undefined && schemaAndPolicy.policy !== undefined;\n}\n\n/**\n * Specifies the behavior of a component that puts data in a summary.\n */\nexport interface Summarizable {\n\t/**\n\t * Field name in summary json under which this element stores its data.\n\t */\n\treadonly key: string;\n\n\t/**\n\t * {@inheritDoc @fluidframework/datastore-definitions#(IChannel:interface).summarize}\n\t * @param stringify - Serializes the contents of the component (including {@link (IFluidHandle:interface)}s) for storage.\n\t * @param fullTree - A flag indicating whether the attempt should generate a full\n\t * summary tree without any handles for unchanged subtrees. It should only be set to true when generating\n\t * a summary from the entire container. The default value is false.\n\t * @param trackState - An optimization for tracking state of objects across summaries. If the state\n\t * of an object did not change since last successful summary, an\n\t * {@link @fluidframework/protocol-definitions#ISummaryHandle} can be used\n\t * instead of re-summarizing it. If this is `false`, the expectation is that you should never\n\t * send an `ISummaryHandle`, since you are not expected to track state. The default value is true.\n\t * @param telemetryContext - See {@link @fluidframework/runtime-definitions#ITelemetryContext}.\n\t * @param incrementalSummaryContext - See {@link @fluidframework/runtime-definitions#IExperimentalIncrementalSummaryContext}.\n\t */\n\tsummarize(props: {\n\t\tstringify: SummaryElementStringifier;\n\t\tfullTree?: boolean;\n\t\ttrackState?: boolean;\n\t\ttelemetryContext?: ITelemetryContext;\n\t\tincrementalSummaryContext?: IExperimentalIncrementalSummaryContext;\n\t}): ISummaryTreeWithStats;\n\n\t/**\n\t * Allows the component to perform custom loading. The storage service is scoped to this component and therefore\n\t * paths in this component will not collide with those in other components, even if they are the same string.\n\t * @param service - Storage used by the component\n\t * @param parse - Parses serialized data from storage into runtime objects for the component\n\t */\n\tload(service: IChannelStorageService, parse: SummaryElementParser): Promise<void>;\n}\n\n/**\n * Serializes the given contents into a string acceptable for storing in summaries, i.e. all\n * Fluid handles have been replaced appropriately by an IFluidSerializer\n */\nexport type SummaryElementStringifier = (contents: unknown) => string;\n\n/**\n * Parses a serialized/summarized string into an object, rehydrating any Fluid handles as necessary\n */\nexport type SummaryElementParser = (contents: string) => unknown;\n\n/**\n * Compose an {@link IChannelStorageService} which prefixes all paths before forwarding them to the original service\n */\nfunction scopeStorageService(\n\tservice: IChannelStorageService,\n\t...pathElements: string[]\n): IChannelStorageService {\n\tconst scope = `${pathElements.join(\"/\")}/`;\n\n\treturn {\n\t\tasync readBlob(path: string): Promise<ArrayBufferLike> {\n\t\t\treturn service.readBlob(`${scope}${path}`);\n\t\t},\n\t\tasync contains(path) {\n\t\t\treturn service.contains(`${scope}${path}`);\n\t\t},\n\t\tasync list(path) {\n\t\t\treturn service.list(`${scope}${path}`);\n\t\t},\n\t};\n}\n"]}
@@ -118,7 +118,7 @@ export function checkUnion(union, preventAmbiguity, ambiguityErrors) {
118
118
  break;
119
119
  }
120
120
  case NodeKind.Object: {
121
- assert(isObjectNodeSchema(schema), "Expected object schema.");
121
+ assert(isObjectNodeSchema(schema), 0xbde /* Expected object schema. */);
122
122
  objects.push(schema);
123
123
  for (const key of schema.fields.keys()) {
124
124
  getOrCreate(allObjectKeys, key, () => new Set()).add(schema);
@@ -126,17 +126,17 @@ export function checkUnion(union, preventAmbiguity, ambiguityErrors) {
126
126
  break;
127
127
  }
128
128
  case NodeKind.Array: {
129
- assert(isArrayNodeSchema(schema), "Expected array schema.");
129
+ assert(isArrayNodeSchema(schema), 0xbdf /* Expected array schema. */);
130
130
  arrays.push(schema);
131
131
  break;
132
132
  }
133
133
  case NodeKind.Map: {
134
- assert(isMapNodeSchema(schema), "Expected map schema.");
134
+ assert(isMapNodeSchema(schema), 0xbe0 /* Expected map schema. */);
135
135
  maps.push(schema);
136
136
  break;
137
137
  }
138
138
  case NodeKind.Record: {
139
- assert(isRecordNodeSchema(schema), "Expected record schema.");
139
+ assert(isRecordNodeSchema(schema), 0xbe1 /* Expected record schema. */);
140
140
  records.push(schema);
141
141
  break;
142
142
  }
@@ -1 +1 @@
1
- {"version":3,"file":"configuration.js","sourceRoot":"","sources":["../../../src/simple-tree/api/configuration.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACN,MAAM,EACN,WAAW,EACX,IAAI,EACJ,GAAG,EACH,eAAe,GACf,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,0CAA0C,CAAC;AAEtE,OAAO,EAGN,kBAAkB,EAClB,SAAS,EACT,sBAAsB,EACtB,qBAAqB,EACrB,oBAAoB,GACpB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,QAAQ,EAAuB,MAAM,kBAAkB,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EACN,iBAAiB,EACjB,eAAe,EACf,kBAAkB,EAClB,kBAAkB,GAKlB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAElD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAiGxD,MAAM,+BAA+B,GAAwC;IAC5E,sBAAsB,EAAE,KAAK;IAC7B,gBAAgB,EAAE,KAAK;CACvB,CAAC;AAeF;;;GAGG;AACH,MAAM,OAAO,qBAAqB;IA0BjC;;;;;;;;;;;;OAYG;IACH,YAAmB,KAAsC;QACxD,MAAM,MAAM,GAAG,EAAE,GAAG,+BAA+B,EAAE,GAAG,KAAK,EAAE,CAAC;QAChE,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;QAC5B,IAAI,CAAC,sBAAsB,GAAG,MAAM,CAAC,sBAAsB,CAAC;QAC5D,IAAI,CAAC,gBAAgB,GAAG,MAAM,CAAC,gBAAgB,CAAC;QAEhD,sIAAsI;QACtI,MAAM,eAAe,GAAa,EAAE,CAAC;QAErC,4DAA4D;QAC5D,sDAAsD;QACtD,cAAc,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAE9B,MAAM,WAAW,GAAG,IAAI,GAAG,EAA6C,CAAC;QAEzE,eAAe,CAAC,MAAM,CAAC,MAAM,EAAE;YAC9B,IAAI,EAAE,CAAC,MAAM,EAAE,EAAE;gBAChB,0DAA0D;gBAC1D,iMAAiM;gBACjM,wBAAwB;gBACxB,qBAAqB,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;gBAEpC,WAAW,CAAC,GAAG,EAAE,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC;gBACvD,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,EAAE,MAA2C,CAAC,CAAC;YACjF,CAAC;YACD,YAAY,CAAC,EAAE,KAAK,EAAE;gBACrB,UAAU,CACT,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,kBAAkB,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAC5E,MAAM,CAAC,gBAAgB,EACvB,eAAe,CACf,CAAC;YACH,CAAC;SACD,CAAC,CAAC;QAEH,IAAI,CAAC,mBAAmB,GAAG,WAAW,CAAC;QAEvC,IAAI,eAAe,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAClC,gFAAgF;YAChF,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,eAAe,CAAC,CAAC;YAC9C,MAAM,IAAI,UAAU,CAAC,4BAA4B,CAAC,GAAG,YAAY,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAClF,CAAC;IACF,CAAC;CACD;AAED;;;GAGG;AACH,MAAM,OAAO,0BAGZ,SAAQ,qBAA8B;IAQtC;;OAEG;IACH,IAAW,WAAW;QACrB,OAAO,IAAI,CAAC,mBAA6E,CAAC;IAC3F,CAAC;IAED,YAAmB,KAAsC;QACxD,KAAK,CAAC,KAAK,CAAC,CAAC;QACb,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAChD,CAAC;CACD;AAkBD;;GAEG;AACH,SAAS,WAAW,CAAC,OAAiC;IACrD,sDAAsD;IACtD,qFAAqF;IACrF,OAAO,IAAI,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;AACnF,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,UAAU,CACzB,KAA+B,EAC/B,gBAAyB,EACzB,eAAyB;IAEzB,MAAM,OAAO,GAAwB,IAAI,GAAG,EAAE,CAAC;IAC/C,MAAM,IAAI,GAAoB,EAAE,CAAC;IACjC,MAAM,MAAM,GAAsB,EAAE,CAAC;IACrC,MAAM,OAAO,GAAuB,EAAE,CAAC;IACvC,MAAM,OAAO,GAAuB,EAAE,CAAC;IAEvC,wCAAwC;IACxC,MAAM,aAAa,GAAqC,IAAI,GAAG,EAAE,CAAC;IAElE,KAAK,MAAM,MAAM,IAAI,KAAK,EAAE,CAAC;QAC5B,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YACzB,MAAM,IAAI,UAAU,CAAC,sCAAsC,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC;QACjF,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAEpB,QAAQ,MAAM,CAAC,IAAI,EAAE,CAAC;YACrB,KAAK,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;gBACpB,gBAAgB;gBAChB,MAAM;YACP,CAAC;YACD,KAAK,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;gBACtB,MAAM,CAAC,kBAAkB,CAAC,MAAM,CAAC,EAAE,yBAAyB,CAAC,CAAC;gBAC9D,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBACrB,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;oBACxC,WAAW,CAAC,aAAa,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,IAAI,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;gBAC9D,CAAC;gBACD,MAAM;YACP,CAAC;YACD,KAAK,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;gBACrB,MAAM,CAAC,iBAAiB,CAAC,MAAM,CAAC,EAAE,wBAAwB,CAAC,CAAC;gBAC5D,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBACpB,MAAM;YACP,CAAC;YACD,KAAK,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;gBACnB,MAAM,CAAC,eAAe,CAAC,MAAM,CAAC,EAAE,sBAAsB,CAAC,CAAC;gBACxD,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBAClB,MAAM;YACP,CAAC;YACD,KAAK,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;gBACtB,MAAM,CAAC,kBAAkB,CAAC,MAAM,CAAC,EAAE,yBAAyB,CAAC,CAAC;gBAC9D,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBACrB,MAAM;YACP,CAAC;YACD,OAAO,CAAC,CAAC,CAAC;gBACT,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAC9B,CAAC;QACF,CAAC;IACF,CAAC;IAED,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACvB,uFAAuF;QACvF,OAAO;IACR,CAAC;IAED,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,eAAe,CAAC,IAAI,CACnB,qDAAqD,WAAW,CAAC,MAAM,CAAC,qGAAqG,CAC7K,CAAC;IACH,CAAC;IAED,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrB,eAAe,CAAC,IAAI,CACnB,mDAAmD,WAAW,CAAC,IAAI,CAAC,mGAAmG,CACvK,CAAC;IACH,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,eAAe,CAAC,IAAI,CACnB,sDAAsD,WAAW,CAAC,OAAO,CAAC,sGAAsG,CAChL,CAAC;IACH,CAAC;IAED,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1C,eAAe,CAAC,IAAI,CACnB,iDAAiD,WAAW,CAAC,CAAC,GAAG,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC,gHAAgH,CAClM,CAAC;IACH,CAAC;IAED,MAAM,mBAAmB,GAAG,EAAE,CAAC;IAC/B,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,mBAAmB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACrC,CAAC;IACD,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrB,mBAAmB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAClC,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,mBAAmB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACrC,CAAC;IACD,IAAI,mBAAmB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACpC,MAAM,kBAAkB,GACvB,mBAAmB,CAAC,MAAM,KAAK,CAAC;YAC/B,CAAC,CAAC,GAAG,mBAAmB,CAAC,CAAC,CAAC,IAAI,GAAG,EAAE,QAAQ,mBAAmB,CAAC,CAAC,CAAC,IAAI,GAAG,EAAE,EAAE;YAC7E,CAAC,CAAC,GAAG,mBAAmB,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,mBAAmB,CAAC,mBAAmB,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,CAAC;QACjH,eAAe,CAAC,IAAI,CACnB,oBAAoB,kBAAkB,6BAA6B,WAAW,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,IAAI,EAAE,GAAG,OAAO,CAAC,CAAC,gEAAgE,CACjL,CAAC;IACH,CAAC;IAED,yCAAyC;IACzC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC9B,6DAA6D;QAC7D,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC;QAE3C,0CAA0C;QAC1C,iBAAiB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAEjC,6FAA6F;QAC7F,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;YAC1C,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC,QAAQ,EAAE,CAAC;gBACvC,MAAM,OAAO,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC;gBAC3E,KAAK,MAAM,SAAS,IAAI,iBAAiB,EAAE,CAAC;oBAC3C,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;wBAC7B,iBAAiB,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;oBACrC,CAAC;gBACF,CAAC;YACF,CAAC;QACF,CAAC;QAED,IAAI,iBAAiB,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;YAChC,yCAAyC;YACzC,8EAA8E;YAC9E,kJAAkJ;YAClJ,4MAA4M;YAC5M,mJAAmJ;YAEnJ,eAAe,CAAC,IAAI,CACnB,0BAA0B,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,UAAU,CAAC,mEAAmE,WAAW,CAAC,iBAAiB,CAAC,oIAAoI,CAChS,CAAC;QACH,CAAC;IACF,CAAC;AACF,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport {\n\tassert,\n\tdebugAssert,\n\tfail,\n\toob,\n\tunreachableCase,\n} from \"@fluidframework/core-utils/internal\";\nimport { UsageError } from \"@fluidframework/telemetry-utils/internal\";\n\nimport {\n\ttype FieldSchemaAlpha,\n\ttype ImplicitFieldSchema,\n\tevaluateLazySchema,\n\tFieldKind,\n\tisAnnotatedAllowedType,\n\tmarkSchemaMostDerived,\n\tnormalizeFieldSchema,\n} from \"../schemaTypes.js\";\nimport { NodeKind, type TreeNodeSchema } from \"../core/index.js\";\nimport { toStoredSchema } from \"../toStoredSchema.js\";\nimport {\n\tisArrayNodeSchema,\n\tisMapNodeSchema,\n\tisObjectNodeSchema,\n\tisRecordNodeSchema,\n\ttype ArrayNodeSchema,\n\ttype MapNodeSchema,\n\ttype ObjectNodeSchema,\n\ttype RecordNodeSchema,\n} from \"../node-kinds/index.js\";\nimport { getOrCreate } from \"../../util/index.js\";\nimport type { MakeNominal } from \"../../util/index.js\";\nimport { walkFieldSchema } from \"../walkFieldSchema.js\";\nimport type { SimpleNodeSchema, SimpleTreeSchema } from \"../simpleSchema.js\";\n\n/**\n * Options when constructing a tree view.\n * @public\n */\nexport interface ITreeConfigurationOptions {\n\t/**\n\t * If `true`, the tree will validate new content against its stored schema at insertion time\n\t * and throw an error if the new content doesn't match the expected schema.\n\t *\n\t * @defaultValue `false`.\n\t *\n\t * @remarks Enabling schema validation has a performance penalty when inserting new content into the tree because\n\t * additional checks are done. Enable this option only in scenarios where you are ok with that operation being a\n\t * bit slower.\n\t */\n\tenableSchemaValidation?: boolean;\n\n\t/**\n\t * A flag used to opt into strict rules ensuring that the schema avoids cases which can make the type of nodes ambiguous when importing or exporting data.\n\t * @defaultValue `false`.\n\t *\n\t * @remarks\n\t * When this is true, it ensures that the compile time type safety for data when constructing nodes is sufficient to ensure that the runtime behavior will not give node data ambiguity errors.\n\t *\n\t * This ensures that the canonical JSON-like representation of all unions in the tree are lossless and unambiguous.\n\t * This canonical JSON-like representation consists of arrays, plain old JavaScript objects with string keys, booleans, numbers (excluding NaN, -0 and infinities), strings, null and {@link @fluidframework/core-interfaces#IFluidHandle}s.\n\t * It is compatible with the node creation APIs (such as schema class constructors) and is also compatible with JSON assuming any IFluidHandles get special handling (since they are not JSON compatible).\n\t * Currently these cases can cause ambiguity in a union:\n\t *\n\t * - More than one ArrayNode type: it's impossible to tell which array type is intended in the case of empty arrays (`[]`).\n\t *\n\t * - More than one MapNode type: it's impossible to tell which map type is intended in the case of an empty map (`{}`).\n\t *\n\t * - Both a MapNode and an ArrayNode: this case is not a problem for the canonical JSON representation, but is an issue when constructing from an Iterable, which is supported for both MapNode and ArrayNode.\n\t *\n\t * - Both a MapNode and an ObjectNode: when the input is valid for the ObjectNode, the current parser always considers it ambiguous with being a MapNode.\n\t *\n\t * - ObjectNodes which have fields (required or optional) which include all required fields of another ObjectNode: currently each ObjectNode is differentiated by the presence of its required fields.\n\t *\n\t * This check is conservative: some complex cases may error if the current simple algorithm cannot show no ambiguity is possible.\n\t * This check may become more permissive over time.\n\t *\n\t * @example Ambiguous schema (with `preventAmbiguity: false`), and how to disambiguate it using {@link Unhydrated} nodes:\n\t * ```typescript\n\t * const schemaFactory = new SchemaFactory(\"com.example\");\n\t * class Feet extends schemaFactory.object(\"Feet\", { length: schemaFactory.number }) {}\n\t * class Meters extends schemaFactory.object(\"Meters\", { length: schemaFactory.number }) {}\n\t * const config = new TreeViewConfiguration({\n\t * \t// This combination of schema can lead to ambiguous cases and will error if `preventAmbiguity` is true.\n\t * \tschema: [Feet, Meters],\n\t * \tpreventAmbiguity: false,\n\t * });\n\t * const view = tree.viewWith(config);\n\t * // This is invalid since it is ambiguous which type of node is being constructed:\n\t * // view.initialize({ length: 5 });\n\t * // To work, an explicit type can be provided by using an {@link Unhydrated} Node:\n\t * view.initialize(new Meters({ length: 5 }));\n\t * ```\n\t *\n\t * @example Schema disambiguated by adjusting field names, validated with `preventAmbiguity: true:`\n\t * ```typescript\n\t * const schemaFactory = new SchemaFactory(\"com.example\");\n\t * class Feet extends schemaFactory.object(\"Feet\", { length: schemaFactory.number }) {}\n\t * class Meters extends schemaFactory.object(\"Meters\", {\n\t * \t// To avoid ambiguity when parsing unions of Feet and Meters, this renames the length field to \"meters\".\n\t * \t// To preserve compatibility with existing data from the ambiguous case,\n\t * \t// `{ key: \"length\" }` is set, so when persisted in the tree \"length\" is used as the field name.\n\t * \tmeters: schemaFactory.required(schemaFactory.number, { key: \"length\" }),\n\t * }) {}\n\t * const config = new TreeViewConfiguration({\n\t * \t// This combination of schema is not ambiguous because `Feet` and `Meters` have different required keys.\n\t * \tschema: [Feet, Meters],\n\t * \tpreventAmbiguity: true,\n\t * });\n\t * const view = tree.viewWith(config);\n\t * // This now works, since the field is sufficient to determine this is a `Meters` node.\n\t * view.initialize({ meters: 5 });\n\t * ```\n\t *\n\t * @privateRemarks\n\t * In the future, we can support lossless round tripping via the canonical JSON-like representation above when unambiguous.\n\t * This could be done via methods added to `Tree` to export and import such objects, which would give us a place to explicitly define the type of this representation.\n\t *\n\t * To make this more permissive in the future we can:\n\t *\n\t * - Make unhydratedFlexTreeFromInsertable more permissive (ex: allow disambiguation based on leaf type)\n\t * - Update this check to more tightly match unhydratedFlexTreeFromInsertable\n\t * - Add options to help schema authors disambiguate their types, such as \"constant fields\" which are not persisted, and always have a constant value.\n\t *\n\t * The above examples exist in executable form in this files tests, and should be updated there then copied back here.\n\t */\n\treadonly preventAmbiguity?: boolean;\n}\n\nconst defaultTreeConfigurationOptions: Required<ITreeConfigurationOptions> = {\n\tenableSchemaValidation: false,\n\tpreventAmbiguity: false,\n};\n\n/**\n * Property-bag configuration for {@link TreeViewConfiguration} construction.\n * @public\n */\nexport interface ITreeViewConfiguration<\n\tTSchema extends ImplicitFieldSchema = ImplicitFieldSchema,\n> extends ITreeConfigurationOptions {\n\t/**\n\t * The schema which the application wants to view the tree with.\n\t */\n\treadonly schema: TSchema;\n}\n\n/**\n * Configuration for {@link ViewableTree.viewWith}.\n * @sealed @public\n */\nexport class TreeViewConfiguration<\n\tconst TSchema extends ImplicitFieldSchema = ImplicitFieldSchema,\n> implements Required<ITreeViewConfiguration<TSchema>>\n{\n\tprotected _typeCheck!: MakeNominal;\n\n\t/**\n\t * {@inheritDoc ITreeViewConfiguration.schema}\n\t */\n\tpublic readonly schema: TSchema;\n\n\t/**\n\t * {@inheritDoc ITreeConfigurationOptions.enableSchemaValidation}\n\t */\n\tpublic readonly enableSchemaValidation: boolean;\n\n\t/**\n\t * {@inheritDoc ITreeConfigurationOptions.preventAmbiguity}\n\t */\n\tpublic readonly preventAmbiguity: boolean;\n\n\t/**\n\t * {@link TreeSchema.definitions} but with public types.\n\t */\n\tprotected readonly definitionsInternal: ReadonlyMap<string, TreeNodeSchema>;\n\n\t/**\n\t * Construct a new {@link TreeViewConfiguration}.\n\t *\n\t * @param props - Property bag of configuration options.\n\t *\n\t * @remarks\n\t * Performing this construction deeply validates the provided schema.\n\t * This means that when this constructor is called, all {@link LazyItem} {@link TreeNodeSchema} references will be evaluated (using {@link evaluateLazySchema}).\n\t * This means that the declarations for all transitively reachable {@link TreeNodeSchema} must be available at this time.\n\t *\n\t * For example, a schema reachable from this configuration cannot reference this configuration during its declaration,\n\t * since this would be a cyclic dependency that will cause an error when constructing this configuration.\n\t */\n\tpublic constructor(props: ITreeViewConfiguration<TSchema>) {\n\t\tconst config = { ...defaultTreeConfigurationOptions, ...props };\n\t\tthis.schema = config.schema;\n\t\tthis.enableSchemaValidation = config.enableSchemaValidation;\n\t\tthis.preventAmbiguity = config.preventAmbiguity;\n\n\t\t// Ambiguity errors are lower priority to report than invalid schema errors, so collect these in an array and report them all at once.\n\t\tconst ambiguityErrors: string[] = [];\n\n\t\t// Eagerly perform this conversion to surface errors sooner.\n\t\t// Includes detection of duplicate schema identifiers.\n\t\ttoStoredSchema(config.schema);\n\n\t\tconst definitions = new Map<string, SimpleNodeSchema & TreeNodeSchema>();\n\n\t\twalkFieldSchema(config.schema, {\n\t\t\tnode: (schema) => {\n\t\t\t\t// Ensure all reachable schema are marked as most derived.\n\t\t\t\t// This ensures if multiple schema extending the same schema factory generated class are present (or have had instances of them constructed, or get instances of them constructed in the future),\n\t\t\t\t// an error is reported.\n\t\t\t\tmarkSchemaMostDerived(schema, true);\n\n\t\t\t\tdebugAssert(() => !definitions.has(schema.identifier));\n\t\t\t\tdefinitions.set(schema.identifier, schema as SimpleNodeSchema & TreeNodeSchema);\n\t\t\t},\n\t\t\tallowedTypes({ types }): void {\n\t\t\t\tcheckUnion(\n\t\t\t\t\ttypes.map((t) => evaluateLazySchema(isAnnotatedAllowedType(t) ? t.type : t)),\n\t\t\t\t\tconfig.preventAmbiguity,\n\t\t\t\t\tambiguityErrors,\n\t\t\t\t);\n\t\t\t},\n\t\t});\n\n\t\tthis.definitionsInternal = definitions;\n\n\t\tif (ambiguityErrors.length !== 0) {\n\t\t\t// Duplicate errors are common since when two types conflict, both orders error:\n\t\t\tconst deduplicated = new Set(ambiguityErrors);\n\t\t\tthrow new UsageError(`Ambiguous schema found:\\n${[...deduplicated].join(\"\\n\")}`);\n\t\t}\n\t}\n}\n\n/**\n * {@link TreeViewConfiguration} extended with some alpha APIs.\n * @sealed @alpha\n */\nexport class TreeViewConfigurationAlpha<\n\t\tconst TSchema extends ImplicitFieldSchema = ImplicitFieldSchema,\n\t>\n\textends TreeViewConfiguration<TSchema>\n\timplements TreeSchema\n{\n\t/**\n\t * {@inheritDoc TreeSchema.root}\n\t */\n\tpublic readonly root: FieldSchemaAlpha;\n\n\t/**\n\t * {@inheritDoc TreeSchema.definitions}\n\t */\n\tpublic get definitions(): ReadonlyMap<string, SimpleNodeSchema & TreeNodeSchema> {\n\t\treturn this.definitionsInternal as ReadonlyMap<string, SimpleNodeSchema & TreeNodeSchema>;\n\t}\n\n\tpublic constructor(props: ITreeViewConfiguration<TSchema>) {\n\t\tsuper(props);\n\t\tthis.root = normalizeFieldSchema(props.schema);\n\t}\n}\n\n/**\n * {@link TreeViewConfigurationAlpha}\n * @sealed @alpha\n */\nexport interface TreeSchema extends SimpleTreeSchema {\n\t/**\n\t * {@inheritDoc SimpleTreeSchema.root}\n\t */\n\treadonly root: FieldSchemaAlpha;\n\n\t/**\n\t * {@inheritDoc SimpleTreeSchema.definitions}\n\t */\n\treadonly definitions: ReadonlyMap<string, SimpleNodeSchema & TreeNodeSchema>;\n}\n\n/**\n * Pretty print a set of types for use in error messages.\n */\nfunction formatTypes(allowed: Iterable<TreeNodeSchema>): string {\n\t// Use JSON.stringify to quote and escape identifiers.\n\t// Don't just use a single array JSON.stringify since that omits spaces between items\n\treturn `[${Array.from(allowed, (s) => JSON.stringify(s.identifier)).join(\", \")}]`;\n}\n\n/**\n * Check if union contents are valid (shallowly).\n *\n * @param union - The union of {@link TreeNodeSchema} to check.\n * @param preventAmbiguity - If true, detect cases documented in {@link ITreeConfigurationOptions.preventAmbiguity}, reporting them to `ambiguityErrors`.\n * @param ambiguityErrors - An array into which this function inserts any ambiguity errors, see {@link ITreeConfigurationOptions.preventAmbiguity}.\n *\n * @remarks\n * Includes checks for non-ambiguity errors as well: such as duplicate schemas in the union.\n * Any non-ambiguity errors are thrown as exceptions: `UsageError`s if causable by incorrect API use, and asserts if violating internal invariants.\n */\nexport function checkUnion(\n\tunion: Iterable<TreeNodeSchema>,\n\tpreventAmbiguity: boolean,\n\tambiguityErrors: string[],\n): void {\n\tconst checked: Set<TreeNodeSchema> = new Set();\n\tconst maps: MapNodeSchema[] = [];\n\tconst arrays: ArrayNodeSchema[] = [];\n\tconst records: RecordNodeSchema[] = [];\n\tconst objects: ObjectNodeSchema[] = [];\n\n\t// Map from key to schema using that key\n\tconst allObjectKeys: Map<string, Set<TreeNodeSchema>> = new Map();\n\n\tfor (const schema of union) {\n\t\tif (checked.has(schema)) {\n\t\t\tthrow new UsageError(`Duplicate schema in allowed types: ${schema.identifier}`);\n\t\t}\n\t\tchecked.add(schema);\n\n\t\tswitch (schema.kind) {\n\t\t\tcase NodeKind.Leaf: {\n\t\t\t\t// nothing to do\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase NodeKind.Object: {\n\t\t\t\tassert(isObjectNodeSchema(schema), \"Expected object schema.\");\n\t\t\t\tobjects.push(schema);\n\t\t\t\tfor (const key of schema.fields.keys()) {\n\t\t\t\t\tgetOrCreate(allObjectKeys, key, () => new Set()).add(schema);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase NodeKind.Array: {\n\t\t\t\tassert(isArrayNodeSchema(schema), \"Expected array schema.\");\n\t\t\t\tarrays.push(schema);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase NodeKind.Map: {\n\t\t\t\tassert(isMapNodeSchema(schema), \"Expected map schema.\");\n\t\t\t\tmaps.push(schema);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase NodeKind.Record: {\n\t\t\t\tassert(isRecordNodeSchema(schema), \"Expected record schema.\");\n\t\t\t\trecords.push(schema);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdefault: {\n\t\t\t\tunreachableCase(schema.kind);\n\t\t\t}\n\t\t}\n\t}\n\n\tif (!preventAmbiguity) {\n\t\t// All remaining checks are for the preventAmbiguity case, so skip them if not enabled.\n\t\treturn;\n\t}\n\n\tif (arrays.length > 1) {\n\t\tambiguityErrors.push(\n\t\t\t`More than one kind of array allowed within union (${formatTypes(arrays)}). This would require type disambiguation which is not supported by arrays during import or export.`,\n\t\t);\n\t}\n\n\tif (maps.length > 1) {\n\t\tambiguityErrors.push(\n\t\t\t`More than one kind of map allowed within union (${formatTypes(maps)}). This would require type disambiguation which is not supported by maps during import or export.`,\n\t\t);\n\t}\n\n\tif (records.length > 1) {\n\t\tambiguityErrors.push(\n\t\t\t`More than one kind of record allowed within union (${formatTypes(records)}). This would require type disambiguation which is not supported by records during import or export.`,\n\t\t);\n\t}\n\n\tif (maps.length > 0 && arrays.length > 0) {\n\t\tambiguityErrors.push(\n\t\t\t`Both a map and an array allowed within union (${formatTypes([...arrays, ...maps])}). Both can be implicitly constructed from iterables like arrays, which are ambiguous when the array is empty.`,\n\t\t);\n\t}\n\n\tconst nodeKindListEntries = [];\n\tif (objects.length > 0) {\n\t\tnodeKindListEntries.push(\"objects\");\n\t}\n\tif (maps.length > 0) {\n\t\tnodeKindListEntries.push(\"maps\");\n\t}\n\tif (records.length > 0) {\n\t\tnodeKindListEntries.push(\"records\");\n\t}\n\tif (nodeKindListEntries.length > 1) {\n\t\tconst nodeKindListString =\n\t\t\tnodeKindListEntries.length === 2\n\t\t\t\t? `${nodeKindListEntries[0] ?? oob()} and ${nodeKindListEntries[1] ?? oob()}`\n\t\t\t\t: `${nodeKindListEntries.slice(0, -1).join(\", \")}, and ${nodeKindListEntries[nodeKindListEntries.length - 1]}`;\n\t\tambiguityErrors.push(\n\t\t\t`A combination of ${nodeKindListString} is allowed within union (${formatTypes([...objects, ...maps, ...records])}). These can be constructed from objects and can be ambiguous.`,\n\t\t);\n\t}\n\n\t// Check for objects which fully overlap:\n\tfor (const schema of objects) {\n\t\t// All objects which might be ambiguous relative to `schema`.\n\t\tconst possiblyAmbiguous = new Set(objects);\n\n\t\t// A schema can't be ambiguous with itself\n\t\tpossiblyAmbiguous.delete(schema);\n\n\t\t// For each field of schema, remove schema from possiblyAmbiguous that do not have that field\n\t\tfor (const [key, field] of schema.fields) {\n\t\t\tif (field.kind === FieldKind.Required) {\n\t\t\t\tconst withKey = allObjectKeys.get(key) ?? fail(0xb35 /* missing schema */);\n\t\t\t\tfor (const candidate of possiblyAmbiguous) {\n\t\t\t\t\tif (!withKey.has(candidate)) {\n\t\t\t\t\t\tpossiblyAmbiguous.delete(candidate);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (possiblyAmbiguous.size > 0) {\n\t\t\t// TODO: make this check more permissive.\n\t\t\t// Allow using the type of the field to disambiguate, at least for leaf types.\n\t\t\t// Add \"constant\" fields which can be used to disambiguate even more cases without adding persisted data: maybe make them optional in constructor?\n\t\t\t// Consider separating unambiguous implicit construction format from constructor arguments at type level, allowing constructor to superset the implicit construction options (ex: optional constant fields).\n\t\t\t// The policy here however must remain at least as conservative as shallowCompatibilityTest in src/simple-tree/unhydratedFlexTreeFromInsertable.ts.\n\n\t\t\tambiguityErrors.push(\n\t\t\t\t`The required fields of ${JSON.stringify(schema.identifier)} are insufficient to differentiate it from the following types: ${formatTypes(possiblyAmbiguous)}. For objects to be considered unambiguous, each must have required fields that do not all occur on any other object in the union.`,\n\t\t\t);\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"configuration.js","sourceRoot":"","sources":["../../../src/simple-tree/api/configuration.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport {\n\tassert,\n\tdebugAssert,\n\tfail,\n\toob,\n\tunreachableCase,\n} from \"@fluidframework/core-utils/internal\";\nimport { UsageError } from \"@fluidframework/telemetry-utils/internal\";\n\nimport {\n\ttype FieldSchemaAlpha,\n\ttype ImplicitFieldSchema,\n\tevaluateLazySchema,\n\tFieldKind,\n\tisAnnotatedAllowedType,\n\tmarkSchemaMostDerived,\n\tnormalizeFieldSchema,\n} from \"../schemaTypes.js\";\nimport { NodeKind, type TreeNodeSchema } from \"../core/index.js\";\nimport { toStoredSchema } from \"../toStoredSchema.js\";\nimport {\n\tisArrayNodeSchema,\n\tisMapNodeSchema,\n\tisObjectNodeSchema,\n\tisRecordNodeSchema,\n\ttype ArrayNodeSchema,\n\ttype MapNodeSchema,\n\ttype ObjectNodeSchema,\n\ttype RecordNodeSchema,\n} from \"../node-kinds/index.js\";\nimport { getOrCreate } from \"../../util/index.js\";\nimport type { MakeNominal } from \"../../util/index.js\";\nimport { walkFieldSchema } from \"../walkFieldSchema.js\";\nimport type { SimpleNodeSchema, SimpleTreeSchema } from \"../simpleSchema.js\";\n\n/**\n * Options when constructing a tree view.\n * @public\n */\nexport interface ITreeConfigurationOptions {\n\t/**\n\t * If `true`, the tree will validate new content against its stored schema at insertion time\n\t * and throw an error if the new content doesn't match the expected schema.\n\t *\n\t * @defaultValue `false`.\n\t *\n\t * @remarks Enabling schema validation has a performance penalty when inserting new content into the tree because\n\t * additional checks are done. Enable this option only in scenarios where you are ok with that operation being a\n\t * bit slower.\n\t */\n\tenableSchemaValidation?: boolean;\n\n\t/**\n\t * A flag used to opt into strict rules ensuring that the schema avoids cases which can make the type of nodes ambiguous when importing or exporting data.\n\t * @defaultValue `false`.\n\t *\n\t * @remarks\n\t * When this is true, it ensures that the compile time type safety for data when constructing nodes is sufficient to ensure that the runtime behavior will not give node data ambiguity errors.\n\t *\n\t * This ensures that the canonical JSON-like representation of all unions in the tree are lossless and unambiguous.\n\t * This canonical JSON-like representation consists of arrays, plain old JavaScript objects with string keys, booleans, numbers (excluding NaN, -0 and infinities), strings, null and {@link @fluidframework/core-interfaces#IFluidHandle}s.\n\t * It is compatible with the node creation APIs (such as schema class constructors) and is also compatible with JSON assuming any IFluidHandles get special handling (since they are not JSON compatible).\n\t * Currently these cases can cause ambiguity in a union:\n\t *\n\t * - More than one ArrayNode type: it's impossible to tell which array type is intended in the case of empty arrays (`[]`).\n\t *\n\t * - More than one MapNode type: it's impossible to tell which map type is intended in the case of an empty map (`{}`).\n\t *\n\t * - Both a MapNode and an ArrayNode: this case is not a problem for the canonical JSON representation, but is an issue when constructing from an Iterable, which is supported for both MapNode and ArrayNode.\n\t *\n\t * - Both a MapNode and an ObjectNode: when the input is valid for the ObjectNode, the current parser always considers it ambiguous with being a MapNode.\n\t *\n\t * - ObjectNodes which have fields (required or optional) which include all required fields of another ObjectNode: currently each ObjectNode is differentiated by the presence of its required fields.\n\t *\n\t * This check is conservative: some complex cases may error if the current simple algorithm cannot show no ambiguity is possible.\n\t * This check may become more permissive over time.\n\t *\n\t * @example Ambiguous schema (with `preventAmbiguity: false`), and how to disambiguate it using {@link Unhydrated} nodes:\n\t * ```typescript\n\t * const schemaFactory = new SchemaFactory(\"com.example\");\n\t * class Feet extends schemaFactory.object(\"Feet\", { length: schemaFactory.number }) {}\n\t * class Meters extends schemaFactory.object(\"Meters\", { length: schemaFactory.number }) {}\n\t * const config = new TreeViewConfiguration({\n\t * \t// This combination of schema can lead to ambiguous cases and will error if `preventAmbiguity` is true.\n\t * \tschema: [Feet, Meters],\n\t * \tpreventAmbiguity: false,\n\t * });\n\t * const view = tree.viewWith(config);\n\t * // This is invalid since it is ambiguous which type of node is being constructed:\n\t * // view.initialize({ length: 5 });\n\t * // To work, an explicit type can be provided by using an {@link Unhydrated} Node:\n\t * view.initialize(new Meters({ length: 5 }));\n\t * ```\n\t *\n\t * @example Schema disambiguated by adjusting field names, validated with `preventAmbiguity: true:`\n\t * ```typescript\n\t * const schemaFactory = new SchemaFactory(\"com.example\");\n\t * class Feet extends schemaFactory.object(\"Feet\", { length: schemaFactory.number }) {}\n\t * class Meters extends schemaFactory.object(\"Meters\", {\n\t * \t// To avoid ambiguity when parsing unions of Feet and Meters, this renames the length field to \"meters\".\n\t * \t// To preserve compatibility with existing data from the ambiguous case,\n\t * \t// `{ key: \"length\" }` is set, so when persisted in the tree \"length\" is used as the field name.\n\t * \tmeters: schemaFactory.required(schemaFactory.number, { key: \"length\" }),\n\t * }) {}\n\t * const config = new TreeViewConfiguration({\n\t * \t// This combination of schema is not ambiguous because `Feet` and `Meters` have different required keys.\n\t * \tschema: [Feet, Meters],\n\t * \tpreventAmbiguity: true,\n\t * });\n\t * const view = tree.viewWith(config);\n\t * // This now works, since the field is sufficient to determine this is a `Meters` node.\n\t * view.initialize({ meters: 5 });\n\t * ```\n\t *\n\t * @privateRemarks\n\t * In the future, we can support lossless round tripping via the canonical JSON-like representation above when unambiguous.\n\t * This could be done via methods added to `Tree` to export and import such objects, which would give us a place to explicitly define the type of this representation.\n\t *\n\t * To make this more permissive in the future we can:\n\t *\n\t * - Make unhydratedFlexTreeFromInsertable more permissive (ex: allow disambiguation based on leaf type)\n\t * - Update this check to more tightly match unhydratedFlexTreeFromInsertable\n\t * - Add options to help schema authors disambiguate their types, such as \"constant fields\" which are not persisted, and always have a constant value.\n\t *\n\t * The above examples exist in executable form in this files tests, and should be updated there then copied back here.\n\t */\n\treadonly preventAmbiguity?: boolean;\n}\n\nconst defaultTreeConfigurationOptions: Required<ITreeConfigurationOptions> = {\n\tenableSchemaValidation: false,\n\tpreventAmbiguity: false,\n};\n\n/**\n * Property-bag configuration for {@link TreeViewConfiguration} construction.\n * @public\n */\nexport interface ITreeViewConfiguration<\n\tTSchema extends ImplicitFieldSchema = ImplicitFieldSchema,\n> extends ITreeConfigurationOptions {\n\t/**\n\t * The schema which the application wants to view the tree with.\n\t */\n\treadonly schema: TSchema;\n}\n\n/**\n * Configuration for {@link ViewableTree.viewWith}.\n * @sealed @public\n */\nexport class TreeViewConfiguration<\n\tconst TSchema extends ImplicitFieldSchema = ImplicitFieldSchema,\n> implements Required<ITreeViewConfiguration<TSchema>>\n{\n\tprotected _typeCheck!: MakeNominal;\n\n\t/**\n\t * {@inheritDoc ITreeViewConfiguration.schema}\n\t */\n\tpublic readonly schema: TSchema;\n\n\t/**\n\t * {@inheritDoc ITreeConfigurationOptions.enableSchemaValidation}\n\t */\n\tpublic readonly enableSchemaValidation: boolean;\n\n\t/**\n\t * {@inheritDoc ITreeConfigurationOptions.preventAmbiguity}\n\t */\n\tpublic readonly preventAmbiguity: boolean;\n\n\t/**\n\t * {@link TreeSchema.definitions} but with public types.\n\t */\n\tprotected readonly definitionsInternal: ReadonlyMap<string, TreeNodeSchema>;\n\n\t/**\n\t * Construct a new {@link TreeViewConfiguration}.\n\t *\n\t * @param props - Property bag of configuration options.\n\t *\n\t * @remarks\n\t * Performing this construction deeply validates the provided schema.\n\t * This means that when this constructor is called, all {@link LazyItem} {@link TreeNodeSchema} references will be evaluated (using {@link evaluateLazySchema}).\n\t * This means that the declarations for all transitively reachable {@link TreeNodeSchema} must be available at this time.\n\t *\n\t * For example, a schema reachable from this configuration cannot reference this configuration during its declaration,\n\t * since this would be a cyclic dependency that will cause an error when constructing this configuration.\n\t */\n\tpublic constructor(props: ITreeViewConfiguration<TSchema>) {\n\t\tconst config = { ...defaultTreeConfigurationOptions, ...props };\n\t\tthis.schema = config.schema;\n\t\tthis.enableSchemaValidation = config.enableSchemaValidation;\n\t\tthis.preventAmbiguity = config.preventAmbiguity;\n\n\t\t// Ambiguity errors are lower priority to report than invalid schema errors, so collect these in an array and report them all at once.\n\t\tconst ambiguityErrors: string[] = [];\n\n\t\t// Eagerly perform this conversion to surface errors sooner.\n\t\t// Includes detection of duplicate schema identifiers.\n\t\ttoStoredSchema(config.schema);\n\n\t\tconst definitions = new Map<string, SimpleNodeSchema & TreeNodeSchema>();\n\n\t\twalkFieldSchema(config.schema, {\n\t\t\tnode: (schema) => {\n\t\t\t\t// Ensure all reachable schema are marked as most derived.\n\t\t\t\t// This ensures if multiple schema extending the same schema factory generated class are present (or have had instances of them constructed, or get instances of them constructed in the future),\n\t\t\t\t// an error is reported.\n\t\t\t\tmarkSchemaMostDerived(schema, true);\n\n\t\t\t\tdebugAssert(() => !definitions.has(schema.identifier));\n\t\t\t\tdefinitions.set(schema.identifier, schema as SimpleNodeSchema & TreeNodeSchema);\n\t\t\t},\n\t\t\tallowedTypes({ types }): void {\n\t\t\t\tcheckUnion(\n\t\t\t\t\ttypes.map((t) => evaluateLazySchema(isAnnotatedAllowedType(t) ? t.type : t)),\n\t\t\t\t\tconfig.preventAmbiguity,\n\t\t\t\t\tambiguityErrors,\n\t\t\t\t);\n\t\t\t},\n\t\t});\n\n\t\tthis.definitionsInternal = definitions;\n\n\t\tif (ambiguityErrors.length !== 0) {\n\t\t\t// Duplicate errors are common since when two types conflict, both orders error:\n\t\t\tconst deduplicated = new Set(ambiguityErrors);\n\t\t\tthrow new UsageError(`Ambiguous schema found:\\n${[...deduplicated].join(\"\\n\")}`);\n\t\t}\n\t}\n}\n\n/**\n * {@link TreeViewConfiguration} extended with some alpha APIs.\n * @sealed @alpha\n */\nexport class TreeViewConfigurationAlpha<\n\t\tconst TSchema extends ImplicitFieldSchema = ImplicitFieldSchema,\n\t>\n\textends TreeViewConfiguration<TSchema>\n\timplements TreeSchema\n{\n\t/**\n\t * {@inheritDoc TreeSchema.root}\n\t */\n\tpublic readonly root: FieldSchemaAlpha;\n\n\t/**\n\t * {@inheritDoc TreeSchema.definitions}\n\t */\n\tpublic get definitions(): ReadonlyMap<string, SimpleNodeSchema & TreeNodeSchema> {\n\t\treturn this.definitionsInternal as ReadonlyMap<string, SimpleNodeSchema & TreeNodeSchema>;\n\t}\n\n\tpublic constructor(props: ITreeViewConfiguration<TSchema>) {\n\t\tsuper(props);\n\t\tthis.root = normalizeFieldSchema(props.schema);\n\t}\n}\n\n/**\n * {@link TreeViewConfigurationAlpha}\n * @sealed @alpha\n */\nexport interface TreeSchema extends SimpleTreeSchema {\n\t/**\n\t * {@inheritDoc SimpleTreeSchema.root}\n\t */\n\treadonly root: FieldSchemaAlpha;\n\n\t/**\n\t * {@inheritDoc SimpleTreeSchema.definitions}\n\t */\n\treadonly definitions: ReadonlyMap<string, SimpleNodeSchema & TreeNodeSchema>;\n}\n\n/**\n * Pretty print a set of types for use in error messages.\n */\nfunction formatTypes(allowed: Iterable<TreeNodeSchema>): string {\n\t// Use JSON.stringify to quote and escape identifiers.\n\t// Don't just use a single array JSON.stringify since that omits spaces between items\n\treturn `[${Array.from(allowed, (s) => JSON.stringify(s.identifier)).join(\", \")}]`;\n}\n\n/**\n * Check if union contents are valid (shallowly).\n *\n * @param union - The union of {@link TreeNodeSchema} to check.\n * @param preventAmbiguity - If true, detect cases documented in {@link ITreeConfigurationOptions.preventAmbiguity}, reporting them to `ambiguityErrors`.\n * @param ambiguityErrors - An array into which this function inserts any ambiguity errors, see {@link ITreeConfigurationOptions.preventAmbiguity}.\n *\n * @remarks\n * Includes checks for non-ambiguity errors as well: such as duplicate schemas in the union.\n * Any non-ambiguity errors are thrown as exceptions: `UsageError`s if causable by incorrect API use, and asserts if violating internal invariants.\n */\nexport function checkUnion(\n\tunion: Iterable<TreeNodeSchema>,\n\tpreventAmbiguity: boolean,\n\tambiguityErrors: string[],\n): void {\n\tconst checked: Set<TreeNodeSchema> = new Set();\n\tconst maps: MapNodeSchema[] = [];\n\tconst arrays: ArrayNodeSchema[] = [];\n\tconst records: RecordNodeSchema[] = [];\n\tconst objects: ObjectNodeSchema[] = [];\n\n\t// Map from key to schema using that key\n\tconst allObjectKeys: Map<string, Set<TreeNodeSchema>> = new Map();\n\n\tfor (const schema of union) {\n\t\tif (checked.has(schema)) {\n\t\t\tthrow new UsageError(`Duplicate schema in allowed types: ${schema.identifier}`);\n\t\t}\n\t\tchecked.add(schema);\n\n\t\tswitch (schema.kind) {\n\t\t\tcase NodeKind.Leaf: {\n\t\t\t\t// nothing to do\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase NodeKind.Object: {\n\t\t\t\tassert(isObjectNodeSchema(schema), 0xbde /* Expected object schema. */);\n\t\t\t\tobjects.push(schema);\n\t\t\t\tfor (const key of schema.fields.keys()) {\n\t\t\t\t\tgetOrCreate(allObjectKeys, key, () => new Set()).add(schema);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase NodeKind.Array: {\n\t\t\t\tassert(isArrayNodeSchema(schema), 0xbdf /* Expected array schema. */);\n\t\t\t\tarrays.push(schema);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase NodeKind.Map: {\n\t\t\t\tassert(isMapNodeSchema(schema), 0xbe0 /* Expected map schema. */);\n\t\t\t\tmaps.push(schema);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase NodeKind.Record: {\n\t\t\t\tassert(isRecordNodeSchema(schema), 0xbe1 /* Expected record schema. */);\n\t\t\t\trecords.push(schema);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdefault: {\n\t\t\t\tunreachableCase(schema.kind);\n\t\t\t}\n\t\t}\n\t}\n\n\tif (!preventAmbiguity) {\n\t\t// All remaining checks are for the preventAmbiguity case, so skip them if not enabled.\n\t\treturn;\n\t}\n\n\tif (arrays.length > 1) {\n\t\tambiguityErrors.push(\n\t\t\t`More than one kind of array allowed within union (${formatTypes(arrays)}). This would require type disambiguation which is not supported by arrays during import or export.`,\n\t\t);\n\t}\n\n\tif (maps.length > 1) {\n\t\tambiguityErrors.push(\n\t\t\t`More than one kind of map allowed within union (${formatTypes(maps)}). This would require type disambiguation which is not supported by maps during import or export.`,\n\t\t);\n\t}\n\n\tif (records.length > 1) {\n\t\tambiguityErrors.push(\n\t\t\t`More than one kind of record allowed within union (${formatTypes(records)}). This would require type disambiguation which is not supported by records during import or export.`,\n\t\t);\n\t}\n\n\tif (maps.length > 0 && arrays.length > 0) {\n\t\tambiguityErrors.push(\n\t\t\t`Both a map and an array allowed within union (${formatTypes([...arrays, ...maps])}). Both can be implicitly constructed from iterables like arrays, which are ambiguous when the array is empty.`,\n\t\t);\n\t}\n\n\tconst nodeKindListEntries = [];\n\tif (objects.length > 0) {\n\t\tnodeKindListEntries.push(\"objects\");\n\t}\n\tif (maps.length > 0) {\n\t\tnodeKindListEntries.push(\"maps\");\n\t}\n\tif (records.length > 0) {\n\t\tnodeKindListEntries.push(\"records\");\n\t}\n\tif (nodeKindListEntries.length > 1) {\n\t\tconst nodeKindListString =\n\t\t\tnodeKindListEntries.length === 2\n\t\t\t\t? `${nodeKindListEntries[0] ?? oob()} and ${nodeKindListEntries[1] ?? oob()}`\n\t\t\t\t: `${nodeKindListEntries.slice(0, -1).join(\", \")}, and ${nodeKindListEntries[nodeKindListEntries.length - 1]}`;\n\t\tambiguityErrors.push(\n\t\t\t`A combination of ${nodeKindListString} is allowed within union (${formatTypes([...objects, ...maps, ...records])}). These can be constructed from objects and can be ambiguous.`,\n\t\t);\n\t}\n\n\t// Check for objects which fully overlap:\n\tfor (const schema of objects) {\n\t\t// All objects which might be ambiguous relative to `schema`.\n\t\tconst possiblyAmbiguous = new Set(objects);\n\n\t\t// A schema can't be ambiguous with itself\n\t\tpossiblyAmbiguous.delete(schema);\n\n\t\t// For each field of schema, remove schema from possiblyAmbiguous that do not have that field\n\t\tfor (const [key, field] of schema.fields) {\n\t\t\tif (field.kind === FieldKind.Required) {\n\t\t\t\tconst withKey = allObjectKeys.get(key) ?? fail(0xb35 /* missing schema */);\n\t\t\t\tfor (const candidate of possiblyAmbiguous) {\n\t\t\t\t\tif (!withKey.has(candidate)) {\n\t\t\t\t\t\tpossiblyAmbiguous.delete(candidate);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (possiblyAmbiguous.size > 0) {\n\t\t\t// TODO: make this check more permissive.\n\t\t\t// Allow using the type of the field to disambiguate, at least for leaf types.\n\t\t\t// Add \"constant\" fields which can be used to disambiguate even more cases without adding persisted data: maybe make them optional in constructor?\n\t\t\t// Consider separating unambiguous implicit construction format from constructor arguments at type level, allowing constructor to superset the implicit construction options (ex: optional constant fields).\n\t\t\t// The policy here however must remain at least as conservative as shallowCompatibilityTest in src/simple-tree/unhydratedFlexTreeFromInsertable.ts.\n\n\t\t\tambiguityErrors.push(\n\t\t\t\t`The required fields of ${JSON.stringify(schema.identifier)} are insufficient to differentiate it from the following types: ${formatTypes(possiblyAmbiguous)}. For objects to be considered unambiguous, each must have required fields that do not all occur on any other object in the union.`,\n\t\t\t);\n\t\t}\n\t}\n}\n"]}