1
- import { fromJS , isCollection , isList , isMap , List , Map } from "immutable" ;
1
+ import { isCollection , isList , isMap , List , Map } from "immutable" ;
2
2
import { Ok , Result } from "ts-results" ;
3
+ import { isArrayLike } from "../util" ;
4
+
5
+ // NEVER_IMMUTIFY is a string because that's easily serializable with different algorithms - symbols are not
6
+ export type NeverImmutify = typeof NEVER_IMMUTIFY ;
7
+ export const NEVER_IMMUTIFY = '__NEVER_IMMUTIFY__' ;
3
8
4
9
type Primitive = boolean | number | bigint | string | null | undefined | symbol ;
10
+ type NoImmutify = Primitive | ArrayBuffer | ArrayBufferView | { [ NEVER_IMMUTIFY ] : any } ;
5
11
6
12
type Prefix < P , T extends any [ ] > = [ P , ...T ] ;
7
13
type First < T extends any [ ] > = T extends Prefix < infer F , any [ ] > ? F : never ;
@@ -18,7 +24,7 @@ type Lens<T, P extends PropertyKey[]> =
18
24
: T ;
19
25
20
26
type Immutify < T > =
21
- T extends Primitive
27
+ T extends NoImmutify
22
28
? T
23
29
: T extends ArrayLike < infer E >
24
30
? List < Immutify < E > >
@@ -89,7 +95,7 @@ export class TransactionalLens<M extends object> {
89
95
90
96
initialize ( data : M ) {
91
97
this . db . tx ( update => {
92
- const coll = fromJS ( data ) ;
98
+ const coll = toImmutable ( data ) ;
93
99
if ( ! isCollection ( coll ) ) throw new Error ( 'Not an Immutable.Map' ) ;
94
100
update ( curr => curr . setIn ( [ ...this . prefix ] , coll ) ) ;
95
101
return Ok ( undefined ) ;
@@ -112,8 +118,7 @@ export class TransactionalLens<M extends object> {
112
118
return this . db . tx ( update => {
113
119
const setter : LensSetter < M > = < P extends PropertyKey [ ] > ( ...path : P ) =>
114
120
( value : Lens < M , P > | Immutify < Lens < M , P > > ) => {
115
- const v = isCollection ( value ) ? value : fromJS ( value ) ;
116
- update ( curr => curr . setIn ( [ ...this . prefix , ...path ] , v ) ) ;
121
+ update ( curr => curr . setIn ( [ ...this . prefix , ...path ] , toImmutable ( value ) ) ) ;
117
122
}
118
123
const deleter : LensDeleter = < P extends PropertyKey [ ] > ( ...path : P ) => {
119
124
update ( curr => curr . deleteIn ( [ ...this . prefix , ...path ] ) ) ;
@@ -129,14 +134,52 @@ export class TransactionalLens<M extends object> {
129
134
get data ( ) { return this . db . data . getIn ( [ ...this . prefix ] ) as Immutify < M > }
130
135
}
131
136
137
+ function toImmutable ( value : any ) : any {
138
+ // passthru Immutable collections
139
+ if ( isCollection ( value ) ) return value ;
140
+
141
+ // don't touch ArrayBuffers & ArrayBufferViews - freeze them
142
+ if ( ArrayBuffer . isView ( value ) ) {
143
+ Object . freeze ( value . buffer ) ;
144
+ return value ;
145
+ }
146
+ if ( value instanceof ArrayBuffer ) {
147
+ Object . freeze ( value ) ;
148
+ return value ;
149
+ }
150
+
151
+ // recurse into arrays & objects, converting them to lists & maps
152
+ // skip primitives & objects that don't want to be touched
153
+ if ( typeof value === 'object' && ! ( NEVER_IMMUTIFY in value ) ) {
154
+ if ( isArrayLike ( value ) ) {
155
+ return List ( value . map ( item => toImmutable ( item ) ) ) ;
156
+ } else {
157
+ return Map (
158
+ Object . entries ( value ) . map (
159
+ ( [ key , value ] ) => [ key , toImmutable ( value ) ]
160
+ )
161
+ ) ;
162
+ }
163
+ }
164
+
165
+ return value ;
166
+ }
167
+
132
168
function fromImmutable ( value : any ) : any {
169
+ // reverse Immutable maps & lists
133
170
if ( isMap ( value ) ) {
134
171
return fromImmutable ( value . toObject ( ) ) ;
135
172
}
136
- else if ( isList ( value ) ) {
173
+ if ( isList ( value ) ) {
137
174
return fromImmutable ( value . toArray ( ) ) ;
138
175
}
139
- else if ( typeof value === 'object' ) {
176
+
177
+ // passthru ArrayBuffers & ArrayBufferViews
178
+ if ( value instanceof ArrayBuffer || ArrayBuffer . isView ( value ) ) return value ;
179
+
180
+ // revert objects & arrays
181
+ // but: passthru objects w/ NEVER_IMMUTIFY
182
+ if ( typeof value === 'object' && ! ( NEVER_IMMUTIFY in value ) ) {
140
183
if ( typeof value . length === 'number' && 0 in value && value . length - 1 in value ) {
141
184
for ( let i = 0 ; i < value . length ; ++ i ) {
142
185
value [ i ] = fromImmutable ( value [ i ] ) ;
@@ -149,7 +192,6 @@ function fromImmutable(value: any): any {
149
192
}
150
193
return value ;
151
194
}
152
- else {
153
- return value ;
154
- }
195
+
196
+ return value ;
155
197
}
0 commit comments