@stndrds/schema 0.1.0-alpha.32 → 0.1.0-alpha.34
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-ZTEGJPX2.mjs → chunk-EIRY4I7Q.mjs} +232 -119
- package/dist/{chunk-FG3I6YHA.js → chunk-UCUFSIOX.js} +233 -120
- package/dist/index.d.mts +50 -9
- package/dist/index.d.ts +50 -9
- package/dist/index.js +10 -6
- package/dist/index.mjs +5 -1
- package/dist/{runtime-DyTAIaum.d.mts → runtime-BWBRGKY1.d.mts} +352 -282
- package/dist/{runtime-DyTAIaum.d.ts → runtime-BWBRGKY1.d.ts} +352 -282
- package/dist/runtime.d.mts +1 -1
- package/dist/runtime.d.ts +1 -1
- package/dist/runtime.js +2 -2
- package/dist/runtime.mjs +1 -1
- package/package.json +2 -2
|
@@ -4559,14 +4559,6 @@ var RollupAttributeBuilder = class extends BaseAttributeBuilder {
|
|
|
4559
4559
|
this.attr.decimals = value;
|
|
4560
4560
|
return this;
|
|
4561
4561
|
}
|
|
4562
|
-
/**
|
|
4563
|
-
* Display the rollup value using the target attribute's type formatter
|
|
4564
|
-
* instead of the default rollup-specific formatting
|
|
4565
|
-
*/
|
|
4566
|
-
showAsOriginal() {
|
|
4567
|
-
this.attr.showAsOriginal = true;
|
|
4568
|
-
return this;
|
|
4569
|
-
}
|
|
4570
4562
|
/**
|
|
4571
4563
|
* Set multi-level path for traversing nested relations (Phase 4+)
|
|
4572
4564
|
* @param path - Dot notation path (e.g., "orders.items")
|
|
@@ -4575,6 +4567,24 @@ var RollupAttributeBuilder = class extends BaseAttributeBuilder {
|
|
|
4575
4567
|
this.attr.relationPath = relationPath;
|
|
4576
4568
|
return this;
|
|
4577
4569
|
}
|
|
4570
|
+
/**
|
|
4571
|
+
* Set the target attribute type for display purposes
|
|
4572
|
+
* Required when using function="original" to render values correctly
|
|
4573
|
+
* @param type - The type of the target attribute (e.g., "select", "status", "text")
|
|
4574
|
+
*/
|
|
4575
|
+
targetType(type) {
|
|
4576
|
+
this.attr.targetAttributeType = type;
|
|
4577
|
+
return this;
|
|
4578
|
+
}
|
|
4579
|
+
/**
|
|
4580
|
+
* Set the target attribute options for select-like types
|
|
4581
|
+
* Required when function="original" and target is select/status/multiselect
|
|
4582
|
+
* @param options - The options array from the target attribute
|
|
4583
|
+
*/
|
|
4584
|
+
targetOptions(options) {
|
|
4585
|
+
this.attr.targetAttributeOptions = options;
|
|
4586
|
+
return this;
|
|
4587
|
+
}
|
|
4578
4588
|
// Override to prevent making rollups required
|
|
4579
4589
|
// biome-ignore lint/suspicious/noExplicitAny: intentional override
|
|
4580
4590
|
required() {
|
|
@@ -5529,7 +5539,7 @@ var ViewBuilder = class {
|
|
|
5529
5539
|
return this;
|
|
5530
5540
|
}
|
|
5531
5541
|
/**
|
|
5532
|
-
* Mark as default view for the object
|
|
5542
|
+
* Mark as default view for the object (within its layout)
|
|
5533
5543
|
*/
|
|
5534
5544
|
default() {
|
|
5535
5545
|
this.data.default = true;
|
|
@@ -5542,6 +5552,23 @@ var ViewBuilder = class {
|
|
|
5542
5552
|
this.data.system = true;
|
|
5543
5553
|
return this;
|
|
5544
5554
|
}
|
|
5555
|
+
/**
|
|
5556
|
+
* Set the layout mode for the view
|
|
5557
|
+
* @param value - "page" for full tabs, "modal" for single form
|
|
5558
|
+
* @default "page"
|
|
5559
|
+
*/
|
|
5560
|
+
layout(value) {
|
|
5561
|
+
this.data.layout = value;
|
|
5562
|
+
return this;
|
|
5563
|
+
}
|
|
5564
|
+
/**
|
|
5565
|
+
* Shorthand for .layout("modal")
|
|
5566
|
+
* Modal views have a single form tab without tabs UI
|
|
5567
|
+
*/
|
|
5568
|
+
modal() {
|
|
5569
|
+
this.data.layout = "modal";
|
|
5570
|
+
return this;
|
|
5571
|
+
}
|
|
5545
5572
|
/**
|
|
5546
5573
|
* Set metadata
|
|
5547
5574
|
*/
|
|
@@ -5586,6 +5613,14 @@ var ViewBuilder = class {
|
|
|
5586
5613
|
}
|
|
5587
5614
|
tabNames.add(tab.name);
|
|
5588
5615
|
}
|
|
5616
|
+
if (this.data.layout === "modal") {
|
|
5617
|
+
if (this.data.tabs.length !== 1) {
|
|
5618
|
+
throw new Error("[ViewBuilder] Modal views must have exactly one tab");
|
|
5619
|
+
}
|
|
5620
|
+
if (this.data.tabs[0].type !== "form") {
|
|
5621
|
+
throw new Error("[ViewBuilder] Modal views must have a form tab");
|
|
5622
|
+
}
|
|
5623
|
+
}
|
|
5589
5624
|
this.validated = true;
|
|
5590
5625
|
return this.data;
|
|
5591
5626
|
}
|
|
@@ -5706,6 +5741,25 @@ function isSystemAttributeObject(attr) {
|
|
|
5706
5741
|
|
|
5707
5742
|
// src/validation/validators.ts
|
|
5708
5743
|
import { z as z4 } from "zod";
|
|
5744
|
+
var DEFAULT_VALIDATION_MESSAGES = {
|
|
5745
|
+
required: (attr) => `${attr.label} is required`,
|
|
5746
|
+
invalidType: (attr, expected) => `${attr.label} must be a ${expected}`,
|
|
5747
|
+
minLength: (attr, min) => `${attr.label} must be at least ${min} characters`,
|
|
5748
|
+
maxLength: (attr, max) => `${attr.label} must be at most ${max} characters`,
|
|
5749
|
+
invalidPattern: (attr) => `${attr.label} format is invalid`,
|
|
5750
|
+
minValue: (attr, min) => `${attr.label} must be at least ${min}`,
|
|
5751
|
+
maxValue: (attr, max) => `${attr.label} must be at most ${max}`,
|
|
5752
|
+
mustBeInteger: (attr) => `${attr.label} must be an integer`,
|
|
5753
|
+
invalidDate: (attr) => `${attr.label} must be a valid date`,
|
|
5754
|
+
invalidOption: (attr, options) => `${attr.label} must be one of: ${options.join(", ")}`,
|
|
5755
|
+
invalidId: (attr) => `${attr.label} must be a valid ID`,
|
|
5756
|
+
minItems: (attr, min) => `${attr.label} must have at least ${min} item${min > 1 ? "s" : ""}`,
|
|
5757
|
+
maxItems: (attr, max) => `${attr.label} must have at most ${max} item${max > 1 ? "s" : ""}`,
|
|
5758
|
+
invalidRichtext: (attr) => `${attr.label} must be valid rich text content`,
|
|
5759
|
+
invalidPhone: (attr) => `${attr.label} must be a valid phone number`,
|
|
5760
|
+
invalidCurrency: (attr) => `${attr.label} must be a valid currency value`,
|
|
5761
|
+
invalidLocation: (attr) => `${attr.label} must be a valid location`
|
|
5762
|
+
};
|
|
5709
5763
|
var baseConfigSchema = z4.object({
|
|
5710
5764
|
disabled: z4.boolean().optional(),
|
|
5711
5765
|
placeholder: z4.string().optional(),
|
|
@@ -5830,10 +5884,11 @@ var rollupConfigSchema = baseConfigSchema.extend({
|
|
|
5830
5884
|
"countEmpty",
|
|
5831
5885
|
// Percent (universal)
|
|
5832
5886
|
"percentEmpty",
|
|
5833
|
-
"percentNotEmpty"
|
|
5887
|
+
"percentNotEmpty",
|
|
5888
|
+
// Lookup (universal)
|
|
5889
|
+
"original"
|
|
5834
5890
|
]),
|
|
5835
5891
|
decimals: z4.number().int().min(0).max(10).optional(),
|
|
5836
|
-
showAsOriginal: z4.boolean().optional(),
|
|
5837
5892
|
targetAttributeType: z4.string().optional(),
|
|
5838
5893
|
targetAttributeOptions: z4.array(
|
|
5839
5894
|
z4.object({
|
|
@@ -5890,158 +5945,152 @@ function safeParseAttributeConfig(type, config) {
|
|
|
5890
5945
|
const result = schema.strip().safeParse(config);
|
|
5891
5946
|
return result.success ? result.data : void 0;
|
|
5892
5947
|
}
|
|
5893
|
-
function createTextValidator(attr) {
|
|
5948
|
+
function createTextValidator(attr, messages = DEFAULT_VALIDATION_MESSAGES) {
|
|
5894
5949
|
let schema = z4.string();
|
|
5895
5950
|
if (attr.minLength !== void 0) {
|
|
5896
|
-
schema = schema.min(
|
|
5897
|
-
attr.minLength,
|
|
5898
|
-
`${attr.label} must be at least ${attr.minLength} characters`
|
|
5899
|
-
);
|
|
5951
|
+
schema = schema.min(attr.minLength, messages.minLength(attr, attr.minLength));
|
|
5900
5952
|
}
|
|
5901
5953
|
if (attr.maxLength !== void 0) {
|
|
5902
|
-
schema = schema.max(
|
|
5903
|
-
attr.maxLength,
|
|
5904
|
-
`${attr.label} must be at most ${attr.maxLength} characters`
|
|
5905
|
-
);
|
|
5954
|
+
schema = schema.max(attr.maxLength, messages.maxLength(attr, attr.maxLength));
|
|
5906
5955
|
}
|
|
5907
5956
|
if (attr.pattern) {
|
|
5908
|
-
schema = schema.regex(new RegExp(attr.pattern),
|
|
5957
|
+
schema = schema.regex(new RegExp(attr.pattern), messages.invalidPattern(attr));
|
|
5909
5958
|
}
|
|
5910
5959
|
return schema;
|
|
5911
5960
|
}
|
|
5912
|
-
function createNumberValidator(attr) {
|
|
5961
|
+
function createNumberValidator(attr, messages = DEFAULT_VALIDATION_MESSAGES) {
|
|
5913
5962
|
let schema = z4.number();
|
|
5914
5963
|
if (attr.min !== void 0) {
|
|
5915
|
-
schema = schema.min(attr.min,
|
|
5964
|
+
schema = schema.min(attr.min, messages.minValue(attr, attr.min));
|
|
5916
5965
|
}
|
|
5917
5966
|
if (attr.max !== void 0) {
|
|
5918
|
-
schema = schema.max(attr.max,
|
|
5967
|
+
schema = schema.max(attr.max, messages.maxValue(attr, attr.max));
|
|
5919
5968
|
}
|
|
5920
5969
|
if (attr.unit === "integer") {
|
|
5921
|
-
schema = schema.int(
|
|
5970
|
+
schema = schema.int(messages.mustBeInteger(attr));
|
|
5922
5971
|
}
|
|
5923
5972
|
return schema;
|
|
5924
5973
|
}
|
|
5925
|
-
function createCheckboxValidator(_attr) {
|
|
5974
|
+
function createCheckboxValidator(_attr, _messages = DEFAULT_VALIDATION_MESSAGES) {
|
|
5926
5975
|
return z4.boolean();
|
|
5927
5976
|
}
|
|
5928
|
-
function createDateValidator(attr) {
|
|
5929
|
-
return z4.coerce.date({ message:
|
|
5977
|
+
function createDateValidator(attr, messages = DEFAULT_VALIDATION_MESSAGES) {
|
|
5978
|
+
return z4.coerce.date({ message: messages.invalidDate(attr) });
|
|
5930
5979
|
}
|
|
5931
|
-
function createPhoneValidator(
|
|
5932
|
-
return z4.object(
|
|
5933
|
-
|
|
5934
|
-
|
|
5935
|
-
|
|
5980
|
+
function createPhoneValidator(attr, messages = DEFAULT_VALIDATION_MESSAGES) {
|
|
5981
|
+
return z4.object(
|
|
5982
|
+
{
|
|
5983
|
+
countryCode: z4.string().length(3),
|
|
5984
|
+
phoneNumber: z4.string().min(1)
|
|
5985
|
+
},
|
|
5986
|
+
{ message: messages.invalidPhone(attr) }
|
|
5987
|
+
);
|
|
5936
5988
|
}
|
|
5937
|
-
function createCurrencyValidator(
|
|
5938
|
-
return z4.object(
|
|
5939
|
-
|
|
5940
|
-
|
|
5941
|
-
|
|
5989
|
+
function createCurrencyValidator(attr, messages = DEFAULT_VALIDATION_MESSAGES) {
|
|
5990
|
+
return z4.object(
|
|
5991
|
+
{
|
|
5992
|
+
code: z4.string().length(3),
|
|
5993
|
+
value: z4.number().min(0)
|
|
5994
|
+
},
|
|
5995
|
+
{ message: messages.invalidCurrency(attr) }
|
|
5996
|
+
);
|
|
5942
5997
|
}
|
|
5943
|
-
function createStatusValidator(attr) {
|
|
5998
|
+
function createStatusValidator(attr, messages = DEFAULT_VALIDATION_MESSAGES) {
|
|
5944
5999
|
const validValues = attr.options.map((opt) => opt.value);
|
|
5945
6000
|
return z4.enum(validValues, {
|
|
5946
|
-
|
|
6001
|
+
message: messages.invalidOption(attr, validValues)
|
|
5947
6002
|
});
|
|
5948
6003
|
}
|
|
5949
|
-
function createSelectValidator(attr) {
|
|
6004
|
+
function createSelectValidator(attr, messages = DEFAULT_VALIDATION_MESSAGES) {
|
|
5950
6005
|
const validValues = attr.options.map((opt) => opt.value);
|
|
5951
6006
|
return z4.enum(validValues, {
|
|
5952
|
-
|
|
6007
|
+
message: messages.invalidOption(attr, validValues)
|
|
5953
6008
|
});
|
|
5954
6009
|
}
|
|
5955
|
-
function createMultiselectValidator(attr) {
|
|
6010
|
+
function createMultiselectValidator(attr, messages = DEFAULT_VALIDATION_MESSAGES) {
|
|
5956
6011
|
const validValues = attr.options.map((opt) => opt.value);
|
|
5957
6012
|
return z4.array(
|
|
5958
6013
|
z4.enum(validValues, {
|
|
5959
|
-
|
|
6014
|
+
message: messages.invalidOption(attr, validValues)
|
|
5960
6015
|
})
|
|
5961
6016
|
);
|
|
5962
6017
|
}
|
|
5963
|
-
function createLocationValidator(
|
|
5964
|
-
return z4.object(
|
|
5965
|
-
|
|
5966
|
-
|
|
5967
|
-
|
|
5968
|
-
|
|
5969
|
-
|
|
5970
|
-
|
|
5971
|
-
|
|
5972
|
-
|
|
5973
|
-
|
|
6018
|
+
function createLocationValidator(attr, messages = DEFAULT_VALIDATION_MESSAGES) {
|
|
6019
|
+
return z4.object(
|
|
6020
|
+
{
|
|
6021
|
+
address: z4.string().optional(),
|
|
6022
|
+
address2: z4.string().optional(),
|
|
6023
|
+
city: z4.string().optional(),
|
|
6024
|
+
state: z4.string().optional(),
|
|
6025
|
+
postalCode: z4.string().optional(),
|
|
6026
|
+
country: z4.string().length(3).optional(),
|
|
6027
|
+
latitude: z4.number().optional(),
|
|
6028
|
+
longitude: z4.number().optional()
|
|
6029
|
+
},
|
|
6030
|
+
{ message: messages.invalidLocation(attr) }
|
|
6031
|
+
);
|
|
5974
6032
|
}
|
|
5975
|
-
function createFileValidator(attr) {
|
|
6033
|
+
function createFileValidator(attr, messages = DEFAULT_VALIDATION_MESSAGES) {
|
|
5976
6034
|
const uuidSchema = z4.uuid({
|
|
5977
|
-
message:
|
|
6035
|
+
message: messages.invalidId(attr)
|
|
5978
6036
|
});
|
|
5979
6037
|
if (attr.multiple) {
|
|
5980
6038
|
let arraySchema = z4.array(uuidSchema);
|
|
5981
6039
|
if (attr.maxFiles) {
|
|
5982
|
-
arraySchema = arraySchema.max(
|
|
5983
|
-
attr.maxFiles,
|
|
5984
|
-
`${attr.label} cannot have more than ${attr.maxFiles} file${attr.maxFiles > 1 ? "s" : ""}`
|
|
5985
|
-
);
|
|
6040
|
+
arraySchema = arraySchema.max(attr.maxFiles, messages.maxItems(attr, attr.maxFiles));
|
|
5986
6041
|
}
|
|
5987
6042
|
return arraySchema;
|
|
5988
6043
|
}
|
|
5989
6044
|
return uuidSchema;
|
|
5990
6045
|
}
|
|
5991
|
-
function createUserValidator(attr) {
|
|
6046
|
+
function createUserValidator(attr, messages = DEFAULT_VALIDATION_MESSAGES) {
|
|
5992
6047
|
const uuidSchema = z4.uuid({
|
|
5993
|
-
message:
|
|
6048
|
+
message: messages.invalidId(attr)
|
|
5994
6049
|
});
|
|
5995
6050
|
if (attr.multiple) {
|
|
5996
6051
|
return z4.array(uuidSchema);
|
|
5997
6052
|
}
|
|
5998
6053
|
return uuidSchema;
|
|
5999
6054
|
}
|
|
6000
|
-
function createSingleRelationValidator(attr) {
|
|
6055
|
+
function createSingleRelationValidator(attr, messages = DEFAULT_VALIDATION_MESSAGES) {
|
|
6001
6056
|
const uuidSchema = z4.uuid({
|
|
6002
|
-
message:
|
|
6057
|
+
message: messages.invalidId(attr)
|
|
6003
6058
|
});
|
|
6004
6059
|
return z4.union([uuidSchema, z4.null()]);
|
|
6005
6060
|
}
|
|
6006
|
-
function createMultiRelationValidator(attr) {
|
|
6061
|
+
function createMultiRelationValidator(attr, messages = DEFAULT_VALIDATION_MESSAGES) {
|
|
6007
6062
|
const uuidSchema = z4.uuid({
|
|
6008
|
-
message:
|
|
6063
|
+
message: messages.invalidId(attr)
|
|
6009
6064
|
});
|
|
6010
6065
|
let arraySchema = z4.array(uuidSchema);
|
|
6011
6066
|
if (attr.minItems !== void 0) {
|
|
6012
|
-
arraySchema = arraySchema.min(
|
|
6013
|
-
attr.minItems,
|
|
6014
|
-
`${attr.label} must have at least ${attr.minItems} item${attr.minItems > 1 ? "s" : ""}`
|
|
6015
|
-
);
|
|
6067
|
+
arraySchema = arraySchema.min(attr.minItems, messages.minItems(attr, attr.minItems));
|
|
6016
6068
|
}
|
|
6017
6069
|
if (attr.maxItems !== void 0) {
|
|
6018
|
-
arraySchema = arraySchema.max(
|
|
6019
|
-
attr.maxItems,
|
|
6020
|
-
`${attr.label} must have at most ${attr.maxItems} item${attr.maxItems > 1 ? "s" : ""}`
|
|
6021
|
-
);
|
|
6070
|
+
arraySchema = arraySchema.max(attr.maxItems, messages.maxItems(attr, attr.maxItems));
|
|
6022
6071
|
}
|
|
6023
6072
|
return arraySchema;
|
|
6024
6073
|
}
|
|
6025
|
-
function createRelationValidator(attr) {
|
|
6074
|
+
function createRelationValidator(attr, messages = DEFAULT_VALIDATION_MESSAGES) {
|
|
6026
6075
|
if (attr.cardinality === "many") {
|
|
6027
|
-
return createMultiRelationValidator(attr);
|
|
6076
|
+
return createMultiRelationValidator(attr, messages);
|
|
6028
6077
|
}
|
|
6029
|
-
return createSingleRelationValidator(attr);
|
|
6078
|
+
return createSingleRelationValidator(attr, messages);
|
|
6030
6079
|
}
|
|
6031
|
-
function createRatingValidator(attr) {
|
|
6080
|
+
function createRatingValidator(attr, messages = DEFAULT_VALIDATION_MESSAGES) {
|
|
6032
6081
|
let schema = z4.number().min(0);
|
|
6033
6082
|
if (attr.max !== void 0) {
|
|
6034
|
-
schema = schema.max(attr.max,
|
|
6083
|
+
schema = schema.max(attr.max, messages.maxValue(attr, attr.max));
|
|
6035
6084
|
}
|
|
6036
6085
|
return schema;
|
|
6037
6086
|
}
|
|
6038
|
-
function createFormulaValidator(_attr) {
|
|
6087
|
+
function createFormulaValidator(_attr, _messages = DEFAULT_VALIDATION_MESSAGES) {
|
|
6039
6088
|
return z4.unknown();
|
|
6040
6089
|
}
|
|
6041
|
-
function createRollupValidator(_attr) {
|
|
6090
|
+
function createRollupValidator(_attr, _messages = DEFAULT_VALIDATION_MESSAGES) {
|
|
6042
6091
|
return z4.unknown();
|
|
6043
6092
|
}
|
|
6044
|
-
function createTextAreaValidator(_attr) {
|
|
6093
|
+
function createTextAreaValidator(_attr, _messages = DEFAULT_VALIDATION_MESSAGES) {
|
|
6045
6094
|
return z4.string();
|
|
6046
6095
|
}
|
|
6047
6096
|
var blockNoteBlockSchema = z4.lazy(
|
|
@@ -6053,53 +6102,60 @@ var blockNoteBlockSchema = z4.lazy(
|
|
|
6053
6102
|
children: z4.array(z4.any())
|
|
6054
6103
|
})
|
|
6055
6104
|
);
|
|
6056
|
-
function createRichtextValidator(attr) {
|
|
6105
|
+
function createRichtextValidator(attr, messages = DEFAULT_VALIDATION_MESSAGES) {
|
|
6057
6106
|
return z4.array(blockNoteBlockSchema, {
|
|
6058
|
-
message:
|
|
6107
|
+
message: messages.invalidRichtext(attr)
|
|
6059
6108
|
});
|
|
6060
6109
|
}
|
|
6061
|
-
function createAttributeValidator(attr) {
|
|
6110
|
+
function createAttributeValidator(attr, messages = DEFAULT_VALIDATION_MESSAGES) {
|
|
6062
6111
|
switch (attr.type) {
|
|
6063
6112
|
case "text":
|
|
6064
|
-
return createTextValidator(attr);
|
|
6113
|
+
return createTextValidator(attr, messages);
|
|
6065
6114
|
case "textarea":
|
|
6066
|
-
return createTextAreaValidator(attr);
|
|
6115
|
+
return createTextAreaValidator(attr, messages);
|
|
6067
6116
|
case "richtext":
|
|
6068
|
-
return createRichtextValidator(attr);
|
|
6117
|
+
return createRichtextValidator(attr, messages);
|
|
6069
6118
|
case "number":
|
|
6070
|
-
return createNumberValidator(attr);
|
|
6119
|
+
return createNumberValidator(attr, messages);
|
|
6071
6120
|
case "checkbox":
|
|
6072
|
-
return createCheckboxValidator(attr);
|
|
6121
|
+
return createCheckboxValidator(attr, messages);
|
|
6073
6122
|
case "date":
|
|
6074
|
-
return createDateValidator(attr);
|
|
6123
|
+
return createDateValidator(attr, messages);
|
|
6075
6124
|
case "phone":
|
|
6076
|
-
return createPhoneValidator(attr);
|
|
6125
|
+
return createPhoneValidator(attr, messages);
|
|
6077
6126
|
case "currency":
|
|
6078
|
-
return createCurrencyValidator(attr);
|
|
6127
|
+
return createCurrencyValidator(attr, messages);
|
|
6079
6128
|
case "status":
|
|
6080
|
-
return createStatusValidator(attr);
|
|
6129
|
+
return createStatusValidator(attr, messages);
|
|
6081
6130
|
case "location":
|
|
6082
|
-
return createLocationValidator(attr);
|
|
6131
|
+
return createLocationValidator(attr, messages);
|
|
6083
6132
|
case "select":
|
|
6084
|
-
return createSelectValidator(attr);
|
|
6133
|
+
return createSelectValidator(attr, messages);
|
|
6085
6134
|
case "multiselect":
|
|
6086
|
-
return createMultiselectValidator(attr);
|
|
6135
|
+
return createMultiselectValidator(attr, messages);
|
|
6087
6136
|
case "file":
|
|
6088
|
-
return createFileValidator(attr);
|
|
6137
|
+
return createFileValidator(attr, messages);
|
|
6089
6138
|
case "user":
|
|
6090
|
-
return createUserValidator(attr);
|
|
6139
|
+
return createUserValidator(attr, messages);
|
|
6091
6140
|
case "relation":
|
|
6092
|
-
return createRelationValidator(attr);
|
|
6141
|
+
return createRelationValidator(attr, messages);
|
|
6093
6142
|
case "rating":
|
|
6094
|
-
return createRatingValidator(attr);
|
|
6143
|
+
return createRatingValidator(attr, messages);
|
|
6095
6144
|
case "formula":
|
|
6096
|
-
return createFormulaValidator(attr);
|
|
6145
|
+
return createFormulaValidator(attr, messages);
|
|
6097
6146
|
case "rollup":
|
|
6098
|
-
return createRollupValidator(attr);
|
|
6147
|
+
return createRollupValidator(attr, messages);
|
|
6099
6148
|
default:
|
|
6100
6149
|
return z4.unknown();
|
|
6101
6150
|
}
|
|
6102
6151
|
}
|
|
6152
|
+
function createFormAttributeValidator(attr, messages = DEFAULT_VALIDATION_MESSAGES) {
|
|
6153
|
+
const validator = createAttributeValidator(attr, messages);
|
|
6154
|
+
if (!attr.required) {
|
|
6155
|
+
return validator.nullish();
|
|
6156
|
+
}
|
|
6157
|
+
return validator;
|
|
6158
|
+
}
|
|
6103
6159
|
function createObjectValidator(objectDef) {
|
|
6104
6160
|
const shape = {};
|
|
6105
6161
|
for (const attr of objectDef.attributes) {
|
|
@@ -7892,6 +7948,9 @@ var RollupService = class {
|
|
|
7892
7948
|
const empty = values.filter((v) => v == null || v === "").length;
|
|
7893
7949
|
return empty / values.length;
|
|
7894
7950
|
}
|
|
7951
|
+
// Lookup - returns all values as array
|
|
7952
|
+
case "original":
|
|
7953
|
+
return values;
|
|
7895
7954
|
}
|
|
7896
7955
|
}
|
|
7897
7956
|
/**
|
|
@@ -7911,6 +7970,8 @@ var RollupService = class {
|
|
|
7911
7970
|
case "earliest":
|
|
7912
7971
|
case "latest":
|
|
7913
7972
|
return null;
|
|
7973
|
+
case "original":
|
|
7974
|
+
return [];
|
|
7914
7975
|
}
|
|
7915
7976
|
}
|
|
7916
7977
|
/**
|
|
@@ -8268,7 +8329,6 @@ var RecordService = class extends TenantAwareService {
|
|
|
8268
8329
|
* Resolve relation IDs to their display labels
|
|
8269
8330
|
* @internal
|
|
8270
8331
|
*/
|
|
8271
|
-
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: complexity is ok here
|
|
8272
8332
|
async resolveRelationLabels(relationAttrs, values) {
|
|
8273
8333
|
const resolvedMap = /* @__PURE__ */ new Map();
|
|
8274
8334
|
const idsWithAttrId = [];
|
|
@@ -8512,6 +8572,12 @@ var RecordService = class extends TenantAwareService {
|
|
|
8512
8572
|
if (!options?.skipHooks) {
|
|
8513
8573
|
await this.hookRegistry.execute("beforeUpdate", schema.name, hookCtx);
|
|
8514
8574
|
}
|
|
8575
|
+
const hookModifiedValues = {};
|
|
8576
|
+
for (const key of Object.keys(hookCtx.newValues)) {
|
|
8577
|
+
if (!(key in data) && hookCtx.newValues[key] !== existing.values[key]) {
|
|
8578
|
+
hookModifiedValues[key] = hookCtx.newValues[key];
|
|
8579
|
+
}
|
|
8580
|
+
}
|
|
8515
8581
|
if (options?.validate !== false) {
|
|
8516
8582
|
if (options?.partial) {
|
|
8517
8583
|
validateDraftOrThrow(schema, mergedData);
|
|
@@ -8519,16 +8585,23 @@ var RecordService = class extends TenantAwareService {
|
|
|
8519
8585
|
validateObjectOrThrow(schema, mergedData);
|
|
8520
8586
|
}
|
|
8521
8587
|
if (!options?.skipRelationValidation) {
|
|
8522
|
-
await this.relationService.validateRelationsOrThrow(schema,
|
|
8588
|
+
await this.relationService.validateRelationsOrThrow(schema, {
|
|
8589
|
+
...data,
|
|
8590
|
+
...hookModifiedValues
|
|
8591
|
+
});
|
|
8523
8592
|
}
|
|
8524
8593
|
if (!options?.skipUserValidation) {
|
|
8525
|
-
await this.userService.validateUsersOrThrow(schema,
|
|
8594
|
+
await this.userService.validateUsersOrThrow(schema, {
|
|
8595
|
+
...data,
|
|
8596
|
+
...hookModifiedValues
|
|
8597
|
+
});
|
|
8526
8598
|
}
|
|
8527
8599
|
}
|
|
8528
8600
|
const completionStatus = computeRecordStatus(schema, mergedData);
|
|
8529
8601
|
const label = await this.computeLabel(schema, mergedData);
|
|
8530
8602
|
const updatePayload = {
|
|
8531
8603
|
...data,
|
|
8604
|
+
...hookModifiedValues,
|
|
8532
8605
|
__completionStatus: completionStatus,
|
|
8533
8606
|
__label: label,
|
|
8534
8607
|
__lastUpdatedBy: this.userId
|
|
@@ -8550,11 +8623,15 @@ var RecordService = class extends TenantAwareService {
|
|
|
8550
8623
|
await this.hookRegistry.execute("afterUpdate", schema.name, afterCtx);
|
|
8551
8624
|
}
|
|
8552
8625
|
await this.recalculateParentRollups(updated, schema);
|
|
8553
|
-
|
|
8554
|
-
|
|
8626
|
+
const allChangedAttributes = [
|
|
8627
|
+
...changedAttributes,
|
|
8628
|
+
...Object.keys(hookModifiedValues).filter((k) => !changedAttributes.includes(k))
|
|
8629
|
+
];
|
|
8630
|
+
if (this.auditService && this.userId && allChangedAttributes.length > 0) {
|
|
8631
|
+
const changes = allChangedAttributes.map((attr) => ({
|
|
8555
8632
|
field: attr,
|
|
8556
8633
|
oldValue: hookCtx.oldValues?.[attr],
|
|
8557
|
-
newValue:
|
|
8634
|
+
newValue: hookCtx.newValues[attr]
|
|
8558
8635
|
}));
|
|
8559
8636
|
await this.auditService.logRecordAction({
|
|
8560
8637
|
action: "record.updated",
|
|
@@ -9494,20 +9571,22 @@ var ViewService = class extends TenantAwareService {
|
|
|
9494
9571
|
* Get the default view for an object
|
|
9495
9572
|
*
|
|
9496
9573
|
* Priority:
|
|
9497
|
-
* 1. Custom view marked as default
|
|
9498
|
-
* 2. Native view marked as default
|
|
9499
|
-
* 3. First available view
|
|
9574
|
+
* 1. Custom view marked as default (for the specified layout)
|
|
9575
|
+
* 2. Native view marked as default (for the specified layout)
|
|
9576
|
+
* 3. First available view (for the specified layout)
|
|
9500
9577
|
*
|
|
9501
9578
|
* @param objectName - Object name
|
|
9579
|
+
* @param layout - Optional layout filter ("page" or "modal")
|
|
9502
9580
|
* @returns Default view or null
|
|
9503
9581
|
*/
|
|
9504
|
-
async getDefaultView(objectName) {
|
|
9582
|
+
async getDefaultView(objectName, layout) {
|
|
9505
9583
|
const views = await this.getViewsForObject(objectName);
|
|
9506
|
-
const
|
|
9584
|
+
const filtered = layout ? views.filter((v) => (v.layout ?? "page") === layout) : views;
|
|
9585
|
+
const customDefault = filtered.find((v) => v.default && !v.system);
|
|
9507
9586
|
if (customDefault) return customDefault;
|
|
9508
|
-
const nativeDefault =
|
|
9587
|
+
const nativeDefault = filtered.find((v) => v.default && v.system);
|
|
9509
9588
|
if (nativeDefault) return nativeDefault;
|
|
9510
|
-
return
|
|
9589
|
+
return filtered[0] ?? null;
|
|
9511
9590
|
}
|
|
9512
9591
|
/**
|
|
9513
9592
|
* Create a custom view.
|
|
@@ -9518,6 +9597,7 @@ var ViewService = class extends TenantAwareService {
|
|
|
9518
9597
|
*/
|
|
9519
9598
|
async createView(input) {
|
|
9520
9599
|
this.validateViewName(input.name);
|
|
9600
|
+
this.validateModalLayout(input.tabs, input.layout);
|
|
9521
9601
|
const existing = await this.adapter.views.findByName(input.objectName, input.name);
|
|
9522
9602
|
if (existing) {
|
|
9523
9603
|
throw new SchemaError(
|
|
@@ -9539,6 +9619,7 @@ var ViewService = class extends TenantAwareService {
|
|
|
9539
9619
|
label: input.label,
|
|
9540
9620
|
description: input.description,
|
|
9541
9621
|
icon: input.icon,
|
|
9622
|
+
layout: input.layout,
|
|
9542
9623
|
tabs: input.tabs ?? [],
|
|
9543
9624
|
default: input.default ?? false,
|
|
9544
9625
|
system: false,
|
|
@@ -9562,10 +9643,14 @@ var ViewService = class extends TenantAwareService {
|
|
|
9562
9643
|
if (dbView.system) {
|
|
9563
9644
|
throw new ProtectedResourceError("view", dbView.name, "modify");
|
|
9564
9645
|
}
|
|
9646
|
+
const effectiveLayout = input.layout ?? dbView.layout;
|
|
9647
|
+
const effectiveTabs = input.tabs ?? dbView.tabs;
|
|
9648
|
+
this.validateModalLayout(effectiveTabs, effectiveLayout);
|
|
9565
9649
|
const updated = await this.adapter.views.update(viewId, {
|
|
9566
9650
|
label: input.label,
|
|
9567
9651
|
description: input.description,
|
|
9568
9652
|
icon: input.icon,
|
|
9653
|
+
layout: input.layout,
|
|
9569
9654
|
tabs: input.tabs,
|
|
9570
9655
|
default: input.default,
|
|
9571
9656
|
metadata: input.metadata
|
|
@@ -9588,7 +9673,8 @@ var ViewService = class extends TenantAwareService {
|
|
|
9588
9673
|
await this.adapter.views.delete(viewId);
|
|
9589
9674
|
}
|
|
9590
9675
|
/**
|
|
9591
|
-
* Set a view as default for its object.
|
|
9676
|
+
* Set a view as default for its object and layout.
|
|
9677
|
+
* Only unsets other defaults for the same layout.
|
|
9592
9678
|
* Automatically uses tenant context from AsyncLocalStorage.
|
|
9593
9679
|
*
|
|
9594
9680
|
* @param viewId - View ID
|
|
@@ -9599,9 +9685,11 @@ var ViewService = class extends TenantAwareService {
|
|
|
9599
9685
|
if (!dbView) {
|
|
9600
9686
|
throw new NotFoundError("View", viewId);
|
|
9601
9687
|
}
|
|
9688
|
+
const viewLayout = dbView.layout ?? "page";
|
|
9602
9689
|
const currentViews = await this.adapter.views.findByObjectName(dbView.objectName);
|
|
9603
9690
|
for (const v of currentViews) {
|
|
9604
|
-
|
|
9691
|
+
const vLayout = v.layout ?? "page";
|
|
9692
|
+
if (v.default && v.id !== viewId && !v.system && vLayout === viewLayout) {
|
|
9605
9693
|
await this.adapter.views.update(v.id, { default: false });
|
|
9606
9694
|
}
|
|
9607
9695
|
}
|
|
@@ -9635,6 +9723,28 @@ var ViewService = class extends TenantAwareService {
|
|
|
9635
9723
|
]);
|
|
9636
9724
|
}
|
|
9637
9725
|
}
|
|
9726
|
+
/**
|
|
9727
|
+
* Validate modal layout constraints.
|
|
9728
|
+
* Modal views must have exactly one form tab.
|
|
9729
|
+
*/
|
|
9730
|
+
validateModalLayout(tabs, layout) {
|
|
9731
|
+
if (layout !== "modal") return;
|
|
9732
|
+
if (!tabs || tabs.length === 0) {
|
|
9733
|
+
throw new ValidationError("Modal views must have exactly one form tab", [
|
|
9734
|
+
{ path: ["tabs"], message: "Modal views must have exactly one form tab" }
|
|
9735
|
+
]);
|
|
9736
|
+
}
|
|
9737
|
+
if (tabs.length > 1) {
|
|
9738
|
+
throw new ValidationError("Modal views can only have one tab", [
|
|
9739
|
+
{ path: ["tabs"], message: "Modal views can only have one tab" }
|
|
9740
|
+
]);
|
|
9741
|
+
}
|
|
9742
|
+
if (tabs[0].type !== "form") {
|
|
9743
|
+
throw new ValidationError("Modal views must have a form tab", [
|
|
9744
|
+
{ path: ["tabs"], message: "Modal views must have a form tab, not a " + tabs[0].type }
|
|
9745
|
+
]);
|
|
9746
|
+
}
|
|
9747
|
+
}
|
|
9638
9748
|
/**
|
|
9639
9749
|
* Convert database view to ViewDefinition
|
|
9640
9750
|
*/
|
|
@@ -9646,6 +9756,7 @@ var ViewService = class extends TenantAwareService {
|
|
|
9646
9756
|
description: dbView.description,
|
|
9647
9757
|
icon: dbView.icon,
|
|
9648
9758
|
object: dbView.objectName,
|
|
9759
|
+
layout: dbView.layout,
|
|
9649
9760
|
tabs: dbView.tabs,
|
|
9650
9761
|
default: dbView.default,
|
|
9651
9762
|
system: dbView.system,
|
|
@@ -10070,6 +10181,7 @@ export {
|
|
|
10070
10181
|
view,
|
|
10071
10182
|
group,
|
|
10072
10183
|
registry,
|
|
10184
|
+
DEFAULT_VALIDATION_MESSAGES,
|
|
10073
10185
|
textConfigSchema,
|
|
10074
10186
|
textareaConfigSchema,
|
|
10075
10187
|
richtextConfigSchema,
|
|
@@ -10114,6 +10226,7 @@ export {
|
|
|
10114
10226
|
createTextAreaValidator,
|
|
10115
10227
|
createRichtextValidator,
|
|
10116
10228
|
createAttributeValidator,
|
|
10229
|
+
createFormAttributeValidator,
|
|
10117
10230
|
createObjectValidator,
|
|
10118
10231
|
validateAttribute,
|
|
10119
10232
|
validateObject,
|