@@ -3,6 +3,7 @@ import * as VariantSchema from '@effect/experimental/VariantSchema';
33import * as Data from 'effect/Data' ;
44import * as Schema from 'effect/Schema' ;
55import { generateId } from './utils/generateId.js' ;
6+ import { hasArrayField } from './utils/hasArrayField.js' ;
67
78const {
89 Class,
@@ -35,7 +36,6 @@ export type AnyNoContext = Schema.Schema.AnyNoContext & {
3536
3637export type Update < S extends Any > = S [ 'update' ] ;
3738export type Insert < S extends Any > = S [ 'insert' ] ;
38-
3939export interface Generated < S extends Schema . Schema . All | Schema . PropertySignature . All >
4040 extends VariantSchema . Field < {
4141 readonly select : S ;
@@ -88,6 +88,7 @@ type DecodedEntitiesCache = Map<
8888 string , // type name
8989 {
9090 decoder : ( data : unknown ) => unknown ;
91+ type : Any ; // TODO should be the type of the entity
9192 entities : Map < string , Entity < AnyNoContext > > ; // holds all entities of this type
9293 queries : Map <
9394 string , // instead of serializedQueryKey as string we could also have the actual params
@@ -147,7 +148,39 @@ export const subscribeToDocumentChanges = (handle: DocHandle<DocumentContent>) =
147148 const cacheEntry = decodedEntitiesCache . get ( typeName ) ;
148149 if ( ! cacheEntry ) continue ;
149150
150- const decoded = cacheEntry . decoder ( { ...entity , id : entityId } ) ;
151+ const relations : Record < string , Entity < AnyNoContext > > = { } ;
152+ for ( const [ fieldName , field ] of Object . entries ( cacheEntry . type . fields ) ) {
153+ // check if the type exists in the cach and is a proper relation
154+ // TODO: what's the right way to get the name of the type?
155+ // @ts -expect-error name is defined
156+ const fieldCacheEntry = decodedEntitiesCache . get ( field . name ) ;
157+ if ( ! fieldCacheEntry ) continue ;
158+
159+ const relationEntities : Array < Entity < AnyNoContext > > = [ ] ;
160+
161+ if ( hasArrayField ( entity , fieldName ) ) {
162+ for ( const relationEntityId of entity [ fieldName ] ) {
163+ const relationEntity = doc . entities ?. [ relationEntityId ] ;
164+ if (
165+ ! relationEntity ||
166+ typeof relationEntity !== 'object' ||
167+ ! ( '@@types@@' in relationEntity ) ||
168+ ! Array . isArray ( relationEntity [ '@@types@@' ] )
169+ )
170+ continue ;
171+
172+ relationEntities . push ( { ...relationEntity , id : relationEntityId } ) ;
173+ }
174+ }
175+
176+ relations [ fieldName ] = relationEntities ;
177+ }
178+
179+ const decoded = cacheEntry . decoder ( {
180+ ...entity ,
181+ ...relations ,
182+ id : entityId ,
183+ } ) ;
151184 cacheEntry . entities . set ( entityId , decoded ) ;
152185
153186 const query = cacheEntry . queries . get ( 'all' ) ;
@@ -379,11 +412,54 @@ export function subscribeToFindMany<const S extends AnyNoContext>(
379412
380413 decodedEntitiesCache . set ( typeName , {
381414 decoder : decode ,
415+ type,
382416 entities : entitiesMap ,
383417 queries,
384418 } ) ;
385419 }
386420
421+ const allTypes = new Set < S > ( ) ;
422+ for ( const [ _key , field ] of Object . entries ( type . fields ) ) {
423+ // TODO check if it is a class instead of specific name
424+ // TODO: what's the right way to extract the name from the ast
425+ // @ts -expect-error rest is defined
426+ if ( field . ast . rest ) {
427+ // @ts -expect-error name is defined
428+ const typeName = field . ast . rest [ 0 ] . type . to . toString ( ) ;
429+ if ( typeName === 'User' ) {
430+ allTypes . add ( field as S ) ;
431+ }
432+ }
433+ }
434+
435+ for ( const type of allTypes ) {
436+ // TODO: what's the right way to get the name of the type?
437+ // @ts -expect-error name is defined
438+ const typeName = type . name ;
439+ const entities = findMany ( handle , type ) ;
440+
441+ if ( decodedEntitiesCache . has ( typeName ) ) {
442+ // add a listener to the existing query
443+ const cacheEntry = decodedEntitiesCache . get ( typeName ) ;
444+
445+ for ( const entity of entities ) {
446+ cacheEntry ?. entities . set ( entity . id , entity ) ;
447+ }
448+ } else {
449+ const entitiesMap = new Map ( ) ;
450+ for ( const entity of entities ) {
451+ entitiesMap . set ( entity . id , entity ) ;
452+ }
453+
454+ decodedEntitiesCache . set ( typeName , {
455+ decoder : decode ,
456+ type,
457+ entities : entitiesMap ,
458+ queries : new Map ( ) ,
459+ } ) ;
460+ }
461+ }
462+
387463 const subscribe = ( callback : ( ) => void ) => {
388464 const query = decodedEntitiesCache . get ( typeName ) ?. queries . get ( queryKey ) ;
389465 if ( query ?. listeners ) {
@@ -435,3 +511,12 @@ export const findOne =
435511
436512 return undefined ;
437513 } ;
514+
515+ export const Reference = < S extends Schema . Schema . All | Schema . PropertySignature . All > ( schema : S ) =>
516+ Field ( {
517+ select : schema ,
518+ insert : Schema . optional ( Schema . Array ( Schema . String ) ) ,
519+ update : Schema . optional ( Schema . Array ( Schema . String ) ) ,
520+ } ) ;
521+
522+ export const ReferenceArray = Schema . Array ;
0 commit comments