@@ -196,8 +196,10 @@ export abstract class Relation {
196196 const update = event . data
197197
198198 if (
199+ update . path . length > path . length &&
199200 path . every ( ( key , index ) => key === update . path [ index ] ) &&
200- ! isRecord ( update . nextValue )
201+ ! isRecord ( update . nextValue ) &&
202+ typeof update . path [ path . length ] !== 'number'
201203 ) {
202204 event . preventDefault ( )
203205 event . stopImmediatePropagation ( )
@@ -230,6 +232,156 @@ export abstract class Relation {
230232 this . ownerCollection . hooks . on ( 'update' , ( event ) => {
231233 const update = event . data
232234
235+ if ( this instanceof Many && isEqual ( update . path , path ) ) {
236+ if ( Array . isArray ( update . nextValue ) ) {
237+ event . preventDefault ( )
238+
239+ const nextForeignRecords = update . nextValue
240+
241+ const otherOwnersAssociatedWithForeignRecord =
242+ this . #getOtherOwnerForRecords( nextForeignRecords )
243+
244+ invariant . as (
245+ RelationError . for (
246+ RelationErrorCodes . FORBIDDEN_UNIQUE_UPDATE ,
247+ this . #createErrorDetails( ) ,
248+ ) ,
249+ this . options . unique ? otherOwnersAssociatedWithForeignRecord == null : true ,
250+ 'Failed to update a unique relation at "%s": the foreign record is already associated with another owner' ,
251+ update . path . join ( '.' ) ,
252+ )
253+
254+ const nextForeignKeys = new Set < string > ( )
255+ for ( const foreignRecord of nextForeignRecords ) {
256+ invariant . as (
257+ RelationError . for (
258+ RelationErrorCodes . INVALID_FOREIGN_RECORD ,
259+ this . #createErrorDetails( ) ,
260+ ) ,
261+ isRecord ( foreignRecord ) ,
262+ 'Failed to update a relation at "%s": expected relational value to be a record but got "%j"' ,
263+ update . path . join ( '.' ) ,
264+ foreignRecord ,
265+ )
266+
267+ const foreignKey = foreignRecord [ kPrimaryKey ]
268+ invariant . as (
269+ RelationError . for (
270+ RelationErrorCodes . INVALID_FOREIGN_RECORD ,
271+ this . #createErrorDetails( ) ,
272+ ) ,
273+ foreignKey != null ,
274+ 'Failed to update a relation at "%s": foreign record is missing primary key' ,
275+ update . path . join ( '.' ) ,
276+ )
277+
278+ nextForeignKeys . add ( foreignKey )
279+ }
280+
281+ // Remove associations that are no longer present.
282+ for ( const foreignKey of this . foreignKeys ) {
283+ if ( nextForeignKeys . has ( foreignKey ) ) {
284+ continue
285+ }
286+
287+ this . foreignKeys . delete ( foreignKey )
288+
289+ for ( const foreignCollection of this . foreignCollections ) {
290+ const foreignRecord = foreignCollection . findFirst ( ( q ) =>
291+ q . where ( ( record ) => record [ kPrimaryKey ] === foreignKey ) ,
292+ )
293+
294+ if ( foreignRecord ) {
295+ for ( const foreignRelation of this . getRelationsToOwner (
296+ foreignRecord ,
297+ ) ) {
298+ foreignRelation . foreignKeys . delete (
299+ update . prevRecord [ kPrimaryKey ] ,
300+ )
301+ }
302+ }
303+ }
304+ }
305+
306+ // Add new associations.
307+ for ( const foreignRecord of nextForeignRecords ) {
308+ const foreignKey = foreignRecord [ kPrimaryKey ]
309+ invariant . as (
310+ RelationError . for (
311+ RelationErrorCodes . INVALID_FOREIGN_RECORD ,
312+ this . #createErrorDetails( ) ,
313+ ) ,
314+ foreignKey != null ,
315+ 'Failed to update a relation at "%s": foreign record is missing primary key' ,
316+ update . path . join ( '.' ) ,
317+ )
318+
319+ if ( foreignKey == null ) {
320+ continue
321+ }
322+
323+ const isNewForeignKey = ! this . foreignKeys . has ( foreignKey )
324+
325+ if ( isNewForeignKey ) {
326+ this . foreignKeys . add ( foreignKey )
327+ }
328+
329+ for ( const foreignRelation of this . getRelationsToOwner (
330+ foreignRecord ,
331+ ) ) {
332+ foreignRelation . foreignKeys . add ( update . prevRecord [ kPrimaryKey ] )
333+ }
334+ }
335+ }
336+ }
337+
338+ if (
339+ this instanceof Many &&
340+ path . length + 1 === update . path . length &&
341+ path . every ( ( key , index ) => key === update . path [ index ] )
342+ ) {
343+ const prevValue = update . prevValue
344+ const nextValue = update . nextValue
345+
346+ const prevForeignRecord = isRecord ( prevValue ) ? prevValue : undefined
347+ const nextForeignRecord = isRecord ( nextValue ) ? nextValue : undefined
348+
349+ if ( prevForeignRecord ) {
350+ this . foreignKeys . delete ( prevForeignRecord [ kPrimaryKey ] )
351+
352+ for ( const foreignRelation of this . getRelationsToOwner (
353+ prevForeignRecord ,
354+ ) ) {
355+ foreignRelation . foreignKeys . delete ( update . prevRecord [ kPrimaryKey ] )
356+ }
357+ }
358+
359+ if ( nextForeignRecord ) {
360+ const otherOwnersAssociatedWithForeignRecord =
361+ this . options . unique
362+ ? this . #getOtherOwnerForRecords( [ nextForeignRecord ] )
363+ : undefined
364+
365+ invariant . as (
366+ RelationError . for (
367+ RelationErrorCodes . FORBIDDEN_UNIQUE_UPDATE ,
368+ this . #createErrorDetails( ) ,
369+ ) ,
370+ this . options . unique ? otherOwnersAssociatedWithForeignRecord == null : true ,
371+ 'Failed to update a unique relation at "%s": the foreign record is already associated with another owner' ,
372+ update . path . join ( '.' ) ,
373+ )
374+
375+ this . foreignKeys . add ( nextForeignRecord [ kPrimaryKey ] )
376+
377+ for ( const foreignRelation of this . getRelationsToOwner (
378+ nextForeignRecord ,
379+ ) ) {
380+ foreignRelation . foreignKeys . add ( update . prevRecord [ kPrimaryKey ] )
381+ }
382+ }
383+ }
384+
233385 if ( isEqual ( update . path , path ) && isRecord ( update . nextValue ) ) {
234386 event . preventDefault ( )
235387
@@ -524,21 +676,33 @@ class One extends Relation {
524676}
525677
526678export class Many extends Relation {
679+ private resolvedCache ?: Array < RecordType >
680+
527681 public resolve ( foreignKeys : Set < string > ) : unknown {
528682 if ( foreignKeys . size === 0 ) {
683+ this . resolvedCache ??= [ ]
684+ this . resolvedCache . length = 0
529685 return
530686 }
531687
532- return this . foreignCollections . flatMap < RecordType > ( ( foreignCollection ) => {
688+ const resolved = this . foreignCollections . flatMap < RecordType > ( ( foreignCollection ) => {
533689 return foreignCollection . findMany ( ( q ) =>
534690 q . where ( ( record ) => {
535691 return foreignKeys . has ( record [ kPrimaryKey ] )
536692 } ) ,
537693 )
538694 } )
695+
696+ this . resolvedCache ??= [ ]
697+ this . resolvedCache . length = 0
698+ this . resolvedCache . push ( ...resolved )
699+
700+ return this . resolvedCache
539701 }
540702
541703 public getDefaultValue ( ) : unknown {
542- return [ ]
704+ this . resolvedCache ??= [ ]
705+ this . resolvedCache . length = 0
706+ return this . resolvedCache
543707 }
544708}
0 commit comments