@@ -2,6 +2,7 @@ import {Model} from '../model';
22import { PatchLog } from './PatchLog' ;
33import { FileModelEncoding } from './constants' ;
44import { Encoder as SidecarEncoder } from '../codec/sidecar/binary/Encoder' ;
5+ import { Decoder as SidecarDecoder } from '../codec/sidecar/binary/Decoder' ;
56import { Encoder as StructuralEncoderCompact } from '../codec/structural/compact/Encoder' ;
67import { Encoder as StructuralEncoderVerbose } from '../codec/structural/verbose/Encoder' ;
78import { encode as encodeCompact } from '../../json-crdt-patch/codec/compact/encode' ;
@@ -10,15 +11,55 @@ import {Writer} from '../../util/buffers/Writer';
1011import { CborEncoder } from '../../json-pack/cbor/CborEncoder' ;
1112import { JsonEncoder } from '../../json-pack/json/JsonEncoder' ;
1213import { printTree } from '../../util/print/printTree' ;
14+ import { decodeModel , decodeNdjsonComponents , decodePatch , decodeSeqCborComponents } from './util' ;
1315import type * as types from './types' ;
1416import type { Printable } from '../../util/print/types' ;
1517
1618export class File implements Printable {
19+ public static unserialize ( components : types . FileReadSequence ) : File {
20+ const [ view , metadata , model , history , ...frontier ] = components ;
21+ const modelFormat = metadata [ 1 ] ;
22+ let decodedModel : Model < any > | null = null ;
23+ if ( model && modelFormat !== FileModelEncoding . None ) {
24+ const isSidecar = modelFormat === FileModelEncoding . SidecarBinary ;
25+ if ( isSidecar ) {
26+ const decoder = new SidecarDecoder ( ) ;
27+ if ( ! ( model instanceof Uint8Array ) ) throw new Error ( 'NOT_BLOB' ) ;
28+ decodedModel = decoder . decode ( view , model ) ;
29+ } else {
30+ decodedModel = decodeModel ( model ) ;
31+ }
32+ }
33+ let log : PatchLog | null = null ;
34+ if ( history ) {
35+ const [ start , patches ] = history ;
36+ if ( start ) {
37+ const startModel = decodeModel ( start ) ;
38+ log = new PatchLog ( startModel ) ;
39+ for ( const patch of patches ) log . push ( decodePatch ( patch ) ) ;
40+ }
41+ }
42+ if ( ! log ) throw new Error ( 'NO_HISTORY' ) ;
43+ if ( ! decodedModel ) decodedModel = log . replayToEnd ( ) ;
44+ const file = new File ( decodedModel , log ) ;
45+ return file ;
46+ }
47+
48+ public static fromNdjson ( blob : Uint8Array ) : File {
49+ const components = decodeNdjsonComponents ( blob ) ;
50+ return File . unserialize ( components as types . FileReadSequence ) ;
51+ }
52+
53+ public static fromSeqCbor ( blob : Uint8Array ) : File {
54+ const components = decodeSeqCborComponents ( blob ) ;
55+ return File . unserialize ( components as types . FileReadSequence ) ;
56+ }
57+
1758 public static fromModel ( model : Model < any > ) : File {
1859 return new File ( model , PatchLog . fromModel ( model ) ) ;
1960 }
2061
21- constructor ( public readonly model : Model , public readonly history : PatchLog ) { }
62+ constructor ( public readonly model : Model , public readonly log : PatchLog ) { }
2263
2364 public serialize ( params : types . FileSerializeParams = { } ) : types . FileWriteSequence {
2465 const view = this . model . view ( ) ;
@@ -48,33 +89,41 @@ export class File implements Printable {
4889 model = new StructuralEncoderVerbose ( ) . encode ( this . model ) ;
4990 break ;
5091 }
92+ case 'none' : {
93+ metadata [ 1 ] = FileModelEncoding . None ;
94+ model = null ;
95+ break ;
96+ }
5197 default :
5298 throw new Error ( `Invalid model format: ${ modelFormat } ` ) ;
5399 }
54100 const history : types . FileWriteSequenceHistory = [ null , [ ] ] ;
55101 const patchFormat = params . history ?? 'binary' ;
56102 switch ( patchFormat ) {
57103 case 'binary' : {
58- history [ 0 ] = this . history . start . toBinary ( ) ;
59- this . history . patches . forEach ( ( { v} ) => {
104+ history [ 0 ] = this . log . start . toBinary ( ) ;
105+ this . log . patches . forEach ( ( { v} ) => {
60106 history [ 1 ] . push ( v . toBinary ( ) ) ;
61107 } ) ;
62108 break ;
63109 }
64110 case 'compact' : {
65- history [ 0 ] = new StructuralEncoderCompact ( ) . encode ( this . history . start ) ;
66- this . history . patches . forEach ( ( { v} ) => {
111+ history [ 0 ] = new StructuralEncoderCompact ( ) . encode ( this . log . start ) ;
112+ this . log . patches . forEach ( ( { v} ) => {
67113 history [ 1 ] . push ( encodeCompact ( v ) ) ;
68114 } ) ;
69115 break ;
70116 }
71117 case 'verbose' : {
72- history [ 0 ] = new StructuralEncoderVerbose ( ) . encode ( this . history . start ) ;
73- this . history . patches . forEach ( ( { v} ) => {
118+ history [ 0 ] = new StructuralEncoderVerbose ( ) . encode ( this . log . start ) ;
119+ this . log . patches . forEach ( ( { v} ) => {
74120 history [ 1 ] . push ( encodeVerbose ( v ) ) ;
75121 } ) ;
76122 break ;
77123 }
124+ case 'none' : {
125+ break ;
126+ }
78127 default :
79128 throw new Error ( `Invalid history format: ${ patchFormat } ` ) ;
80129 }
@@ -104,6 +153,6 @@ export class File implements Printable {
104153 // ---------------------------------------------------------------- Printable
105154
106155 public toString ( tab ?: string ) {
107- return `file` + printTree ( tab , [ ( tab ) => this . model . toString ( tab ) , ( ) => '' , ( tab ) => this . history . toString ( tab ) ] ) ;
156+ return `file` + printTree ( tab , [ ( tab ) => this . model . toString ( tab ) , ( ) => '' , ( tab ) => this . log . toString ( tab ) ] ) ;
108157 }
109158}
0 commit comments