@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
@@ -1,238 +1,21 @@
1
- /**
2
- * Schema type definitions for bindx.
3
- * Provides type-safe entity and relation definitions.
4
- */
5
-
6
- import type { CommonEntity, SingleRoleMap } from './roles.js'
7
-
8
- /**
9
- * Relation type discriminator
10
- */
11
- export type RelationType = 'hasOne' | 'hasMany'
12
-
13
- /**
14
- * Scalar field definition
15
- */
16
- export interface ScalarFieldDef {
17
- readonly type: 'scalar'
18
- readonly columnType?: string
19
- }
20
-
21
- /**
22
- * Enum scalar field definition — carries the allowed values at runtime
23
- */
24
- export interface EnumFieldDef {
25
- readonly type: 'enum'
26
- readonly enumName: string
27
- readonly values: readonly string[]
28
- }
29
-
30
- /**
31
- * Has-one relation definition
32
- */
33
- export interface HasOneRelationDef<TTarget extends string = string> {
34
- readonly type: 'hasOne'
35
- readonly target: TTarget
36
- /** Inverse field name on the target entity (for bidirectional relations) */
37
- readonly inverse?: string
38
- }
39
-
40
- /**
41
- * Has-many relation definition
42
- */
43
- export interface HasManyRelationDef<TTarget extends string = string> {
44
- readonly type: 'hasMany'
45
- readonly target: TTarget
46
- /** Inverse field name on the target entity (for bidirectional relations) */
47
- readonly inverse?: string
48
- }
49
-
50
- /**
51
- * Union of all field definition types
52
- */
53
- export type FieldDef<TTarget extends string = string> =
54
- | ScalarFieldDef
55
- | EnumFieldDef
56
- | HasOneRelationDef<TTarget>
57
- | HasManyRelationDef<TTarget>
58
-
59
- /**
60
- * Entity schema definition - maps field names to their definitions
61
- */
62
- export interface EntitySchemaDef<TEntityNames extends string = string> {
63
- readonly fields: Record<string, FieldDef<TEntityNames>>
64
- }
65
-
66
- /**
67
- * Complete schema definition mapping entity names to their schemas.
68
- * TModels provides the TypeScript types for each entity.
69
- */
70
- export interface SchemaDefinition<
71
- TModels extends { [K in keyof TModels]: object },
72
- TEntityNames extends string = keyof TModels & string,
73
- > {
74
- readonly entities: {
75
- [K in TEntityNames]: EntitySchemaDef<TEntityNames>
76
- }
77
- }
78
-
79
- /**
80
- * Helper function to create a scalar field definition
81
- */
82
- export function scalar(): ScalarFieldDef {
83
- return { type: 'scalar' }
84
- }
85
-
86
- /**
87
- * Helper function to create an enum scalar field definition with allowed values.
88
- */
89
- export function enumScalar<T extends string>(enumName: string, values: readonly T[]): EnumFieldDef {
90
- return { type: 'enum', enumName, values }
91
- }
92
-
93
- /**
94
- * Helper function to create a has-one relation definition
95
- */
96
- export function hasOne<TTarget extends string>(
97
- target: TTarget,
98
- options?: { inverse?: string },
99
- ): HasOneRelationDef<TTarget> {
100
- return {
101
- type: 'hasOne',
102
- target,
103
- inverse: options?.inverse,
104
- }
105
- }
106
-
107
- /**
108
- * Helper function to create a has-many relation definition
109
- */
110
- export function hasMany<TTarget extends string>(
111
- target: TTarget,
112
- options?: { inverse?: string },
113
- ): HasManyRelationDef<TTarget> {
114
- return {
115
- type: 'hasMany',
116
- target,
117
- inverse: options?.inverse,
118
- }
119
- }
120
-
121
- /**
122
- * Helper function to define a complete schema with full type inference.
123
- *
124
- * @example
125
- * ```typescript
126
- * interface Author {
127
- * id: string
128
- * name: string
129
- * articles: Article[]
130
- * }
131
- *
132
- * interface Article {
133
- * id: string
134
- * title: string
135
- * author: Author
136
- * }
137
- *
138
- * const schema = defineSchema<{
139
- * Author: Author
140
- * Article: Article
141
- * }>({
142
- * entities: {
143
- * Author: {
144
- * fields: {
145
- * id: scalar(),
146
- * name: scalar(),
147
- * articles: hasMany('Article', { inverse: 'author' })
148
- * }
149
- * },
150
- * Article: {
151
- * fields: {
152
- * id: scalar(),
153
- * title: scalar(),
154
- * author: hasOne('Author', { inverse: 'articles' })
155
- * }
156
- * }
157
- * }
158
- * })
159
- * ```
160
- */
161
- export function defineSchema<TModels extends { [K in keyof TModels]: object }>(
162
- definition: SchemaDefinition<TModels>,
163
- ): SchemaDefinition<TModels> {
164
- return definition
165
- }
166
-
167
- /**
168
- * Infers the model type for a given entity name from a schema
169
- */
170
- export type InferModel<
171
- TSchema extends SchemaDefinition<any>,
172
- TEntityName extends keyof TSchema['entities'],
173
- > = TSchema extends SchemaDefinition<infer TModels>
174
- ? TEntityName extends keyof TModels
175
- ? TModels[TEntityName]
176
- : never
177
- : never
178
-
179
- /**
180
- * Infers all entity names from a schema
181
- */
182
- export type InferEntityNames<TSchema extends SchemaDefinition<any>> = keyof TSchema['entities'] & string
183
-
184
- // ============================================================================
185
- // Entity Definitions (type-safe entity references)
186
- // ============================================================================
187
-
188
- /**
189
- * A type-safe reference to an entity in the schema.
190
- * Carries a role map as a phantom type parameter: Record<roleName, entityType>.
191
- *
192
- * For schemas without ACL, the role map is `{ _default: TEntity }`.
193
- * For schemas with ACL, each role maps to a per-role entity type.
194
- *
195
- * @example
196
- * ```ts
197
- * // Without roles (convenience):
198
- * const Article = entityDef<ArticleType>('Article') // → EntityDef<{ _default: ArticleType }>
199
- *
200
- * // With roles (generator):
201
- * const Article = roleEntityDef<{ admin: Article$admin; public: Article$public }>('Article')
202
- * ```
203
- */
204
- export interface EntityDef<TRoleMap extends Record<string, object> = Record<string, object>> {
205
- readonly $name: string
206
- /** @internal phantom type for role map — not present at runtime */
207
- readonly $roleMap?: TRoleMap
208
- /** @internal reference to the schema definition for collection-time field lookups */
209
- readonly $schema?: SchemaDefinition<Record<string, object>>
210
- }
211
-
212
- /**
213
- * Infers the common (default) entity type from an EntityDef.
214
- * Returns the union of all role entity types — only common fields are accessible.
215
- */
216
- export type InferEntityDef<T> = T extends EntityDef<infer TRoleMap> ? CommonEntity<TRoleMap> : never
217
-
218
- /**
219
- * Creates a type-safe entity definition reference.
220
- * Wraps the entity type in a single-role map: `{ _default: TEntity }`.
221
- *
222
- * @param name - The entity name as used in the schema
223
- * @param schema - Optional schema definition for collection-time field lookups
224
- */
225
- export function entityDef<TEntity extends object>(name: string, schema?: SchemaDefinition<Record<string, object>>): EntityDef<SingleRoleMap<TEntity>> {
226
- return schema ? { $name: name, $schema: schema } : { $name: name }
227
- }
228
-
229
- /**
230
- * Creates a role-aware entity definition reference.
231
- * Used by the generator when ACL roles are present.
232
- *
233
- * @param name - The entity name as used in the schema
234
- * @param schema - Optional schema definition for collection-time field lookups
235
- */
236
- export function roleEntityDef<TRoleMap extends Record<string, object>>(name: string, schema?: SchemaDefinition<Record<string, object>>): EntityDef<TRoleMap> {
237
- return schema ? { $name: name, $schema: schema } : { $name: name }
238
- }
1
+ export {
2
+ type RelationType,
3
+ type ScalarFieldDef,
4
+ type HasOneRelationDef,
5
+ type HasManyRelationDef,
6
+ type FieldDef,
7
+ type EntitySchemaDef,
8
+ type SchemaDefinition,
9
+ type InferModel,
10
+ type InferEntityNames,
11
+ type EntityDef,
12
+ type InferEntityDef,
13
+ type EnumFieldDef,
14
+ scalar,
15
+ enumScalar,
16
+ hasOne,
17
+ hasMany,
18
+ defineSchema,
19
+ entityDef,
20
+ roleEntityDef,
21
+ } from '@contember/bindx-client'
@@ -1,122 +1 @@
1
- import type { SelectionMeta, SelectionFieldMeta } from './types.js'
2
- import { generateHasManyAlias } from '../utils/aliasGenerator.js'
3
-
4
- /**
5
- * Class for collecting field selection metadata during collection phase
6
- */
7
- export class SelectionMetaCollector implements SelectionMeta {
8
- readonly fields = new Map<string, SelectionFieldMeta>()
9
-
10
- /**
11
- * Add a field to the selection.
12
- *
13
- * For has-many fields with parameters (filter, orderBy, limit, offset),
14
- * uses alias as the key to allow multiple instances of the same field
15
- * with different parameters.
16
- */
17
- addField(fieldMeta: SelectionFieldMeta): void {
18
- let key: string
19
-
20
- // For has-many fields with parameters, use alias as the key
21
- // This allows multiple HasMany for the same field with different params
22
- if (fieldMeta.isArray && fieldMeta.hasManyParams) {
23
- const hasParams = fieldMeta.hasManyParams.filter !== undefined ||
24
- fieldMeta.hasManyParams.orderBy !== undefined ||
25
- fieldMeta.hasManyParams.limit !== undefined ||
26
- fieldMeta.hasManyParams.offset !== undefined
27
-
28
- if (hasParams) {
29
- // Generate auto-alias if alias equals fieldName
30
- if (fieldMeta.alias === fieldMeta.fieldName) {
31
- fieldMeta.alias = generateHasManyAlias(fieldMeta.fieldName, fieldMeta.hasManyParams)
32
- }
33
- // Use alias-based key for has-many with params
34
- const pathWithAlias = [...fieldMeta.path.slice(0, -1), fieldMeta.alias]
35
- key = pathWithAlias.join('.')
36
- } else {
37
- key = fieldMeta.path.join('.')
38
- }
39
- } else {
40
- key = fieldMeta.path.join('.')
41
- }
42
-
43
- const existing = this.fields.get(key)
44
-
45
- if (existing) {
46
- // Update relation/array flags if source has more specific info
47
- if (fieldMeta.isRelation && !existing.isRelation) {
48
- existing.isRelation = true
49
- }
50
- if (fieldMeta.isArray && !existing.isArray) {
51
- existing.isArray = true
52
- }
53
- // Merge nested selections if both have them
54
- if (fieldMeta.nested && existing.nested) {
55
- mergeSelections(existing.nested, fieldMeta.nested)
56
- } else if (fieldMeta.nested) {
57
- existing.nested = fieldMeta.nested
58
- }
59
- } else {
60
- this.fields.set(key, { ...fieldMeta })
61
- }
62
- }
63
-
64
- /**
65
- * Get all root-level fields (not nested)
66
- */
67
- getRootFields(): SelectionFieldMeta[] {
68
- const result: SelectionFieldMeta[] = []
69
- for (const field of this.fields.values()) {
70
- if (field.path.length === 1) {
71
- result.push(field)
72
- }
73
- }
74
- return result
75
- }
76
-
77
- /**
78
- * Convert to plain object for serialization
79
- */
80
- toJSON(): SelectionMeta {
81
- return {
82
- fields: new Map(this.fields),
83
- }
84
- }
85
- }
86
-
87
- /**
88
- * Merge two selections together
89
- */
90
- export function mergeSelections(target: SelectionMeta, source: SelectionMeta): void {
91
- for (const [key, field] of source.fields) {
92
- const existing = target.fields.get(key)
93
- if (existing) {
94
- // Update relation/array flags if source has more specific info
95
- if (field.isRelation && !existing.isRelation) {
96
- existing.isRelation = true
97
- }
98
- if (field.isArray && !existing.isArray) {
99
- existing.isArray = true
100
- }
101
- // Merge hasManyParams if source has them
102
- if (field.hasManyParams && !existing.hasManyParams) {
103
- existing.hasManyParams = field.hasManyParams
104
- }
105
- // Merge nested selections
106
- if (field.nested && existing.nested) {
107
- mergeSelections(existing.nested, field.nested)
108
- } else if (field.nested) {
109
- existing.nested = field.nested
110
- }
111
- } else {
112
- target.fields.set(key, { ...field })
113
- }
114
- }
115
- }
116
-
117
- /**
118
- * Create empty selection metadata
119
- */
120
- export function createEmptySelection(): SelectionMeta {
121
- return { fields: new Map() }
122
- }
1
+ export { SelectionMetaCollector, mergeSelections, createEmptySelection } from '@contember/bindx-client'
@@ -1,208 +1 @@
1
- import type { SelectionMeta, SelectionFieldMeta } from './types.js'
2
- import { generateHasManyAlias } from '../utils/aliasGenerator.js'
3
-
4
- /**
5
- * HasMany parameters for filtering, ordering, pagination
6
- */
7
- export interface HasManyParams {
8
- filter?: unknown
9
- orderBy?: unknown
10
- limit?: number
11
- offset?: number
12
- totalCount?: boolean
13
- }
14
-
15
- /**
16
- * Relation metadata stored at parent level
17
- */
18
- interface RelationMeta {
19
- isArray: boolean
20
- params?: HasManyParams
21
- }
22
-
23
- /**
24
- * Tree-based selection scope for collecting field selections.
25
- *
26
- * Unlike the flat path-based SelectionMetaCollector, SelectionScope
27
- * maintains a hierarchical tree structure that naturally represents
28
- * nested entity selections.
29
- *
30
- * Key benefits:
31
- * - Natural tree structure matches entity relationships
32
- * - No path string manipulation or collision risks
33
- * - Scope carries its context - no need for symbol passing
34
- * - Simple merge: just combine tree nodes
35
- */
36
- export class SelectionScope {
37
- // Tree structure
38
- private parent: SelectionScope | null = null
39
- private children: Map<string, SelectionScope> = new Map()
40
-
41
- // Fields at this level
42
- private scalarFields: Set<string> = new Set()
43
- private relationMeta: Map<string, RelationMeta> = new Map()
44
-
45
- /**
46
- * Create or get a child scope for a relation field.
47
- * Automatically removes the field from scalars if it was there.
48
- * New child scopes always include 'id' as a minimum selection.
49
- */
50
- child(fieldName: string): SelectionScope {
51
- // Remove from scalars if it was added as scalar first
52
- this.scalarFields.delete(fieldName)
53
-
54
- let childScope = this.children.get(fieldName)
55
- if (!childScope) {
56
- childScope = new SelectionScope()
57
- childScope.parent = this
58
- childScope.addScalar('id')
59
- this.children.set(fieldName, childScope)
60
- this.relationMeta.set(fieldName, { isArray: false })
61
- }
62
- return childScope
63
- }
64
-
65
- /**
66
- * Add a scalar field to this scope.
67
- * If the field is already a relation, this is a no-op.
68
- */
69
- addScalar(fieldName: string): void {
70
- if (!this.children.has(fieldName)) {
71
- this.scalarFields.add(fieldName)
72
- }
73
- }
74
-
75
- /**
76
- * Mark a relation field as array (hasMany).
77
- */
78
- markAsArray(fieldName: string): void {
79
- const meta = this.relationMeta.get(fieldName)
80
- if (meta) {
81
- meta.isArray = true
82
- }
83
- }
84
-
85
- /**
86
- * Set hasMany parameters for a relation field.
87
- */
88
- setHasManyParams(fieldName: string, params: HasManyParams): void {
89
- const meta = this.relationMeta.get(fieldName)
90
- if (meta) {
91
- meta.params = params
92
- }
93
- }
94
-
95
- /**
96
- * Check if this scope has any fields (scalar or relation).
97
- */
98
- hasFields(): boolean {
99
- return this.scalarFields.size > 0 || this.children.size > 0
100
- }
101
-
102
- /**
103
- * Merge another scope into this one.
104
- * Scalar fields are added, relation children are recursively merged.
105
- */
106
- merge(other: SelectionScope): void {
107
- // Merge scalar fields
108
- for (const field of other.scalarFields) {
109
- this.addScalar(field)
110
- }
111
-
112
- // Merge relation children
113
- for (const [fieldName, otherChild] of other.children) {
114
- const thisChild = this.child(fieldName)
115
- thisChild.merge(otherChild)
116
-
117
- // Merge relation metadata
118
- const otherMeta = other.relationMeta.get(fieldName)
119
- if (otherMeta) {
120
- const thisMeta = this.relationMeta.get(fieldName)!
121
- if (otherMeta.isArray) {
122
- thisMeta.isArray = true
123
- }
124
- if (otherMeta.params && !thisMeta.params) {
125
- thisMeta.params = otherMeta.params
126
- }
127
- }
128
- }
129
- }
130
-
131
- /**
132
- * Merge fields from a SelectionMeta into this scope.
133
- * Useful for integrating with existing fluent builder selections.
134
- */
135
- mergeFromSelectionMeta(meta: SelectionMeta): void {
136
- for (const [_key, field] of meta.fields) {
137
- // Only process root-level fields
138
- if (field.path.length !== 1) continue
139
-
140
- if (field.isRelation && field.nested) {
141
- const childScope = this.child(field.fieldName)
142
- if (field.isArray) {
143
- this.markAsArray(field.fieldName)
144
- }
145
- if (field.hasManyParams) {
146
- this.setHasManyParams(field.fieldName, field.hasManyParams)
147
- }
148
- childScope.mergeFromSelectionMeta(field.nested)
149
- } else {
150
- this.addScalar(field.fieldName)
151
- }
152
- }
153
- }
154
-
155
- /**
156
- * Convert this scope to SelectionMeta format for backwards compatibility.
157
- * This allows the new SelectionScope to be used with existing query builders.
158
- *
159
- * For has-many fields with params, generates auto-alias to allow multiple
160
- * HasMany components for the same field with different parameters.
161
- */
162
- toSelectionMeta(): SelectionMeta {
163
- const fields = new Map<string, SelectionFieldMeta>()
164
-
165
- // Add scalar fields
166
- for (const fieldName of this.scalarFields) {
167
- fields.set(fieldName, {
168
- fieldName,
169
- alias: fieldName,
170
- path: [fieldName],
171
- isRelation: false,
172
- isArray: false,
173
- })
174
- }
175
-
176
- // Add relation fields with nested selections
177
- for (const [fieldName, childScope] of this.children) {
178
- const meta = this.relationMeta.get(fieldName)!
179
-
180
- // Generate alias for has-many fields with params
181
- const alias = meta.isArray && meta.params
182
- ? generateHasManyAlias(fieldName, meta.params)
183
- : fieldName
184
-
185
- fields.set(alias, {
186
- fieldName,
187
- alias,
188
- path: [fieldName],
189
- isRelation: true,
190
- isArray: meta.isArray,
191
- nested: childScope.toSelectionMeta(),
192
- hasManyParams: meta.params,
193
- })
194
- }
195
-
196
- return { fields }
197
- }
198
-
199
- /**
200
- * Create a SelectionScope from an existing SelectionMeta.
201
- * Useful for converting explicit selections to scope format.
202
- */
203
- static fromSelectionMeta(meta: SelectionMeta): SelectionScope {
204
- const scope = new SelectionScope()
205
- scope.mergeFromSelectionMeta(meta)
206
- return scope
207
- }
208
- }
1
+ export { SelectionScope, type HasManyParams } from '@contember/bindx-client'