@@ -12,11 +12,9 @@ import {
1212
1313import type { ICodecOptions } from "../codec/index.js" ;
1414import {
15- type ITreeCursorSynchronous ,
1615 type RevisionTag ,
1716 RevisionTagCodec ,
1817 SchemaVersion ,
19- type TreeStoredSchema ,
2018 TreeStoredSchemaRepository ,
2119} from "../core/index.js" ;
2220import {
@@ -34,11 +32,23 @@ import type {
3432 TreeViewConfiguration ,
3533 ImplicitFieldSchema ,
3634 TreeViewAlpha ,
35+ ITreeAlpha ,
36+ ViewableTree ,
37+ TreeView ,
38+ ReadSchema ,
39+ VerboseTree ,
40+ SimpleTreeSchema ,
3741} from "../simple-tree/index.js" ;
38- import { type JsonCompatibleReadOnly , type JsonCompatible , Breakable } from "../util/index.js" ;
42+ import {
43+ type JsonCompatibleReadOnly ,
44+ type JsonCompatible ,
45+ Breakable ,
46+ oneFromIterable ,
47+ } from "../util/index.js" ;
3948import {
4049 buildConfiguredForest ,
4150 defaultSharedTreeOptions ,
51+ exportSimpleSchema ,
4252 type ForestOptions ,
4353} from "./sharedTree.js" ;
4454import { createTreeCheckout } from "./treeCheckout.js" ;
@@ -59,30 +69,9 @@ export function independentView<const TSchema extends ImplicitFieldSchema>(
5969 config : TreeViewConfiguration < TSchema > ,
6070 options : ForestOptions & { idCompressor ?: IIdCompressor | undefined } ,
6171) : TreeViewAlpha < TSchema > {
62- const breaker = new Breakable ( "independentView" ) ;
63- const idCompressor : IIdCompressor = options . idCompressor ?? createIdCompressor ( ) ;
64- const mintRevisionTag = ( ) : RevisionTag => idCompressor . generateCompressedId ( ) ;
65- const revisionTagCodec = new RevisionTagCodec ( idCompressor ) ;
66- const schema = new TreeStoredSchemaRepository ( ) ;
67- const forest = buildConfiguredForest (
68- breaker ,
69- options . forest ?? defaultSharedTreeOptions . forest ,
70- schema ,
71- idCompressor ,
72- defaultIncrementalEncodingPolicy ,
73- ) ;
74- const checkout = createTreeCheckout ( idCompressor , mintRevisionTag , revisionTagCodec , {
75- forest,
76- schema,
77- breaker,
78- } ) ;
79- const out : TreeViewAlpha < TSchema > = new SchematizingSimpleTreeView < TSchema > (
80- checkout ,
81- config ,
82- createNodeIdentifierManager ( idCompressor ) ,
83- ) ;
84- return out ;
72+ return createIndependentTreeAlpha ( options ) . viewWith ( config ) as TreeViewAlpha < TSchema > ;
8573}
74+
8675/**
8776 * Create an initialized {@link TreeView} that is not tied to any {@link ITree} instance.
8877 *
@@ -99,52 +88,101 @@ export function independentInitializedView<const TSchema extends ImplicitFieldSc
9988 options : ForestOptions & ICodecOptions ,
10089 content : ViewContent ,
10190) : TreeViewAlpha < TSchema > {
102- const idCompressor : IIdCompressor = content . idCompressor ;
103- const fieldBatchCodec = makeFieldBatchCodec ( options , 1 ) ;
104- const schemaCodec = makeSchemaCodec ( options , SchemaVersion . v1 ) ;
105-
106- const schema = schemaCodec . decode ( content . schema as Format ) ;
107- const context : FieldBatchEncodingContext = {
108- encodeType : TreeCompressionStrategy . Compressed ,
109- idCompressor,
110- originatorId : idCompressor . localSessionId , // Is this right? If so, why is is needed?
111- schema : { schema, policy : defaultSchemaPolicy } ,
112- } ;
113-
114- const fieldCursors = fieldBatchCodec . decode ( content . tree as JsonCompatibleReadOnly , context ) ;
115- assert ( fieldCursors . length === 1 , 0xa5b /* must have exactly 1 field in batch */ ) ;
116-
117- return independentInitializedViewInternal (
91+ return createIndependentTreeAlpha ( { ...options , content } ) . viewWith (
11892 config ,
119- options ,
120- schema ,
121- // Checked above.
122- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
123- fieldCursors [ 0 ] ! ,
124- idCompressor ,
125- ) ;
93+ ) as TreeViewAlpha < TSchema > ;
12694}
12795
12896/**
129- * {@link independentInitializedView } but using internal types instead of persisted data formats.
97+ * Create a {@link ViewableTree} that is not tied to any Fluid runtimes or services.
98+ *
99+ * @remarks
100+ * Such a tree can never experience collaboration or be persisted to to a Fluid Container.
101+ *
102+ * This can be useful for testing, as well as use-cases like working on local files instead of documents stored in some Fluid service.
103+ *
104+ * @example
105+ * ```typescript
106+ * const tree = createIndependentTreeBeta();
107+ *
108+ * const stagedConfig = new TreeViewConfiguration({
109+ * schema: SchemaFactoryAlpha.types([
110+ * SchemaFactory.number,
111+ * SchemaFactoryAlpha.staged(SchemaFactory.string),
112+ * ]),
113+ * });
114+ * const afterConfig = new TreeViewConfigurationAlpha({
115+ * schema: [SchemaFactory.number, SchemaFactory.string],
116+ * });
117+ *
118+ * // Initialize tree
119+ * {
120+ * const view = tree.viewWith(stagedConfig);
121+ * view.initialize(1);
122+ * view.dispose();
123+ * }
124+ *
125+ * // Do schema upgrade
126+ * {
127+ * const view = tree.viewWith(afterConfig);
128+ * view.upgradeSchema();
129+ * view.root = "A";
130+ * view.dispose();
131+ * }
132+ *
133+ * // Can still view tree with staged schema
134+ * {
135+ * const view = tree.viewWith(stagedConfig);
136+ * assert.equal(view.root, "A");
137+ * view.dispose();
138+ * }
139+ * ```
140+ * @privateRemarks
141+ * Before stabilizing this as public, consider if we can instead just expose a better way to create regular Fluid service based SharedTrees for tests.
142+ * Something like https://github.com/microsoft/FluidFramework/pull/25422 might be a better long term stable/public solution.
143+ * @beta
130144 */
131- export function independentInitializedViewInternal < const TSchema extends ImplicitFieldSchema > (
132- config : TreeViewConfiguration < TSchema > ,
133- options : ForestOptions & ICodecOptions ,
134- schema : TreeStoredSchema ,
135- rootFieldCursor : ITreeCursorSynchronous ,
136- idCompressor : IIdCompressor ,
137- ) : SchematizingSimpleTreeView < TSchema > {
138- const breaker = new Breakable ( "independentInitializedView" ) ;
139- const revisionTagCodec = new RevisionTagCodec ( idCompressor ) ;
145+ export function createIndependentTreeBeta < const TSchema extends ImplicitFieldSchema > (
146+ options ?: ForestOptions ,
147+ ) : ViewableTree {
148+ return createIndependentTreeAlpha < TSchema > ( options ) ;
149+ }
150+
151+ /**
152+ * Alpha extensions to {@link createIndependentTreeBeta}.
153+ *
154+ * @param options - Configuration options for the independent tree.
155+ * This can be used to create an uninitialized tree, or `content` can be provided to create an initialized tree.
156+ * If content is provided, the idCompressor is a required part of it: otherwise it is optional and provided at the top level.
157+ *
158+ * @privateRemarks
159+ * TODO: Support more of {@link ITreeAlpha}, including branching APIs to allow for merges.
160+ * TODO: Better unify this logic with SharedTreeKernel and SharedTreeCore.
161+ *
162+ * Before further stabilizing: consider better ways to handle initialized vs uninitialized trees.
163+ * Perhaps it would be better to not allow initialize here at all, but expose the ability to load compressed tree content and stored schema via ITree or TreeView?
164+ * If keeping the option here, maybe a separate function of overload would be better? Or maybe flatten ViewContent inline to deduplicate the idCompressor options?
165+ * @alpha
166+ */
167+ export function createIndependentTreeAlpha < const TSchema extends ImplicitFieldSchema > (
168+ options ?: ForestOptions &
169+ (
170+ | ( { idCompressor ?: IIdCompressor | undefined } & { content ?: undefined } )
171+ | ( ICodecOptions & { content : ViewContent } & { idCompressor ?: undefined } )
172+ ) ,
173+ ) : ViewableTree & Pick < ITreeAlpha , "exportVerbose" | "exportSimpleSchema" > {
174+ const breaker = new Breakable ( "independentView" ) ;
175+ const idCompressor : IIdCompressor =
176+ options ?. idCompressor ?? options ?. content ?. idCompressor ?? createIdCompressor ( ) ;
140177 const mintRevisionTag = ( ) : RevisionTag => idCompressor . generateCompressedId ( ) ;
178+ const revisionTagCodec = new RevisionTagCodec ( idCompressor ) ;
141179
142180 // To ensure the forest is in schema when constructed, start it with an empty schema and set the schema repository content later.
143181 const schemaRepository = new TreeStoredSchemaRepository ( ) ;
144182
145183 const forest = buildConfiguredForest (
146184 breaker ,
147- options . forest ?? defaultSharedTreeOptions . forest ,
185+ options ? .forest ?? defaultSharedTreeOptions . forest ,
148186 schemaRepository ,
149187 idCompressor ,
150188 defaultIncrementalEncodingPolicy ,
@@ -156,18 +194,55 @@ export function independentInitializedViewInternal<const TSchema extends Implici
156194 breaker,
157195 } ) ;
158196
159- initialize (
160- checkout ,
161- schema ,
162- initializerFromChunk ( checkout , ( ) =>
163- combineChunks ( checkout . forest . chunkField ( rootFieldCursor ) ) ,
164- ) ,
165- ) ;
166- return new SchematizingSimpleTreeView < TSchema > (
167- checkout ,
168- config ,
169- createNodeIdentifierManager ( idCompressor ) ,
170- ) ;
197+ if ( options ?. content !== undefined ) {
198+ const schemaCodec = makeSchemaCodec ( options , SchemaVersion . v1 ) ;
199+ const fieldBatchCodec = makeFieldBatchCodec ( options , 1 ) ;
200+ const newSchema = schemaCodec . decode ( options . content . schema as Format ) ;
201+
202+ const context : FieldBatchEncodingContext = {
203+ encodeType : TreeCompressionStrategy . Compressed ,
204+ idCompressor,
205+ originatorId : idCompressor . localSessionId , // Is this right? If so, why is is needed?
206+ schema : { schema : newSchema , policy : defaultSchemaPolicy } ,
207+ } ;
208+ const fieldCursors = fieldBatchCodec . decode (
209+ options . content . tree as JsonCompatibleReadOnly ,
210+ context ,
211+ ) ;
212+ assert ( fieldCursors . length === 1 , 0xa5b /* must have exactly 1 field in batch */ ) ;
213+
214+ const fieldCursor = oneFromIterable ( fieldCursors ) ;
215+ assert ( fieldCursor !== undefined , "expected exactly one field in batch" ) ;
216+
217+ initialize (
218+ checkout ,
219+ newSchema ,
220+ initializerFromChunk ( checkout , ( ) =>
221+ combineChunks ( checkout . forest . chunkField ( fieldCursor ) ) ,
222+ ) ,
223+ ) ;
224+ }
225+
226+ return {
227+ viewWith < TRoot extends ImplicitFieldSchema > (
228+ config : TreeViewConfiguration < TRoot > ,
229+ ) : TreeView < TRoot > {
230+ const out : TreeViewAlpha < TSchema > = new SchematizingSimpleTreeView < TSchema > (
231+ checkout ,
232+ config as TreeViewConfiguration as TreeViewConfiguration < ReadSchema < TSchema > > ,
233+ createNodeIdentifierManager ( idCompressor ) ,
234+ ) ;
235+ return out as unknown as TreeView < TRoot > ;
236+ } ,
237+
238+ exportVerbose ( ) : VerboseTree | undefined {
239+ return checkout . exportVerbose ( ) ;
240+ } ,
241+
242+ exportSimpleSchema ( ) : SimpleTreeSchema {
243+ return exportSimpleSchema ( checkout . storedSchema ) ;
244+ } ,
245+ } ;
171246}
172247
173248/**
0 commit comments