1- import { fromJS , isCollection , isList , isMap , List , Map } from "immutable" ;
1+ import { isCollection , isList , isMap , List , Map } from "immutable" ;
22import { 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__' ;
38
49type Primitive = boolean | number | bigint | string | null | undefined | symbol ;
10+ type NoImmutify = Primitive | ArrayBuffer | ArrayBufferView | { [ NEVER_IMMUTIFY ] : any } ;
511
612type Prefix < P , T extends any [ ] > = [ P , ...T ] ;
713type First < T extends any [ ] > = T extends Prefix < infer F , any [ ] > ? F : never ;
@@ -18,7 +24,7 @@ type Lens<T, P extends PropertyKey[]> =
1824 : T ;
1925
2026type Immutify < T > =
21- T extends Primitive
27+ T extends NoImmutify
2228 ? T
2329 : T extends ArrayLike < infer E >
2430 ? List < Immutify < E > >
@@ -89,7 +95,7 @@ export class TransactionalLens<M extends object> {
8995
9096 initialize ( data : M ) {
9197 this . db . tx ( update => {
92- const coll = fromJS ( data ) ;
98+ const coll = toImmutable ( data ) ;
9399 if ( ! isCollection ( coll ) ) throw new Error ( 'Not an Immutable.Map' ) ;
94100 update ( curr => curr . setIn ( [ ...this . prefix ] , coll ) ) ;
95101 return Ok ( undefined ) ;
@@ -112,8 +118,7 @@ export class TransactionalLens<M extends object> {
112118 return this . db . tx ( update => {
113119 const setter : LensSetter < M > = < P extends PropertyKey [ ] > ( ...path : P ) =>
114120 ( 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 ) ) ) ;
117122 }
118123 const deleter : LensDeleter = < P extends PropertyKey [ ] > ( ...path : P ) => {
119124 update ( curr => curr . deleteIn ( [ ...this . prefix , ...path ] ) ) ;
@@ -129,14 +134,52 @@ export class TransactionalLens<M extends object> {
129134 get data ( ) { return this . db . data . getIn ( [ ...this . prefix ] ) as Immutify < M > }
130135}
131136
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+
132168function fromImmutable ( value : any ) : any {
169+ // reverse Immutable maps & lists
133170 if ( isMap ( value ) ) {
134171 return fromImmutable ( value . toObject ( ) ) ;
135172 }
136- else if ( isList ( value ) ) {
173+ if ( isList ( value ) ) {
137174 return fromImmutable ( value . toArray ( ) ) ;
138175 }
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 ) ) {
140183 if ( typeof value . length === 'number' && 0 in value && value . length - 1 in value ) {
141184 for ( let i = 0 ; i < value . length ; ++ i ) {
142185 value [ i ] = fromImmutable ( value [ i ] ) ;
@@ -149,7 +192,6 @@ function fromImmutable(value: any): any {
149192 }
150193 return value ;
151194 }
152- else {
153- return value ;
154- }
195+
196+ return value ;
155197}
0 commit comments