@stndrds/schema 0.1.0-alpha.58 → 0.1.0-alpha.59
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/{chunk-2OS63USQ.js → chunk-DCTLP3ZD.js} +982 -154
- package/dist/{chunk-JRY236AO.mjs → chunk-O7FGYLYG.mjs} +853 -25
- package/dist/index.d.mts +177 -6
- package/dist/index.d.ts +177 -6
- package/dist/index.js +42 -6
- package/dist/index.mjs +37 -1
- package/dist/{runtime-C4vB_EcQ.d.ts → runtime-BRkoIwsC.d.ts} +258 -2
- package/dist/{runtime-SCsDOQi0.d.mts → runtime-Chz-bTNq.d.mts} +258 -2
- package/dist/runtime.d.mts +2 -2
- package/dist/runtime.d.ts +2 -2
- package/dist/runtime.js +4 -2
- package/dist/runtime.mjs +3 -1
- package/dist/validation/validators.d.mts +1 -1
- package/dist/validation/validators.d.ts +1 -1
- package/dist/{validators-BgmWgr-G.d.mts → validators-BIAmz0CD.d.mts} +145 -2
- package/dist/{validators-miARInAq.d.ts → validators-BPIB7Miq.d.ts} +145 -2
- package/package.json +2 -2
|
@@ -4059,6 +4059,91 @@ function createMockObjectRecordsRepository(stores) {
|
|
|
4059
4059
|
};
|
|
4060
4060
|
}
|
|
4061
4061
|
|
|
4062
|
+
// src/runtime/mock/mock-relation-attributes.ts
|
|
4063
|
+
import { randomUUID } from "crypto";
|
|
4064
|
+
function createMockRelationAttributesRepository(stores) {
|
|
4065
|
+
return {
|
|
4066
|
+
async upsertBatch(items) {
|
|
4067
|
+
const context = getContext2();
|
|
4068
|
+
if (!context) {
|
|
4069
|
+
throw new Error("Context required for relationAttributes operations");
|
|
4070
|
+
}
|
|
4071
|
+
const results = [];
|
|
4072
|
+
for (const item of items) {
|
|
4073
|
+
const _key = `${context.tenantId}:${item.fromObject}:${item.fromId}:${item.fromAttribute}:${item.toId}`;
|
|
4074
|
+
const existing = Array.from(stores.relationAttributes.values()).find(
|
|
4075
|
+
(row) => row.tenantId === context.tenantId && row.fromObject === item.fromObject && row.fromId === item.fromId && row.fromAttribute === item.fromAttribute && row.toId === item.toId
|
|
4076
|
+
);
|
|
4077
|
+
if (existing) {
|
|
4078
|
+
existing.properties = item.properties ?? {};
|
|
4079
|
+
existing.updatedAt = /* @__PURE__ */ new Date();
|
|
4080
|
+
existing.updatedBy = item.updatedBy ?? context.userId ?? null;
|
|
4081
|
+
results.push(existing);
|
|
4082
|
+
} else {
|
|
4083
|
+
const row = {
|
|
4084
|
+
id: randomUUID(),
|
|
4085
|
+
tenantId: context.tenantId,
|
|
4086
|
+
fromObject: item.fromObject,
|
|
4087
|
+
fromId: item.fromId,
|
|
4088
|
+
fromAttribute: item.fromAttribute,
|
|
4089
|
+
toId: item.toId,
|
|
4090
|
+
properties: item.properties ?? {},
|
|
4091
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
4092
|
+
updatedAt: /* @__PURE__ */ new Date(),
|
|
4093
|
+
createdBy: item.createdBy ?? context.userId ?? null,
|
|
4094
|
+
updatedBy: item.updatedBy ?? context.userId ?? null
|
|
4095
|
+
};
|
|
4096
|
+
stores.relationAttributes.set(row.id, row);
|
|
4097
|
+
results.push(row);
|
|
4098
|
+
}
|
|
4099
|
+
}
|
|
4100
|
+
return results;
|
|
4101
|
+
},
|
|
4102
|
+
async findBySource(fromObject, fromId, fromAttribute) {
|
|
4103
|
+
const context = getContext2();
|
|
4104
|
+
if (!context) {
|
|
4105
|
+
throw new Error("Context required for relationAttributes operations");
|
|
4106
|
+
}
|
|
4107
|
+
return Array.from(stores.relationAttributes.values()).filter(
|
|
4108
|
+
(row) => row.tenantId === context.tenantId && row.fromObject === fromObject && row.fromId === fromId && row.fromAttribute === fromAttribute
|
|
4109
|
+
);
|
|
4110
|
+
},
|
|
4111
|
+
async findByTarget(toId) {
|
|
4112
|
+
const context = getContext2();
|
|
4113
|
+
if (!context) {
|
|
4114
|
+
throw new Error("Context required for relationAttributes operations");
|
|
4115
|
+
}
|
|
4116
|
+
return Array.from(stores.relationAttributes.values()).filter(
|
|
4117
|
+
(row) => row.tenantId === context.tenantId && row.toId === toId
|
|
4118
|
+
);
|
|
4119
|
+
},
|
|
4120
|
+
async deleteBySource(fromObject, fromId, fromAttribute) {
|
|
4121
|
+
const context = getContext2();
|
|
4122
|
+
if (!context) {
|
|
4123
|
+
throw new Error("Context required for relationAttributes operations");
|
|
4124
|
+
}
|
|
4125
|
+
const toDelete = Array.from(stores.relationAttributes.entries()).filter(
|
|
4126
|
+
([_id, row]) => row.tenantId === context.tenantId && row.fromObject === fromObject && row.fromId === fromId && row.fromAttribute === fromAttribute
|
|
4127
|
+
);
|
|
4128
|
+
for (const [id] of toDelete) {
|
|
4129
|
+
stores.relationAttributes.delete(id);
|
|
4130
|
+
}
|
|
4131
|
+
},
|
|
4132
|
+
async deleteByTarget(toId) {
|
|
4133
|
+
const context = getContext2();
|
|
4134
|
+
if (!context) {
|
|
4135
|
+
throw new Error("Context required for relationAttributes operations");
|
|
4136
|
+
}
|
|
4137
|
+
const toDelete = Array.from(stores.relationAttributes.entries()).filter(
|
|
4138
|
+
([_id, row]) => row.tenantId === context.tenantId && row.toId === toId
|
|
4139
|
+
);
|
|
4140
|
+
for (const [id] of toDelete) {
|
|
4141
|
+
stores.relationAttributes.delete(id);
|
|
4142
|
+
}
|
|
4143
|
+
}
|
|
4144
|
+
};
|
|
4145
|
+
}
|
|
4146
|
+
|
|
4062
4147
|
// src/runtime/mock/mock-stores.ts
|
|
4063
4148
|
function createEmptyStores() {
|
|
4064
4149
|
return {
|
|
@@ -4079,7 +4164,8 @@ function createEmptyStores() {
|
|
|
4079
4164
|
aiConversations: /* @__PURE__ */ new Map(),
|
|
4080
4165
|
aiMessages: /* @__PURE__ */ new Map(),
|
|
4081
4166
|
aiUserMemory: /* @__PURE__ */ new Map(),
|
|
4082
|
-
aiUsageMetrics: /* @__PURE__ */ new Map()
|
|
4167
|
+
aiUsageMetrics: /* @__PURE__ */ new Map(),
|
|
4168
|
+
relationAttributes: /* @__PURE__ */ new Map()
|
|
4083
4169
|
};
|
|
4084
4170
|
}
|
|
4085
4171
|
|
|
@@ -4994,6 +5080,8 @@ function createMockAdapter() {
|
|
|
4994
5080
|
aiConversations: createMockAIConversationsRepository(stores),
|
|
4995
5081
|
aiUserMemory: createMockAIUserMemoryRepository(stores),
|
|
4996
5082
|
aiUsageMetrics: createMockAIUsageMetricsRepository(stores),
|
|
5083
|
+
// Relation attributes repository
|
|
5084
|
+
relationAttributes: createMockRelationAttributesRepository(stores),
|
|
4997
5085
|
async transaction(callback) {
|
|
4998
5086
|
return await callback(adapter);
|
|
4999
5087
|
},
|
|
@@ -5019,6 +5107,7 @@ function createMockAdapter() {
|
|
|
5019
5107
|
stores.aiMessages.clear();
|
|
5020
5108
|
stores.aiUserMemory.clear();
|
|
5021
5109
|
stores.aiUsageMetrics.clear();
|
|
5110
|
+
stores.relationAttributes.clear();
|
|
5022
5111
|
}
|
|
5023
5112
|
};
|
|
5024
5113
|
return adapter;
|
|
@@ -5497,6 +5586,292 @@ function validateOptions(options, attributeName) {
|
|
|
5497
5586
|
}
|
|
5498
5587
|
}
|
|
5499
5588
|
|
|
5589
|
+
// src/types/relation-properties.ts
|
|
5590
|
+
var FORBIDDEN_PROPERTY_TYPES = [
|
|
5591
|
+
"formula",
|
|
5592
|
+
"rollup",
|
|
5593
|
+
"relation",
|
|
5594
|
+
"file",
|
|
5595
|
+
"user",
|
|
5596
|
+
"document",
|
|
5597
|
+
"richtext"
|
|
5598
|
+
];
|
|
5599
|
+
|
|
5600
|
+
// src/builders/property-schema-builder.ts
|
|
5601
|
+
var PropertySchemaBuilder = class {
|
|
5602
|
+
constructor() {
|
|
5603
|
+
this.definitions = [];
|
|
5604
|
+
}
|
|
5605
|
+
/**
|
|
5606
|
+
* Add a property to the schema
|
|
5607
|
+
* The type of property is automatically detected based on the builder methods used
|
|
5608
|
+
*/
|
|
5609
|
+
add(name, configure) {
|
|
5610
|
+
const builder = new PropertyTypeBuilder(name);
|
|
5611
|
+
const configured = configure(builder);
|
|
5612
|
+
const definition = configured.build();
|
|
5613
|
+
this.definitions.push(definition);
|
|
5614
|
+
return this;
|
|
5615
|
+
}
|
|
5616
|
+
/**
|
|
5617
|
+
* Build the final PropertySchema
|
|
5618
|
+
*/
|
|
5619
|
+
build() {
|
|
5620
|
+
return {
|
|
5621
|
+
definitions: this.definitions
|
|
5622
|
+
};
|
|
5623
|
+
}
|
|
5624
|
+
};
|
|
5625
|
+
var PropertyTypeBuilder = class {
|
|
5626
|
+
constructor(name) {
|
|
5627
|
+
this.name = name;
|
|
5628
|
+
}
|
|
5629
|
+
// Explicit type constructors
|
|
5630
|
+
text() {
|
|
5631
|
+
return new TextPropertyBuilder(this.name);
|
|
5632
|
+
}
|
|
5633
|
+
textarea() {
|
|
5634
|
+
return new TextareaPropertyBuilder(this.name);
|
|
5635
|
+
}
|
|
5636
|
+
number() {
|
|
5637
|
+
return new NumberPropertyBuilder(this.name);
|
|
5638
|
+
}
|
|
5639
|
+
checkbox() {
|
|
5640
|
+
return new CheckboxPropertyBuilder(this.name);
|
|
5641
|
+
}
|
|
5642
|
+
date() {
|
|
5643
|
+
return new DatePropertyBuilder(this.name);
|
|
5644
|
+
}
|
|
5645
|
+
phone() {
|
|
5646
|
+
return new PhonePropertyBuilder(this.name);
|
|
5647
|
+
}
|
|
5648
|
+
currency() {
|
|
5649
|
+
return new CurrencyPropertyBuilder(this.name);
|
|
5650
|
+
}
|
|
5651
|
+
status() {
|
|
5652
|
+
return new StatusPropertyBuilder(this.name);
|
|
5653
|
+
}
|
|
5654
|
+
select() {
|
|
5655
|
+
return new SelectPropertyBuilder(this.name);
|
|
5656
|
+
}
|
|
5657
|
+
multiselect() {
|
|
5658
|
+
return new MultiselectPropertyBuilder(this.name);
|
|
5659
|
+
}
|
|
5660
|
+
rating() {
|
|
5661
|
+
return new RatingPropertyBuilder(this.name);
|
|
5662
|
+
}
|
|
5663
|
+
location() {
|
|
5664
|
+
return new LocationPropertyBuilder(this.name);
|
|
5665
|
+
}
|
|
5666
|
+
};
|
|
5667
|
+
var BasePropertyBuilder = class {
|
|
5668
|
+
constructor(name) {
|
|
5669
|
+
this.definition = { name };
|
|
5670
|
+
}
|
|
5671
|
+
/**
|
|
5672
|
+
* Set the label
|
|
5673
|
+
*/
|
|
5674
|
+
label(label) {
|
|
5675
|
+
this.definition.label = label;
|
|
5676
|
+
return this;
|
|
5677
|
+
}
|
|
5678
|
+
/**
|
|
5679
|
+
* Mark as required
|
|
5680
|
+
*/
|
|
5681
|
+
required() {
|
|
5682
|
+
this.definition.required = true;
|
|
5683
|
+
return this;
|
|
5684
|
+
}
|
|
5685
|
+
/**
|
|
5686
|
+
* Set description
|
|
5687
|
+
*/
|
|
5688
|
+
description(description) {
|
|
5689
|
+
this.definition.description = description;
|
|
5690
|
+
return this;
|
|
5691
|
+
}
|
|
5692
|
+
/**
|
|
5693
|
+
* Build the final definition
|
|
5694
|
+
*/
|
|
5695
|
+
build() {
|
|
5696
|
+
return this.definition;
|
|
5697
|
+
}
|
|
5698
|
+
};
|
|
5699
|
+
var TextPropertyBuilder = class extends BasePropertyBuilder {
|
|
5700
|
+
constructor(name) {
|
|
5701
|
+
super(name);
|
|
5702
|
+
this.definition.type = "text";
|
|
5703
|
+
}
|
|
5704
|
+
minLength(value) {
|
|
5705
|
+
this.definition.minLength = value;
|
|
5706
|
+
return this;
|
|
5707
|
+
}
|
|
5708
|
+
maxLength(value) {
|
|
5709
|
+
this.definition.maxLength = value;
|
|
5710
|
+
return this;
|
|
5711
|
+
}
|
|
5712
|
+
pattern(pattern) {
|
|
5713
|
+
this.definition.pattern = pattern;
|
|
5714
|
+
return this;
|
|
5715
|
+
}
|
|
5716
|
+
placeholder(value) {
|
|
5717
|
+
this.definition.placeholder = value;
|
|
5718
|
+
return this;
|
|
5719
|
+
}
|
|
5720
|
+
};
|
|
5721
|
+
var TextareaPropertyBuilder = class extends BasePropertyBuilder {
|
|
5722
|
+
constructor(name) {
|
|
5723
|
+
super(name);
|
|
5724
|
+
this.definition.type = "textarea";
|
|
5725
|
+
}
|
|
5726
|
+
minLength(value) {
|
|
5727
|
+
this.definition.minLength = value;
|
|
5728
|
+
return this;
|
|
5729
|
+
}
|
|
5730
|
+
maxLength(value) {
|
|
5731
|
+
this.definition.maxLength = value;
|
|
5732
|
+
return this;
|
|
5733
|
+
}
|
|
5734
|
+
placeholder(value) {
|
|
5735
|
+
this.definition.placeholder = value;
|
|
5736
|
+
return this;
|
|
5737
|
+
}
|
|
5738
|
+
};
|
|
5739
|
+
var NumberPropertyBuilder = class extends BasePropertyBuilder {
|
|
5740
|
+
constructor(name) {
|
|
5741
|
+
super(name);
|
|
5742
|
+
this.definition.type = "number";
|
|
5743
|
+
}
|
|
5744
|
+
min(value) {
|
|
5745
|
+
this.definition.min = value;
|
|
5746
|
+
return this;
|
|
5747
|
+
}
|
|
5748
|
+
max(value) {
|
|
5749
|
+
this.definition.max = value;
|
|
5750
|
+
return this;
|
|
5751
|
+
}
|
|
5752
|
+
decimal(places) {
|
|
5753
|
+
this.definition.decimal = places;
|
|
5754
|
+
return this;
|
|
5755
|
+
}
|
|
5756
|
+
integer() {
|
|
5757
|
+
this.definition.integer = true;
|
|
5758
|
+
return this;
|
|
5759
|
+
}
|
|
5760
|
+
placeholder(value) {
|
|
5761
|
+
this.definition.placeholder = value;
|
|
5762
|
+
return this;
|
|
5763
|
+
}
|
|
5764
|
+
};
|
|
5765
|
+
var CheckboxPropertyBuilder = class extends BasePropertyBuilder {
|
|
5766
|
+
constructor(name) {
|
|
5767
|
+
super(name);
|
|
5768
|
+
this.definition.type = "checkbox";
|
|
5769
|
+
}
|
|
5770
|
+
};
|
|
5771
|
+
var DatePropertyBuilder = class extends BasePropertyBuilder {
|
|
5772
|
+
constructor(name) {
|
|
5773
|
+
super(name);
|
|
5774
|
+
this.definition.type = "date";
|
|
5775
|
+
}
|
|
5776
|
+
includeTime() {
|
|
5777
|
+
this.definition.includeTime = true;
|
|
5778
|
+
return this;
|
|
5779
|
+
}
|
|
5780
|
+
min(date2) {
|
|
5781
|
+
this.definition.min = date2;
|
|
5782
|
+
return this;
|
|
5783
|
+
}
|
|
5784
|
+
max(date2) {
|
|
5785
|
+
this.definition.max = date2;
|
|
5786
|
+
return this;
|
|
5787
|
+
}
|
|
5788
|
+
};
|
|
5789
|
+
var PhonePropertyBuilder = class extends BasePropertyBuilder {
|
|
5790
|
+
constructor(name) {
|
|
5791
|
+
super(name);
|
|
5792
|
+
this.definition.type = "phone";
|
|
5793
|
+
}
|
|
5794
|
+
};
|
|
5795
|
+
var CurrencyPropertyBuilder = class extends BasePropertyBuilder {
|
|
5796
|
+
constructor(name) {
|
|
5797
|
+
super(name);
|
|
5798
|
+
this.definition.type = "currency";
|
|
5799
|
+
}
|
|
5800
|
+
currency(code) {
|
|
5801
|
+
this.definition.currency = code;
|
|
5802
|
+
return this;
|
|
5803
|
+
}
|
|
5804
|
+
min(value) {
|
|
5805
|
+
this.definition.min = value;
|
|
5806
|
+
return this;
|
|
5807
|
+
}
|
|
5808
|
+
max(value) {
|
|
5809
|
+
this.definition.max = value;
|
|
5810
|
+
return this;
|
|
5811
|
+
}
|
|
5812
|
+
};
|
|
5813
|
+
var StatusPropertyBuilder = class extends BasePropertyBuilder {
|
|
5814
|
+
constructor(name) {
|
|
5815
|
+
super(name);
|
|
5816
|
+
this.definition.type = "status";
|
|
5817
|
+
}
|
|
5818
|
+
options(options) {
|
|
5819
|
+
this.definition.options = options;
|
|
5820
|
+
return this;
|
|
5821
|
+
}
|
|
5822
|
+
};
|
|
5823
|
+
var SelectPropertyBuilder = class extends BasePropertyBuilder {
|
|
5824
|
+
constructor(name) {
|
|
5825
|
+
super(name);
|
|
5826
|
+
this.definition.type = "select";
|
|
5827
|
+
}
|
|
5828
|
+
options(options) {
|
|
5829
|
+
this.definition.options = options;
|
|
5830
|
+
return this;
|
|
5831
|
+
}
|
|
5832
|
+
};
|
|
5833
|
+
var MultiselectPropertyBuilder = class extends BasePropertyBuilder {
|
|
5834
|
+
constructor(name) {
|
|
5835
|
+
super(name);
|
|
5836
|
+
this.definition.type = "multiselect";
|
|
5837
|
+
}
|
|
5838
|
+
options(options) {
|
|
5839
|
+
this.definition.options = options;
|
|
5840
|
+
return this;
|
|
5841
|
+
}
|
|
5842
|
+
maxSelections(value) {
|
|
5843
|
+
this.definition.maxSelections = value;
|
|
5844
|
+
return this;
|
|
5845
|
+
}
|
|
5846
|
+
};
|
|
5847
|
+
var RatingPropertyBuilder = class extends BasePropertyBuilder {
|
|
5848
|
+
constructor(name) {
|
|
5849
|
+
super(name);
|
|
5850
|
+
this.definition.type = "rating";
|
|
5851
|
+
}
|
|
5852
|
+
max(value) {
|
|
5853
|
+
this.definition.max = value;
|
|
5854
|
+
return this;
|
|
5855
|
+
}
|
|
5856
|
+
icon(icon) {
|
|
5857
|
+
this.definition.icon = icon;
|
|
5858
|
+
return this;
|
|
5859
|
+
}
|
|
5860
|
+
};
|
|
5861
|
+
var LocationPropertyBuilder = class extends BasePropertyBuilder {
|
|
5862
|
+
constructor(name) {
|
|
5863
|
+
super(name);
|
|
5864
|
+
this.definition.type = "location";
|
|
5865
|
+
}
|
|
5866
|
+
};
|
|
5867
|
+
function validatePropertyType(type) {
|
|
5868
|
+
if (FORBIDDEN_PROPERTY_TYPES.includes(type)) {
|
|
5869
|
+
throw new Error(
|
|
5870
|
+
`Property type "${type}" is not supported in .qualifyWith(). Only simple types (text, number, date, select, etc.) are allowed. Complex types (formula, rollup, relation, file, user, document) would require duplicating backend behavior.`
|
|
5871
|
+
);
|
|
5872
|
+
}
|
|
5873
|
+
}
|
|
5874
|
+
|
|
5500
5875
|
// src/builders/attribute-builders.ts
|
|
5501
5876
|
var BaseAttributeBuilder = class {
|
|
5502
5877
|
constructor(type, name, label) {
|
|
@@ -6053,6 +6428,32 @@ var SingleRelationAttributeBuilder = class extends BaseAttributeBuilder {
|
|
|
6053
6428
|
);
|
|
6054
6429
|
return multiBuilder;
|
|
6055
6430
|
}
|
|
6431
|
+
/**
|
|
6432
|
+
* Add properties to qualify the relation
|
|
6433
|
+
* Must be called AFTER .to() to ensure targets are defined
|
|
6434
|
+
*
|
|
6435
|
+
* @example
|
|
6436
|
+
* ```typescript
|
|
6437
|
+
* relation({ name: "mainCompany", label: "Main Company" })
|
|
6438
|
+
* .to("companies")
|
|
6439
|
+
* .qualifyWith(props => props
|
|
6440
|
+
* .add("role", select => select.options([...]).required())
|
|
6441
|
+
* .add("shares", number => number.min(0))
|
|
6442
|
+
* )
|
|
6443
|
+
* ```
|
|
6444
|
+
*/
|
|
6445
|
+
qualifyWith(configure) {
|
|
6446
|
+
const targets = this.attr.targets;
|
|
6447
|
+
if (!targets || targets.length === 0) {
|
|
6448
|
+
throw new Error(
|
|
6449
|
+
'.qualifyWith() must be called AFTER .to(). Example: relation({ name: "mainCompany" }).to("companies").qualifyWith(...)'
|
|
6450
|
+
);
|
|
6451
|
+
}
|
|
6452
|
+
const builder = new PropertySchemaBuilder();
|
|
6453
|
+
const schema = configure(builder).build();
|
|
6454
|
+
this.attr.properties = schema;
|
|
6455
|
+
return this;
|
|
6456
|
+
}
|
|
6056
6457
|
required() {
|
|
6057
6458
|
this.setRequired(true);
|
|
6058
6459
|
return this;
|
|
@@ -6114,6 +6515,33 @@ var MultiRelationAttributeBuilder = class extends BaseAttributeBuilder {
|
|
|
6114
6515
|
this.attr.maxItems = count;
|
|
6115
6516
|
return this;
|
|
6116
6517
|
}
|
|
6518
|
+
/**
|
|
6519
|
+
* Add properties to qualify the relation
|
|
6520
|
+
* Must be called AFTER .to() or .many() to ensure targets are defined
|
|
6521
|
+
*
|
|
6522
|
+
* @example
|
|
6523
|
+
* ```typescript
|
|
6524
|
+
* relation({ name: "companies", label: "Companies" })
|
|
6525
|
+
* .to("companies")
|
|
6526
|
+
* .many()
|
|
6527
|
+
* .qualifyWith(props => props
|
|
6528
|
+
* .add("role", select => select.options([...]).required())
|
|
6529
|
+
* .add("shares", number => number.min(0))
|
|
6530
|
+
* )
|
|
6531
|
+
* ```
|
|
6532
|
+
*/
|
|
6533
|
+
qualifyWith(configure) {
|
|
6534
|
+
const targets = this.attr.targets;
|
|
6535
|
+
if (!targets || targets.length === 0) {
|
|
6536
|
+
throw new Error(
|
|
6537
|
+
'.qualifyWith() must be called AFTER .to() or .many(). Example: relation({ name: "companies" }).to("companies").many().qualifyWith(...)'
|
|
6538
|
+
);
|
|
6539
|
+
}
|
|
6540
|
+
const builder = new PropertySchemaBuilder();
|
|
6541
|
+
const schema = configure(builder).build();
|
|
6542
|
+
this.attr.properties = schema;
|
|
6543
|
+
return this;
|
|
6544
|
+
}
|
|
6117
6545
|
required() {
|
|
6118
6546
|
this.setRequired(true);
|
|
6119
6547
|
return this;
|
|
@@ -6499,7 +6927,7 @@ function object(config) {
|
|
|
6499
6927
|
}
|
|
6500
6928
|
|
|
6501
6929
|
// src/builders/view-builder.ts
|
|
6502
|
-
import { randomUUID } from "crypto";
|
|
6930
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
6503
6931
|
import { z as z3 } from "zod";
|
|
6504
6932
|
var GroupBuilder = class {
|
|
6505
6933
|
constructor(id, label) {
|
|
@@ -7359,7 +7787,7 @@ var ListViewBuilder = class {
|
|
|
7359
7787
|
columns: this.data.columns,
|
|
7360
7788
|
columnSizing: this.data.columnSizing,
|
|
7361
7789
|
defaultFilters: this.data.defaultFilters ? {
|
|
7362
|
-
id:
|
|
7790
|
+
id: randomUUID2(),
|
|
7363
7791
|
combinator: this.data.defaultFilters.combinator,
|
|
7364
7792
|
rules: this.data.defaultFilters.rules
|
|
7365
7793
|
} : void 0,
|
|
@@ -7370,7 +7798,7 @@ var ListViewBuilder = class {
|
|
|
7370
7798
|
label: tab.label,
|
|
7371
7799
|
icon: tab.icon,
|
|
7372
7800
|
filters: tab.filters ? {
|
|
7373
|
-
id:
|
|
7801
|
+
id: randomUUID2(),
|
|
7374
7802
|
combinator: tab.filters.combinator,
|
|
7375
7803
|
rules: tab.filters.rules
|
|
7376
7804
|
} : void 0,
|
|
@@ -9799,6 +10227,13 @@ var RecordQueryService = class extends BaseService {
|
|
|
9799
10227
|
effectiveTotal = exhausted ? collected.length : Math.max(collected.length, result.total);
|
|
9800
10228
|
filteredRecords = collected.slice(requestedOffset, requestedOffset + requestedLimit);
|
|
9801
10229
|
}
|
|
10230
|
+
if (options?.include && options.include.length > 0) {
|
|
10231
|
+
filteredRecords = await this.includeRelationsWithProperties(
|
|
10232
|
+
filteredRecords,
|
|
10233
|
+
schema,
|
|
10234
|
+
options.include
|
|
10235
|
+
);
|
|
10236
|
+
}
|
|
9802
10237
|
if (!options?.skipFormulas) {
|
|
9803
10238
|
return {
|
|
9804
10239
|
records: enrichRecordsWithFormulas(filteredRecords, schema),
|
|
@@ -9874,6 +10309,79 @@ var RecordQueryService = class extends BaseService {
|
|
|
9874
10309
|
}
|
|
9875
10310
|
return result;
|
|
9876
10311
|
}
|
|
10312
|
+
// ============================================================================
|
|
10313
|
+
// INCLUDE RELATIONS WITH PROPERTIES
|
|
10314
|
+
// ============================================================================
|
|
10315
|
+
/**
|
|
10316
|
+
* Include relation properties in records.
|
|
10317
|
+
*
|
|
10318
|
+
* For each requested relation attribute:
|
|
10319
|
+
* - If attribute has properties → Fetch from relation_attributes and return hybrid format
|
|
10320
|
+
* - If attribute has NO properties → Return legacy format (string[] or string)
|
|
10321
|
+
*
|
|
10322
|
+
* Uses batch loading to avoid N+1 queries.
|
|
10323
|
+
*
|
|
10324
|
+
* @param records - Records to enrich with relation properties
|
|
10325
|
+
* @param schema - Object schema
|
|
10326
|
+
* @param includes - Array of relation attribute names to include
|
|
10327
|
+
* @returns Records enriched with relation properties in hybrid format
|
|
10328
|
+
* @private
|
|
10329
|
+
*/
|
|
10330
|
+
async includeRelationsWithProperties(records, schema, includes) {
|
|
10331
|
+
if (records.length === 0 || includes.length === 0) {
|
|
10332
|
+
return records;
|
|
10333
|
+
}
|
|
10334
|
+
for (const includeName of includes) {
|
|
10335
|
+
const attr = schema.attributes.find((a) => a.name === includeName);
|
|
10336
|
+
if (!attr || attr.type !== "relation") {
|
|
10337
|
+
continue;
|
|
10338
|
+
}
|
|
10339
|
+
if (attr.properties && this.adapter.relationAttributes) {
|
|
10340
|
+
const recordIds = records.map((r) => r.id);
|
|
10341
|
+
const relationAttributesRepo = this.adapter.relationAttributes;
|
|
10342
|
+
const allRelationProps = await Promise.all(
|
|
10343
|
+
recordIds.map(
|
|
10344
|
+
(recordId) => relationAttributesRepo.findBySource(schema.name, recordId, includeName)
|
|
10345
|
+
)
|
|
10346
|
+
);
|
|
10347
|
+
const propsByRecord = /* @__PURE__ */ new Map();
|
|
10348
|
+
allRelationProps.forEach((props, index) => {
|
|
10349
|
+
const recordId = recordIds[index];
|
|
10350
|
+
const propsMap = /* @__PURE__ */ new Map();
|
|
10351
|
+
for (const prop of props) {
|
|
10352
|
+
propsMap.set(prop.toId, prop.properties);
|
|
10353
|
+
}
|
|
10354
|
+
propsByRecord.set(recordId, propsMap);
|
|
10355
|
+
});
|
|
10356
|
+
for (const record of records) {
|
|
10357
|
+
const currentValue = record.values[includeName];
|
|
10358
|
+
const propsMap = propsByRecord.get(record.id);
|
|
10359
|
+
if (!currentValue) {
|
|
10360
|
+
continue;
|
|
10361
|
+
}
|
|
10362
|
+
if (!propsMap) {
|
|
10363
|
+
continue;
|
|
10364
|
+
}
|
|
10365
|
+
if (attr.cardinality === "many" && Array.isArray(currentValue)) {
|
|
10366
|
+
record.values[includeName] = currentValue.map((id) => {
|
|
10367
|
+
if (typeof id === "string") {
|
|
10368
|
+
const props = propsMap.get(id);
|
|
10369
|
+
return props ? { id, props } : { id };
|
|
10370
|
+
}
|
|
10371
|
+
return id;
|
|
10372
|
+
});
|
|
10373
|
+
} else if (attr.cardinality === "one") {
|
|
10374
|
+
const id = typeof currentValue === "string" ? currentValue : null;
|
|
10375
|
+
if (id) {
|
|
10376
|
+
const props = propsMap.get(id);
|
|
10377
|
+
record.values[includeName] = props ? { id, props } : { id };
|
|
10378
|
+
}
|
|
10379
|
+
}
|
|
10380
|
+
}
|
|
10381
|
+
}
|
|
10382
|
+
}
|
|
10383
|
+
return records;
|
|
10384
|
+
}
|
|
9877
10385
|
};
|
|
9878
10386
|
|
|
9879
10387
|
// src/runtime/services/record/record-resolver.service.ts
|
|
@@ -9968,6 +10476,280 @@ var RecordResolverService = class extends BaseService {
|
|
|
9968
10476
|
}
|
|
9969
10477
|
};
|
|
9970
10478
|
|
|
10479
|
+
// src/runtime/services/record/relation-properties.service.ts
|
|
10480
|
+
import { z as z5 } from "zod";
|
|
10481
|
+
var RelationPropertiesService = class extends BaseService {
|
|
10482
|
+
constructor(adapter) {
|
|
10483
|
+
super(adapter);
|
|
10484
|
+
}
|
|
10485
|
+
// ============================================================================
|
|
10486
|
+
// PUBLIC API
|
|
10487
|
+
// ============================================================================
|
|
10488
|
+
/**
|
|
10489
|
+
* Normalize relation values for storage in object_records table.
|
|
10490
|
+
*
|
|
10491
|
+
* Extracts IDs from hybrid format ({ id, props }) and returns legacy format (string[] or string).
|
|
10492
|
+
* This ensures object_records.values only contains IDs, while properties are in relation_attributes.
|
|
10493
|
+
*
|
|
10494
|
+
* @param schema - Object schema
|
|
10495
|
+
* @param data - Record data with hybrid relation values
|
|
10496
|
+
* @returns Data with relation values normalized to ID-only format
|
|
10497
|
+
*/
|
|
10498
|
+
normalizeRelationValuesForStorage(schema, data) {
|
|
10499
|
+
const normalized = { ...data };
|
|
10500
|
+
for (const attr of schema.attributes) {
|
|
10501
|
+
if (attr.type !== "relation" || !attr.properties) {
|
|
10502
|
+
continue;
|
|
10503
|
+
}
|
|
10504
|
+
const value = data[attr.name];
|
|
10505
|
+
if (value === null || value === void 0) {
|
|
10506
|
+
continue;
|
|
10507
|
+
}
|
|
10508
|
+
if (attr.cardinality === "many" && Array.isArray(value)) {
|
|
10509
|
+
normalized[attr.name] = value.map((item) => {
|
|
10510
|
+
if (typeof item === "string") return item;
|
|
10511
|
+
if (typeof item === "object" && item !== null && "id" in item) {
|
|
10512
|
+
return item.id;
|
|
10513
|
+
}
|
|
10514
|
+
return item;
|
|
10515
|
+
});
|
|
10516
|
+
} else if (typeof value === "object" && value !== null && "id" in value) {
|
|
10517
|
+
normalized[attr.name] = value.id;
|
|
10518
|
+
}
|
|
10519
|
+
}
|
|
10520
|
+
return normalized;
|
|
10521
|
+
}
|
|
10522
|
+
/**
|
|
10523
|
+
* Synchronize relation properties for a given attribute.
|
|
10524
|
+
*
|
|
10525
|
+
* Handles:
|
|
10526
|
+
* - Format normalization (legacy → new)
|
|
10527
|
+
* - Validation of properties
|
|
10528
|
+
* - Upsert for present IDs
|
|
10529
|
+
* - Delete for absent IDs
|
|
10530
|
+
*
|
|
10531
|
+
* @param schema - Object schema
|
|
10532
|
+
* @param recordId - Source record ID
|
|
10533
|
+
* @param attributeName - Relation attribute name
|
|
10534
|
+
* @param relationValue - Relation value (hybrid format)
|
|
10535
|
+
* @param adapter - Database adapter
|
|
10536
|
+
*/
|
|
10537
|
+
async syncRelationProperties(schema, recordId, attributeName, relationValue, adapter) {
|
|
10538
|
+
const attribute = schema.attributes.find((a) => a.name === attributeName);
|
|
10539
|
+
if (!attribute || attribute.type !== "relation") {
|
|
10540
|
+
return;
|
|
10541
|
+
}
|
|
10542
|
+
if (!attribute.properties) {
|
|
10543
|
+
return;
|
|
10544
|
+
}
|
|
10545
|
+
const normalized = this.normalizeRelationValue(relationValue);
|
|
10546
|
+
for (const item of normalized) {
|
|
10547
|
+
if (item.props) {
|
|
10548
|
+
this.validateProperties(attribute.properties, item.props);
|
|
10549
|
+
}
|
|
10550
|
+
}
|
|
10551
|
+
const existing = await adapter.relationAttributes?.findBySource(
|
|
10552
|
+
schema.name,
|
|
10553
|
+
recordId,
|
|
10554
|
+
attributeName
|
|
10555
|
+
);
|
|
10556
|
+
const existingIds = new Set((existing ?? []).map((r) => r.toId));
|
|
10557
|
+
const newIds = new Set(normalized.map((item) => item.id));
|
|
10558
|
+
const toUpsert = normalized.filter((item) => item.props !== void 0);
|
|
10559
|
+
const toDelete = Array.from(existingIds).filter((id) => !newIds.has(id));
|
|
10560
|
+
if (toUpsert.length > 0 && adapter.relationAttributes) {
|
|
10561
|
+
const inputs = toUpsert.map((item) => ({
|
|
10562
|
+
fromObject: schema.name,
|
|
10563
|
+
fromId: recordId,
|
|
10564
|
+
fromAttribute: attributeName,
|
|
10565
|
+
toId: item.id,
|
|
10566
|
+
properties: item.props ?? {},
|
|
10567
|
+
updatedBy: this.userId ?? void 0,
|
|
10568
|
+
createdBy: this.userId ?? void 0
|
|
10569
|
+
}));
|
|
10570
|
+
await adapter.relationAttributes.upsertBatch(inputs);
|
|
10571
|
+
}
|
|
10572
|
+
if (toDelete.length > 0 && adapter.relationAttributes && existing) {
|
|
10573
|
+
for (const toId of toDelete) {
|
|
10574
|
+
const relation2 = existing.find((r) => r.toId === toId);
|
|
10575
|
+
if (relation2) {
|
|
10576
|
+
}
|
|
10577
|
+
}
|
|
10578
|
+
await adapter.relationAttributes.deleteBySource(schema.name, recordId, attributeName);
|
|
10579
|
+
if (toUpsert.length > 0) {
|
|
10580
|
+
const inputs = toUpsert.map((item) => ({
|
|
10581
|
+
fromObject: schema.name,
|
|
10582
|
+
fromId: recordId,
|
|
10583
|
+
fromAttribute: attributeName,
|
|
10584
|
+
toId: item.id,
|
|
10585
|
+
properties: item.props ?? {},
|
|
10586
|
+
updatedBy: this.userId ?? void 0,
|
|
10587
|
+
createdBy: this.userId ?? void 0
|
|
10588
|
+
}));
|
|
10589
|
+
await adapter.relationAttributes.upsertBatch(inputs);
|
|
10590
|
+
}
|
|
10591
|
+
}
|
|
10592
|
+
}
|
|
10593
|
+
/**
|
|
10594
|
+
* Validate relation properties against PropertySchema.
|
|
10595
|
+
*
|
|
10596
|
+
* Uses Zod for runtime validation based on PropertyDefinition types.
|
|
10597
|
+
*
|
|
10598
|
+
* @param propertySchema - Schema defining allowed properties
|
|
10599
|
+
* @param properties - Properties to validate
|
|
10600
|
+
* @throws {z.ZodError} if validation fails
|
|
10601
|
+
*/
|
|
10602
|
+
validateProperties(propertySchema, properties) {
|
|
10603
|
+
const schema = this.buildZodSchema(propertySchema);
|
|
10604
|
+
schema.parse(properties);
|
|
10605
|
+
}
|
|
10606
|
+
// ============================================================================
|
|
10607
|
+
// PRIVATE HELPERS
|
|
10608
|
+
// ============================================================================
|
|
10609
|
+
/**
|
|
10610
|
+
* Normalize relation value to unified internal format.
|
|
10611
|
+
*
|
|
10612
|
+
* Converts:
|
|
10613
|
+
* - string[] → Array<{ id, props?: undefined }>
|
|
10614
|
+
* - string → [{ id, props?: undefined }]
|
|
10615
|
+
* - null → []
|
|
10616
|
+
* - Array<{ id, props }> → Array<{ id, props }> (passthrough)
|
|
10617
|
+
* - { id, props } → [{ id, props }] (single to array)
|
|
10618
|
+
*
|
|
10619
|
+
* @param value - Relation value in hybrid format
|
|
10620
|
+
* @returns Normalized array of relation items
|
|
10621
|
+
* @private
|
|
10622
|
+
*/
|
|
10623
|
+
normalizeRelationValue(value) {
|
|
10624
|
+
if (value === null || value === void 0) {
|
|
10625
|
+
return [];
|
|
10626
|
+
}
|
|
10627
|
+
if (typeof value === "string") {
|
|
10628
|
+
return [{ id: value }];
|
|
10629
|
+
}
|
|
10630
|
+
if (!Array.isArray(value) && typeof value === "object" && "id" in value) {
|
|
10631
|
+
return [value];
|
|
10632
|
+
}
|
|
10633
|
+
if (Array.isArray(value)) {
|
|
10634
|
+
return value.map((item) => {
|
|
10635
|
+
if (typeof item === "string") {
|
|
10636
|
+
return { id: item };
|
|
10637
|
+
}
|
|
10638
|
+
return item;
|
|
10639
|
+
});
|
|
10640
|
+
}
|
|
10641
|
+
return [];
|
|
10642
|
+
}
|
|
10643
|
+
/**
|
|
10644
|
+
* Build Zod schema from PropertySchema definition.
|
|
10645
|
+
*
|
|
10646
|
+
* Dynamically generates validation schema based on PropertyDefinition types.
|
|
10647
|
+
*
|
|
10648
|
+
* @param propertySchema - PropertySchema with definitions
|
|
10649
|
+
* @returns Zod schema for validation
|
|
10650
|
+
* @private
|
|
10651
|
+
*/
|
|
10652
|
+
buildZodSchema(propertySchema) {
|
|
10653
|
+
const shape = {};
|
|
10654
|
+
for (const def of propertySchema.definitions) {
|
|
10655
|
+
let fieldSchema = this.buildFieldSchema(def);
|
|
10656
|
+
if (!def.required) {
|
|
10657
|
+
fieldSchema = fieldSchema.optional();
|
|
10658
|
+
}
|
|
10659
|
+
shape[def.name] = fieldSchema;
|
|
10660
|
+
}
|
|
10661
|
+
return z5.object(shape);
|
|
10662
|
+
}
|
|
10663
|
+
/**
|
|
10664
|
+
* Build Zod schema for a single property field.
|
|
10665
|
+
*
|
|
10666
|
+
* @param def - PropertyDefinition
|
|
10667
|
+
* @returns Zod schema for the field
|
|
10668
|
+
* @private
|
|
10669
|
+
*/
|
|
10670
|
+
buildFieldSchema(def) {
|
|
10671
|
+
switch (def.type) {
|
|
10672
|
+
case "text":
|
|
10673
|
+
case "textarea": {
|
|
10674
|
+
let schema = z5.string();
|
|
10675
|
+
if (def.minLength !== void 0) {
|
|
10676
|
+
schema = schema.min(def.minLength);
|
|
10677
|
+
}
|
|
10678
|
+
if (def.maxLength !== void 0) {
|
|
10679
|
+
schema = schema.max(def.maxLength);
|
|
10680
|
+
}
|
|
10681
|
+
if (def.type === "text" && def.pattern) {
|
|
10682
|
+
schema = schema.regex(new RegExp(def.pattern));
|
|
10683
|
+
}
|
|
10684
|
+
return schema;
|
|
10685
|
+
}
|
|
10686
|
+
case "number": {
|
|
10687
|
+
let schema = z5.number();
|
|
10688
|
+
if (def.min !== void 0) {
|
|
10689
|
+
schema = schema.min(def.min);
|
|
10690
|
+
}
|
|
10691
|
+
if (def.max !== void 0) {
|
|
10692
|
+
schema = schema.max(def.max);
|
|
10693
|
+
}
|
|
10694
|
+
if (def.integer) {
|
|
10695
|
+
schema = schema.int();
|
|
10696
|
+
}
|
|
10697
|
+
return schema;
|
|
10698
|
+
}
|
|
10699
|
+
case "checkbox": {
|
|
10700
|
+
return z5.boolean();
|
|
10701
|
+
}
|
|
10702
|
+
case "date": {
|
|
10703
|
+
const schema = z5.string().datetime();
|
|
10704
|
+
return schema;
|
|
10705
|
+
}
|
|
10706
|
+
case "phone": {
|
|
10707
|
+
return z5.string();
|
|
10708
|
+
}
|
|
10709
|
+
case "currency": {
|
|
10710
|
+
let schema = z5.number();
|
|
10711
|
+
if (def.min !== void 0) {
|
|
10712
|
+
schema = schema.min(def.min);
|
|
10713
|
+
}
|
|
10714
|
+
if (def.max !== void 0) {
|
|
10715
|
+
schema = schema.max(def.max);
|
|
10716
|
+
}
|
|
10717
|
+
return schema;
|
|
10718
|
+
}
|
|
10719
|
+
case "status":
|
|
10720
|
+
case "select": {
|
|
10721
|
+
const validValues = def.options.map((opt) => opt.value);
|
|
10722
|
+
return z5.enum(validValues);
|
|
10723
|
+
}
|
|
10724
|
+
case "multiselect": {
|
|
10725
|
+
const validValues = def.options.map((opt) => opt.value);
|
|
10726
|
+
let schema = z5.array(z5.enum(validValues));
|
|
10727
|
+
if (def.maxSelections !== void 0) {
|
|
10728
|
+
schema = schema.max(def.maxSelections);
|
|
10729
|
+
}
|
|
10730
|
+
return schema;
|
|
10731
|
+
}
|
|
10732
|
+
case "rating": {
|
|
10733
|
+
let schema = z5.number().int();
|
|
10734
|
+
if (def.max !== void 0) {
|
|
10735
|
+
schema = schema.max(def.max);
|
|
10736
|
+
}
|
|
10737
|
+
return schema.min(0);
|
|
10738
|
+
}
|
|
10739
|
+
case "location": {
|
|
10740
|
+
return z5.object({
|
|
10741
|
+
address: z5.string().optional(),
|
|
10742
|
+
lat: z5.number().optional(),
|
|
10743
|
+
lng: z5.number().optional()
|
|
10744
|
+
});
|
|
10745
|
+
}
|
|
10746
|
+
default: {
|
|
10747
|
+
return z5.unknown();
|
|
10748
|
+
}
|
|
10749
|
+
}
|
|
10750
|
+
}
|
|
10751
|
+
};
|
|
10752
|
+
|
|
9971
10753
|
// src/runtime/services/record/relation.service.ts
|
|
9972
10754
|
var RelationService = class extends BaseService {
|
|
9973
10755
|
constructor(adapter, nativeRegistry, options) {
|
|
@@ -10796,6 +11578,7 @@ var RecordService = class extends BaseService {
|
|
|
10796
11578
|
queryService: this.queryService,
|
|
10797
11579
|
recordResolver: this.recordResolver
|
|
10798
11580
|
});
|
|
11581
|
+
this.relationPropertiesService = new RelationPropertiesService(adapter);
|
|
10799
11582
|
this.rollupService = new RollupService(adapter, {
|
|
10800
11583
|
recordResolver: this.recordResolver
|
|
10801
11584
|
});
|
|
@@ -10840,29 +11623,45 @@ var RecordService = class extends BaseService {
|
|
|
10840
11623
|
if (!options?.skipHooks) {
|
|
10841
11624
|
await this.hookRegistry.execute("beforeCreate", schema.name, hookCtx);
|
|
10842
11625
|
}
|
|
11626
|
+
const normalizedData = this.relationPropertiesService.normalizeRelationValuesForStorage(
|
|
11627
|
+
schema,
|
|
11628
|
+
dataWithDefaults
|
|
11629
|
+
);
|
|
10843
11630
|
if (options?.validate !== false) {
|
|
10844
11631
|
if (options?.allowDraft) {
|
|
10845
|
-
validateDraftOrThrow(schema,
|
|
11632
|
+
validateDraftOrThrow(schema, normalizedData);
|
|
10846
11633
|
} else {
|
|
10847
|
-
validateObjectOrThrow(schema,
|
|
11634
|
+
validateObjectOrThrow(schema, normalizedData);
|
|
10848
11635
|
}
|
|
10849
11636
|
if (!options?.skipRelationValidation) {
|
|
10850
|
-
await this.relationService.validateRelationsOrThrow(schema,
|
|
11637
|
+
await this.relationService.validateRelationsOrThrow(schema, normalizedData);
|
|
10851
11638
|
}
|
|
10852
11639
|
if (!options?.skipUserValidation) {
|
|
10853
|
-
await this.userService.validateUsersOrThrow(schema,
|
|
11640
|
+
await this.userService.validateUsersOrThrow(schema, normalizedData);
|
|
10854
11641
|
}
|
|
10855
11642
|
}
|
|
10856
|
-
const completionStatus = computeRecordStatus(schema,
|
|
10857
|
-
const label = await computeLabel(schema,
|
|
11643
|
+
const completionStatus = computeRecordStatus(schema, normalizedData);
|
|
11644
|
+
const label = await computeLabel(schema, normalizedData, this.labelResolver);
|
|
10858
11645
|
const record = await this.adapter.objectRecords.create({
|
|
10859
11646
|
objectId,
|
|
10860
|
-
data:
|
|
11647
|
+
data: normalizedData,
|
|
10861
11648
|
label,
|
|
10862
11649
|
completionStatus,
|
|
10863
11650
|
metadata: options?.metadata,
|
|
10864
11651
|
createdBy: this.userId
|
|
10865
11652
|
});
|
|
11653
|
+
for (const [attrName, value] of Object.entries(dataWithDefaults)) {
|
|
11654
|
+
const attr = schema.attributes.find((a) => a.name === attrName);
|
|
11655
|
+
if (attr?.type === "relation" && attr.properties) {
|
|
11656
|
+
await this.relationPropertiesService.syncRelationProperties(
|
|
11657
|
+
schema,
|
|
11658
|
+
record.id,
|
|
11659
|
+
attrName,
|
|
11660
|
+
value,
|
|
11661
|
+
this.adapter
|
|
11662
|
+
);
|
|
11663
|
+
}
|
|
11664
|
+
}
|
|
10866
11665
|
if (!options?.skipHooks) {
|
|
10867
11666
|
const afterCtx = {
|
|
10868
11667
|
...hookCtx,
|
|
@@ -10993,30 +11792,29 @@ var RecordService = class extends BaseService {
|
|
|
10993
11792
|
hookModifiedValues[key] = hookCtx.newValues[key];
|
|
10994
11793
|
}
|
|
10995
11794
|
}
|
|
11795
|
+
const dataToUpdate = { ...data, ...hookModifiedValues };
|
|
11796
|
+
const normalizedUpdate = this.relationPropertiesService.normalizeRelationValuesForStorage(
|
|
11797
|
+
schema,
|
|
11798
|
+
dataToUpdate
|
|
11799
|
+
);
|
|
11800
|
+
const normalizedMergedData = { ...existing.values, ...normalizedUpdate };
|
|
10996
11801
|
if (options?.validate !== false) {
|
|
10997
11802
|
if (options?.partial) {
|
|
10998
|
-
validateDraftOrThrow(schema,
|
|
11803
|
+
validateDraftOrThrow(schema, normalizedMergedData);
|
|
10999
11804
|
} else {
|
|
11000
|
-
validateObjectOrThrow(schema,
|
|
11805
|
+
validateObjectOrThrow(schema, normalizedMergedData);
|
|
11001
11806
|
}
|
|
11002
11807
|
if (!options?.skipRelationValidation) {
|
|
11003
|
-
await this.relationService.validateRelationsOrThrow(schema,
|
|
11004
|
-
...data,
|
|
11005
|
-
...hookModifiedValues
|
|
11006
|
-
});
|
|
11808
|
+
await this.relationService.validateRelationsOrThrow(schema, normalizedUpdate);
|
|
11007
11809
|
}
|
|
11008
11810
|
if (!options?.skipUserValidation) {
|
|
11009
|
-
await this.userService.validateUsersOrThrow(schema,
|
|
11010
|
-
...data,
|
|
11011
|
-
...hookModifiedValues
|
|
11012
|
-
});
|
|
11811
|
+
await this.userService.validateUsersOrThrow(schema, normalizedUpdate);
|
|
11013
11812
|
}
|
|
11014
11813
|
}
|
|
11015
|
-
const completionStatus = computeRecordStatus(schema,
|
|
11016
|
-
const label = await computeLabel(schema,
|
|
11814
|
+
const completionStatus = computeRecordStatus(schema, normalizedMergedData);
|
|
11815
|
+
const label = await computeLabel(schema, normalizedMergedData, this.labelResolver);
|
|
11017
11816
|
const updatePayload = {
|
|
11018
|
-
...
|
|
11019
|
-
...hookModifiedValues,
|
|
11817
|
+
...normalizedUpdate,
|
|
11020
11818
|
__completionStatus: completionStatus,
|
|
11021
11819
|
__label: label,
|
|
11022
11820
|
__lastUpdatedBy: this.userId,
|
|
@@ -11032,6 +11830,18 @@ var RecordService = class extends BaseService {
|
|
|
11032
11830
|
}
|
|
11033
11831
|
const updated = await this.adapter.objectRecords.update(recordId, updatePayload);
|
|
11034
11832
|
await this.invalidateRecordCaches(recordId, existing.objectId);
|
|
11833
|
+
for (const [attrName, value] of Object.entries(dataToUpdate)) {
|
|
11834
|
+
const attr = schema.attributes.find((a) => a.name === attrName);
|
|
11835
|
+
if (attr?.type === "relation" && attr.properties) {
|
|
11836
|
+
await this.relationPropertiesService.syncRelationProperties(
|
|
11837
|
+
schema,
|
|
11838
|
+
recordId,
|
|
11839
|
+
attrName,
|
|
11840
|
+
value,
|
|
11841
|
+
this.adapter
|
|
11842
|
+
);
|
|
11843
|
+
}
|
|
11844
|
+
}
|
|
11035
11845
|
if (!options?.skipHooks) {
|
|
11036
11846
|
const afterCtx = {
|
|
11037
11847
|
...hookCtx,
|
|
@@ -17132,6 +17942,7 @@ export {
|
|
|
17132
17942
|
SYSTEM_FIELD_NAMES,
|
|
17133
17943
|
RESERVED_ATTRIBUTE_NAMES,
|
|
17134
17944
|
PolicyViolationError,
|
|
17945
|
+
FORBIDDEN_PROPERTY_TYPES,
|
|
17135
17946
|
SYSTEM_ATTRIBUTES,
|
|
17136
17947
|
getSystemAttributeList,
|
|
17137
17948
|
isSystemAttribute,
|
|
@@ -17230,6 +18041,22 @@ export {
|
|
|
17230
18041
|
RoleNotFoundError,
|
|
17231
18042
|
isForbiddenError,
|
|
17232
18043
|
ConcurrentModificationError,
|
|
18044
|
+
PropertySchemaBuilder,
|
|
18045
|
+
PropertyTypeBuilder,
|
|
18046
|
+
BasePropertyBuilder,
|
|
18047
|
+
TextPropertyBuilder,
|
|
18048
|
+
TextareaPropertyBuilder,
|
|
18049
|
+
NumberPropertyBuilder,
|
|
18050
|
+
CheckboxPropertyBuilder,
|
|
18051
|
+
DatePropertyBuilder,
|
|
18052
|
+
PhonePropertyBuilder,
|
|
18053
|
+
CurrencyPropertyBuilder,
|
|
18054
|
+
StatusPropertyBuilder,
|
|
18055
|
+
SelectPropertyBuilder,
|
|
18056
|
+
MultiselectPropertyBuilder,
|
|
18057
|
+
RatingPropertyBuilder,
|
|
18058
|
+
LocationPropertyBuilder,
|
|
18059
|
+
validatePropertyType,
|
|
17233
18060
|
text,
|
|
17234
18061
|
textarea,
|
|
17235
18062
|
richtext,
|
|
@@ -17399,6 +18226,7 @@ export {
|
|
|
17399
18226
|
recalculateParentRollups,
|
|
17400
18227
|
RecordQueryService,
|
|
17401
18228
|
RecordResolverService,
|
|
18229
|
+
RelationPropertiesService,
|
|
17402
18230
|
RelationService,
|
|
17403
18231
|
RollupService,
|
|
17404
18232
|
RecordService,
|