@contember/bindx 0.1.13 → 0.1.15
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.
- package/dist/handles/EntityHandle.d.ts +4 -4
- package/dist/handles/EntityHandle.d.ts.map +1 -1
- package/dist/handles/FieldHandle.d.ts +4 -4
- package/dist/handles/FieldHandle.d.ts.map +1 -1
- package/dist/handles/HasManyListHandle.d.ts +2 -2
- package/dist/handles/HasManyListHandle.d.ts.map +1 -1
- package/dist/handles/HasOneHandle.d.ts +2 -2
- package/dist/handles/HasOneHandle.d.ts.map +1 -1
- package/dist/handles/PlaceholderHandle.d.ts +7 -4
- package/dist/handles/PlaceholderHandle.d.ts.map +1 -1
- package/dist/handles/index.d.ts +1 -1
- package/dist/handles/index.d.ts.map +1 -1
- package/dist/handles/types.d.ts +58 -261
- package/dist/handles/types.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/handles/EntityHandle.ts +11 -11
- package/src/handles/FieldHandle.ts +6 -6
- package/src/handles/HasManyListHandle.ts +3 -3
- package/src/handles/HasOneHandle.ts +3 -2
- package/src/handles/PlaceholderHandle.ts +31 -5
- package/src/handles/index.ts +9 -11
- package/src/handles/types.ts +100 -406
- package/src/index.ts +9 -11
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
clearEntityErrors,
|
|
12
12
|
clearAllErrors as clearAllErrorsAction,
|
|
13
13
|
} from '../core/actions.js'
|
|
14
|
-
import { type
|
|
14
|
+
import { type FieldAccessor, type HasOneAccessor, type HasManyAccessor, type EntityFieldsAccessor, 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 {
|
|
@@ -38,12 +38,12 @@ interface RelationHandleRaw {
|
|
|
38
38
|
|
|
39
39
|
interface CachedFieldHandle {
|
|
40
40
|
readonly raw: FieldHandle<unknown>
|
|
41
|
-
readonly proxy:
|
|
41
|
+
readonly proxy: FieldAccessor<unknown>
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
interface CachedRelationHandle {
|
|
45
45
|
readonly raw: RelationHandleRaw
|
|
46
|
-
readonly proxy: HasOneAccessor<object> |
|
|
46
|
+
readonly proxy: HasOneAccessor<object> | HasManyAccessor<object>
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
/**
|
|
@@ -241,12 +241,12 @@ export class EntityHandle<T extends object = object, TSelected = T> extends Enti
|
|
|
241
241
|
* Gets a field handle for a specific field.
|
|
242
242
|
* Returns cached handle to ensure stable identity.
|
|
243
243
|
*/
|
|
244
|
-
field<K extends keyof T>(fieldName: K):
|
|
244
|
+
field<K extends keyof T>(fieldName: K): FieldAccessor<T[K]> {
|
|
245
245
|
const cacheKey = String(fieldName)
|
|
246
246
|
const cached = this.fieldHandleCache.get(cacheKey)
|
|
247
247
|
|
|
248
248
|
if (cached) {
|
|
249
|
-
return cached.proxy as
|
|
249
|
+
return cached.proxy as FieldAccessor<T[K]>
|
|
250
250
|
}
|
|
251
251
|
|
|
252
252
|
const enumName = this.schema.getEnumName(this.entityType, cacheKey)
|
|
@@ -307,13 +307,13 @@ export class EntityHandle<T extends object = object, TSelected = T> extends Enti
|
|
|
307
307
|
* @param alias - Optional alias for the relation. When the same field is used multiple times
|
|
308
308
|
* with different parameters (filter, orderBy, limit), each needs a unique alias.
|
|
309
309
|
*/
|
|
310
|
-
hasMany<TItem extends object>(fieldName: string, alias?: string, nestedSelection?: SelectionMeta):
|
|
310
|
+
hasMany<TItem extends object>(fieldName: string, alias?: string, nestedSelection?: SelectionMeta): HasManyAccessor<TItem> {
|
|
311
311
|
const effectiveAlias = alias ?? fieldName
|
|
312
312
|
const cacheKey = `hasMany:${effectiveAlias}`
|
|
313
313
|
const cached = this.relationHandleCache.get(cacheKey)
|
|
314
314
|
|
|
315
315
|
if (cached) {
|
|
316
|
-
return cached.proxy as
|
|
316
|
+
return cached.proxy as HasManyAccessor<TItem>
|
|
317
317
|
}
|
|
318
318
|
|
|
319
319
|
const targetType = this.schema.getRelationTarget(this.entityType, fieldName)
|
|
@@ -380,12 +380,12 @@ export class EntityHandle<T extends object = object, TSelected = T> extends Enti
|
|
|
380
380
|
* Gets a raw (unproxied) has-many relation handle from cache.
|
|
381
381
|
* Creates the handle if not cached yet.
|
|
382
382
|
*/
|
|
383
|
-
private getHasManyHandleRaw(fieldName: string):
|
|
383
|
+
private getHasManyHandleRaw(fieldName: string): HasManyAccessor<object> {
|
|
384
384
|
const cacheKey = `hasMany:${fieldName}`
|
|
385
385
|
const cached = this.relationHandleCache.get(cacheKey)
|
|
386
386
|
|
|
387
387
|
if (cached) {
|
|
388
|
-
return cached.proxy as
|
|
388
|
+
return cached.proxy as HasManyAccessor<object>
|
|
389
389
|
}
|
|
390
390
|
|
|
391
391
|
const targetType = this.schema.getRelationTarget(this.entityType, fieldName)
|
|
@@ -460,8 +460,8 @@ export class EntityHandle<T extends object = object, TSelected = T> extends Enti
|
|
|
460
460
|
*
|
|
461
461
|
* Implements EntityRef.fields - returns selection-aware field accessors.
|
|
462
462
|
*/
|
|
463
|
-
get fields():
|
|
464
|
-
return new Proxy({} as
|
|
463
|
+
get fields(): EntityFieldsAccessor<T, TSelected> {
|
|
464
|
+
return new Proxy({} as EntityFieldsAccessor<T, TSelected>, {
|
|
465
465
|
get: (_, fieldName: string) => {
|
|
466
466
|
// Selection validation
|
|
467
467
|
if (this.selection && !this.selection.fields.has(fieldName)) {
|
|
@@ -2,7 +2,7 @@ import { EntityRelatedHandle } from './BaseHandle.js'
|
|
|
2
2
|
import type { ActionDispatcher } from '../core/ActionDispatcher.js'
|
|
3
3
|
import type { SnapshotStore } from '../store/SnapshotStore.js'
|
|
4
4
|
import { addFieldError, clearFieldErrors, setField } from '../core/actions.js'
|
|
5
|
-
import { FIELD_REF_META, type
|
|
5
|
+
import { FIELD_REF_META, type FieldAccessor, type FieldRefMeta, type InputProps, type Unsubscribe } from './types.js'
|
|
6
6
|
import { deepEqual } from '../utils/deepEqual.js'
|
|
7
7
|
import { createClientError, type ErrorInput, type FieldError } from '../errors/types.js'
|
|
8
8
|
import type {
|
|
@@ -49,8 +49,8 @@ export class FieldHandle<T = unknown> extends EntityRelatedHandle {
|
|
|
49
49
|
dispatcher: ActionDispatcher,
|
|
50
50
|
enumName?: string,
|
|
51
51
|
columnType?: string,
|
|
52
|
-
):
|
|
53
|
-
return createAliasProxy<FieldHandle<T>,
|
|
52
|
+
): FieldAccessor<T> {
|
|
53
|
+
return createAliasProxy<FieldHandle<T>, FieldAccessor<T>>(new FieldHandle<T>(entityType, entityId, fieldPath, store, dispatcher, enumName, columnType))
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
static createRaw<T = unknown>(
|
|
@@ -65,8 +65,8 @@ export class FieldHandle<T = unknown> extends EntityRelatedHandle {
|
|
|
65
65
|
return new FieldHandle<T>(entityType, entityId, fieldPath, store, dispatcher, enumName, columnType)
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
static wrapProxy<T>(handle: FieldHandle<T>):
|
|
69
|
-
return createAliasProxy<FieldHandle<T>,
|
|
68
|
+
static wrapProxy<T>(handle: FieldHandle<T>): FieldAccessor<T> {
|
|
69
|
+
return createAliasProxy<FieldHandle<T>, FieldAccessor<T>>(handle)
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
/**
|
|
@@ -220,7 +220,7 @@ export class FieldHandle<T = unknown> extends EntityRelatedHandle {
|
|
|
220
220
|
*/
|
|
221
221
|
nested<K extends keyof NonNullable<T>>(
|
|
222
222
|
key: K,
|
|
223
|
-
):
|
|
223
|
+
): FieldAccessor<NonNullable<T>[K]> {
|
|
224
224
|
return FieldHandle.create<NonNullable<T>[K]>(
|
|
225
225
|
this.entityType,
|
|
226
226
|
this.entityId,
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
addRelationError,
|
|
9
9
|
clearRelationErrors,
|
|
10
10
|
} from '../core/actions.js'
|
|
11
|
-
import { FIELD_REF_META, type
|
|
11
|
+
import { FIELD_REF_META, type HasManyAccessor, type FieldRefMeta, type EntityAccessor, type Unsubscribe } from './types.js'
|
|
12
12
|
import { createClientError, type ErrorInput, type FieldError } from '../errors/types.js'
|
|
13
13
|
import type {
|
|
14
14
|
EventListener,
|
|
@@ -85,8 +85,8 @@ export class HasManyListHandle<TEntity extends object = object, TSelected = TEnt
|
|
|
85
85
|
brands?: Set<symbol>,
|
|
86
86
|
alias?: string,
|
|
87
87
|
selection?: SelectionMeta,
|
|
88
|
-
):
|
|
89
|
-
return createAliasProxy<HasManyListHandle<TEntity, TSelected>,
|
|
88
|
+
): HasManyAccessor<TEntity, TSelected> {
|
|
89
|
+
return createAliasProxy<HasManyListHandle<TEntity, TSelected>, HasManyAccessor<TEntity, TSelected>>(new HasManyListHandle<TEntity, TSelected>(parentEntityType, parentEntityId, fieldName, itemType, store, dispatcher, schema, brands, alias, selection))
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
/**
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
import {
|
|
14
14
|
FIELD_REF_META,
|
|
15
15
|
type FieldRefMeta,
|
|
16
|
-
type
|
|
16
|
+
type EntityFieldsAccessor,
|
|
17
17
|
type Unsubscribe,
|
|
18
18
|
type EntityAccessor,
|
|
19
19
|
type HasOneAccessor,
|
|
@@ -198,7 +198,7 @@ export class HasOneHandle<TEntity extends object = object, TSelected = TEntity>
|
|
|
198
198
|
* Implements HasOneRef interface.
|
|
199
199
|
* Delegates to the entity (either real EntityHandle or PlaceholderHandle).
|
|
200
200
|
*/
|
|
201
|
-
get fields():
|
|
201
|
+
get fields(): EntityFieldsAccessor<TEntity, TSelected> {
|
|
202
202
|
return this.entity.$fields
|
|
203
203
|
}
|
|
204
204
|
|
|
@@ -236,6 +236,7 @@ export class HasOneHandle<TEntity extends object = object, TSelected = TEntity>
|
|
|
236
236
|
this.targetType,
|
|
237
237
|
this.store,
|
|
238
238
|
this.dispatcher,
|
|
239
|
+
this.schema,
|
|
239
240
|
this.__brands,
|
|
240
241
|
)
|
|
241
242
|
this.placeholderCacheProxy = PlaceholderHandle.wrapProxy(this.placeholderCacheRaw)
|
|
@@ -3,7 +3,7 @@ import type { SnapshotStore } from '../store/SnapshotStore.js'
|
|
|
3
3
|
import { generatePlaceholderId } from '../store/SnapshotStore.js'
|
|
4
4
|
import {
|
|
5
5
|
FIELD_REF_META,
|
|
6
|
-
type
|
|
6
|
+
type EntityFieldsAccessor,
|
|
7
7
|
type FieldRefMeta,
|
|
8
8
|
type Unsubscribe,
|
|
9
9
|
type EntityAccessor,
|
|
@@ -19,6 +19,7 @@ import type {
|
|
|
19
19
|
EntityPersistingEvent,
|
|
20
20
|
} from '../events/types.js'
|
|
21
21
|
import { createAliasProxy } from './proxyFactory.js'
|
|
22
|
+
import type { SchemaRegistry } from '../schema/SchemaRegistry.js'
|
|
22
23
|
|
|
23
24
|
/**
|
|
24
25
|
* PlaceholderHandle provides access to a placeholder entity (for creating new entities).
|
|
@@ -42,6 +43,7 @@ export class PlaceholderHandle<TEntity extends object = object, TSelected = TEnt
|
|
|
42
43
|
private readonly targetType: string,
|
|
43
44
|
private readonly store: SnapshotStore,
|
|
44
45
|
private readonly dispatcher: ActionDispatcher,
|
|
46
|
+
private readonly schema: SchemaRegistry | null,
|
|
45
47
|
brands?: Set<symbol>,
|
|
46
48
|
) {
|
|
47
49
|
this.__brands = brands
|
|
@@ -55,9 +57,10 @@ export class PlaceholderHandle<TEntity extends object = object, TSelected = TEnt
|
|
|
55
57
|
targetType: string,
|
|
56
58
|
store: SnapshotStore,
|
|
57
59
|
dispatcher: ActionDispatcher,
|
|
60
|
+
schema?: SchemaRegistry | null,
|
|
58
61
|
brands?: Set<symbol>,
|
|
59
62
|
): EntityAccessor<TEntity, TSelected> {
|
|
60
|
-
return PlaceholderHandle.wrapProxy(new PlaceholderHandle<TEntity, TSelected>(parentEntityType, parentEntityId, fieldName, targetType, store, dispatcher, brands))
|
|
63
|
+
return PlaceholderHandle.wrapProxy(new PlaceholderHandle<TEntity, TSelected>(parentEntityType, parentEntityId, fieldName, targetType, store, dispatcher, schema ?? null, brands))
|
|
61
64
|
}
|
|
62
65
|
|
|
63
66
|
static createRaw<TEntity extends object = object, TSelected = TEntity>(
|
|
@@ -67,9 +70,10 @@ export class PlaceholderHandle<TEntity extends object = object, TSelected = TEnt
|
|
|
67
70
|
targetType: string,
|
|
68
71
|
store: SnapshotStore,
|
|
69
72
|
dispatcher: ActionDispatcher,
|
|
73
|
+
schema?: SchemaRegistry | null,
|
|
70
74
|
brands?: Set<symbol>,
|
|
71
75
|
): PlaceholderHandle<TEntity, TSelected> {
|
|
72
|
-
return new PlaceholderHandle<TEntity, TSelected>(parentEntityType, parentEntityId, fieldName, targetType, store, dispatcher, brands)
|
|
76
|
+
return new PlaceholderHandle<TEntity, TSelected>(parentEntityType, parentEntityId, fieldName, targetType, store, dispatcher, schema ?? null, brands)
|
|
73
77
|
}
|
|
74
78
|
|
|
75
79
|
static wrapProxy<TEntity extends object, TSelected>(handle: PlaceholderHandle<TEntity, TSelected>): EntityAccessor<TEntity, TSelected> {
|
|
@@ -134,8 +138,8 @@ export class PlaceholderHandle<TEntity extends object = object, TSelected = TEnt
|
|
|
134
138
|
/**
|
|
135
139
|
* Gets field accessors that read/write placeholder data.
|
|
136
140
|
*/
|
|
137
|
-
get fields():
|
|
138
|
-
return new Proxy({} as
|
|
141
|
+
get fields(): EntityFieldsAccessor<TEntity, TSelected> {
|
|
142
|
+
return new Proxy({} as EntityFieldsAccessor<TEntity, TSelected>, {
|
|
139
143
|
get: (_, fieldName: string) => {
|
|
140
144
|
return this.createPlaceholderFieldHandle(fieldName)
|
|
141
145
|
},
|
|
@@ -144,8 +148,24 @@ export class PlaceholderHandle<TEntity extends object = object, TSelected = TEnt
|
|
|
144
148
|
|
|
145
149
|
/**
|
|
146
150
|
* Creates a field handle for placeholder data.
|
|
151
|
+
* For has-many relations, returns an empty has-many-like handle with items/map/length.
|
|
147
152
|
*/
|
|
148
153
|
private createPlaceholderFieldHandle(fieldName: string): unknown {
|
|
154
|
+
// For has-many relations, return an empty has-many-like handle
|
|
155
|
+
if (this.schema?.isHasMany(this.targetType, fieldName)) {
|
|
156
|
+
const emptyItems: EntityAccessor<object>[] = []
|
|
157
|
+
const emptyHasMany: Record<string, unknown> = {
|
|
158
|
+
get items() { return emptyItems },
|
|
159
|
+
get length() { return 0 },
|
|
160
|
+
map: () => [],
|
|
161
|
+
add: () => { throw new Error(`Cannot add items to disconnected has-many relation "${fieldName}" on placeholder entity`) },
|
|
162
|
+
remove: () => {},
|
|
163
|
+
$state: 'disconnected',
|
|
164
|
+
[Symbol.iterator]: () => emptyItems[Symbol.iterator](),
|
|
165
|
+
}
|
|
166
|
+
return emptyHasMany
|
|
167
|
+
}
|
|
168
|
+
|
|
149
169
|
const self = this
|
|
150
170
|
|
|
151
171
|
return {
|
|
@@ -215,6 +235,12 @@ export class PlaceholderHandle<TEntity extends object = object, TSelected = TEnt
|
|
|
215
235
|
onChange: setValue,
|
|
216
236
|
}
|
|
217
237
|
},
|
|
238
|
+
touch(): void {
|
|
239
|
+
// Placeholder fields are not persisted yet, so touch is a no-op
|
|
240
|
+
},
|
|
241
|
+
get isTouched(): boolean {
|
|
242
|
+
return false
|
|
243
|
+
},
|
|
218
244
|
path: [fieldName],
|
|
219
245
|
fieldName,
|
|
220
246
|
// Error properties for FieldRef interface
|
package/src/handles/index.ts
CHANGED
|
@@ -8,27 +8,25 @@ export {
|
|
|
8
8
|
FIELD_REF_META,
|
|
9
9
|
type HasOneRelationState,
|
|
10
10
|
type EntityFields,
|
|
11
|
-
type
|
|
12
|
-
type
|
|
11
|
+
type EntityFieldsAccessor,
|
|
12
|
+
type EntityFieldsRef,
|
|
13
13
|
type ScalarKeys,
|
|
14
14
|
type HasManyKeys,
|
|
15
15
|
type HasOneKeys,
|
|
16
16
|
type FieldRefMeta,
|
|
17
17
|
type InputProps,
|
|
18
|
-
//
|
|
18
|
+
// Ref types (pointer, no data access)
|
|
19
19
|
type FieldRef,
|
|
20
20
|
type HasManyRef,
|
|
21
21
|
type HasOneRef,
|
|
22
|
-
type
|
|
22
|
+
type HasOneRefInterface,
|
|
23
23
|
type EntityRef,
|
|
24
|
+
type EntityRefInterface,
|
|
25
|
+
// Accessor types (live data access, extends Ref)
|
|
26
|
+
type FieldAccessor,
|
|
27
|
+
type HasManyAccessor,
|
|
28
|
+
type HasOneAccessor,
|
|
24
29
|
type EntityAccessor,
|
|
25
|
-
// Base types (for implicit selection - no .value, .length, etc.)
|
|
26
|
-
type FieldRefBase,
|
|
27
|
-
type HasManyRefBase,
|
|
28
|
-
type HasOneRefBase,
|
|
29
|
-
type HasOneAccessorBase,
|
|
30
|
-
type EntityRefBase,
|
|
31
|
-
type EntityAccessorBase,
|
|
32
30
|
type Unsubscribe,
|
|
33
31
|
// Type extraction helpers
|
|
34
32
|
type ExtractHasOneEntityName,
|