@contember/bindx 0.1.4 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/dist/adapter/ContemberAdapter.d.ts +11 -14
  2. package/dist/adapter/ContemberAdapter.d.ts.map +1 -1
  3. package/dist/brand/ComponentBrand.d.ts +1 -24
  4. package/dist/brand/ComponentBrand.d.ts.map +1 -1
  5. package/dist/contember/index.d.ts +1 -2
  6. package/dist/contember/index.d.ts.map +1 -1
  7. package/dist/handles/EntityHandle.d.ts +18 -29
  8. package/dist/handles/EntityHandle.d.ts.map +1 -1
  9. package/dist/handles/FieldHandle.d.ts +5 -3
  10. package/dist/handles/FieldHandle.d.ts.map +1 -1
  11. package/dist/handles/HasManyListHandle.d.ts +4 -3
  12. package/dist/handles/HasManyListHandle.d.ts.map +1 -1
  13. package/dist/handles/HasOneHandle.d.ts +33 -36
  14. package/dist/handles/HasOneHandle.d.ts.map +1 -1
  15. package/dist/handles/PlaceholderHandle.d.ts +9 -19
  16. package/dist/handles/PlaceholderHandle.d.ts.map +1 -1
  17. package/dist/handles/proxyFactory.d.ts +9 -37
  18. package/dist/handles/proxyFactory.d.ts.map +1 -1
  19. package/dist/handles/types.d.ts +4 -0
  20. package/dist/handles/types.d.ts.map +1 -1
  21. package/dist/index.d.ts +3 -2
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/persistence/ContemberSchemaMutationAdapter.d.ts +24 -1
  24. package/dist/persistence/ContemberSchemaMutationAdapter.d.ts.map +1 -1
  25. package/dist/schema/ContemberSchema.d.ts +1 -97
  26. package/dist/schema/ContemberSchema.d.ts.map +1 -1
  27. package/dist/schema/SchemaLoader.d.ts +1 -27
  28. package/dist/schema/SchemaLoader.d.ts.map +1 -1
  29. package/dist/schema/SchemaRegistry.d.ts +1 -94
  30. package/dist/schema/SchemaRegistry.d.ts.map +1 -1
  31. package/dist/schema/roles.d.ts +1 -61
  32. package/dist/schema/roles.d.ts.map +1 -1
  33. package/dist/schema/types.d.ts +1 -178
  34. package/dist/schema/types.d.ts.map +1 -1
  35. package/dist/selection/SelectionMetaCollector.d.ts +1 -31
  36. package/dist/selection/SelectionMetaCollector.d.ts.map +1 -1
  37. package/dist/selection/SelectionScope.d.ts +1 -76
  38. package/dist/selection/SelectionScope.d.ts.map +1 -1
  39. package/dist/selection/buildQuery.d.ts +1 -44
  40. package/dist/selection/buildQuery.d.ts.map +1 -1
  41. package/dist/selection/createFragment.d.ts +1 -28
  42. package/dist/selection/createFragment.d.ts.map +1 -1
  43. package/dist/selection/createSelectionBuilder.d.ts +1 -17
  44. package/dist/selection/createSelectionBuilder.d.ts.map +1 -1
  45. package/dist/selection/queryTypes.d.ts +1 -83
  46. package/dist/selection/queryTypes.d.ts.map +1 -1
  47. package/dist/selection/types.d.ts +1 -237
  48. package/dist/selection/types.d.ts.map +1 -1
  49. package/dist/utils/aliasGenerator.d.ts +1 -24
  50. package/dist/utils/aliasGenerator.d.ts.map +1 -1
  51. package/package.json +4 -4
  52. package/src/adapter/ContemberAdapter.ts +193 -123
  53. package/src/brand/ComponentBrand.ts +1 -28
  54. package/src/contember/index.ts +1 -8
  55. package/src/handles/EntityHandle.ts +131 -61
  56. package/src/handles/FieldHandle.ts +19 -3
  57. package/src/handles/HasManyListHandle.ts +15 -12
  58. package/src/handles/HasOneHandle.ts +98 -66
  59. package/src/handles/PlaceholderHandle.ts +26 -24
  60. package/src/handles/proxyFactory.ts +29 -128
  61. package/src/handles/types.ts +6 -0
  62. package/src/index.ts +22 -3
  63. package/src/persistence/ContemberSchemaMutationAdapter.ts +19 -1
  64. package/src/schema/ContemberSchema.ts +14 -132
  65. package/src/schema/SchemaLoader.ts +1 -93
  66. package/src/schema/SchemaRegistry.ts +1 -286
  67. package/src/schema/roles.ts +10 -83
  68. package/src/schema/types.ts +21 -238
  69. package/src/selection/SelectionMetaCollector.ts +1 -122
  70. package/src/selection/SelectionScope.ts +1 -208
  71. package/src/selection/buildQuery.ts +1 -134
  72. package/src/selection/createFragment.ts +1 -50
  73. package/src/selection/createSelectionBuilder.ts +1 -283
  74. package/src/selection/queryTypes.ts +8 -135
  75. package/src/selection/types.ts +13 -320
  76. package/src/utils/aliasGenerator.ts +1 -56
