@contember/bindx 0.1.40 → 0.1.41
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.
|
@@ -24,6 +24,11 @@ export declare class PlaceholderHandle<TEntity extends object = object, TSelecte
|
|
|
24
24
|
readonly __brands?: Set<symbol>;
|
|
25
25
|
/** Placeholder ID for this handle */
|
|
26
26
|
private readonly placeholderId;
|
|
27
|
+
/**
|
|
28
|
+
* Stable temp id this placeholder collapses into once it must become a real entity
|
|
29
|
+
* (the first time a child is added to one of its has-many relations). Lazily minted.
|
|
30
|
+
*/
|
|
31
|
+
private materializedId;
|
|
27
32
|
private constructor();
|
|
28
33
|
static create<TEntity extends object = object, TSelected = TEntity>(parentEntityType: string, parentEntityId: string, fieldName: string, targetType: string, store: SnapshotStore, dispatcher: ActionDispatcher, schema?: SchemaRegistry | null, brands?: Set<symbol>): EntityAccessor<TEntity, TSelected>;
|
|
29
34
|
static createRaw<TEntity extends object = object, TSelected = TEntity>(parentEntityType: string, parentEntityId: string, fieldName: string, targetType: string, store: SnapshotStore, dispatcher: ActionDispatcher, schema?: SchemaRegistry | null, brands?: Set<symbol>): PlaceholderHandle<TEntity, TSelected>;
|
|
@@ -56,9 +61,23 @@ export declare class PlaceholderHandle<TEntity extends object = object, TSelecte
|
|
|
56
61
|
* Gets field accessors that read/write placeholder data.
|
|
57
62
|
*/
|
|
58
63
|
get fields(): EntityFieldsAccessor<TEntity, TSelected>;
|
|
64
|
+
/**
|
|
65
|
+
* Stable temp id this placeholder collapses into when it must become a real entity.
|
|
66
|
+
*/
|
|
67
|
+
private getMaterializedId;
|
|
68
|
+
/**
|
|
69
|
+
* Promotes this placeholder into a real (temp) entity connected to its parent relation.
|
|
70
|
+
* Called lazily the first time a child is added to one of the placeholder's has-many
|
|
71
|
+
* relations — a has-many child needs a real parent to belong to. The entity is seeded
|
|
72
|
+
* with whatever placeholder scalar data was typed so far, then connected via the normal
|
|
73
|
+
* create path, so persist collects both the entity and its children (see collectHasOne/
|
|
74
|
+
* collectHasManyOperations → create). Idempotent.
|
|
75
|
+
*/
|
|
76
|
+
private materializeIntoParent;
|
|
59
77
|
/**
|
|
60
78
|
* Creates a field handle for placeholder data.
|
|
61
|
-
* For has-many relations, returns
|
|
79
|
+
* For has-many relations, returns a real (empty) has-many handle bound to this
|
|
80
|
+
* placeholder entity, so it behaves identically to a connected one.
|
|
62
81
|
*/
|
|
63
82
|
private createPlaceholderFieldHandle;
|
|
64
83
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PlaceholderHandle.d.ts","sourceRoot":"","sources":["../../src/handles/PlaceholderHandle.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAA;AACnE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAA;AAE9D,OAAO,EAEN,KAAK,oBAAoB,EAEzB,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,MAAM,YAAY,CAAA;AACnB,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAChE,OAAO,KAAK,EACX,YAAY,EACZ,eAAe,EACf,gBAAgB,EAChB,aAAa,EACb,WAAW,EACX,oBAAoB,EACpB,qBAAqB,EACrB,MAAM,oBAAoB,CAAA;
|
|
1
|
+
{"version":3,"file":"PlaceholderHandle.d.ts","sourceRoot":"","sources":["../../src/handles/PlaceholderHandle.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAA;AACnE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAA;AAE9D,OAAO,EAEN,KAAK,oBAAoB,EAEzB,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,MAAM,YAAY,CAAA;AACnB,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAChE,OAAO,KAAK,EACX,YAAY,EACZ,eAAe,EACf,gBAAgB,EAChB,aAAa,EACb,WAAW,EACX,oBAAoB,EACpB,qBAAqB,EACrB,MAAM,oBAAoB,CAAA;AAK3B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAEjE;;;;;;;GAOG;AACH,qBAAa,iBAAiB,CAAC,OAAO,SAAS,MAAM,GAAG,MAAM,EAAE,SAAS,GAAG,OAAO;IAcjF,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IACjC,OAAO,CAAC,QAAQ,CAAC,cAAc;IAC/B,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,MAAM;IAnBxB,2CAA2C;IAC3C,QAAQ,CAAC,QAAQ,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IAE/B,qCAAqC;IACrC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAQ;IAEtC;;;OAGG;IACH,OAAO,CAAC,cAAc,CAAsB;IAE5C,OAAO;IAcP,MAAM,CAAC,MAAM,CAAC,OAAO,SAAS,MAAM,GAAG,MAAM,EAAE,SAAS,GAAG,OAAO,EACjE,gBAAgB,EAAE,MAAM,EACxB,cAAc,EAAE,MAAM,EACtB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,aAAa,EACpB,UAAU,EAAE,gBAAgB,EAC5B,MAAM,CAAC,EAAE,cAAc,GAAG,IAAI,EAC9B,MAAM,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,GAClB,cAAc,CAAC,OAAO,EAAE,SAAS,CAAC;IAIrC,MAAM,CAAC,SAAS,CAAC,OAAO,SAAS,MAAM,GAAG,MAAM,EAAE,SAAS,GAAG,OAAO,EACpE,gBAAgB,EAAE,MAAM,EACxB,cAAc,EAAE,MAAM,EACtB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,aAAa,EACpB,UAAU,EAAE,gBAAgB,EAC5B,MAAM,CAAC,EAAE,cAAc,GAAG,IAAI,EAC9B,MAAM,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,GAClB,iBAAiB,CAAC,OAAO,EAAE,SAAS,CAAC;IAIxC,MAAM,CAAC,SAAS,CAAC,OAAO,SAAS,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,iBAAiB,CAAC,OAAO,EAAE,SAAS,CAAC,GAAG,cAAc,CAAC,OAAO,EAAE,SAAS,CAAC;IAItI;;OAEG;IACH,IAAI,EAAE,IAAI,MAAM,CAEf;IAED;;OAEG;IACH,IAAI,IAAI,IAAI,SAAS,GAAG,IAAI,CAU3B;IAED;;OAEG;IACH,IAAI,OAAO,IAAI,OAAO,CAOrB;IAED;;OAEG;IACH,IAAI,YAAY,IAAI,OAAO,CAE1B;IAED;;OAEG;IACH,IAAI,WAAW,IAAI,IAAI,CAEtB;IAED;;OAEG;IACH,IAAI,KAAK,IAAI,OAAO,CAEnB;IAED;;OAEG;IACH,IAAI,MAAM,IAAI,oBAAoB,CAAC,OAAO,EAAE,SAAS,CAAC,CAMrD;IAED;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAKzB;;;;;;;OAOG;IACH,OAAO,CAAC,qBAAqB;IAY7B;;;;OAIG;IACH,OAAO,CAAC,4BAA4B;IAoIpC;;OAEG;IACH,IAAI,YAAY,IAAI,OAAO,CAE1B;IAED;;OAEG;IACH,IAAI,YAAY,IAAI,MAAM,CAEzB;IAED;;;OAGG;IACH,IAAI,MAAM,IAAI,SAAS,UAAU,EAAE,CAElC;IAED;;OAEG;IACH,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED;;OAEG;IACH,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IAIlC;;OAEG;IACH,WAAW,IAAI,IAAI;IAInB;;OAEG;IACH,cAAc,IAAI,IAAI;IAOtB;;OAEG;IACH,EAAE,CAAC,CAAC,SAAS,eAAe,EAC3B,UAAU,EAAE,CAAC,EACb,SAAS,EAAE,aAAa,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,GACvC,WAAW;IAId;;OAEG;IACH,SAAS,CAAC,CAAC,SAAS,gBAAgB,EACnC,UAAU,EAAE,CAAC,EACb,YAAY,EAAE,WAAW,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,GACxC,WAAW;IAId;;OAEG;IACH,WAAW,CAAC,SAAS,EAAE,aAAa,CAAC,oBAAoB,CAAC,GAAG,WAAW;IAIxE;;OAEG;IACH,mBAAmB,CAAC,YAAY,EAAE,WAAW,CAAC,qBAAqB,CAAC,GAAG,WAAW;CAIlF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contember/bindx",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.41",
|
|
4
4
|
"description": "Type-safe data binding framework (core)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.ts",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
],
|
|
18
18
|
"license": "MIT",
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@contember/bindx-client": "0.1.
|
|
20
|
+
"@contember/bindx-client": "0.1.41",
|
|
21
21
|
"@contember/graphql-builder": "^2.1.0-beta.1",
|
|
22
22
|
"@contember/graphql-client": "^2.1.0-beta.1"
|
|
23
23
|
},
|
|
@@ -19,6 +19,9 @@ import type {
|
|
|
19
19
|
EntityPersistingEvent,
|
|
20
20
|
} from '../events/types.js'
|
|
21
21
|
import { createHandleProxy } from './proxyFactory.js'
|
|
22
|
+
import { HasManyListHandle } from './HasManyListHandle.js'
|
|
23
|
+
import { generateTempId } from '../store/entityId.js'
|
|
24
|
+
import { connectRelation } from '../core/actions.js'
|
|
22
25
|
import type { SchemaRegistry } from '../schema/SchemaRegistry.js'
|
|
23
26
|
|
|
24
27
|
/**
|
|
@@ -36,6 +39,12 @@ export class PlaceholderHandle<TEntity extends object = object, TSelected = TEnt
|
|
|
36
39
|
/** Placeholder ID for this handle */
|
|
37
40
|
private readonly placeholderId: string
|
|
38
41
|
|
|
42
|
+
/**
|
|
43
|
+
* Stable temp id this placeholder collapses into once it must become a real entity
|
|
44
|
+
* (the first time a child is added to one of its has-many relations). Lazily minted.
|
|
45
|
+
*/
|
|
46
|
+
private materializedId: string | null = null
|
|
47
|
+
|
|
39
48
|
private constructor(
|
|
40
49
|
private readonly parentEntityType: string,
|
|
41
50
|
private readonly parentEntityId: string,
|
|
@@ -146,24 +155,76 @@ export class PlaceholderHandle<TEntity extends object = object, TSelected = TEnt
|
|
|
146
155
|
})
|
|
147
156
|
}
|
|
148
157
|
|
|
158
|
+
/**
|
|
159
|
+
* Stable temp id this placeholder collapses into when it must become a real entity.
|
|
160
|
+
*/
|
|
161
|
+
private getMaterializedId(): string {
|
|
162
|
+
if (!this.materializedId) this.materializedId = generateTempId()
|
|
163
|
+
return this.materializedId
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Promotes this placeholder into a real (temp) entity connected to its parent relation.
|
|
168
|
+
* Called lazily the first time a child is added to one of the placeholder's has-many
|
|
169
|
+
* relations — a has-many child needs a real parent to belong to. The entity is seeded
|
|
170
|
+
* with whatever placeholder scalar data was typed so far, then connected via the normal
|
|
171
|
+
* create path, so persist collects both the entity and its children (see collectHasOne/
|
|
172
|
+
* collectHasManyOperations → create). Idempotent.
|
|
173
|
+
*/
|
|
174
|
+
private materializeIntoParent(): void {
|
|
175
|
+
const tempId = this.getMaterializedId()
|
|
176
|
+
const relation = this.store.getRelation(this.parentEntityType, this.parentEntityId, this.fieldName)
|
|
177
|
+
if (relation?.currentId === tempId) return // already materialized
|
|
178
|
+
if (!this.store.getEntitySnapshot(this.targetType, tempId)) {
|
|
179
|
+
this.store.createEntity(this.targetType, { ...(relation?.placeholderData ?? {}), id: tempId })
|
|
180
|
+
}
|
|
181
|
+
this.dispatcher.dispatch(
|
|
182
|
+
connectRelation(this.parentEntityType, this.parentEntityId, this.fieldName, tempId, this.targetType),
|
|
183
|
+
)
|
|
184
|
+
}
|
|
185
|
+
|
|
149
186
|
/**
|
|
150
187
|
* Creates a field handle for placeholder data.
|
|
151
|
-
* For has-many relations, returns
|
|
188
|
+
* For has-many relations, returns a real (empty) has-many handle bound to this
|
|
189
|
+
* placeholder entity, so it behaves identically to a connected one.
|
|
152
190
|
*/
|
|
153
191
|
private createPlaceholderFieldHandle(fieldName: string): unknown {
|
|
154
|
-
// For has-many relations, return
|
|
192
|
+
// For has-many relations, return a real HasManyListHandle bound to this placeholder's
|
|
193
|
+
// stable temp id. Hand-rolling a degenerate stub here used to drop FIELD_REF_META,
|
|
194
|
+
// getById and a working add() — which crashed the BlockEditor
|
|
195
|
+
// (`references[FIELD_REF_META].entityType`) on render for any parent without a
|
|
196
|
+
// connected entity yet. Reads stay empty (no snapshot for the temp id until something
|
|
197
|
+
// is added); the first add() promotes the placeholder into a real connected entity
|
|
198
|
+
// (materializeIntoParent) so the children — and the parent itself — round-trip on persist.
|
|
155
199
|
if (this.schema?.isHasMany(this.targetType, fieldName)) {
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
|
|
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](),
|
|
200
|
+
const itemType = this.schema.getRelationTarget(this.targetType, fieldName)
|
|
201
|
+
if (!itemType) {
|
|
202
|
+
throw new Error(`Field "${fieldName}" is not a relation on entity "${this.targetType}"`)
|
|
165
203
|
}
|
|
166
|
-
|
|
204
|
+
const handle = HasManyListHandle.create(
|
|
205
|
+
this.targetType,
|
|
206
|
+
this.getMaterializedId(),
|
|
207
|
+
fieldName,
|
|
208
|
+
itemType,
|
|
209
|
+
this.store,
|
|
210
|
+
this.dispatcher,
|
|
211
|
+
this.schema,
|
|
212
|
+
this.__brands,
|
|
213
|
+
)
|
|
214
|
+
// Intercept add() to lazily promote the placeholder to a real entity first — a child
|
|
215
|
+
// can't belong to a parent that doesn't exist. Reads pass through untouched.
|
|
216
|
+
return new Proxy(handle as object, {
|
|
217
|
+
get: (target, prop, receiver) => {
|
|
218
|
+
const value = Reflect.get(target, prop, receiver)
|
|
219
|
+
if (prop === 'add' && typeof value === 'function') {
|
|
220
|
+
return (...args: unknown[]): unknown => {
|
|
221
|
+
this.materializeIntoParent()
|
|
222
|
+
return (value as (...a: unknown[]) => unknown).apply(target, args)
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return value
|
|
226
|
+
},
|
|
227
|
+
})
|
|
167
228
|
}
|
|
168
229
|
|
|
169
230
|
const self = this
|