@@ -66,6 +66,7 @@ const symbolList = [
66
66
const RecordSymbols = new Set ( symbolList ) ;
67
67
68
68
type RecordSymbol = ( typeof symbolList ) [ number ] ;
69
+ type ProxiedMethod = ( ...args : unknown [ ] ) => unknown ;
69
70
70
71
function isPathMatch ( a : string [ ] , b : string [ ] ) {
71
72
return a . length === b . length && a . every ( ( v , i ) => v === b [ i ] ) ;
@@ -106,6 +107,7 @@ export class SchemaRecord {
106
107
const schema = store . schema as unknown as SchemaService ;
107
108
const cache = store . cache ;
108
109
const identityField = schema . resource ( identifier ) . identity ;
110
+ const BoundFns = new Map < string | symbol , ProxiedMethod > ( ) ;
109
111
110
112
this [ EmbeddedType ] = embeddedType ;
111
113
this [ EmbeddedPath ] = embeddedPath ;
@@ -136,14 +138,20 @@ export class SchemaRecord {
136
138
if ( ! fields . has ( prop as string ) ) {
137
139
throw new Error ( `No field named ${ String ( prop ) } on ${ identifier . type } ` ) ;
138
140
}
139
- const schemaForField = fields . get ( prop as string ) ! ;
141
+ const schemaForField = prop === identityField ?. name ? identityField : fields . get ( prop as string ) ! ;
140
142
switch ( schemaForField . kind ) {
141
143
case 'derived' :
142
144
return {
143
145
writable : false ,
144
146
enumerable : true ,
145
147
configurable : true ,
146
148
} ;
149
+ case '@id' :
150
+ return {
151
+ writable : identifier . id === null ,
152
+ enumerable : true ,
153
+ configurable : true ,
154
+ } ;
147
155
case '@local' :
148
156
case 'field' :
149
157
case 'attribute' :
@@ -161,6 +169,12 @@ export class SchemaRecord {
161
169
enumerable : true ,
162
170
configurable : true ,
163
171
} ;
172
+ default :
173
+ return {
174
+ writable : false ,
175
+ enumerable : false ,
176
+ configurable : false ,
177
+ } ;
164
178
}
165
179
} ,
166
180
@@ -169,26 +183,6 @@ export class SchemaRecord {
169
183
return target [ prop as keyof SchemaRecord ] ;
170
184
}
171
185
172
- if ( prop === Symbol . toStringTag ) {
173
- return `SchemaRecord<${ identifier . type } :${ identifier . id } (${ identifier . lid } )>` ;
174
- }
175
-
176
- if ( prop === 'toString' ) {
177
- return function ( ) {
178
- return `SchemaRecord<${ identifier . type } :${ identifier . id } (${ identifier . lid } )>` ;
179
- } ;
180
- }
181
-
182
- if ( prop === 'toHTML' ) {
183
- return function ( ) {
184
- return `<div>SchemaRecord<${ identifier . type } :${ identifier . id } (${ identifier . lid } )></div>` ;
185
- } ;
186
- }
187
-
188
- if ( prop === Symbol . toPrimitive ) {
189
- return null ;
190
- }
191
-
192
186
// TODO make this a symbol
193
187
if ( prop === '___notifications' ) {
194
188
return target . ___notifications ;
@@ -203,19 +197,65 @@ export class SchemaRecord {
203
197
if ( IgnoredGlobalFields . has ( prop as string ) ) {
204
198
return undefined ;
205
199
}
200
+
201
+ /////////////////////////////////////////////////////////////
202
+ //// Note these bound function behaviors are essentially ////
203
+ //// built-in but overrideable derivations. ////
204
+ //// ////
205
+ //// The bar for this has to be "basic expectations of ////
206
+ /// an object" – very, very high ////
207
+ /////////////////////////////////////////////////////////////
208
+
209
+ if ( prop === Symbol . toStringTag || prop === 'toString' ) {
210
+ let fn = BoundFns . get ( 'toString' ) ;
211
+ if ( ! fn ) {
212
+ fn = function ( ) {
213
+ entangleSignal ( signals , receiver , '@identity' ) ;
214
+ return `Record<${ identifier . type } :${ identifier . id } (${ identifier . lid } )>` ;
215
+ } ;
216
+ BoundFns . set ( prop , fn ) ;
217
+ }
218
+ return fn ;
219
+ }
220
+
221
+ if ( prop === 'toHTML' ) {
222
+ let fn = BoundFns . get ( 'toHTML' ) ;
223
+ if ( ! fn ) {
224
+ fn = function ( ) {
225
+ entangleSignal ( signals , receiver , '@identity' ) ;
226
+ return `<span>Record<${ identifier . type } :${ identifier . id } (${ identifier . lid } )></span>` ;
227
+ } ;
228
+ BoundFns . set ( prop , fn ) ;
229
+ }
230
+ return fn ;
231
+ }
232
+
233
+ if ( prop === 'toJSON' ) {
234
+ let fn = BoundFns . get ( 'toJSON' ) ;
235
+ if ( ! fn ) {
236
+ fn = function ( ) {
237
+ const json : Record < string , unknown > = { } ;
238
+ for ( const key in receiver ) {
239
+ json [ key ] = receiver [ key as keyof typeof receiver ] ;
240
+ }
241
+
242
+ return json ;
243
+ } ;
244
+ BoundFns . set ( prop , fn ) ;
245
+ }
246
+ return fn ;
247
+ }
248
+
249
+ if ( prop === Symbol . toPrimitive ) return ( ) => null ;
250
+
206
251
if ( prop === 'constructor' ) {
207
252
return SchemaRecord ;
208
253
}
209
254
// too many things check for random symbols
210
- if ( typeof prop === 'symbol' ) {
211
- return undefined ;
212
- }
213
- let type = identifier . type ;
214
- if ( isEmbedded ) {
215
- type = embeddedType ! ;
216
- }
255
+ if ( typeof prop === 'symbol' ) return undefined ;
217
256
218
- throw new Error ( `No field named ${ String ( prop ) } on ${ type } ` ) ;
257
+ assert ( `No field named ${ String ( prop ) } on ${ isEmbedded ? embeddedType ! : identifier . type } ` ) ;
258
+ return undefined ;
219
259
}
220
260
221
261
const field = maybeField . kind === 'alias' ? maybeField . options : maybeField ;
0 commit comments