@@ -11,7 +11,7 @@ import {
11
11
  clearEntityErrors,
12
12
  clearAllErrors as clearAllErrorsAction,
13
13
  } from '../core/actions.js'
14
- import { type EntityRef, type SelectedEntityFields, type Unsubscribe, type EntityAccessor } from './types.js'
14
+ import { type FieldRef, type HasOneAccessor, type HasManyRef, type SelectedEntityFields, type Unsubscribe, type EntityAccessor } from './types.js'
15
15
  import { deepEqual } from '../utils/deepEqual.js'
16
16
  import { createClientError, type ErrorInput, type FieldError } from '../errors/types.js'
17
17
  import type {
@@ -25,12 +25,21 @@ import type {
25
25
  } from '../events/types.js'
26
26
  import { HasOneHandle } from './HasOneHandle.js'
27
27
  import { HasManyListHandle } from './HasManyListHandle.js'
28
- import { createHandleProxy, ENTITY_HANDLE_PROPERTIES } from './proxyFactory.js'
28
+ import { createHandleProxy } from './proxyFactory.js'
29
29
  import type { SelectionMeta } from '../selection/types.js'
30
30
  import { UnfetchedFieldError } from '../errors/UnfetchedFieldError.js'
31
31
 
32
- // Type for relation handle cache
33
- type RelationHandle = HasOneHandle<object> | HasManyListHandle<object>
32
+ interface CachedHandle<TRaw, TProxy> {
33
+ readonly raw: TRaw
34
+ readonly proxy: TProxy
35
+ }
36
+
37
+ /** Minimal internal interface for cached relation handles that need reset/dispose.
38
+ * At runtime, the proxied handles satisfy this through delegation, even if the public type doesn't expose dispose(). */
39
+ interface RelationHandleRaw {
40
+ reset(): void
41
+ dispose(): void
42
+ }
34
43
 
35
44
  /**
36
45
  * EntityHandle provides stable access to an entity.
@@ -49,35 +58,16 @@ type RelationHandle = HasOneHandle<object> | HasManyListHandle<object>
49
58
  * @typeParam T - The full entity type
50
59
  * @typeParam TSelected - The selected subset of fields (defaults to T for backwards compatibility)
51
60
  */
52
- export class EntityHandle<T extends object = object, TSelected = T> extends EntityRelatedHandle implements EntityRef<T, TSelected> {
61
+ export class EntityHandle<T extends object = object, TSelected = T> extends EntityRelatedHandle {
53
62
  /** Cache for field handles to ensure stable identity */
54
- private readonly fieldHandleCache = new Map<string, FieldHandle<unknown>>()
63
+ private readonly fieldHandleCache = new Map<string, CachedHandle<FieldHandle<unknown>, FieldRef<unknown>>>()
55
64
 
56
65
  /** Cache for relation handles */
57
- private readonly relationHandleCache = new Map<string, RelationHandle>()
66
+ private readonly relationHandleCache = new Map<string, CachedHandle<RelationHandleRaw, HasOneAccessor<object> | HasManyRef<object>>>()
58
67
 
59
68
  /** Runtime brand symbols for validation */
60
69
  readonly __brands?: Set<symbol>
61
70
 
62
- /** Type brand for schema - placeholder at runtime */
63
- declare readonly __schema: Record<string, object>
64
-
65
- // $ aliases - handled by proxy at runtime, declared for TypeScript
66
- declare readonly $fields: SelectedEntityFields<T, TSelected>
67
- declare readonly $data: TSelected | null
68
- declare readonly $isDirty: boolean
69
- declare readonly $persistedId: string | null
70
- declare readonly $isNew: boolean
71
- declare readonly $errors: readonly FieldError[]
72
- declare readonly $hasError: boolean
73
- declare $addError: (error: ErrorInput) => void
74
- declare $clearErrors: () => void
75
- declare $clearAllErrors: () => void
76
- declare $on: <E extends AfterEventTypes>(eventType: E, listener: EventListener<EventTypeMap[E]>) => Unsubscribe
77
- declare $intercept: <E extends BeforeEventTypes>(eventType: E, interceptor: Interceptor<EventTypeMap[E]>) => Unsubscribe
78
- declare $onPersisted: (listener: EventListener<EntityPersistedEvent>) => Unsubscribe
79
- declare $interceptPersisting: (interceptor: Interceptor<EntityPersistingEvent>) => Unsubscribe
80
-
81
71
  private constructor(
82
72
  id: string,
83
73
  entityType: string,
@@ -99,11 +89,24 @@ export class EntityHandle<T extends object = object, TSelected = T> extends Enti
99
89
  schema: SchemaRegistry,
100
90
  brands?: Set<symbol>,
101
91
  selection?: SelectionMeta,
92
+ ): EntityAccessor<T, TSelected> {
93
+ return EntityHandle.wrapProxy(new EntityHandle<T, TSelected>(id, entityType, store, dispatcher, schema, brands, selection))
94
+ }
95
+
96
+ static createRaw<T extends object = object, TSelected = T>(
97
+ id: string,
98
+ entityType: string,
99
+ store: SnapshotStore,
100
+ dispatcher: ActionDispatcher,
101
+ schema: SchemaRegistry,
102
+ brands?: Set<symbol>,
103
+ selection?: SelectionMeta,
102
104
  ): EntityHandle<T, TSelected> {
103
- return createHandleProxy(new EntityHandle<T, TSelected>(id, entityType, store, dispatcher, schema, brands, selection), {
104
- knownProperties: ENTITY_HANDLE_PROPERTIES,
105
- getFields: (target) => target.fields,
106
- })
105
+ return new EntityHandle<T, TSelected>(id, entityType, store, dispatcher, schema, brands, selection)
106
+ }
107
+
108
+ static wrapProxy<T extends object, TSelected>(handle: EntityHandle<T, TSelected>): EntityAccessor<T, TSelected> {
109
+ return createHandleProxy<EntityHandle<T, TSelected>, EntityAccessor<T, TSelected>>(handle, (target) => target.fields)
107
110
  }
108
111
 
109
112
  /**
@@ -113,13 +116,6 @@ export class EntityHandle<T extends object = object, TSelected = T> extends Enti
113
116
  return this.entityId
114
117
  }
115
118
 
116
- /**
117
- * Gets the entity type.
118
- */
119
- get type(): string {
120
- return this.entityType
121
- }
122
-
123
119
  /**
124
120
  * Gets the current entity snapshot.
125
121
  */
@@ -201,18 +197,18 @@ export class EntityHandle<T extends object = object, TSelected = T> extends Enti
201
197
  const relationType = this.schema.getRelationType(this.entityType, fieldName)
202
198
 
203
199
  if (relationType === 'hasOne') {
204
- const handle = this.hasOne(fieldName)
200
+ const rawHandle = this.getHasOneHandleRaw(fieldName)
205
201
  // Check if the relation itself is dirty (connect/disconnect)
206
- if (handle.$isDirty) {
202
+ if (rawHandle.isDirty) {
207
203
  return true
208
204
  }
209
205
  // Check if the target entity has dirty fields
210
- if (handle.$entity.$isDirty) {
206
+ if (rawHandle.entityRaw.isDirty) {
211
207
  return true
212
208
  }
213
209
  } else if (relationType === 'hasMany') {
214
- const handle = this.hasMany(fieldName)
215
- if (handle.isDirty) {
210
+ const rawHandle = this.getHasManyHandleRaw(fieldName)
211
+ if (rawHandle.isDirty) {
216
212
  return true
217
213
  }
218
214
  }
@@ -240,17 +236,17 @@ export class EntityHandle<T extends object = object, TSelected = T> extends Enti
240
236
  * Gets a field handle for a specific field.
241
237
  * Returns cached handle to ensure stable identity.
242
238
  */
243
- field<K extends keyof T>(fieldName: K): FieldHandle<T[K]> {
239
+ field<K extends keyof T>(fieldName: K): FieldRef<T[K]> {
244
240
  const cacheKey = String(fieldName)
245
241
  const cached = this.fieldHandleCache.get(cacheKey)
246
242
 
247
243
  if (cached) {
248
- return cached as FieldHandle<T[K]>
244
+ return cached.proxy as FieldRef<T[K]>
249
245
  }
250
246
 
251
247
  const enumName = this.schema.getEnumName(this.entityType, cacheKey)
252
248
  const columnType = this.schema.getColumnType(this.entityType, cacheKey)
253
- const handle = FieldHandle.create<T[K]>(
249
+ const raw = FieldHandle.createRaw<T[K]>(
254
250
  this.entityType,
255
251
  this.entityId,
256
252
  [cacheKey],
@@ -259,20 +255,21 @@ export class EntityHandle<T extends object = object, TSelected = T> extends Enti
259
255
  enumName,
260
256
  columnType,
261
257
  )
262
- this.fieldHandleCache.set(cacheKey, handle as FieldHandle<unknown>)
258
+ const proxy = FieldHandle.wrapProxy(raw)
259
+ this.fieldHandleCache.set(cacheKey, { raw, proxy } as CachedHandle<FieldHandle<unknown>, FieldRef<unknown>>)
263
260
 
264
- return handle
261
+ return proxy
265
262
  }
266
263
 
267
264
  /**
268
265
  * Gets a has-one relation handle.
269
266
  */
270
- hasOne<TRelated extends object>(fieldName: string, nestedSelection?: SelectionMeta): HasOneHandle<TRelated> {
267
+ hasOne<TRelated extends object>(fieldName: string, nestedSelection?: SelectionMeta): HasOneAccessor<TRelated> {
271
268
  const cacheKey = `hasOne:${fieldName}`
272
269
  const cached = this.relationHandleCache.get(cacheKey)
273
270
 
274
271
  if (cached) {
275
- return cached as HasOneHandle<TRelated>
272
+ return cached.proxy as HasOneAccessor<TRelated>
276
273
  }
277
274
 
278
275
  const targetType = this.schema.getRelationTarget(this.entityType, fieldName)
@@ -282,7 +279,7 @@ export class EntityHandle<T extends object = object, TSelected = T> extends Enti
282
279
  )
283
280
  }
284
281
 
285
- const handle = HasOneHandle.create<TRelated>(
282
+ const raw = HasOneHandle.createRaw<TRelated>(
286
283
  this.entityType,
287
284
  this.entityId,
288
285
  fieldName,
@@ -293,9 +290,10 @@ export class EntityHandle<T extends object = object, TSelected = T> extends Enti
293
290
  undefined,
294
291
  nestedSelection,
295
292
  )
296
- this.relationHandleCache.set(cacheKey, handle as RelationHandle)
293
+ const proxy = HasOneHandle.wrapProxy(raw)
294
+ this.relationHandleCache.set(cacheKey, { raw, proxy } as unknown as CachedHandle<RelationHandleRaw, HasOneAccessor<object> | HasManyRef<object>>)
297
295
 
298
- return handle
296
+ return proxy
299
297
  }
300
298
 
301
299
  /**
@@ -304,13 +302,13 @@ export class EntityHandle<T extends object = object, TSelected = T> extends Enti
304
302
  * @param alias - Optional alias for the relation. When the same field is used multiple times
305
303
  * with different parameters (filter, orderBy, limit), each needs a unique alias.
306
304
  */
307
- hasMany<TItem extends object>(fieldName: string, alias?: string, nestedSelection?: SelectionMeta): HasManyListHandle<TItem> {
305
+ hasMany<TItem extends object>(fieldName: string, alias?: string, nestedSelection?: SelectionMeta): HasManyRef<TItem> {
308
306
  const effectiveAlias = alias ?? fieldName
309
307
  const cacheKey = `hasMany:${effectiveAlias}`
310
308
  const cached = this.relationHandleCache.get(cacheKey)
311
309
 
312
310
  if (cached) {
313
- return cached as HasManyListHandle<TItem>
311
+ return cached.proxy as HasManyRef<TItem>
314
312
  }
315
313
 
316
314
  const targetType = this.schema.getRelationTarget(this.entityType, fieldName)
@@ -332,7 +330,79 @@ export class EntityHandle<T extends object = object, TSelected = T> extends Enti
332
330
  effectiveAlias,
333
331
  nestedSelection,
334
332
  )
335
- this.relationHandleCache.set(cacheKey, handle as RelationHandle)
333
+ this.relationHandleCache.set(cacheKey, { raw: handle, proxy: handle } as unknown as CachedHandle<RelationHandleRaw, HasOneAccessor<object> | HasManyRef<object>>)
334
+
335
+ return handle
336
+ }
337
+
338
+ /**
339
+ * Gets a raw (unproxied) has-one relation handle from cache.
340
+ * Creates the handle if not cached yet.
341
+ */
342
+ private getHasOneHandleRaw(fieldName: string): HasOneHandle<object> {
343
+ const cacheKey = `hasOne:${fieldName}`
344
+ const cached = this.relationHandleCache.get(cacheKey)
345
+
346
+ if (cached) {
347
+ return cached.raw as HasOneHandle<object>
348
+ }
349
+
350
+ const targetType = this.schema.getRelationTarget(this.entityType, fieldName)
351
+ if (!targetType) {
352
+ throw new Error(
353
+ `Field "${fieldName}" is not a relation on entity "${this.entityType}"`,
354
+ )
355
+ }
356
+
357
+ const raw = HasOneHandle.createRaw(
358
+ this.entityType,
359
+ this.entityId,
360
+ fieldName,
361
+ targetType,
362
+ this.store,
363
+ this.dispatcher,
364
+ this.schema,
365
+ undefined,
366
+ undefined,
367
+ )
368
+ const proxy = HasOneHandle.wrapProxy(raw)
369
+ this.relationHandleCache.set(cacheKey, { raw, proxy } as unknown as CachedHandle<RelationHandleRaw, HasOneAccessor<object> | HasManyRef<object>>)
370
+
371
+ return raw
372
+ }
373
+
374
+ /**
375
+ * Gets a raw (unproxied) has-many relation handle from cache.
376
+ * Creates the handle if not cached yet.
377
+ */
378
+ private getHasManyHandleRaw(fieldName: string): HasManyRef<object> {
379
+ const cacheKey = `hasMany:${fieldName}`
380
+ const cached = this.relationHandleCache.get(cacheKey)
381
+
382
+ if (cached) {
383
+ return cached.proxy as HasManyRef<object>
384
+ }
385
+
386
+ const targetType = this.schema.getRelationTarget(this.entityType, fieldName)
387
+ if (!targetType) {
388
+ throw new Error(
389
+ `Field "${fieldName}" is not a relation on entity "${this.entityType}"`,
390
+ )
391
+ }
392
+
393
+ const handle = HasManyListHandle.create(
394
+ this.entityType,
395
+ this.entityId,
396
+ fieldName,
397
+ targetType,
398
+ this.store,
399
+ this.dispatcher,
400
+ this.schema,
401
+ this.__brands,
402
+ fieldName,
403
+ undefined,
404
+ )
405
+ this.relationHandleCache.set(cacheKey, { raw: handle, proxy: handle } as unknown as CachedHandle<RelationHandleRaw, HasOneAccessor<object> | HasManyRef<object>>)
336
406
 
337
407
  return handle
338
408
  }
@@ -346,8 +416,8 @@ export class EntityHandle<T extends object = object, TSelected = T> extends Enti
346
416
  this.dispatcher.dispatch(resetEntity(this.entityType, this.entityId))
347
417
 
348
418
  // Reset all cached relation handles
349
- for (const handle of this.relationHandleCache.values()) {
350
- handle.reset()
419
+ for (const { raw } of this.relationHandleCache.values()) {
420
+ raw.reset()
351
421
  }
352
422
  }
353
423
 
@@ -365,13 +435,13 @@ export class EntityHandle<T extends object = object, TSelected = T> extends Enti
365
435
  override dispose(): void {
366
436
  super.dispose()
367
437
 
368
- for (const handle of this.fieldHandleCache.values()) {
369
- handle.dispose()
438
+ for (const { raw } of this.fieldHandleCache.values()) {
439
+ raw.dispose()
370
440
  }
371
441
  this.fieldHandleCache.clear()
372
442
 
373
- for (const handle of this.relationHandleCache.values()) {
374
- handle.dispose()
443
+ for (const { raw } of this.relationHandleCache.values()) {
444
+ raw.dispose()
375
445
  }
376
446
  this.relationHandleCache.clear()
377
447
  }
@@ -23,7 +23,7 @@ import { createAliasProxy } from './proxyFactory.js'
23
23
  * - Provides inputProps for form integration
24
24
  * - Implements FieldRef interface for JSX compatibility
25
25
  */
26
- export class FieldHandle<T = unknown> extends EntityRelatedHandle implements FieldRef<T> {
26
+ export class FieldHandle<T = unknown> extends EntityRelatedHandle {
27
27
  private _cachedInputProps: InputProps<T> | null = null
28
28
  private _cachedInputPropsValue: T | null | undefined = undefined
29
29
 
@@ -49,8 +49,24 @@ export class FieldHandle<T = unknown> extends EntityRelatedHandle implements Fie
49
49
  dispatcher: ActionDispatcher,
50
50
  enumName?: string,
51
51
  columnType?: string,
52
+ ): FieldRef<T> {
53
+ return createAliasProxy<FieldHandle<T>, FieldRef<T>>(new FieldHandle<T>(entityType, entityId, fieldPath, store, dispatcher, enumName, columnType))
54
+ }
55
+
56
+ static createRaw<T = unknown>(
57
+ entityType: string,
58
+ entityId: string,
59
+ fieldPath: string[],
60
+ store: SnapshotStore,
61
+ dispatcher: ActionDispatcher,
62
+ enumName?: string,
63
+ columnType?: string,
52
64
  ): FieldHandle<T> {
53
- return createAliasProxy(new FieldHandle<T>(entityType, entityId, fieldPath, store, dispatcher, enumName, columnType))
65
+ return new FieldHandle<T>(entityType, entityId, fieldPath, store, dispatcher, enumName, columnType)
66
+ }
67
+
68
+ static wrapProxy<T>(handle: FieldHandle<T>): FieldRef<T> {
69
+ return createAliasProxy<FieldHandle<T>, FieldRef<T>>(handle)
54
70
  }
55
71
 
56
72
  /**
@@ -204,7 +220,7 @@ export class FieldHandle<T = unknown> extends EntityRelatedHandle implements Fie
204
220
  */
205
221
  nested<K extends keyof NonNullable<T>>(
206
222
  key: K,
207
- ): FieldHandle<NonNullable<T>[K]> {
223
+ ): FieldRef<NonNullable<T>[K]> {
208
224
  return FieldHandle.create<NonNullable<T>[K]>(
209
225
  this.entityType,
210
226
  this.entityId,
@@ -27,8 +27,9 @@ import { createAliasProxy } from './proxyFactory.js'
27
27
  * @typeParam TEntity - The full entity type of items in the relation
28
28
  * @typeParam TSelected - The selected subset of fields (defaults to TEntity for backwards compatibility)
29
29
  */
30
- export class HasManyListHandle<TEntity extends object = object, TSelected = TEntity> extends EntityRelatedHandle implements HasManyRef<TEntity, TSelected> {
31
- private itemHandleCache = new Map<string, EntityHandle<TEntity, TSelected>>()
30
+ export class HasManyListHandle<TEntity extends object = object, TSelected = TEntity> extends EntityRelatedHandle {
31
+ private itemHandleCacheRaw = new Map<string, EntityHandle<TEntity, TSelected>>()
32
+ private itemHandleCacheProxy = new Map<string, EntityAccessor<TEntity, TSelected>>()
32
33
 
33
34
  /** Runtime brand symbols for validation */
34
35
  readonly __brands?: Set<symbol>
@@ -84,8 +85,8 @@ export class HasManyListHandle<TEntity extends object = object, TSelected = TEnt
84
85
  brands?: Set<symbol>,
85
86
  alias?: string,
86
87
  selection?: SelectionMeta,
87
- ): HasManyListHandle<TEntity, TSelected> {
88
- return createAliasProxy(new HasManyListHandle<TEntity, TSelected>(parentEntityType, parentEntityId, fieldName, itemType, store, dispatcher, schema, brands, alias, selection))
88
+ ): HasManyRef<TEntity, TSelected> {
89
+ return createAliasProxy<HasManyListHandle<TEntity, TSelected>, HasManyRef<TEntity, TSelected>>(new HasManyListHandle<TEntity, TSelected>(parentEntityType, parentEntityId, fieldName, itemType, store, dispatcher, schema, brands, alias, selection))
89
90
  }
90
91
 
91
92
  /**
@@ -221,10 +222,10 @@ export class HasManyListHandle<TEntity extends object = object, TSelected = TEnt
221
222
  * Returns selection-aware EntityAccessor that supports direct field access.
222
223
  */
223
224
  getItemHandle(itemId: string): EntityAccessor<TEntity, TSelected> {
224
- let handle = this.itemHandleCache.get(itemId)
225
+ let proxy = this.itemHandleCacheProxy.get(itemId)
225
226
 
226
- if (!handle) {
227
- handle = EntityHandle.create<TEntity, TSelected>(
227
+ if (!proxy) {
228
+ const raw = EntityHandle.createRaw<TEntity, TSelected>(
228
229
  itemId,
229
230
  this.itemType,
230
231
  this.store,
@@ -233,11 +234,12 @@ export class HasManyListHandle<TEntity extends object = object, TSelected = TEnt
233
234
  this.__brands,
234
235
  this.selection,
235
236
  )
236
- this.itemHandleCache.set(itemId, handle)
237
+ proxy = EntityHandle.wrapProxy(raw)
238
+ this.itemHandleCacheRaw.set(itemId, raw)
239
+ this.itemHandleCacheProxy.set(itemId, proxy)
237
240
  }
238
241
 
239
- // EntityHandle constructor returns a Proxy that implements EntityAccessor
240
- return handle as unknown as EntityAccessor<TEntity, TSelected>
242
+ return proxy
241
243
  }
242
244
 
243
245
  /**
@@ -391,10 +393,11 @@ export class HasManyListHandle<TEntity extends object = object, TSelected = TEnt
391
393
  override dispose(): void {
392
394
  super.dispose()
393
395
 
394
- for (const handle of this.itemHandleCache.values()) {
396
+ for (const handle of this.itemHandleCacheRaw.values()) {
395
397
  handle.dispose()
396
398
  }
397
- this.itemHandleCache.clear()
399
+ this.itemHandleCacheRaw.clear()
400
+ this.itemHandleCacheProxy.clear()
398
401
  }
399
402
 
400
403
  /**