1+ enum FieldType {
2+ scalar ,
3+ customobject
4+ }
5+ interface Field {
6+ name : string ;
7+ type : FieldType ;
8+ }
9+ interface Scalar {
10+ fields : Field [ ] ;
11+ }
12+
13+ type TypeInformation = Scalar ;
14+
15+ type NativeScalar = string | number | boolean | null ;
16+
17+ interface ResultTree {
18+ [ name : string ] : StubObject | NativeScalar | Omit < StubObject [ ] , 'typedKey' > ;
19+ }
20+
21+
22+ interface BackingObjectListenerMap {
23+ [ name : string ] : StubObject [ ] ;
24+ }
25+
26+ type FDCScalarValue = string | number | boolean | null ;
27+
28+ class BackingDataObject {
29+ typedKey : string ;
30+ private serverValues = new Map < string , FDCScalarValue > ( ) ;
31+ private listeners : BackingObjectListenerMap = { } ;
32+ updateFromServer ( value : FDCScalarValue , key : string ) {
33+ this . serverValues [ key ] = value ;
34+ this . notifyListeners ( key ) ;
35+ }
36+ listenTo ( keys : string [ ] , listener : StubObject ) {
37+ for ( const key of keys ) {
38+ if ( this . listeners [ key ] ) {
39+ this . listeners [ key ] . push ( listener ) ;
40+ } else {
41+ this . listeners [ key ] = [ listener ] ;
42+ }
43+ }
44+ }
45+ notifyListeners ( key : string ) {
46+ if ( this . listeners [ key ] ) {
47+ this . listeners [ key ] . forEach ( listener =>
48+ listener . updateValue ( key , this . serverValues [ key ] )
49+ ) ;
50+ }
51+ }
52+
53+ serverValue ( key : string ) : FDCScalarValue {
54+ return this . serverValues [ key ] ;
55+ }
56+ toJson ( ) {
57+ const toReturn : object = { } ;
58+ for ( const [ key , value ] of this . serverValues ) {
59+ toReturn [ key ] = value ;
60+ }
61+ return toReturn ;
62+ }
63+ }
64+ /**
65+ * StubObject: field: 'movies',
66+ * StubObject: Array of Movies:
67+ * StubObject(movie0)
68+ * BackingDataObject(movie0)
69+ */
70+
71+ // interface StubObject {
72+ // [name: string]: FDCScalarValue;
73+ // }
74+ /**
75+ * StubObject({
76+ * 'movies': StubArray([
77+ * StubObject(BDO),
78+ * StubObject(BDO)
79+ * ])
80+ * })
81+ */
82+
83+ class StubArray {
84+ _valueMap : ( FDCScalarValue | StubObject ) [ ] = [ ] ;
85+ constructor (
86+ private _values : FDCScalarValue [ ] | object [ ] ,
87+ private _bdoHandler : BackingDataObjectHandler
88+ ) {
89+ for ( const value of _values ) {
90+ if ( typeof value === 'object' ) {
91+ // TODO: What about arrays?
92+ const stubObject = new StubObject ( value as CachedObject , _bdoHandler ) ; // What about instances where there's no typename?
93+ this . _valueMap . push ( stubObject ) ;
94+ } else {
95+ this . _valueMap . push ( value ) ;
96+ }
97+ }
98+ }
99+ value ( ) : ( FDCScalarValue | object ) [ ] {
100+ return this . _valueMap . map ( v => {
101+ // print
102+ if ( v instanceof StubObject ) {
103+ return v . toJson ( ) ;
104+ } else {
105+ return v ;
106+ }
107+ } ) ;
108+ }
109+ }
110+
111+ class BackingDataObjectHandler {
112+ private backingDataObjects = new Map < string , BackingDataObject > ( ) ;
113+
114+ getOrCreate ( typedKey : string ) : BackingDataObject {
115+ if ( ! this . backingDataObjects . has ( typedKey ) ) {
116+ this . backingDataObjects . set ( typedKey , new BackingDataObject ( ) ) ;
117+ }
118+ return this . backingDataObjects . get ( typedKey ) ! ;
119+ }
120+ }
121+
122+ interface CachedObject {
123+ __typename : string ;
124+ [ field : string ] : FDCScalarValue | CachedObject | FDCScalarValue [ ] | CachedObject [ ] ;
125+ }
126+
127+ class StubObject {
128+ constructor (
129+ value : CachedObject ,
130+ public bdoHandler : BackingDataObjectHandler
131+ ) {
132+ // TODO: Register value with typename system
133+ for ( const [ k , v ] of Object . entries ( value ) ) {
134+ if ( typeof v === 'object' ) {
135+ this . addObject ( k , v ) ;
136+ } else {
137+ this . addField ( k , v ) ;
138+ }
139+ }
140+ }
141+ // TODO: Check whether object is right here.
142+ map : Map < string , BackingDataObject | FDCScalarValue | StubObject | StubArray > = new Map ( ) ;
143+
144+ parentListeners : Map <
145+ string ,
146+ ( ( value : FDCScalarValue | FDCScalarValue [ ] ) => void ) [ ]
147+ > = new Map ( ) ;
148+
149+ updateValue ( key : string , value : FDCScalarValue | FDCScalarValue [ ] ) {
150+ // TODO: Propagate to other listeners.
151+ // this.map.set(key, value);
152+ // if (this.parentListeners.has(key)) {
153+ // this.parentListeners.get(key).forEach(listener => listener(value));
154+ // }
155+ }
156+
157+ addField ( key : string , value : FDCScalarValue ) {
158+ this . map . set ( key , value ) ;
159+ }
160+
161+ addObject ( key : string , value : object | FDCScalarValue ) {
162+ /**
163+ * Every key maps to a backing data object.
164+ */
165+ if ( Array . isArray ( value ) ) {
166+ this . map . set ( key , new StubArray ( value , this . bdoHandler ) ) ;
167+ } else if ( typeof value === 'object' ) {
168+ // Field is an object
169+ const toListenTo : string [ ] = [ ] ;
170+ for ( const [ k , v ] of Object . entries ( value ) ) {
171+ if ( typeof v === 'object' ) {
172+ const stubObject = new StubObject ( v , this . bdoHandler ) ;
173+ this . map . set ( k , stubObject ) ;
174+ } else {
175+ const bdo = this . bdoHandler . getOrCreate ( ( value as CachedObject ) . __typename as string ) ;
176+ if ( '__typename' in value && typeof v !== 'object' ) {
177+ toListenTo . push ( k ) ;
178+ console . log ( 'bdo for ' + k ) ;
179+ this . map . set ( k , bdo ) ;
180+ bdo . updateFromServer ( v as FDCScalarValue , k ) ;
181+ }
182+ }
183+ }
184+ if ( toListenTo . length > 0 ) {
185+ const bdo = this . bdoHandler . getOrCreate ( ( value as CachedObject ) . __typename as string ) ;
186+ bdo . listenTo ( toListenTo , this ) ;
187+ }
188+ } else {
189+ // TODO: throw an error
190+ throw new Error ( 'Add Object called on non-object' ) ;
191+ }
192+ }
193+
194+ toJson ( ) : object {
195+ const toReturn : object = { } ;
196+ for ( const [ key , value ] of this . map ) {
197+ let retV ;
198+ if ( value instanceof StubObject ) {
199+ retV = value . toJson ( ) ;
200+ } else if ( value instanceof StubArray ) {
201+ retV = value . value ( ) ;
202+ } else if ( value instanceof BackingDataObject ) {
203+ retV = value . serverValue ( key ) ;
204+ } else {
205+ retV = value ;
206+ }
207+ toReturn [ key ] = retV ;
208+ }
209+ return toReturn ;
210+ }
211+ }
212+
213+ const stubDataObject = new StubObject ( {
214+ __typename : '__moviesArray' ,
215+ movies : [
216+ {
217+ __typename : 'abc' ,
218+ id : 'abc' ,
219+ title : 'abc' ,
220+ nestedField : {
221+ a : 'abc' ,
222+ b : 'def' ,
223+ __typename : 'nestedField'
224+ } ,
225+ reviews : [
226+ { __typename : 'abc' , id : 'def' , title : 'review!' }
227+ ]
228+ }
229+ ]
230+ } ,
231+ new BackingDataObjectHandler ( )
232+ ) ;
0 commit comments