@stndrds/schema 1.0.0-alpha.192 → 1.0.0-alpha.194
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/attributes-DP08iPCh.d.mts +889 -0
- package/dist/attributes-Ddhps_AU.d.ts +889 -0
- package/dist/{chunk-MAKSIK3P.js → chunk-5WATIVCA.js} +2 -2
- package/dist/{chunk-4KRLCWJM.mjs → chunk-6I2R22CX.mjs} +1 -1
- package/dist/{chunk-DC7YYE3U.js → chunk-ACKMC6FL.js} +2 -2
- package/dist/{chunk-TEQNVO7W.mjs → chunk-AYVHMVKQ.mjs} +1 -1
- package/dist/{chunk-P2BE2A2G.js → chunk-BV3IUMMW.js} +4 -7
- package/dist/{chunk-CCB4OY2O.js → chunk-E6JUYGJX.js} +2 -2
- package/dist/{chunk-7A3Y6O32.js → chunk-EBGRZUIH.js} +2 -2
- package/dist/{chunk-QWZHR2OO.js → chunk-EW5XR2X3.js} +2 -2
- package/dist/{chunk-UQVW4KPP.mjs → chunk-HRQEZUAA.mjs} +0 -1
- package/dist/{chunk-OAQWRMTP.js → chunk-HTSTPTNZ.js} +2 -2
- package/dist/{chunk-U4JC7P6D.mjs → chunk-HXDDJLHD.mjs} +37 -52
- package/dist/{chunk-Z75P63VF.mjs → chunk-JUKLL5RJ.mjs} +1 -1
- package/dist/{chunk-YQROI4IE.js → chunk-M5QIBMIH.js} +37 -52
- package/dist/{chunk-FQ2ZYOVS.js → chunk-MZOEL7FN.js} +47 -41
- package/dist/{chunk-KWVCGQ7N.mjs → chunk-NOMHDOVE.mjs} +2 -5
- package/dist/{chunk-OLJLCVNY.mjs → chunk-NS63SPNN.mjs} +1 -1
- package/dist/{chunk-J5HRK3WD.js → chunk-O44XVGHE.js} +2 -2
- package/dist/{chunk-TMYBEXBT.mjs → chunk-OSSGN43B.mjs} +1 -1
- package/dist/{chunk-LTHSU4NF.mjs → chunk-PMKCDRRO.mjs} +26 -21
- package/dist/{chunk-4TC27SMF.mjs → chunk-QEZUDVTN.mjs} +1 -1
- package/dist/{chunk-BLXQGQLK.mjs → chunk-QVLQPW3O.mjs} +1 -1
- package/dist/{chunk-EZHMVZQ4.js → chunk-QWKLQRBX.js} +4 -4
- package/dist/{chunk-ROAZLZA2.js → chunk-ROLWVTU3.js} +2 -2
- package/dist/{chunk-3JINFUN3.js → chunk-SGSGREPG.js} +2 -6
- package/dist/{chunk-VXOTYFEW.js → chunk-T6L2MUG2.js} +2 -2
- package/dist/{chunk-KNYZH2WD.mjs → chunk-UANQJ2GH.mjs} +8 -1
- package/dist/{chunk-IRTRJH37.mjs → chunk-UXAHLSWC.mjs} +1 -1
- package/dist/{chunk-ACQ3TUFK.js → chunk-V2UA2D4E.js} +2 -2
- package/dist/{chunk-R4CJHCC4.mjs → chunk-XZ5RG2OG.mjs} +1 -4
- package/dist/{chunk-TKN73433.js → chunk-YIP5FJ5H.js} +9 -2
- package/dist/{chunk-T225DB55.js → chunk-YKWSHBT5.js} +0 -1
- package/dist/{chunk-TBYXYSGA.mjs → chunk-YUFTHPOE.mjs} +1 -1
- package/dist/{chunk-ITTQ4FGR.mjs → chunk-ZDMOXF3U.mjs} +1 -1
- package/dist/{chunk-SMOHDR6M.mjs → chunk-ZDQ5AP26.mjs} +1 -1
- package/dist/{helpers-Y9rNQ6sz.d.ts → helpers-Bph8AoY7.d.mts} +15 -3
- package/dist/{helpers-HrKmSkLi.d.mts → helpers-D9klos6x.d.ts} +15 -3
- package/dist/index.d.mts +1121 -235
- package/dist/index.d.ts +1121 -235
- package/dist/index.js +2369 -854
- package/dist/index.mjs +2264 -774
- package/dist/{types-Bdlhgrf7.d.ts → types-D38kriFw.d.mts} +1 -2
- package/dist/{types-C5DittlR.d.mts → types-iKvYBe6v.d.ts} +1 -2
- package/dist/validation/all.d.mts +3 -3
- package/dist/validation/all.d.ts +3 -3
- package/dist/validation/all.js +30 -27
- package/dist/validation/all.mjs +17 -18
- package/dist/validation/complex/currency.d.mts +2 -2
- package/dist/validation/complex/currency.d.ts +2 -2
- package/dist/validation/complex/currency.js +3 -3
- package/dist/validation/complex/currency.mjs +2 -2
- package/dist/validation/complex/file.d.mts +2 -2
- package/dist/validation/complex/file.d.ts +2 -2
- package/dist/validation/complex/file.js +3 -3
- package/dist/validation/complex/file.mjs +2 -2
- package/dist/validation/complex/location.d.mts +2 -2
- package/dist/validation/complex/location.d.ts +2 -2
- package/dist/validation/complex/location.js +3 -3
- package/dist/validation/complex/location.mjs +2 -2
- package/dist/validation/complex/phone.d.mts +2 -2
- package/dist/validation/complex/phone.d.ts +2 -2
- package/dist/validation/complex/phone.js +3 -3
- package/dist/validation/complex/phone.mjs +2 -2
- package/dist/validation/complex/relation.d.mts +2 -2
- package/dist/validation/complex/relation.d.ts +2 -2
- package/dist/validation/complex/relation.js +5 -5
- package/dist/validation/complex/relation.mjs +2 -2
- package/dist/validation/complex/richtext.d.mts +2 -2
- package/dist/validation/complex/richtext.d.ts +2 -2
- package/dist/validation/complex/richtext.js +3 -3
- package/dist/validation/complex/richtext.mjs +2 -2
- package/dist/validation/complex/select.d.mts +2 -2
- package/dist/validation/complex/select.d.ts +2 -2
- package/dist/validation/complex/select.js +5 -5
- package/dist/validation/complex/select.mjs +2 -2
- package/dist/validation/complex/user.d.mts +2 -2
- package/dist/validation/complex/user.d.ts +2 -2
- package/dist/validation/complex/user.js +3 -3
- package/dist/validation/complex/user.mjs +2 -2
- package/dist/validation/computed/formula.d.mts +2 -2
- package/dist/validation/computed/formula.d.ts +2 -2
- package/dist/validation/computed/formula.js +3 -3
- package/dist/validation/computed/formula.mjs +2 -2
- package/dist/validation/computed/rollup.d.mts +2 -2
- package/dist/validation/computed/rollup.d.ts +2 -2
- package/dist/validation/computed/rollup.js +3 -3
- package/dist/validation/computed/rollup.mjs +2 -2
- package/dist/validation/config/index.d.mts +1 -1
- package/dist/validation/config/index.d.ts +1 -1
- package/dist/validation/config/index.js +4 -4
- package/dist/validation/config/index.mjs +1 -1
- package/dist/validation/core/index.d.mts +3 -3
- package/dist/validation/core/index.d.ts +3 -3
- package/dist/validation/core/index.js +3 -3
- package/dist/validation/core/index.mjs +1 -1
- package/dist/validation/object/index.d.mts +3 -3
- package/dist/validation/object/index.d.ts +3 -3
- package/dist/validation/object/index.js +32 -29
- package/dist/validation/object/index.mjs +16 -17
- package/dist/validation/primitives/checkbox.d.mts +2 -2
- package/dist/validation/primitives/checkbox.d.ts +2 -2
- package/dist/validation/primitives/checkbox.js +3 -3
- package/dist/validation/primitives/checkbox.mjs +2 -2
- package/dist/validation/primitives/date.d.mts +2 -2
- package/dist/validation/primitives/date.d.ts +2 -2
- package/dist/validation/primitives/date.js +3 -3
- package/dist/validation/primitives/date.mjs +2 -2
- package/dist/validation/primitives/number.d.mts +2 -2
- package/dist/validation/primitives/number.d.ts +2 -2
- package/dist/validation/primitives/number.js +3 -3
- package/dist/validation/primitives/number.mjs +2 -2
- package/dist/validation/primitives/text.d.mts +3 -8
- package/dist/validation/primitives/text.d.ts +3 -8
- package/dist/validation/primitives/text.js +3 -7
- package/dist/validation/primitives/text.mjs +2 -2
- package/package.json +2 -13
- package/dist/chunk-D7WFIGXW.js +0 -14
- package/dist/chunk-RUYUFXNW.mjs +0 -12
- package/dist/filters-DVBn5tov.d.ts +0 -1630
- package/dist/filters-HDG4kLoo.d.mts +0 -1630
- package/dist/validation/primitives/rating.d.mts +0 -12
- package/dist/validation/primitives/rating.d.ts +0 -12
- package/dist/validation/primitives/rating.js +0 -11
- package/dist/validation/primitives/rating.mjs +0 -2
package/dist/index.js
CHANGED
|
@@ -2,27 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
var chunkFRCDMQER_js = require('./chunk-FRCDMQER.js');
|
|
4
4
|
require('./chunk-PGERPYDR.js');
|
|
5
|
-
var
|
|
6
|
-
var
|
|
7
|
-
require('./chunk-
|
|
8
|
-
require('./chunk-
|
|
9
|
-
require('./chunk-
|
|
10
|
-
require('./chunk-
|
|
11
|
-
require('./chunk-
|
|
12
|
-
require('./chunk-
|
|
13
|
-
require('./chunk-
|
|
14
|
-
require('./chunk-
|
|
15
|
-
require('./chunk-
|
|
16
|
-
require('./chunk-
|
|
17
|
-
require('./chunk-
|
|
18
|
-
require('./chunk-
|
|
19
|
-
require('./chunk-
|
|
20
|
-
|
|
21
|
-
require('./chunk-
|
|
22
|
-
var chunkT225DB55_js = require('./chunk-T225DB55.js');
|
|
5
|
+
var chunkM5QIBMIH_js = require('./chunk-M5QIBMIH.js');
|
|
6
|
+
var chunkMZOEL7FN_js = require('./chunk-MZOEL7FN.js');
|
|
7
|
+
require('./chunk-5WATIVCA.js');
|
|
8
|
+
require('./chunk-O44XVGHE.js');
|
|
9
|
+
require('./chunk-YIP5FJ5H.js');
|
|
10
|
+
require('./chunk-SGSGREPG.js');
|
|
11
|
+
require('./chunk-EW5XR2X3.js');
|
|
12
|
+
require('./chunk-QWKLQRBX.js');
|
|
13
|
+
require('./chunk-T6L2MUG2.js');
|
|
14
|
+
require('./chunk-HTSTPTNZ.js');
|
|
15
|
+
require('./chunk-V2UA2D4E.js');
|
|
16
|
+
require('./chunk-ACKMC6FL.js');
|
|
17
|
+
require('./chunk-ROLWVTU3.js');
|
|
18
|
+
require('./chunk-E6JUYGJX.js');
|
|
19
|
+
var chunkEBGRZUIH_js = require('./chunk-EBGRZUIH.js');
|
|
20
|
+
require('./chunk-BV3IUMMW.js');
|
|
21
|
+
var chunkYKWSHBT5_js = require('./chunk-YKWSHBT5.js');
|
|
23
22
|
var constants = require('@stndrds/constants');
|
|
24
23
|
var z3 = require('zod');
|
|
25
|
-
var exprEval = require('expr-eval');
|
|
26
24
|
|
|
27
25
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
28
26
|
|
|
@@ -32,7 +30,6 @@ var z3__default = /*#__PURE__*/_interopDefault(z3);
|
|
|
32
30
|
var IDENTITY_PROPERTIES = ["name", "type"];
|
|
33
31
|
var BEHAVIOR_PROPERTIES = [
|
|
34
32
|
"required",
|
|
35
|
-
"disabled",
|
|
36
33
|
"hidden",
|
|
37
34
|
"archived",
|
|
38
35
|
"deprecated",
|
|
@@ -43,6 +40,318 @@ var BEHAVIOR_PROPERTIES = [
|
|
|
43
40
|
];
|
|
44
41
|
var PRESENTATION_PROPERTIES = ["label", "description", "placeholder", "icon"];
|
|
45
42
|
|
|
43
|
+
// src/types/attribute-capabilities.ts
|
|
44
|
+
var TEXT_OPERATORS = [
|
|
45
|
+
"is",
|
|
46
|
+
"is_not",
|
|
47
|
+
"contains",
|
|
48
|
+
"not_contains",
|
|
49
|
+
"starts_with",
|
|
50
|
+
"ends_with",
|
|
51
|
+
"is_empty",
|
|
52
|
+
"is_not_empty"
|
|
53
|
+
];
|
|
54
|
+
var NUMBER_OPERATORS = [
|
|
55
|
+
"eq",
|
|
56
|
+
"neq",
|
|
57
|
+
"lt",
|
|
58
|
+
"gt",
|
|
59
|
+
"lte",
|
|
60
|
+
"gte",
|
|
61
|
+
"is_empty",
|
|
62
|
+
"is_not_empty"
|
|
63
|
+
];
|
|
64
|
+
var CHECKBOX_OPERATORS = [
|
|
65
|
+
"is_checked",
|
|
66
|
+
"is_not_checked"
|
|
67
|
+
];
|
|
68
|
+
var DATE_OPERATORS = [
|
|
69
|
+
"is",
|
|
70
|
+
"is_not",
|
|
71
|
+
"before",
|
|
72
|
+
"after",
|
|
73
|
+
"on_or_before",
|
|
74
|
+
"on_or_after",
|
|
75
|
+
"is_within",
|
|
76
|
+
"day_month_eq",
|
|
77
|
+
"is_empty",
|
|
78
|
+
"is_not_empty"
|
|
79
|
+
];
|
|
80
|
+
var SELECT_OPERATORS = [
|
|
81
|
+
"is",
|
|
82
|
+
"is_not",
|
|
83
|
+
"any_of",
|
|
84
|
+
"none_of",
|
|
85
|
+
"is_empty",
|
|
86
|
+
"is_not_empty"
|
|
87
|
+
];
|
|
88
|
+
var MULTISELECT_OPERATORS = [
|
|
89
|
+
"contains",
|
|
90
|
+
"not_contains",
|
|
91
|
+
"is_empty",
|
|
92
|
+
"is_not_empty"
|
|
93
|
+
];
|
|
94
|
+
var REFERENCE_OPERATORS = [
|
|
95
|
+
"is",
|
|
96
|
+
"is_not",
|
|
97
|
+
"any_of",
|
|
98
|
+
"none_of",
|
|
99
|
+
"contains",
|
|
100
|
+
"not_contains",
|
|
101
|
+
"is_empty",
|
|
102
|
+
"is_not_empty"
|
|
103
|
+
];
|
|
104
|
+
var PRESENCE_OPERATORS = [
|
|
105
|
+
"is_empty",
|
|
106
|
+
"is_not_empty"
|
|
107
|
+
];
|
|
108
|
+
var FORMULA_OPERATORS = [
|
|
109
|
+
"is",
|
|
110
|
+
"is_not",
|
|
111
|
+
"eq",
|
|
112
|
+
"neq",
|
|
113
|
+
"lt",
|
|
114
|
+
"gt",
|
|
115
|
+
"lte",
|
|
116
|
+
"gte",
|
|
117
|
+
"contains",
|
|
118
|
+
"not_contains",
|
|
119
|
+
"is_empty",
|
|
120
|
+
"is_not_empty"
|
|
121
|
+
];
|
|
122
|
+
var ATTRIBUTE_FILTER_OPERATORS = {
|
|
123
|
+
text: TEXT_OPERATORS,
|
|
124
|
+
richtext: [],
|
|
125
|
+
number: NUMBER_OPERATORS,
|
|
126
|
+
checkbox: CHECKBOX_OPERATORS,
|
|
127
|
+
date: DATE_OPERATORS,
|
|
128
|
+
phone: ["is", "is_not", "contains", "not_contains", "is_empty", "is_not_empty"],
|
|
129
|
+
currency: NUMBER_OPERATORS,
|
|
130
|
+
status: SELECT_OPERATORS,
|
|
131
|
+
select: SELECT_OPERATORS,
|
|
132
|
+
multiselect: MULTISELECT_OPERATORS,
|
|
133
|
+
location: [],
|
|
134
|
+
file: PRESENCE_OPERATORS,
|
|
135
|
+
user: REFERENCE_OPERATORS,
|
|
136
|
+
relation: REFERENCE_OPERATORS,
|
|
137
|
+
formula: FORMULA_OPERATORS,
|
|
138
|
+
document: PRESENCE_OPERATORS
|
|
139
|
+
};
|
|
140
|
+
var COMPUTED_TYPES = /* @__PURE__ */ new Set(["formula", "rollup"]);
|
|
141
|
+
var EDGE_BACKED_TYPES = /* @__PURE__ */ new Set(["file", "user", "relation", "document"]);
|
|
142
|
+
var DIRECT_EDIT_TYPES = /* @__PURE__ */ new Set(["checkbox"]);
|
|
143
|
+
var STATIC_DEFAULT_TYPES = /* @__PURE__ */ new Set([
|
|
144
|
+
"text",
|
|
145
|
+
"richtext",
|
|
146
|
+
"number",
|
|
147
|
+
"select",
|
|
148
|
+
"multiselect",
|
|
149
|
+
"status",
|
|
150
|
+
"date",
|
|
151
|
+
"phone",
|
|
152
|
+
"currency",
|
|
153
|
+
"location",
|
|
154
|
+
"user",
|
|
155
|
+
"file",
|
|
156
|
+
"relation",
|
|
157
|
+
"document"
|
|
158
|
+
]);
|
|
159
|
+
var PLACEHOLDER_TYPES = /* @__PURE__ */ new Set([
|
|
160
|
+
"text",
|
|
161
|
+
"number",
|
|
162
|
+
"date",
|
|
163
|
+
"phone",
|
|
164
|
+
"currency",
|
|
165
|
+
"select",
|
|
166
|
+
"multiselect"
|
|
167
|
+
]);
|
|
168
|
+
var SEARCHABLE_TYPES = /* @__PURE__ */ new Set([
|
|
169
|
+
"text",
|
|
170
|
+
"phone",
|
|
171
|
+
"status",
|
|
172
|
+
"select",
|
|
173
|
+
"multiselect"
|
|
174
|
+
]);
|
|
175
|
+
var SORTABLE_TYPES = /* @__PURE__ */ new Set([
|
|
176
|
+
"text",
|
|
177
|
+
"number",
|
|
178
|
+
"checkbox",
|
|
179
|
+
"date",
|
|
180
|
+
"phone",
|
|
181
|
+
"currency",
|
|
182
|
+
"status",
|
|
183
|
+
"select"
|
|
184
|
+
]);
|
|
185
|
+
function getAttributeCapabilities(attribute) {
|
|
186
|
+
const suppressed = Boolean(attribute.hidden || attribute.archived);
|
|
187
|
+
const active = !attribute.archived;
|
|
188
|
+
const computed = COMPUTED_TYPES.has(attribute.type);
|
|
189
|
+
const document2 = attribute.type === "document";
|
|
190
|
+
const edgeBacked = EDGE_BACKED_TYPES.has(attribute.type);
|
|
191
|
+
const cardinality = getAttributeCardinality(attribute);
|
|
192
|
+
const operators = suppressed ? [] : getRawFilterOperators(attribute);
|
|
193
|
+
const filterable = operators.length > 0 && !suppressed;
|
|
194
|
+
const searchable = !suppressed && SEARCHABLE_TYPES.has(attribute.type);
|
|
195
|
+
const sortable = !suppressed && SORTABLE_TYPES.has(attribute.type);
|
|
196
|
+
const groupable = !suppressed && isGroupableType(attribute.type);
|
|
197
|
+
const kanbanGroupable = !suppressed && attribute.type === "status";
|
|
198
|
+
const readOnly = computed || document2;
|
|
199
|
+
const editable = !(suppressed || readOnly);
|
|
200
|
+
const required = isRequiredForbidden(attribute.type) ? "forbidden" : "allowed";
|
|
201
|
+
const defaultValue = getDefaultValueCapability(attribute.type);
|
|
202
|
+
const maxItems = supportsMaxItems(attribute);
|
|
203
|
+
const placeholder = PLACEHOLDER_TYPES.has(attribute.type);
|
|
204
|
+
const warningWhenDeprecated = Boolean(attribute.deprecated && !suppressed);
|
|
205
|
+
return {
|
|
206
|
+
lifecycle: {
|
|
207
|
+
active,
|
|
208
|
+
deprecated: Boolean(attribute.deprecated),
|
|
209
|
+
visibleInArchitect: true,
|
|
210
|
+
visibleInRuntime: !suppressed,
|
|
211
|
+
warningWhenDeprecated
|
|
212
|
+
},
|
|
213
|
+
storage: {
|
|
214
|
+
mode: computed ? "computed" : edgeBacked ? "edge" : "value",
|
|
215
|
+
cardinality,
|
|
216
|
+
edgeBacked,
|
|
217
|
+
preservesExistingValues: Boolean(attribute.archived),
|
|
218
|
+
qualifiedProperties: supportsQualifiedProperties(attribute.type),
|
|
219
|
+
polymorphic: getPolymorphicCapability(attribute),
|
|
220
|
+
maxItems
|
|
221
|
+
},
|
|
222
|
+
authoring: {
|
|
223
|
+
editable,
|
|
224
|
+
readOnly,
|
|
225
|
+
directEdit: editable && DIRECT_EDIT_TYPES.has(attribute.type),
|
|
226
|
+
required,
|
|
227
|
+
defaultValue,
|
|
228
|
+
placeholder,
|
|
229
|
+
maxItems
|
|
230
|
+
},
|
|
231
|
+
presentation: {
|
|
232
|
+
formField: !suppressed && isFormFieldType(attribute.type),
|
|
233
|
+
tableCell: !suppressed && isTableCellType(attribute.type),
|
|
234
|
+
defaultColumn: !suppressed && isDefaultColumnType(attribute.type),
|
|
235
|
+
compactDisplay: !suppressed,
|
|
236
|
+
popoverEditor: !suppressed && isPopoverEditorType(attribute.type),
|
|
237
|
+
placeholder,
|
|
238
|
+
warningWhenDeprecated
|
|
239
|
+
},
|
|
240
|
+
query: {
|
|
241
|
+
filterable,
|
|
242
|
+
searchable,
|
|
243
|
+
sortable,
|
|
244
|
+
groupable,
|
|
245
|
+
kanbanGroupable,
|
|
246
|
+
operators
|
|
247
|
+
},
|
|
248
|
+
agent: {
|
|
249
|
+
readable: !suppressed,
|
|
250
|
+
writable: editable && !document2,
|
|
251
|
+
compactReadable: !suppressed,
|
|
252
|
+
contentReadable: !suppressed && isContentReadableType(attribute.type),
|
|
253
|
+
requiresDedicatedTool: document2
|
|
254
|
+
},
|
|
255
|
+
exchange: {
|
|
256
|
+
importable: !suppressed,
|
|
257
|
+
exportable: !suppressed,
|
|
258
|
+
schemaExportable: true,
|
|
259
|
+
metadataExportable: false
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
function getAttributeFilterOperators(attribute) {
|
|
264
|
+
return getAttributeCapabilities(attribute).query.operators;
|
|
265
|
+
}
|
|
266
|
+
function isAttributeFilterable(attribute) {
|
|
267
|
+
return getAttributeCapabilities(attribute).query.filterable;
|
|
268
|
+
}
|
|
269
|
+
function isAttributeSearchable(attribute) {
|
|
270
|
+
return getAttributeCapabilities(attribute).query.searchable;
|
|
271
|
+
}
|
|
272
|
+
function isAttributeSortable(attribute) {
|
|
273
|
+
return getAttributeCapabilities(attribute).query.sortable;
|
|
274
|
+
}
|
|
275
|
+
function isAttributeGroupable(attribute) {
|
|
276
|
+
return getAttributeCapabilities(attribute).query.groupable;
|
|
277
|
+
}
|
|
278
|
+
function isAttributeKanbanGroupable(attribute) {
|
|
279
|
+
return getAttributeCapabilities(attribute).query.kanbanGroupable;
|
|
280
|
+
}
|
|
281
|
+
function getRollupFilterOperators(attr) {
|
|
282
|
+
const fn = attr.function;
|
|
283
|
+
if (fn === "earliest" || fn === "latest") {
|
|
284
|
+
return ATTRIBUTE_FILTER_OPERATORS.date;
|
|
285
|
+
}
|
|
286
|
+
if (fn === "original") {
|
|
287
|
+
const targetType = attr.targetAttributeType;
|
|
288
|
+
if (targetType && hasFilterOperatorEntry(targetType)) {
|
|
289
|
+
return ATTRIBUTE_FILTER_OPERATORS[targetType];
|
|
290
|
+
}
|
|
291
|
+
return ATTRIBUTE_FILTER_OPERATORS.number;
|
|
292
|
+
}
|
|
293
|
+
return ATTRIBUTE_FILTER_OPERATORS.number;
|
|
294
|
+
}
|
|
295
|
+
function getRawFilterOperators(attribute) {
|
|
296
|
+
if (attribute.type === "rollup") {
|
|
297
|
+
return getRollupFilterOperators(attribute);
|
|
298
|
+
}
|
|
299
|
+
return ATTRIBUTE_FILTER_OPERATORS[attribute.type] ?? [];
|
|
300
|
+
}
|
|
301
|
+
function getAttributeCardinality(attribute) {
|
|
302
|
+
if (attribute.type === "multiselect" || attribute.type === "document") return "many";
|
|
303
|
+
if (attribute.type === "file" && attribute.multiple === true) return "many";
|
|
304
|
+
if (attribute.type === "user" && attribute.multiple === true) return "many";
|
|
305
|
+
if (attribute.type === "relation") return attribute.cardinality === "many" ? "many" : "one";
|
|
306
|
+
return "one";
|
|
307
|
+
}
|
|
308
|
+
function getPolymorphicCapability(attribute) {
|
|
309
|
+
if (attribute.type !== "relation") return "none";
|
|
310
|
+
const relation2 = attribute;
|
|
311
|
+
const targets = relation2.targets ?? [];
|
|
312
|
+
if (targets.length === 1 && targets[0]?.object === "*") return "internal";
|
|
313
|
+
return targets.length > 1 ? "code-only" : "none";
|
|
314
|
+
}
|
|
315
|
+
function supportsMaxItems(attribute) {
|
|
316
|
+
if (attribute.type === "multiselect") return true;
|
|
317
|
+
if (attribute.type === "relation") return attribute.cardinality === "many";
|
|
318
|
+
if (attribute.type === "file") return attribute.multiple === true;
|
|
319
|
+
return false;
|
|
320
|
+
}
|
|
321
|
+
function supportsQualifiedProperties(type) {
|
|
322
|
+
return type === "relation" || type === "document";
|
|
323
|
+
}
|
|
324
|
+
function isRequiredForbidden(type) {
|
|
325
|
+
return type === "checkbox" || type === "formula" || type === "rollup";
|
|
326
|
+
}
|
|
327
|
+
function getDefaultValueCapability(type) {
|
|
328
|
+
if (type === "checkbox") return "automatic";
|
|
329
|
+
if (type === "formula" || type === "rollup") return "forbidden";
|
|
330
|
+
if (STATIC_DEFAULT_TYPES.has(type)) return "static";
|
|
331
|
+
return "forbidden";
|
|
332
|
+
}
|
|
333
|
+
function isDefaultColumnType(type) {
|
|
334
|
+
return isTableCellType(type);
|
|
335
|
+
}
|
|
336
|
+
function isTableCellType(type) {
|
|
337
|
+
return type !== "richtext" && type !== "file" && type !== "document";
|
|
338
|
+
}
|
|
339
|
+
function isFormFieldType(type) {
|
|
340
|
+
return type !== "richtext";
|
|
341
|
+
}
|
|
342
|
+
function isPopoverEditorType(type) {
|
|
343
|
+
return type === "select" || type === "status" || type === "multiselect" || type === "relation" || type === "user";
|
|
344
|
+
}
|
|
345
|
+
function isGroupableType(type) {
|
|
346
|
+
return type === "status" || type === "currency";
|
|
347
|
+
}
|
|
348
|
+
function isContentReadableType(type) {
|
|
349
|
+
return type === "richtext" || type === "document";
|
|
350
|
+
}
|
|
351
|
+
function hasFilterOperatorEntry(type) {
|
|
352
|
+
return Object.prototype.hasOwnProperty.call(ATTRIBUTE_FILTER_OPERATORS, type);
|
|
353
|
+
}
|
|
354
|
+
|
|
46
355
|
// src/types/attributes.ts
|
|
47
356
|
var RELATION_TARGET_ANY = "*";
|
|
48
357
|
function isUniversalRelation(attr) {
|
|
@@ -54,17 +363,8 @@ function isBilateralRelation(attr) {
|
|
|
54
363
|
function inferInverseCardinality(cardinality) {
|
|
55
364
|
return cardinality === "one" ? "many" : "many";
|
|
56
365
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
"file",
|
|
60
|
-
"document",
|
|
61
|
-
"location",
|
|
62
|
-
"user",
|
|
63
|
-
"multiselect",
|
|
64
|
-
"relation"
|
|
65
|
-
]);
|
|
66
|
-
function isAttributeSortable(attr) {
|
|
67
|
-
return !NON_SORTABLE_TYPES.has(attr.type);
|
|
366
|
+
function isAttributeSortable2(attr) {
|
|
367
|
+
return isAttributeSortable(attr);
|
|
68
368
|
}
|
|
69
369
|
|
|
70
370
|
// src/types/document-layout.ts
|
|
@@ -86,95 +386,7 @@ function getErrorMessage(error) {
|
|
|
86
386
|
}
|
|
87
387
|
|
|
88
388
|
// src/types/filters.ts
|
|
89
|
-
var OPERATORS_BY_TYPE =
|
|
90
|
-
text: [
|
|
91
|
-
"is",
|
|
92
|
-
"is_not",
|
|
93
|
-
"contains",
|
|
94
|
-
"not_contains",
|
|
95
|
-
"starts_with",
|
|
96
|
-
"ends_with",
|
|
97
|
-
"is_empty",
|
|
98
|
-
"is_not_empty"
|
|
99
|
-
],
|
|
100
|
-
textarea: [
|
|
101
|
-
"is",
|
|
102
|
-
"is_not",
|
|
103
|
-
"contains",
|
|
104
|
-
"not_contains",
|
|
105
|
-
"starts_with",
|
|
106
|
-
"ends_with",
|
|
107
|
-
"is_empty",
|
|
108
|
-
"is_not_empty"
|
|
109
|
-
],
|
|
110
|
-
richtext: ["is_empty", "is_not_empty"],
|
|
111
|
-
number: ["eq", "neq", "lt", "gt", "lte", "gte", "is_empty", "is_not_empty"],
|
|
112
|
-
checkbox: ["is_checked", "is_not_checked"],
|
|
113
|
-
date: [
|
|
114
|
-
"is",
|
|
115
|
-
"is_not",
|
|
116
|
-
"before",
|
|
117
|
-
"after",
|
|
118
|
-
"on_or_before",
|
|
119
|
-
"on_or_after",
|
|
120
|
-
"is_within",
|
|
121
|
-
"day_month_eq",
|
|
122
|
-
"is_empty",
|
|
123
|
-
"is_not_empty"
|
|
124
|
-
],
|
|
125
|
-
phone: ["is", "is_not", "contains", "not_contains", "is_empty", "is_not_empty"],
|
|
126
|
-
currency: ["eq", "neq", "lt", "gt", "lte", "gte", "is_empty", "is_not_empty"],
|
|
127
|
-
status: ["is", "is_not", "any_of", "none_of", "is_empty", "is_not_empty"],
|
|
128
|
-
select: ["is", "is_not", "any_of", "none_of", "is_empty", "is_not_empty"],
|
|
129
|
-
multiselect: ["contains", "not_contains", "is_empty", "is_not_empty"],
|
|
130
|
-
location: ["contains", "is_empty", "is_not_empty"],
|
|
131
|
-
// Reference attribute types (user, relation) all route through the edge
|
|
132
|
-
// filter resolver, which normalizes is/is_not/any_of/none_of/contains/
|
|
133
|
-
// not_contains via `getOperatorPolarity` + `normalizeForEdgeRpc`. Keep the
|
|
134
|
-
// operator lists symmetric so the schema validation matches what the runtime
|
|
135
|
-
// actually supports.
|
|
136
|
-
// File and document attributes only expose presence operators — qualified
|
|
137
|
-
// filters (via `.qualifyWith()` on document) keep their own operator set
|
|
138
|
-
// based on the property type.
|
|
139
|
-
file: ["is_empty", "is_not_empty"],
|
|
140
|
-
user: [
|
|
141
|
-
"is",
|
|
142
|
-
"is_not",
|
|
143
|
-
"any_of",
|
|
144
|
-
"none_of",
|
|
145
|
-
"contains",
|
|
146
|
-
"not_contains",
|
|
147
|
-
"is_empty",
|
|
148
|
-
"is_not_empty"
|
|
149
|
-
],
|
|
150
|
-
relation: [
|
|
151
|
-
"is",
|
|
152
|
-
"is_not",
|
|
153
|
-
"any_of",
|
|
154
|
-
"none_of",
|
|
155
|
-
"contains",
|
|
156
|
-
"not_contains",
|
|
157
|
-
"is_empty",
|
|
158
|
-
"is_not_empty"
|
|
159
|
-
],
|
|
160
|
-
rating: ["eq", "neq", "lt", "gt", "lte", "gte", "is_empty", "is_not_empty"],
|
|
161
|
-
// Formula: operators depend on returnType, but we provide a general set
|
|
162
|
-
formula: [
|
|
163
|
-
"is",
|
|
164
|
-
"is_not",
|
|
165
|
-
"eq",
|
|
166
|
-
"neq",
|
|
167
|
-
"lt",
|
|
168
|
-
"gt",
|
|
169
|
-
"lte",
|
|
170
|
-
"gte",
|
|
171
|
-
"contains",
|
|
172
|
-
"not_contains",
|
|
173
|
-
"is_empty",
|
|
174
|
-
"is_not_empty"
|
|
175
|
-
],
|
|
176
|
-
document: ["is_empty", "is_not_empty"]
|
|
177
|
-
};
|
|
389
|
+
var OPERATORS_BY_TYPE = ATTRIBUTE_FILTER_OPERATORS;
|
|
178
390
|
var NO_VALUE_OPERATORS = [
|
|
179
391
|
"is_empty",
|
|
180
392
|
"is_not_empty",
|
|
@@ -184,19 +396,8 @@ var NO_VALUE_OPERATORS = [
|
|
|
184
396
|
function isNoValueOperator(operator) {
|
|
185
397
|
return NO_VALUE_OPERATORS.includes(operator);
|
|
186
398
|
}
|
|
187
|
-
function
|
|
188
|
-
|
|
189
|
-
if (fn === "earliest" || fn === "latest") {
|
|
190
|
-
return OPERATORS_BY_TYPE.date;
|
|
191
|
-
}
|
|
192
|
-
if (fn === "original") {
|
|
193
|
-
const targetType = attr.targetAttributeType;
|
|
194
|
-
if (targetType && Object.prototype.hasOwnProperty.call(OPERATORS_BY_TYPE, targetType)) {
|
|
195
|
-
return OPERATORS_BY_TYPE[targetType];
|
|
196
|
-
}
|
|
197
|
-
return OPERATORS_BY_TYPE.number;
|
|
198
|
-
}
|
|
199
|
-
return OPERATORS_BY_TYPE.number;
|
|
399
|
+
function getRollupFilterOperators2(attr) {
|
|
400
|
+
return getRollupFilterOperators(attr);
|
|
200
401
|
}
|
|
201
402
|
|
|
202
403
|
// src/types/forms/definition.ts
|
|
@@ -314,7 +515,6 @@ var SYSTEM_ATTRIBUTES = {
|
|
|
314
515
|
label: "Created time",
|
|
315
516
|
type: "date",
|
|
316
517
|
required: false,
|
|
317
|
-
disabled: true,
|
|
318
518
|
system: true,
|
|
319
519
|
icon: "clock-circle",
|
|
320
520
|
description: "When this record was created"
|
|
@@ -325,7 +525,6 @@ var SYSTEM_ATTRIBUTES = {
|
|
|
325
525
|
label: "Last edited time",
|
|
326
526
|
type: "date",
|
|
327
527
|
required: false,
|
|
328
|
-
disabled: true,
|
|
329
528
|
system: true,
|
|
330
529
|
icon: "clock-circle",
|
|
331
530
|
description: "When this record was last modified"
|
|
@@ -336,7 +535,6 @@ var SYSTEM_ATTRIBUTES = {
|
|
|
336
535
|
label: "Created by",
|
|
337
536
|
type: "user",
|
|
338
537
|
required: false,
|
|
339
|
-
disabled: true,
|
|
340
538
|
system: true,
|
|
341
539
|
multiple: false,
|
|
342
540
|
icon: "user",
|
|
@@ -348,7 +546,6 @@ var SYSTEM_ATTRIBUTES = {
|
|
|
348
546
|
label: "Last edited by",
|
|
349
547
|
type: "user",
|
|
350
548
|
required: false,
|
|
351
|
-
disabled: true,
|
|
352
549
|
system: true,
|
|
353
550
|
multiple: false,
|
|
354
551
|
icon: "user",
|
|
@@ -365,8 +562,6 @@ var SYSTEM_ATTRIBUTES = {
|
|
|
365
562
|
label: "Attachments",
|
|
366
563
|
type: "document",
|
|
367
564
|
required: false,
|
|
368
|
-
disabled: false,
|
|
369
|
-
// Editable via Documents tab
|
|
370
565
|
system: true,
|
|
371
566
|
hidden: true,
|
|
372
567
|
// Hidden from regular form views
|
|
@@ -462,7 +657,6 @@ function validateSlotAgainstConfig(slotName, mimeType, slots) {
|
|
|
462
657
|
// src/lib/qualify-with.ts
|
|
463
658
|
var ALLOWED_PROPERTY_TYPES = Object.freeze([
|
|
464
659
|
"text",
|
|
465
|
-
"textarea",
|
|
466
660
|
"number",
|
|
467
661
|
"checkbox",
|
|
468
662
|
"date",
|
|
@@ -471,7 +665,6 @@ var ALLOWED_PROPERTY_TYPES = Object.freeze([
|
|
|
471
665
|
"status",
|
|
472
666
|
"select",
|
|
473
667
|
"multiselect",
|
|
474
|
-
"rating",
|
|
475
668
|
"location"
|
|
476
669
|
]);
|
|
477
670
|
function buildPropertySchema(builders) {
|
|
@@ -511,6 +704,9 @@ function formatCheckbox(value) {
|
|
|
511
704
|
}
|
|
512
705
|
function formatNumber(value, attribute) {
|
|
513
706
|
if (typeof value !== "number") return String(value);
|
|
707
|
+
if (attribute.renderAs === "rating") {
|
|
708
|
+
return `${value}/${attribute.max ?? 5}`;
|
|
709
|
+
}
|
|
514
710
|
const decimals = attribute.decimals;
|
|
515
711
|
if (attribute.unit === "integer") {
|
|
516
712
|
return value.toLocaleString(void 0, {
|
|
@@ -558,7 +754,7 @@ function formatPhone(value) {
|
|
|
558
754
|
if (!("phoneNumber" in phone2)) return String(value);
|
|
559
755
|
if (!phone2.phoneNumber) return "";
|
|
560
756
|
if (!phone2.countryCode) return phone2.phoneNumber;
|
|
561
|
-
return
|
|
757
|
+
return chunkEBGRZUIH_js.formatPhoneForDisplay(phone2);
|
|
562
758
|
}
|
|
563
759
|
function formatLocation(value, attribute) {
|
|
564
760
|
if (typeof value !== "object" || value === null) return String(value);
|
|
@@ -612,18 +808,12 @@ function formatMultiselect(value, attribute) {
|
|
|
612
808
|
}
|
|
613
809
|
return value.join(", ");
|
|
614
810
|
}
|
|
615
|
-
function formatRating(value, attribute) {
|
|
616
|
-
if (typeof value !== "number") return String(value);
|
|
617
|
-
const max = attribute.max ?? 5;
|
|
618
|
-
return `${value}/${max}`;
|
|
619
|
-
}
|
|
620
811
|
function formatAttributeValue(value, attribute) {
|
|
621
812
|
if (value === null || value === void 0 || value === "") {
|
|
622
813
|
return EMPTY_VALUE_PLACEHOLDER;
|
|
623
814
|
}
|
|
624
815
|
switch (attribute.type) {
|
|
625
816
|
case "text":
|
|
626
|
-
case "textarea":
|
|
627
817
|
return formatText(value);
|
|
628
818
|
case "checkbox":
|
|
629
819
|
return formatCheckbox(value);
|
|
@@ -642,8 +832,6 @@ function formatAttributeValue(value, attribute) {
|
|
|
642
832
|
return formatSelect(value, attribute);
|
|
643
833
|
case "multiselect":
|
|
644
834
|
return formatMultiselect(value, attribute);
|
|
645
|
-
case "rating":
|
|
646
|
-
return formatRating(value, attribute);
|
|
647
835
|
// Unsupported types - return value as-is or placeholder
|
|
648
836
|
case "file":
|
|
649
837
|
case "user":
|
|
@@ -987,41 +1175,25 @@ Icon must be one of the predefined icons from @stndrds/constants.`
|
|
|
987
1175
|
);
|
|
988
1176
|
}
|
|
989
1177
|
}
|
|
990
|
-
function validateOptions(
|
|
991
|
-
if (options.length === 0) return;
|
|
992
|
-
const ids = /* @__PURE__ */ new Set();
|
|
1178
|
+
function validateOptions(attributeName, options) {
|
|
993
1179
|
const values = /* @__PURE__ */ new Set();
|
|
994
1180
|
for (const option of options) {
|
|
995
|
-
if (!option.id?.trim()) {
|
|
996
|
-
throw new Error(
|
|
997
|
-
`[AttributeBuilder] Option in "${attributeName}" has an empty or missing id.`
|
|
998
|
-
);
|
|
999
|
-
}
|
|
1000
1181
|
if (!option.value?.trim()) {
|
|
1001
1182
|
throw new Error(
|
|
1002
|
-
`[AttributeBuilder] Option
|
|
1183
|
+
`[AttributeBuilder] Option in "${attributeName}" has an empty or missing value.`
|
|
1003
1184
|
);
|
|
1004
1185
|
}
|
|
1005
1186
|
if (!option.label?.trim()) {
|
|
1006
1187
|
throw new Error(
|
|
1007
|
-
`[AttributeBuilder] Option "${option.
|
|
1188
|
+
`[AttributeBuilder] Option "${option.value}" in "${attributeName}" has an empty or missing label.`
|
|
1008
1189
|
);
|
|
1009
1190
|
}
|
|
1010
|
-
if (ids.has(option.id)) {
|
|
1011
|
-
throw new Error(
|
|
1012
|
-
`[AttributeBuilder] Duplicate option id "${option.id}" in "${attributeName}".`
|
|
1013
|
-
);
|
|
1014
|
-
}
|
|
1015
|
-
ids.add(option.id);
|
|
1016
1191
|
if (values.has(option.value)) {
|
|
1017
1192
|
throw new Error(
|
|
1018
1193
|
`[AttributeBuilder] Duplicate option value "${option.value}" in "${attributeName}".`
|
|
1019
1194
|
);
|
|
1020
1195
|
}
|
|
1021
1196
|
values.add(option.value);
|
|
1022
|
-
if (option.icon) {
|
|
1023
|
-
validateIconName(option.icon, `option "${option.id}" of "${attributeName}"`);
|
|
1024
|
-
}
|
|
1025
1197
|
}
|
|
1026
1198
|
for (const option of options) {
|
|
1027
1199
|
if (option.inverse === void 0) continue;
|
|
@@ -1053,7 +1225,7 @@ var BaseAttributeBuilder = class {
|
|
|
1053
1225
|
* @see https://standardschema.dev/
|
|
1054
1226
|
*/
|
|
1055
1227
|
get "~standard"() {
|
|
1056
|
-
const zodSchema =
|
|
1228
|
+
const zodSchema = chunkMZOEL7FN_js.createAttributeValidator(this.build());
|
|
1057
1229
|
return createStandardSchemaProps(zodSchema);
|
|
1058
1230
|
}
|
|
1059
1231
|
constructor(type, name, label) {
|
|
@@ -1125,10 +1297,6 @@ var BaseAttributeBuilder = class {
|
|
|
1125
1297
|
this.attr.system = false;
|
|
1126
1298
|
return this;
|
|
1127
1299
|
}
|
|
1128
|
-
disabled() {
|
|
1129
|
-
this.attr.disabled = true;
|
|
1130
|
-
return this;
|
|
1131
|
-
}
|
|
1132
1300
|
hidden() {
|
|
1133
1301
|
this.attr.hidden = true;
|
|
1134
1302
|
return this;
|
|
@@ -1160,32 +1328,6 @@ var BaseAttributeBuilder = class {
|
|
|
1160
1328
|
this.attr.metadata = value;
|
|
1161
1329
|
return this;
|
|
1162
1330
|
}
|
|
1163
|
-
/**
|
|
1164
|
-
* Add a feature gate to conditionally show/hide/disable this attribute.
|
|
1165
|
-
*
|
|
1166
|
-
* @param flagName - Name of the feature flag to check
|
|
1167
|
-
* @param options - Optional configuration for expected value and fallback behavior
|
|
1168
|
-
*
|
|
1169
|
-
* @example Hide attribute when flag is disabled
|
|
1170
|
-
* ```typescript
|
|
1171
|
-
* text({ name: "aiSummary", label: "AI Summary" })
|
|
1172
|
-
* .featureGate("ai-features")
|
|
1173
|
-
* ```
|
|
1174
|
-
*
|
|
1175
|
-
* @example Disable attribute when tier is not enterprise
|
|
1176
|
-
* ```typescript
|
|
1177
|
-
* text({ name: "advancedField", label: "Advanced Field" })
|
|
1178
|
-
* .featureGate("tier", { expectedValue: "enterprise", fallback: "disable" })
|
|
1179
|
-
* ```
|
|
1180
|
-
*/
|
|
1181
|
-
featureGate(flagName, options) {
|
|
1182
|
-
this.attr.featureGate = {
|
|
1183
|
-
flag: flagName,
|
|
1184
|
-
expectedValue: options?.expectedValue,
|
|
1185
|
-
fallback: options?.fallback
|
|
1186
|
-
};
|
|
1187
|
-
return this;
|
|
1188
|
-
}
|
|
1189
1331
|
build() {
|
|
1190
1332
|
return this.attr;
|
|
1191
1333
|
}
|
|
@@ -1206,6 +1348,10 @@ var TextAttributeBuilder = class extends BaseAttributeBuilder {
|
|
|
1206
1348
|
this.attr.pattern = value;
|
|
1207
1349
|
return this;
|
|
1208
1350
|
}
|
|
1351
|
+
multiline(value = true) {
|
|
1352
|
+
this.attr.multiline = value;
|
|
1353
|
+
return this;
|
|
1354
|
+
}
|
|
1209
1355
|
required() {
|
|
1210
1356
|
this.setRequired(true);
|
|
1211
1357
|
return this.asRequiredState();
|
|
@@ -1239,42 +1385,10 @@ var TextAttributeBuilder = class extends BaseAttributeBuilder {
|
|
|
1239
1385
|
function text(config) {
|
|
1240
1386
|
return new TextAttributeBuilder(config.name, config.label);
|
|
1241
1387
|
}
|
|
1242
|
-
var TextAreaAttributeBuilder = class extends BaseAttributeBuilder {
|
|
1243
|
-
constructor(name, label) {
|
|
1244
|
-
super("textarea", name, label);
|
|
1245
|
-
}
|
|
1246
|
-
required() {
|
|
1247
|
-
this.setRequired(true);
|
|
1248
|
-
return this.asRequiredState();
|
|
1249
|
-
}
|
|
1250
|
-
optional() {
|
|
1251
|
-
this.setRequired(false);
|
|
1252
|
-
return this.asRequiredState();
|
|
1253
|
-
}
|
|
1254
|
-
};
|
|
1255
|
-
function textarea(config) {
|
|
1256
|
-
return new TextAreaAttributeBuilder(config.name, config.label);
|
|
1257
|
-
}
|
|
1258
1388
|
var RichtextAttributeBuilder = class extends BaseAttributeBuilder {
|
|
1259
1389
|
constructor(name, label) {
|
|
1260
1390
|
super("richtext", name, label);
|
|
1261
1391
|
}
|
|
1262
|
-
/**
|
|
1263
|
-
* Set enabled features for the richtext editor
|
|
1264
|
-
* If not called, all features are enabled by default
|
|
1265
|
-
*
|
|
1266
|
-
* @param list - Array of features to enable
|
|
1267
|
-
*
|
|
1268
|
-
* @example
|
|
1269
|
-
* ```typescript
|
|
1270
|
-
* richtext({ name: "content", label: "Content" })
|
|
1271
|
-
* .features(["headings", "bold", "italic", "lists", "links"])
|
|
1272
|
-
* ```
|
|
1273
|
-
*/
|
|
1274
|
-
features(list) {
|
|
1275
|
-
this.attr.features = list;
|
|
1276
|
-
return this;
|
|
1277
|
-
}
|
|
1278
1392
|
required() {
|
|
1279
1393
|
this.setRequired(true);
|
|
1280
1394
|
return this.asRequiredState();
|
|
@@ -1313,6 +1427,10 @@ var NumberAttributeBuilder = class extends BaseAttributeBuilder {
|
|
|
1313
1427
|
this.attr.unit = "percentage";
|
|
1314
1428
|
return this;
|
|
1315
1429
|
}
|
|
1430
|
+
renderAs(value) {
|
|
1431
|
+
this.attr.renderAs = value;
|
|
1432
|
+
return this;
|
|
1433
|
+
}
|
|
1316
1434
|
required() {
|
|
1317
1435
|
this.setRequired(true);
|
|
1318
1436
|
return this.asRequiredState();
|
|
@@ -1426,7 +1544,7 @@ var StatusAttributeBuilder = class extends BaseAttributeBuilder {
|
|
|
1426
1544
|
*/
|
|
1427
1545
|
options(value) {
|
|
1428
1546
|
const mutableOptions = asMutableArray(value);
|
|
1429
|
-
validateOptions(
|
|
1547
|
+
validateOptions(this.attr.name ?? "unknown", mutableOptions);
|
|
1430
1548
|
this.attr.options = mutableOptions;
|
|
1431
1549
|
return this.asPhantomState();
|
|
1432
1550
|
}
|
|
@@ -1435,7 +1553,7 @@ var StatusAttributeBuilder = class extends BaseAttributeBuilder {
|
|
|
1435
1553
|
this.attr.options = [];
|
|
1436
1554
|
}
|
|
1437
1555
|
const allOptions = [...this.attr.options, config];
|
|
1438
|
-
validateOptions(
|
|
1556
|
+
validateOptions(this.attr.name ?? "unknown", allOptions);
|
|
1439
1557
|
this.attr.options.push(config);
|
|
1440
1558
|
return this;
|
|
1441
1559
|
}
|
|
@@ -1460,7 +1578,7 @@ var SelectAttributeBuilder = class extends BaseAttributeBuilder {
|
|
|
1460
1578
|
*/
|
|
1461
1579
|
options(value) {
|
|
1462
1580
|
const mutableOptions = asMutableArray(value);
|
|
1463
|
-
validateOptions(
|
|
1581
|
+
validateOptions(this.attr.name ?? "unknown", mutableOptions);
|
|
1464
1582
|
this.attr.options = mutableOptions;
|
|
1465
1583
|
return this.asPhantomState();
|
|
1466
1584
|
}
|
|
@@ -1469,7 +1587,7 @@ var SelectAttributeBuilder = class extends BaseAttributeBuilder {
|
|
|
1469
1587
|
this.attr.options = [];
|
|
1470
1588
|
}
|
|
1471
1589
|
const allOptions = [...this.attr.options, config];
|
|
1472
|
-
validateOptions(
|
|
1590
|
+
validateOptions(this.attr.name ?? "unknown", allOptions);
|
|
1473
1591
|
this.attr.options.push(config);
|
|
1474
1592
|
return this;
|
|
1475
1593
|
}
|
|
@@ -1494,7 +1612,7 @@ var MultiselectAttributeBuilder = class extends BaseAttributeBuilder {
|
|
|
1494
1612
|
*/
|
|
1495
1613
|
options(value) {
|
|
1496
1614
|
const mutableOptions = asMutableArray(value);
|
|
1497
|
-
validateOptions(
|
|
1615
|
+
validateOptions(this.attr.name ?? "unknown", mutableOptions);
|
|
1498
1616
|
this.attr.options = mutableOptions;
|
|
1499
1617
|
return this.asPhantomState();
|
|
1500
1618
|
}
|
|
@@ -1503,7 +1621,7 @@ var MultiselectAttributeBuilder = class extends BaseAttributeBuilder {
|
|
|
1503
1621
|
this.attr.options = [];
|
|
1504
1622
|
}
|
|
1505
1623
|
const allOptions = [...this.attr.options, config];
|
|
1506
|
-
validateOptions(
|
|
1624
|
+
validateOptions(this.attr.name ?? "unknown", allOptions);
|
|
1507
1625
|
this.attr.options.push(config);
|
|
1508
1626
|
return this;
|
|
1509
1627
|
}
|
|
@@ -1527,14 +1645,6 @@ var LocationAttributeBuilder = class extends BaseAttributeBuilder {
|
|
|
1527
1645
|
this.attr.granularity = value;
|
|
1528
1646
|
return this;
|
|
1529
1647
|
}
|
|
1530
|
-
enableAutocomplete() {
|
|
1531
|
-
this.attr.enableAutocomplete = true;
|
|
1532
|
-
return this;
|
|
1533
|
-
}
|
|
1534
|
-
enableMap() {
|
|
1535
|
-
this.attr.enableMap = true;
|
|
1536
|
-
return this;
|
|
1537
|
-
}
|
|
1538
1648
|
defaultCountry(value) {
|
|
1539
1649
|
this.attr.defaultCountry = value;
|
|
1540
1650
|
return this;
|
|
@@ -1543,10 +1653,6 @@ var LocationAttributeBuilder = class extends BaseAttributeBuilder {
|
|
|
1543
1653
|
this.attr.allowedCountries = value;
|
|
1544
1654
|
return this;
|
|
1545
1655
|
}
|
|
1546
|
-
displayFormat(value) {
|
|
1547
|
-
this.attr.displayFormat = value;
|
|
1548
|
-
return this;
|
|
1549
|
-
}
|
|
1550
1656
|
required() {
|
|
1551
1657
|
this.setRequired(true);
|
|
1552
1658
|
return this.asRequiredState();
|
|
@@ -1597,9 +1703,14 @@ function file(config) {
|
|
|
1597
1703
|
var UserAttributeBuilder = class extends BaseAttributeBuilder {
|
|
1598
1704
|
constructor(name, label) {
|
|
1599
1705
|
super("user", name, label);
|
|
1706
|
+
this.attr.types = ["user", "agent"];
|
|
1600
1707
|
}
|
|
1601
|
-
|
|
1602
|
-
|
|
1708
|
+
types(types) {
|
|
1709
|
+
const unique = [...new Set(types)];
|
|
1710
|
+
if (unique.length === 0) {
|
|
1711
|
+
throw new Error("[AttributeBuilder] user.types must contain at least one type.");
|
|
1712
|
+
}
|
|
1713
|
+
this.attr.types = unique;
|
|
1603
1714
|
return this;
|
|
1604
1715
|
}
|
|
1605
1716
|
/**
|
|
@@ -1710,18 +1821,11 @@ var SingleRelationAttributeBuilder = class extends BaseRelationAttributeBuilder
|
|
|
1710
1821
|
);
|
|
1711
1822
|
if (this.attr.system === false) multiBuilder.runtime();
|
|
1712
1823
|
if (this.attr.hidden) multiBuilder.hidden();
|
|
1713
|
-
if (this.attr.disabled) multiBuilder.disabled();
|
|
1714
1824
|
if (this.attr.placeholder) multiBuilder.placeholder(this.attr.placeholder);
|
|
1715
1825
|
if (this.attr.description) multiBuilder.description(this.attr.description);
|
|
1716
1826
|
if (this.attr.icon) multiBuilder.icon(this.attr.icon);
|
|
1717
1827
|
if (this.attr.order !== void 0) multiBuilder.order(this.attr.order);
|
|
1718
1828
|
if (this.attr.metadata) multiBuilder.metadata(this.attr.metadata);
|
|
1719
|
-
if (this.attr.featureGate) {
|
|
1720
|
-
multiBuilder.featureGate(this.attr.featureGate.flag, {
|
|
1721
|
-
expectedValue: this.attr.featureGate.expectedValue,
|
|
1722
|
-
fallback: this.attr.featureGate.fallback
|
|
1723
|
-
});
|
|
1724
|
-
}
|
|
1725
1829
|
return multiBuilder;
|
|
1726
1830
|
}
|
|
1727
1831
|
required() {
|
|
@@ -1743,13 +1847,6 @@ var MultiRelationAttributeBuilder = class extends BaseRelationAttributeBuilder {
|
|
|
1743
1847
|
this.setRequired(true);
|
|
1744
1848
|
}
|
|
1745
1849
|
}
|
|
1746
|
-
/**
|
|
1747
|
-
* Set minimum number of relations required
|
|
1748
|
-
*/
|
|
1749
|
-
minItems(count) {
|
|
1750
|
-
this.attr.minItems = count;
|
|
1751
|
-
return this;
|
|
1752
|
-
}
|
|
1753
1850
|
/**
|
|
1754
1851
|
* Set maximum number of relations allowed
|
|
1755
1852
|
*/
|
|
@@ -1769,30 +1866,6 @@ var MultiRelationAttributeBuilder = class extends BaseRelationAttributeBuilder {
|
|
|
1769
1866
|
function relation(config) {
|
|
1770
1867
|
return new SingleRelationAttributeBuilder(config.name, config.label);
|
|
1771
1868
|
}
|
|
1772
|
-
var RatingAttributeBuilder = class extends BaseAttributeBuilder {
|
|
1773
|
-
constructor(name, label) {
|
|
1774
|
-
super("rating", name, label);
|
|
1775
|
-
}
|
|
1776
|
-
max(value) {
|
|
1777
|
-
this.attr.max = value;
|
|
1778
|
-
return this;
|
|
1779
|
-
}
|
|
1780
|
-
iconType(value) {
|
|
1781
|
-
this.attr.iconType = value;
|
|
1782
|
-
return this;
|
|
1783
|
-
}
|
|
1784
|
-
required() {
|
|
1785
|
-
this.setRequired(true);
|
|
1786
|
-
return this.asRequiredState();
|
|
1787
|
-
}
|
|
1788
|
-
optional() {
|
|
1789
|
-
this.setRequired(false);
|
|
1790
|
-
return this.asRequiredState();
|
|
1791
|
-
}
|
|
1792
|
-
};
|
|
1793
|
-
function rating(config) {
|
|
1794
|
-
return new RatingAttributeBuilder(config.name, config.label);
|
|
1795
|
-
}
|
|
1796
1869
|
var FormulaAttributeBuilder = class extends BaseAttributeBuilder {
|
|
1797
1870
|
constructor(name, label) {
|
|
1798
1871
|
super("formula", name, label);
|
|
@@ -1916,20 +1989,6 @@ var DocumentAttributeBuilder = class extends BaseAttributeBuilder {
|
|
|
1916
1989
|
constructor(name, label) {
|
|
1917
1990
|
super("document", name, label);
|
|
1918
1991
|
}
|
|
1919
|
-
/**
|
|
1920
|
-
* Maximum number of documents.
|
|
1921
|
-
*/
|
|
1922
|
-
maxDocuments(count) {
|
|
1923
|
-
this.attr.maxDocuments = count;
|
|
1924
|
-
return this;
|
|
1925
|
-
}
|
|
1926
|
-
/**
|
|
1927
|
-
* Automatically trigger agent-compatible processing on upload.
|
|
1928
|
-
*/
|
|
1929
|
-
autoProcess() {
|
|
1930
|
-
this.attr.autoProcess = true;
|
|
1931
|
-
return this;
|
|
1932
|
-
}
|
|
1933
1992
|
/**
|
|
1934
1993
|
* Define named upload slots for this document attribute.
|
|
1935
1994
|
* An empty array falls back to a single default slot.
|
|
@@ -1938,38 +1997,6 @@ var DocumentAttributeBuilder = class extends BaseAttributeBuilder {
|
|
|
1938
1997
|
this.attr.slots = configs.length === 0 ? [DEFAULT_DOCUMENT_SLOT] : configs;
|
|
1939
1998
|
return this;
|
|
1940
1999
|
}
|
|
1941
|
-
/**
|
|
1942
|
-
* Add a child attribute that qualifies the record→document edge.
|
|
1943
|
-
*
|
|
1944
|
-
* Equivalent to `.qualifyWith(builder)` for a single attribute, with the
|
|
1945
|
-
* difference that the accepted type is broader (`Attribute`) and the
|
|
1946
|
-
* accumulation is mutating rather than type-tracking. When the produced
|
|
1947
|
-
* attribute is one of the `ALLOWED_PROPERTY_TYPES`, it is also pushed
|
|
1948
|
-
* onto `attribute.properties.definitions`, so hydration and validation
|
|
1949
|
-
* treat the document as qualified — same effect as `.qualifyWith()`.
|
|
1950
|
-
*
|
|
1951
|
-
* Both `attribute.attributes` and `attribute.properties` are populated to
|
|
1952
|
-
* keep backwards compatibility with consumers that read either field.
|
|
1953
|
-
*
|
|
1954
|
-
* @example
|
|
1955
|
-
* document({ name: "invoices" })
|
|
1956
|
-
* .attribute(date({ name: "invoiceDate", label: "Date" }).required())
|
|
1957
|
-
* .attribute(select({ name: "status", label: "Statut" }).options([...]))
|
|
1958
|
-
*/
|
|
1959
|
-
attribute(builder) {
|
|
1960
|
-
const built = builder.build();
|
|
1961
|
-
if (!this.attr.attributes) this.attr.attributes = [];
|
|
1962
|
-
this.attr.attributes = [...this.attr.attributes, built];
|
|
1963
|
-
if (ALLOWED_PROPERTY_TYPES.includes(built.type)) {
|
|
1964
|
-
const props = this.attr.properties ?? {
|
|
1965
|
-
definitions: []
|
|
1966
|
-
};
|
|
1967
|
-
this.attr.properties = {
|
|
1968
|
-
definitions: [...props.definitions, built]
|
|
1969
|
-
};
|
|
1970
|
-
}
|
|
1971
|
-
return this;
|
|
1972
|
-
}
|
|
1973
2000
|
/**
|
|
1974
2001
|
* Add properties to qualify each document using existing attribute builders.
|
|
1975
2002
|
* Returns a new builder type that carries the inferred props shape for $infer.
|
|
@@ -2305,11 +2332,9 @@ function form(name, label) {
|
|
|
2305
2332
|
function resolveBuiltInTransform(targetType) {
|
|
2306
2333
|
const map = {
|
|
2307
2334
|
text: "toString",
|
|
2308
|
-
textarea: "toString",
|
|
2309
2335
|
richtext: "toString",
|
|
2310
2336
|
number: "toNumber",
|
|
2311
2337
|
currency: "toNumber",
|
|
2312
|
-
rating: "toNumber",
|
|
2313
2338
|
date: "toDate",
|
|
2314
2339
|
checkbox: "toBoolean"
|
|
2315
2340
|
};
|
|
@@ -2414,6 +2439,8 @@ var MigrationBuilder = class {
|
|
|
2414
2439
|
var ObjectBuilder = class {
|
|
2415
2440
|
constructor(config) {
|
|
2416
2441
|
this._migrations = [];
|
|
2442
|
+
this._sealed = false;
|
|
2443
|
+
this._toleratedAttributes = [];
|
|
2417
2444
|
this.validateName(config.name);
|
|
2418
2445
|
this.obj = {
|
|
2419
2446
|
name: config.name,
|
|
@@ -2431,7 +2458,7 @@ var ObjectBuilder = class {
|
|
|
2431
2458
|
* @see https://standardschema.dev/
|
|
2432
2459
|
*/
|
|
2433
2460
|
get "~standard"() {
|
|
2434
|
-
const zodSchema =
|
|
2461
|
+
const zodSchema = chunkMZOEL7FN_js.createObjectValidator(this.build());
|
|
2435
2462
|
return createStandardSchemaProps(zodSchema);
|
|
2436
2463
|
}
|
|
2437
2464
|
/**
|
|
@@ -2474,6 +2501,43 @@ var ObjectBuilder = class {
|
|
|
2474
2501
|
this.obj.system = false;
|
|
2475
2502
|
return this;
|
|
2476
2503
|
}
|
|
2504
|
+
/**
|
|
2505
|
+
* Marks this object as sealed — no custom (user-created) attributes allowed.
|
|
2506
|
+
*
|
|
2507
|
+
* When sealed, `standards diff` reports an error if any custom attribute
|
|
2508
|
+
* exists in the DB that is not listed in `.tolerate()`.
|
|
2509
|
+
*
|
|
2510
|
+
* @example
|
|
2511
|
+
* ```typescript
|
|
2512
|
+
* object({ name: "invoice", label: "Invoice" })
|
|
2513
|
+
* .sealed()
|
|
2514
|
+
* .attribute(number({ name: "amount", label: "Amount" }))
|
|
2515
|
+
* ```
|
|
2516
|
+
*/
|
|
2517
|
+
sealed() {
|
|
2518
|
+
this._sealed = true;
|
|
2519
|
+
return this;
|
|
2520
|
+
}
|
|
2521
|
+
/**
|
|
2522
|
+
* Explicitly acknowledges custom attribute names on this object.
|
|
2523
|
+
*
|
|
2524
|
+
* On a sealed object: listed names will NOT trigger a CI failure in `standards diff`.
|
|
2525
|
+
* On an extensible object: purely documentary.
|
|
2526
|
+
*
|
|
2527
|
+
* @param names - Array of custom attribute names to tolerate
|
|
2528
|
+
*
|
|
2529
|
+
* @example
|
|
2530
|
+
* ```typescript
|
|
2531
|
+
* object({ name: "invoice", label: "Invoice" })
|
|
2532
|
+
* .sealed()
|
|
2533
|
+
* .tolerate(["legacy_ref", "old_status"])
|
|
2534
|
+
* .attribute(number({ name: "amount", label: "Amount" }))
|
|
2535
|
+
* ```
|
|
2536
|
+
*/
|
|
2537
|
+
tolerate(names) {
|
|
2538
|
+
this._toleratedAttributes = [...this._toleratedAttributes, ...names];
|
|
2539
|
+
return this;
|
|
2540
|
+
}
|
|
2477
2541
|
/**
|
|
2478
2542
|
* Add an attribute to the object with type accumulation
|
|
2479
2543
|
*
|
|
@@ -2598,17 +2662,17 @@ var ObjectBuilder = class {
|
|
|
2598
2662
|
throw new Error("[ObjectBuilder] Missing required field: label");
|
|
2599
2663
|
}
|
|
2600
2664
|
if (!this._labelExpression) {
|
|
2601
|
-
throw new
|
|
2665
|
+
throw new chunkMZOEL7FN_js.SchemaError(
|
|
2602
2666
|
`Object "${this.obj.name}" must have a labelExpression. The labelExpression defines how records are displayed in lists and relations (e.g. "{{ name }}").`,
|
|
2603
|
-
|
|
2667
|
+
chunkMZOEL7FN_js.SchemaErrorCode.VALIDATION_FAILED
|
|
2604
2668
|
);
|
|
2605
2669
|
}
|
|
2606
2670
|
const labelResult = labelExpressionSchema.safeParse(this._labelExpression);
|
|
2607
2671
|
if (!labelResult.success) {
|
|
2608
2672
|
const errorMsg = labelResult.error.issues[0]?.message ?? "Invalid labelExpression";
|
|
2609
|
-
throw new
|
|
2673
|
+
throw new chunkMZOEL7FN_js.SchemaError(
|
|
2610
2674
|
`Invalid labelExpression for "${this.obj.name}": ${errorMsg}`,
|
|
2611
|
-
|
|
2675
|
+
chunkMZOEL7FN_js.SchemaErrorCode.VALIDATION_FAILED
|
|
2612
2676
|
);
|
|
2613
2677
|
}
|
|
2614
2678
|
const sortedVersions = [...this._migrations].sort((a, b) => a.version - b.version);
|
|
@@ -2626,7 +2690,9 @@ var ObjectBuilder = class {
|
|
|
2626
2690
|
pluralLabel: this._pluralLabel,
|
|
2627
2691
|
labelExpression: this._labelExpression,
|
|
2628
2692
|
schema_version,
|
|
2629
|
-
migrations: sortedVersions
|
|
2693
|
+
migrations: sortedVersions,
|
|
2694
|
+
sealed: this._sealed,
|
|
2695
|
+
toleratedAttributes: this._toleratedAttributes
|
|
2630
2696
|
};
|
|
2631
2697
|
}
|
|
2632
2698
|
/**
|
|
@@ -2645,7 +2711,7 @@ var ObjectBuilder = class {
|
|
|
2645
2711
|
objectNameSchema.parse(name);
|
|
2646
2712
|
} catch (error) {
|
|
2647
2713
|
if (error instanceof z3__default.default.ZodError) {
|
|
2648
|
-
throw new
|
|
2714
|
+
throw new chunkMZOEL7FN_js.SchemaError(error.issues[0].message, chunkMZOEL7FN_js.SchemaErrorCode.INVALID_OBJECT_NAME);
|
|
2649
2715
|
}
|
|
2650
2716
|
throw error;
|
|
2651
2717
|
}
|
|
@@ -4515,9 +4581,9 @@ Available views: ${availableNames}`
|
|
|
4515
4581
|
var viewRegistry = new ViewRegistry();
|
|
4516
4582
|
|
|
4517
4583
|
// src/views/auto-generator.ts
|
|
4518
|
-
function
|
|
4584
|
+
function getRuntimeAttributes(attributes, excludeNames = []) {
|
|
4519
4585
|
return attributes.filter(
|
|
4520
|
-
(attr) => !(attr.
|
|
4586
|
+
(attr) => !(!getAttributeCapabilities(attr).lifecycle.visibleInRuntime || attr.system || excludeNames.includes(attr.name))
|
|
4521
4587
|
);
|
|
4522
4588
|
}
|
|
4523
4589
|
function sortAttributes(attributes) {
|
|
@@ -4528,9 +4594,9 @@ function sortAttributes(attributes) {
|
|
|
4528
4594
|
return a.name.localeCompare(b.name);
|
|
4529
4595
|
});
|
|
4530
4596
|
}
|
|
4531
|
-
function getDefaultFieldSpan(
|
|
4532
|
-
|
|
4533
|
-
|
|
4597
|
+
function getDefaultFieldSpan(attr) {
|
|
4598
|
+
if (attr.type === "text" && attr.multiline) return 12;
|
|
4599
|
+
switch (attr.type) {
|
|
4534
4600
|
case "richtext":
|
|
4535
4601
|
case "location":
|
|
4536
4602
|
return 12;
|
|
@@ -4543,7 +4609,7 @@ function getDefaultFieldSpan(type) {
|
|
|
4543
4609
|
function attributeToField(attr) {
|
|
4544
4610
|
return {
|
|
4545
4611
|
attribute: attr.name,
|
|
4546
|
-
span: getDefaultFieldSpan(attr
|
|
4612
|
+
span: getDefaultFieldSpan(attr)
|
|
4547
4613
|
};
|
|
4548
4614
|
}
|
|
4549
4615
|
function generateDefaultDetailView(object2, options = {}) {
|
|
@@ -4553,8 +4619,10 @@ function generateDefaultDetailView(object2, options = {}) {
|
|
|
4553
4619
|
excludeAttributes = [],
|
|
4554
4620
|
layout = "page"
|
|
4555
4621
|
} = options;
|
|
4556
|
-
const
|
|
4557
|
-
const sortedAttrs = sortAttributes(
|
|
4622
|
+
const runtimeAttrs = getRuntimeAttributes(object2.attributes, excludeAttributes);
|
|
4623
|
+
const sortedAttrs = sortAttributes(
|
|
4624
|
+
runtimeAttrs.filter((attr) => getAttributeCapabilities(attr).presentation.formField)
|
|
4625
|
+
);
|
|
4558
4626
|
const formTab = {
|
|
4559
4627
|
id: "form",
|
|
4560
4628
|
name: "form",
|
|
@@ -4609,8 +4677,10 @@ function generateDefaultDetailView(object2, options = {}) {
|
|
|
4609
4677
|
}
|
|
4610
4678
|
function generateDefaultListView(object2, options = {}) {
|
|
4611
4679
|
const { excludeAttributes = [], maxListColumns = 5 } = options;
|
|
4612
|
-
const
|
|
4613
|
-
const sortedAttrs = sortAttributes(
|
|
4680
|
+
const runtimeAttrs = getRuntimeAttributes(object2.attributes, excludeAttributes);
|
|
4681
|
+
const sortedAttrs = sortAttributes(
|
|
4682
|
+
runtimeAttrs.filter((attr) => getAttributeCapabilities(attr).presentation.defaultColumn)
|
|
4683
|
+
);
|
|
4614
4684
|
const columns = sortedAttrs.slice(0, maxListColumns).map((attr) => attr.name);
|
|
4615
4685
|
const config = {
|
|
4616
4686
|
tabs: [
|
|
@@ -4652,289 +4722,1709 @@ function mergeDetailTabs(generatedTabs, preservedTabs) {
|
|
|
4652
4722
|
if (preservedTabs.length === 0) {
|
|
4653
4723
|
return generatedTabs;
|
|
4654
4724
|
}
|
|
4655
|
-
const formTab = generatedTabs.find((t) => t.type === "form");
|
|
4656
|
-
const otherTabs = generatedTabs.filter((t) => t.type !== "form");
|
|
4657
|
-
const result = [];
|
|
4658
|
-
if (formTab) {
|
|
4659
|
-
result.push(formTab);
|
|
4725
|
+
const formTab = generatedTabs.find((t) => t.type === "form");
|
|
4726
|
+
const otherTabs = generatedTabs.filter((t) => t.type !== "form");
|
|
4727
|
+
const result = [];
|
|
4728
|
+
if (formTab) {
|
|
4729
|
+
result.push(formTab);
|
|
4730
|
+
}
|
|
4731
|
+
let order = 1;
|
|
4732
|
+
for (const tab of preservedTabs) {
|
|
4733
|
+
result.push({ ...tab, order: order++ });
|
|
4734
|
+
}
|
|
4735
|
+
for (const tab of otherTabs) {
|
|
4736
|
+
result.push({ ...tab, order: order++ });
|
|
4737
|
+
}
|
|
4738
|
+
return result;
|
|
4739
|
+
}
|
|
4740
|
+
function resetDetailViewToDefault(view, object2, options) {
|
|
4741
|
+
const { preserveMetadata = true, ...generateOptions } = options;
|
|
4742
|
+
const defaultView = generateDefaultDetailView(object2, generateOptions);
|
|
4743
|
+
const preservedTabs = getPreservedDetailTabs(view, options);
|
|
4744
|
+
const mergedTabs = mergeDetailTabs(defaultView.config.tabs, preservedTabs);
|
|
4745
|
+
const config = {
|
|
4746
|
+
...defaultView.config,
|
|
4747
|
+
tabs: mergedTabs
|
|
4748
|
+
};
|
|
4749
|
+
const resetView = {
|
|
4750
|
+
...defaultView,
|
|
4751
|
+
config
|
|
4752
|
+
};
|
|
4753
|
+
if (preserveMetadata) {
|
|
4754
|
+
resetView.id = view.id;
|
|
4755
|
+
resetView.name = view.name;
|
|
4756
|
+
resetView.label = view.label;
|
|
4757
|
+
resetView.description = view.description;
|
|
4758
|
+
resetView.icon = view.icon;
|
|
4759
|
+
}
|
|
4760
|
+
return resetView;
|
|
4761
|
+
}
|
|
4762
|
+
function resetListViewToDefault(view, object2, options) {
|
|
4763
|
+
const { preserveMetadata = true, ...generateOptions } = options;
|
|
4764
|
+
const defaultView = generateDefaultListView(object2, generateOptions);
|
|
4765
|
+
const resetView = {
|
|
4766
|
+
...defaultView
|
|
4767
|
+
};
|
|
4768
|
+
if (preserveMetadata) {
|
|
4769
|
+
resetView.id = view.id;
|
|
4770
|
+
resetView.name = view.name;
|
|
4771
|
+
resetView.label = view.label;
|
|
4772
|
+
resetView.description = view.description;
|
|
4773
|
+
resetView.icon = view.icon;
|
|
4774
|
+
}
|
|
4775
|
+
return resetView;
|
|
4776
|
+
}
|
|
4777
|
+
function resetViewToDefault(currentView, object2, options = {}) {
|
|
4778
|
+
if (isDetailView(currentView)) {
|
|
4779
|
+
return resetDetailViewToDefault(currentView, object2, options);
|
|
4780
|
+
}
|
|
4781
|
+
if (isListView(currentView)) {
|
|
4782
|
+
return resetListViewToDefault(currentView, object2, options);
|
|
4783
|
+
}
|
|
4784
|
+
throw new Error(`Reset not supported for view type: ${currentView.type}`);
|
|
4785
|
+
}
|
|
4786
|
+
|
|
4787
|
+
// src/views/index.ts
|
|
4788
|
+
function getActiveTab(config, tabId) {
|
|
4789
|
+
if (tabId) {
|
|
4790
|
+
const tab = config.tabs.find((t) => t.id === tabId);
|
|
4791
|
+
if (tab) return tab;
|
|
4792
|
+
}
|
|
4793
|
+
return config.tabs.find((t) => t.default) ?? config.tabs[0];
|
|
4794
|
+
}
|
|
4795
|
+
|
|
4796
|
+
// src/forms/registry.ts
|
|
4797
|
+
var FormRegistry = class {
|
|
4798
|
+
constructor() {
|
|
4799
|
+
this.forms = /* @__PURE__ */ new Map();
|
|
4800
|
+
}
|
|
4801
|
+
/**
|
|
4802
|
+
* Register a form definition (or array of definitions).
|
|
4803
|
+
* @throws Error if a form with the same name is already registered.
|
|
4804
|
+
*/
|
|
4805
|
+
register(formOrForms) {
|
|
4806
|
+
const forms = Array.isArray(formOrForms) ? formOrForms : [formOrForms];
|
|
4807
|
+
for (const f of forms) {
|
|
4808
|
+
if (this.forms.has(f.name)) {
|
|
4809
|
+
throw new Error(`[FormRegistry] Duplicate form: "${f.name}"`);
|
|
4810
|
+
}
|
|
4811
|
+
this.forms.set(f.name, f);
|
|
4812
|
+
}
|
|
4813
|
+
return this;
|
|
4814
|
+
}
|
|
4815
|
+
/**
|
|
4816
|
+
* Get a form definition by name.
|
|
4817
|
+
*/
|
|
4818
|
+
get(name) {
|
|
4819
|
+
return this.forms.get(name);
|
|
4820
|
+
}
|
|
4821
|
+
/**
|
|
4822
|
+
* Get all registered form definitions.
|
|
4823
|
+
*/
|
|
4824
|
+
getAll() {
|
|
4825
|
+
return Array.from(this.forms.values());
|
|
4826
|
+
}
|
|
4827
|
+
/**
|
|
4828
|
+
* List all registered form names.
|
|
4829
|
+
*/
|
|
4830
|
+
listNames() {
|
|
4831
|
+
return Array.from(this.forms.keys());
|
|
4832
|
+
}
|
|
4833
|
+
};
|
|
4834
|
+
var formRegistry = new FormRegistry();
|
|
4835
|
+
|
|
4836
|
+
// src/realtime/channels.ts
|
|
4837
|
+
function liveChannelKey(channel) {
|
|
4838
|
+
switch (channel.kind) {
|
|
4839
|
+
case "workspace":
|
|
4840
|
+
return `workspace:${encodeURIComponent(channel.workspaceId)}`;
|
|
4841
|
+
case "object":
|
|
4842
|
+
return `object:${encodeURIComponent(channel.objectId)}`;
|
|
4843
|
+
case "record":
|
|
4844
|
+
return `record:${encodeURIComponent(channel.objectId)}:${encodeURIComponent(channel.recordId)}`;
|
|
4845
|
+
case "view":
|
|
4846
|
+
return `view:${encodeURIComponent(channel.viewId)}`;
|
|
4847
|
+
case "agent":
|
|
4848
|
+
return `agent:${encodeURIComponent(channel.workspaceId)}`;
|
|
4849
|
+
case "user":
|
|
4850
|
+
return `user:${encodeURIComponent(channel.actorId)}`;
|
|
4851
|
+
case "conversation":
|
|
4852
|
+
return `conversation:${encodeURIComponent(channel.sessionId)}`;
|
|
4853
|
+
}
|
|
4854
|
+
}
|
|
4855
|
+
function decodeLiveChannelPart(part) {
|
|
4856
|
+
try {
|
|
4857
|
+
return decodeURIComponent(part);
|
|
4858
|
+
} catch {
|
|
4859
|
+
return null;
|
|
4860
|
+
}
|
|
4861
|
+
}
|
|
4862
|
+
function parseLiveChannelKey(key) {
|
|
4863
|
+
const parts = key.split(":");
|
|
4864
|
+
if (parts[0] === "workspace" && parts.length === 2 && parts[1]) {
|
|
4865
|
+
const workspaceId = decodeLiveChannelPart(parts[1]);
|
|
4866
|
+
return workspaceId ? { kind: "workspace", workspaceId } : null;
|
|
4867
|
+
}
|
|
4868
|
+
if (parts[0] === "object" && parts.length === 2 && parts[1]) {
|
|
4869
|
+
const objectId = decodeLiveChannelPart(parts[1]);
|
|
4870
|
+
return objectId ? { kind: "object", objectId } : null;
|
|
4871
|
+
}
|
|
4872
|
+
if (parts[0] === "record" && parts.length === 3 && parts[1] && parts[2]) {
|
|
4873
|
+
const objectId = decodeLiveChannelPart(parts[1]);
|
|
4874
|
+
const recordId = decodeLiveChannelPart(parts[2]);
|
|
4875
|
+
if (!(objectId && recordId)) return null;
|
|
4876
|
+
return {
|
|
4877
|
+
kind: "record",
|
|
4878
|
+
objectId,
|
|
4879
|
+
recordId
|
|
4880
|
+
};
|
|
4881
|
+
}
|
|
4882
|
+
if (parts[0] === "view" && parts.length === 2 && parts[1]) {
|
|
4883
|
+
const viewId = decodeLiveChannelPart(parts[1]);
|
|
4884
|
+
return viewId ? { kind: "view", viewId } : null;
|
|
4885
|
+
}
|
|
4886
|
+
if (parts[0] === "agent" && parts.length === 2 && parts[1]) {
|
|
4887
|
+
const workspaceId = decodeLiveChannelPart(parts[1]);
|
|
4888
|
+
return workspaceId ? { kind: "agent", workspaceId } : null;
|
|
4889
|
+
}
|
|
4890
|
+
if (parts[0] === "user" && parts.length === 2 && parts[1]) {
|
|
4891
|
+
const actorId = decodeLiveChannelPart(parts[1]);
|
|
4892
|
+
return actorId ? { kind: "user", actorId } : null;
|
|
4893
|
+
}
|
|
4894
|
+
if (parts[0] === "conversation" && parts.length === 2 && parts[1]) {
|
|
4895
|
+
const sessionId = decodeLiveChannelPart(parts[1]);
|
|
4896
|
+
return sessionId ? { kind: "conversation", sessionId } : null;
|
|
4897
|
+
}
|
|
4898
|
+
return null;
|
|
4899
|
+
}
|
|
4900
|
+
|
|
4901
|
+
// src/realtime/cursors.ts
|
|
4902
|
+
var LIVE_STREAM_START_CURSOR = "0-0";
|
|
4903
|
+
var LIVE_STREAM_CURSOR_PATTERN = /^(0|[1-9]\d*)-(0|[1-9]\d*)$/;
|
|
4904
|
+
function parseLiveStreamCursor(cursor) {
|
|
4905
|
+
if (!LIVE_STREAM_CURSOR_PATTERN.test(cursor)) {
|
|
4906
|
+
return null;
|
|
4907
|
+
}
|
|
4908
|
+
const [milliseconds = "0", sequence = "0"] = cursor.split("-");
|
|
4909
|
+
return [BigInt(milliseconds), BigInt(sequence)];
|
|
4910
|
+
}
|
|
4911
|
+
function isLiveStreamCursor(value) {
|
|
4912
|
+
return typeof value === "string" && parseLiveStreamCursor(value) !== null;
|
|
4913
|
+
}
|
|
4914
|
+
function assertLiveStreamCursor(cursor, label = "cursor") {
|
|
4915
|
+
if (cursor === null) {
|
|
4916
|
+
return;
|
|
4917
|
+
}
|
|
4918
|
+
if (!isLiveStreamCursor(cursor)) {
|
|
4919
|
+
throw new Error(`Invalid live stream cursor for ${label}: ${cursor}`);
|
|
4920
|
+
}
|
|
4921
|
+
}
|
|
4922
|
+
function compareLiveStreamCursors(left, right) {
|
|
4923
|
+
const leftCursor = parseLiveStreamCursor(left);
|
|
4924
|
+
if (!leftCursor) {
|
|
4925
|
+
throw new Error(`Invalid live stream cursor for left: ${left}`);
|
|
4926
|
+
}
|
|
4927
|
+
const rightCursor = parseLiveStreamCursor(right);
|
|
4928
|
+
if (!rightCursor) {
|
|
4929
|
+
throw new Error(`Invalid live stream cursor for right: ${right}`);
|
|
4930
|
+
}
|
|
4931
|
+
const [leftMilliseconds, leftSequence] = leftCursor;
|
|
4932
|
+
const [rightMilliseconds, rightSequence] = rightCursor;
|
|
4933
|
+
if (leftMilliseconds !== rightMilliseconds) {
|
|
4934
|
+
return leftMilliseconds < rightMilliseconds ? -1 : 1;
|
|
4935
|
+
}
|
|
4936
|
+
if (leftSequence !== rightSequence) {
|
|
4937
|
+
return leftSequence < rightSequence ? -1 : 1;
|
|
4938
|
+
}
|
|
4939
|
+
return 0;
|
|
4940
|
+
}
|
|
4941
|
+
|
|
4942
|
+
// src/realtime/events.ts
|
|
4943
|
+
var LIVE_EVENT_TYPE_REGISTRY = {
|
|
4944
|
+
"record.patch": true,
|
|
4945
|
+
"record.deleted": true,
|
|
4946
|
+
"view.projection.stale": true,
|
|
4947
|
+
"agent.session.patch": true,
|
|
4948
|
+
"agent.unread.patch": true,
|
|
4949
|
+
"conversation.renamed": true,
|
|
4950
|
+
"channel.revoked": true
|
|
4951
|
+
};
|
|
4952
|
+
var LIVE_EVENT_TYPES = Object.keys(LIVE_EVENT_TYPE_REGISTRY);
|
|
4953
|
+
var COMPLETION_STATUSES = ["draft", "complete"];
|
|
4954
|
+
var VIEW_PROJECTION_STALE_REASONS = [
|
|
4955
|
+
"membership-unknown",
|
|
4956
|
+
"sort-unknown",
|
|
4957
|
+
"permissions-changed"
|
|
4958
|
+
];
|
|
4959
|
+
var CHANNEL_REVOKED_REASONS = [
|
|
4960
|
+
"permission-revoked",
|
|
4961
|
+
"workspace-left",
|
|
4962
|
+
"object-deleted"
|
|
4963
|
+
];
|
|
4964
|
+
function isLiveEventType(value) {
|
|
4965
|
+
return typeof value === "string" && LIVE_EVENT_TYPES.includes(value);
|
|
4966
|
+
}
|
|
4967
|
+
function isLiveEventPayload(payload) {
|
|
4968
|
+
if (!(isRecord(payload) && isLiveEventType(payload.type))) {
|
|
4969
|
+
return false;
|
|
4970
|
+
}
|
|
4971
|
+
return isLiveEventPayloadForType(payload.type, payload);
|
|
4972
|
+
}
|
|
4973
|
+
function isLiveEventPayloadForType(type, payload) {
|
|
4974
|
+
if (!isRecord(payload)) {
|
|
4975
|
+
return false;
|
|
4976
|
+
}
|
|
4977
|
+
switch (type) {
|
|
4978
|
+
case "record.patch":
|
|
4979
|
+
return isRecordPatchPayload(payload);
|
|
4980
|
+
case "record.deleted":
|
|
4981
|
+
return isRecordDeletedPayload(payload);
|
|
4982
|
+
case "view.projection.stale":
|
|
4983
|
+
return isViewProjectionStalePayload(payload);
|
|
4984
|
+
case "agent.session.patch":
|
|
4985
|
+
return isAgentSessionPatchPayload(payload);
|
|
4986
|
+
case "agent.unread.patch":
|
|
4987
|
+
return isAgentUnreadPatchPayload(payload);
|
|
4988
|
+
case "conversation.renamed":
|
|
4989
|
+
return isConversationRenamedPayload(payload);
|
|
4990
|
+
case "channel.revoked":
|
|
4991
|
+
return isChannelRevokedPayload(payload);
|
|
4992
|
+
default:
|
|
4993
|
+
return false;
|
|
4994
|
+
}
|
|
4995
|
+
}
|
|
4996
|
+
function isRecord(value) {
|
|
4997
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4998
|
+
}
|
|
4999
|
+
function isString(value) {
|
|
5000
|
+
return typeof value === "string";
|
|
5001
|
+
}
|
|
5002
|
+
function isVersion(value) {
|
|
5003
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
5004
|
+
}
|
|
5005
|
+
function hasString(value, key) {
|
|
5006
|
+
return isString(value[key]);
|
|
5007
|
+
}
|
|
5008
|
+
function hasVersion(value, key) {
|
|
5009
|
+
return isVersion(value[key]);
|
|
5010
|
+
}
|
|
5011
|
+
function hasOptional(value, key, predicate) {
|
|
5012
|
+
return !Object.hasOwn(value, key) || predicate(value[key]);
|
|
5013
|
+
}
|
|
5014
|
+
function isOneOf(value, values) {
|
|
5015
|
+
return typeof value === "string" && values.includes(value);
|
|
5016
|
+
}
|
|
5017
|
+
function isCompletionStatus(value) {
|
|
5018
|
+
return isOneOf(value, COMPLETION_STATUSES);
|
|
5019
|
+
}
|
|
5020
|
+
function isRecordFieldPatch(value) {
|
|
5021
|
+
return isRecord(value) && hasString(value, "fieldKey") && Object.hasOwn(value, "value") && hasVersion(value, "version");
|
|
5022
|
+
}
|
|
5023
|
+
function isRecordPatchPayload(payload) {
|
|
5024
|
+
return payload.type === "record.patch" && hasString(payload, "objectId") && hasString(payload, "recordId") && hasVersion(payload, "recordVersion") && hasOptional(payload, "label", isString) && hasOptional(payload, "completionStatus", isCompletionStatus) && hasOptional(payload, "metadata", isRecord) && Array.isArray(payload.fieldPatches) && payload.fieldPatches.every(isRecordFieldPatch) && hasString(payload, "updatedAt");
|
|
5025
|
+
}
|
|
5026
|
+
function isRecordDeletedPayload(payload) {
|
|
5027
|
+
return payload.type === "record.deleted" && hasString(payload, "objectId") && hasString(payload, "recordId") && hasVersion(payload, "recordVersion") && hasString(payload, "deletedAt");
|
|
5028
|
+
}
|
|
5029
|
+
function isViewProjectionStalePayload(payload) {
|
|
5030
|
+
return payload.type === "view.projection.stale" && hasString(payload, "viewId") && isOneOf(payload.reason, VIEW_PROJECTION_STALE_REASONS);
|
|
5031
|
+
}
|
|
5032
|
+
function isAgentSessionPatchPayload(payload) {
|
|
5033
|
+
return payload.type === "agent.session.patch" && hasString(payload, "workspaceId") && hasString(payload, "sessionId") && isRecord(payload.patch) && hasVersion(payload, "version") && hasString(payload, "updatedAt");
|
|
5034
|
+
}
|
|
5035
|
+
function isAgentUnreadPatchPayload(payload) {
|
|
5036
|
+
return payload.type === "agent.unread.patch" && hasString(payload, "workspaceId") && hasString(payload, "targetActorId") && hasString(payload, "sessionId") && hasVersion(payload, "unreadCount") && hasVersion(payload, "version") && hasString(payload, "updatedAt") && (!Object.hasOwn(payload, "session") || isRecord(payload.session) && payload.session.id === payload.sessionId);
|
|
5037
|
+
}
|
|
5038
|
+
function isConversationRenamedPayload(payload) {
|
|
5039
|
+
return payload.type === "conversation.renamed" && hasString(payload, "workspaceId") && hasString(payload, "sessionId") && hasString(payload, "title") && hasVersion(payload, "version") && hasString(payload, "updatedAt");
|
|
5040
|
+
}
|
|
5041
|
+
function isChannelRevokedPayload(payload) {
|
|
5042
|
+
return payload.type === "channel.revoked" && hasString(payload, "channel") && isOneOf(payload.reason, CHANNEL_REVOKED_REASONS);
|
|
5043
|
+
}
|
|
5044
|
+
|
|
5045
|
+
// src/realtime/protocol.ts
|
|
5046
|
+
function isRecord2(value) {
|
|
5047
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5048
|
+
}
|
|
5049
|
+
function payloadChannel(payload) {
|
|
5050
|
+
const { channel } = payload;
|
|
5051
|
+
return typeof channel === "string" && parseLiveChannelKey(channel) ? channel : null;
|
|
5052
|
+
}
|
|
5053
|
+
function getLiveProtocolPayloadChannel(payload) {
|
|
5054
|
+
return isRecord2(payload) ? payloadChannel(payload) : null;
|
|
5055
|
+
}
|
|
5056
|
+
function isLiveEventEnvelope(payload) {
|
|
5057
|
+
if (!isRecord2(payload)) return false;
|
|
5058
|
+
return typeof payload.id === "string" && isLiveStreamCursor(payload.id) && typeof payload.channel === "string" && typeof payload.type === "string" && typeof payload.occurredAt === "string" && isRecord2(payload.payload);
|
|
5059
|
+
}
|
|
5060
|
+
function parseLiveSubscribePayload(payload) {
|
|
5061
|
+
if (!isRecord2(payload)) return null;
|
|
5062
|
+
const channel = payloadChannel(payload);
|
|
5063
|
+
if (!channel) return null;
|
|
5064
|
+
const { cursor } = payload;
|
|
5065
|
+
if (cursor !== void 0 && cursor !== null) {
|
|
5066
|
+
if (typeof cursor !== "string" || !isLiveStreamCursor(cursor)) {
|
|
5067
|
+
return null;
|
|
5068
|
+
}
|
|
5069
|
+
}
|
|
5070
|
+
return { channel, cursor: cursor ?? null };
|
|
5071
|
+
}
|
|
5072
|
+
function parseLiveUnsubscribePayload(payload) {
|
|
5073
|
+
if (payload === void 0 || payload === null) return {};
|
|
5074
|
+
if (!isRecord2(payload)) return null;
|
|
5075
|
+
if (!Object.hasOwn(payload, "channel")) {
|
|
5076
|
+
return {};
|
|
5077
|
+
}
|
|
5078
|
+
const channel = payloadChannel(payload);
|
|
5079
|
+
return channel ? { channel } : null;
|
|
5080
|
+
}
|
|
5081
|
+
function isLiveReplayCompletePayload(payload) {
|
|
5082
|
+
if (!isRecord2(payload)) return false;
|
|
5083
|
+
const channel = payloadChannel(payload);
|
|
5084
|
+
if (!channel) return false;
|
|
5085
|
+
return payload.cursor === null || typeof payload.cursor === "string" && isLiveStreamCursor(payload.cursor);
|
|
5086
|
+
}
|
|
5087
|
+
function isLiveGapPayload(payload) {
|
|
5088
|
+
if (!isRecord2(payload)) return false;
|
|
5089
|
+
return payloadChannel(payload) !== null;
|
|
5090
|
+
}
|
|
5091
|
+
function isLiveErrorPayload(payload) {
|
|
5092
|
+
if (!isRecord2(payload)) return false;
|
|
5093
|
+
return payloadChannel(payload) !== null;
|
|
5094
|
+
}
|
|
5095
|
+
|
|
5096
|
+
// src/computed/functions.ts
|
|
5097
|
+
var COMPUTED_FUNCTION_NAMES = [
|
|
5098
|
+
"if",
|
|
5099
|
+
"concat",
|
|
5100
|
+
"sum",
|
|
5101
|
+
"avg",
|
|
5102
|
+
"count",
|
|
5103
|
+
"min",
|
|
5104
|
+
"max",
|
|
5105
|
+
"earliest",
|
|
5106
|
+
"latest",
|
|
5107
|
+
"unique",
|
|
5108
|
+
"first",
|
|
5109
|
+
"percent_empty",
|
|
5110
|
+
"percent_not_empty",
|
|
5111
|
+
"count_empty",
|
|
5112
|
+
"count_not_empty",
|
|
5113
|
+
"count_unique"
|
|
5114
|
+
];
|
|
5115
|
+
var COMPUTED_FUNCTIONS = {
|
|
5116
|
+
if: { name: "if", returnType: "branch", minArgs: 3, maxArgs: 3 },
|
|
5117
|
+
concat: { name: "concat", returnType: { kind: "scalar", type: "text" }, minArgs: 1 },
|
|
5118
|
+
sum: { name: "sum", returnType: { kind: "scalar", type: "number" }, minArgs: 1, maxArgs: 1 },
|
|
5119
|
+
avg: { name: "avg", returnType: { kind: "scalar", type: "number" }, minArgs: 1, maxArgs: 1 },
|
|
5120
|
+
count: { name: "count", returnType: { kind: "scalar", type: "number" }, minArgs: 1, maxArgs: 1 },
|
|
5121
|
+
min: { name: "min", returnType: "source_item", minArgs: 1, maxArgs: 1 },
|
|
5122
|
+
max: { name: "max", returnType: "source_item", minArgs: 1, maxArgs: 1 },
|
|
5123
|
+
earliest: {
|
|
5124
|
+
name: "earliest",
|
|
5125
|
+
returnType: { kind: "scalar", type: "date" },
|
|
5126
|
+
minArgs: 1,
|
|
5127
|
+
maxArgs: 1
|
|
5128
|
+
},
|
|
5129
|
+
latest: { name: "latest", returnType: { kind: "scalar", type: "date" }, minArgs: 1, maxArgs: 1 },
|
|
5130
|
+
unique: { name: "unique", returnType: "argument", minArgs: 1, maxArgs: 1 },
|
|
5131
|
+
first: { name: "first", returnType: "source_item", minArgs: 1, maxArgs: 1 },
|
|
5132
|
+
percent_empty: {
|
|
5133
|
+
name: "percent_empty",
|
|
5134
|
+
returnType: { kind: "scalar", type: "number" },
|
|
5135
|
+
minArgs: 1,
|
|
5136
|
+
maxArgs: 1
|
|
5137
|
+
},
|
|
5138
|
+
percent_not_empty: {
|
|
5139
|
+
name: "percent_not_empty",
|
|
5140
|
+
returnType: { kind: "scalar", type: "number" },
|
|
5141
|
+
minArgs: 1,
|
|
5142
|
+
maxArgs: 1
|
|
5143
|
+
},
|
|
5144
|
+
count_empty: {
|
|
5145
|
+
name: "count_empty",
|
|
5146
|
+
returnType: { kind: "scalar", type: "number" },
|
|
5147
|
+
minArgs: 1,
|
|
5148
|
+
maxArgs: 1
|
|
5149
|
+
},
|
|
5150
|
+
count_not_empty: {
|
|
5151
|
+
name: "count_not_empty",
|
|
5152
|
+
returnType: { kind: "scalar", type: "number" },
|
|
5153
|
+
minArgs: 1,
|
|
5154
|
+
maxArgs: 1
|
|
5155
|
+
},
|
|
5156
|
+
count_unique: {
|
|
5157
|
+
name: "count_unique",
|
|
5158
|
+
returnType: { kind: "scalar", type: "number" },
|
|
5159
|
+
minArgs: 1,
|
|
5160
|
+
maxArgs: 1
|
|
5161
|
+
}
|
|
5162
|
+
};
|
|
5163
|
+
function isComputedFunctionName(name) {
|
|
5164
|
+
return Object.hasOwn(COMPUTED_FUNCTIONS, name);
|
|
5165
|
+
}
|
|
5166
|
+
|
|
5167
|
+
// src/computed/parser.ts
|
|
5168
|
+
var ComputedFormulaParseError = class extends Error {
|
|
5169
|
+
constructor(message) {
|
|
5170
|
+
super(message);
|
|
5171
|
+
this.name = "ComputedFormulaParseError";
|
|
5172
|
+
}
|
|
5173
|
+
};
|
|
5174
|
+
function parseComputedFormula(expression) {
|
|
5175
|
+
const parser = new Parser(tokenize(expression));
|
|
5176
|
+
const ast = parser.parseExpression();
|
|
5177
|
+
parser.expectEnd();
|
|
5178
|
+
return ast;
|
|
5179
|
+
}
|
|
5180
|
+
function tokenize(expression) {
|
|
5181
|
+
const tokens = [];
|
|
5182
|
+
let index = 0;
|
|
5183
|
+
while (index < expression.length) {
|
|
5184
|
+
const char = expression[index];
|
|
5185
|
+
if (char === " " || char === " " || char === "\n" || char === "\r") {
|
|
5186
|
+
index += 1;
|
|
5187
|
+
continue;
|
|
5188
|
+
}
|
|
5189
|
+
if (char === '"' || char === "'") {
|
|
5190
|
+
const result = scanString(expression, index, char);
|
|
5191
|
+
tokens.push({ kind: "string", value: result.value, position: index });
|
|
5192
|
+
index = result.nextIndex;
|
|
5193
|
+
continue;
|
|
5194
|
+
}
|
|
5195
|
+
if (isDigit(char)) {
|
|
5196
|
+
const result = scanNumber(expression, index);
|
|
5197
|
+
tokens.push({ kind: "number", value: result.value, position: index });
|
|
5198
|
+
index = result.nextIndex;
|
|
5199
|
+
continue;
|
|
5200
|
+
}
|
|
5201
|
+
if (isIdentifierStart(char)) {
|
|
5202
|
+
const result = scanIdentifier(expression, index);
|
|
5203
|
+
const canonical = result.value.toLowerCase();
|
|
5204
|
+
if (canonical === "true") {
|
|
5205
|
+
tokens.push({ kind: "boolean", value: true, position: index });
|
|
5206
|
+
} else if (canonical === "false") {
|
|
5207
|
+
tokens.push({ kind: "boolean", value: false, position: index });
|
|
5208
|
+
} else if (canonical === "null") {
|
|
5209
|
+
tokens.push({ kind: "null", position: index });
|
|
5210
|
+
} else {
|
|
5211
|
+
tokens.push({ kind: "identifier", value: result.value, position: index });
|
|
5212
|
+
}
|
|
5213
|
+
index = result.nextIndex;
|
|
5214
|
+
continue;
|
|
5215
|
+
}
|
|
5216
|
+
const twoChar = expression.slice(index, index + 2);
|
|
5217
|
+
if (twoChar === ">=" || twoChar === "<=" || twoChar === "==" || twoChar === "!=") {
|
|
5218
|
+
tokens.push({ kind: "operator", value: twoChar, position: index });
|
|
5219
|
+
index += 2;
|
|
5220
|
+
continue;
|
|
5221
|
+
}
|
|
5222
|
+
if (char === ">" || char === "<" || char === "+" || char === "-" || char === "*" || char === "/") {
|
|
5223
|
+
tokens.push({ kind: "operator", value: char, position: index });
|
|
5224
|
+
index += 1;
|
|
5225
|
+
continue;
|
|
5226
|
+
}
|
|
5227
|
+
if (char === "(" || char === ")" || char === "," || char === ".") {
|
|
5228
|
+
tokens.push({ kind: "punctuation", value: char, position: index });
|
|
5229
|
+
index += 1;
|
|
5230
|
+
continue;
|
|
5231
|
+
}
|
|
5232
|
+
throw new ComputedFormulaParseError(`Unexpected character "${char}" at position ${index}`);
|
|
5233
|
+
}
|
|
5234
|
+
return tokens;
|
|
5235
|
+
}
|
|
5236
|
+
function scanString(expression, startIndex, delimiter = '"') {
|
|
5237
|
+
let value = "";
|
|
5238
|
+
let index = startIndex + 1;
|
|
5239
|
+
while (index < expression.length) {
|
|
5240
|
+
const char = expression[index];
|
|
5241
|
+
if (char === delimiter) {
|
|
5242
|
+
return { value, nextIndex: index + 1 };
|
|
5243
|
+
}
|
|
5244
|
+
if (char === "\\") {
|
|
5245
|
+
const escaped = expression[index + 1];
|
|
5246
|
+
if (escaped === void 0) {
|
|
5247
|
+
throw new ComputedFormulaParseError(`Unterminated string literal at position ${index}`);
|
|
5248
|
+
}
|
|
5249
|
+
value += decodeEscape(escaped, index);
|
|
5250
|
+
index += 2;
|
|
5251
|
+
continue;
|
|
5252
|
+
}
|
|
5253
|
+
value += char;
|
|
5254
|
+
index += 1;
|
|
5255
|
+
}
|
|
5256
|
+
throw new ComputedFormulaParseError(`Unterminated string literal at position ${startIndex}`);
|
|
5257
|
+
}
|
|
5258
|
+
function decodeEscape(char, position) {
|
|
5259
|
+
switch (char) {
|
|
5260
|
+
case '"':
|
|
5261
|
+
case "\\":
|
|
5262
|
+
return char;
|
|
5263
|
+
case "n":
|
|
5264
|
+
return "\n";
|
|
5265
|
+
case "r":
|
|
5266
|
+
return "\r";
|
|
5267
|
+
case "t":
|
|
5268
|
+
return " ";
|
|
5269
|
+
default:
|
|
5270
|
+
throw new ComputedFormulaParseError(
|
|
5271
|
+
`Unknown escape sequence "\\${char}" at position ${position}`
|
|
5272
|
+
);
|
|
5273
|
+
}
|
|
5274
|
+
}
|
|
5275
|
+
function scanNumber(expression, startIndex) {
|
|
5276
|
+
let index = startIndex;
|
|
5277
|
+
while (index < expression.length && isDigit(expression[index])) {
|
|
5278
|
+
index += 1;
|
|
5279
|
+
}
|
|
5280
|
+
if (expression[index] === ".") {
|
|
5281
|
+
const decimalStart = index;
|
|
5282
|
+
index += 1;
|
|
5283
|
+
if (!isDigit(expression[index])) {
|
|
5284
|
+
throw new ComputedFormulaParseError(`Invalid number literal at position ${decimalStart}`);
|
|
5285
|
+
}
|
|
5286
|
+
while (index < expression.length && isDigit(expression[index])) {
|
|
5287
|
+
index += 1;
|
|
5288
|
+
}
|
|
5289
|
+
}
|
|
5290
|
+
const raw = expression.slice(startIndex, index);
|
|
5291
|
+
const value = Number(raw);
|
|
5292
|
+
if (!Number.isFinite(value)) {
|
|
5293
|
+
throw new ComputedFormulaParseError(`Invalid number literal "${raw}"`);
|
|
5294
|
+
}
|
|
5295
|
+
return { value, nextIndex: index };
|
|
5296
|
+
}
|
|
5297
|
+
function scanIdentifier(expression, startIndex) {
|
|
5298
|
+
let index = startIndex + 1;
|
|
5299
|
+
while (index < expression.length && isIdentifierPart(expression[index])) {
|
|
5300
|
+
index += 1;
|
|
5301
|
+
}
|
|
5302
|
+
return { value: expression.slice(startIndex, index), nextIndex: index };
|
|
5303
|
+
}
|
|
5304
|
+
function isDigit(char) {
|
|
5305
|
+
return char !== void 0 && char >= "0" && char <= "9";
|
|
5306
|
+
}
|
|
5307
|
+
function isIdentifierStart(char) {
|
|
5308
|
+
return char !== void 0 && /[A-Za-z_]/.test(char);
|
|
5309
|
+
}
|
|
5310
|
+
function isIdentifierPart(char) {
|
|
5311
|
+
return char !== void 0 && /[A-Za-z0-9_]/.test(char);
|
|
5312
|
+
}
|
|
5313
|
+
var Parser = class {
|
|
5314
|
+
constructor(tokens) {
|
|
5315
|
+
this.tokens = tokens;
|
|
5316
|
+
this.position = 0;
|
|
5317
|
+
}
|
|
5318
|
+
parseExpression() {
|
|
5319
|
+
return this.parseBinaryExpression(0);
|
|
5320
|
+
}
|
|
5321
|
+
expectEnd() {
|
|
5322
|
+
const token = this.peek();
|
|
5323
|
+
if (token !== void 0) {
|
|
5324
|
+
throw new ComputedFormulaParseError(
|
|
5325
|
+
`Unexpected token ${describeToken(token)} at position ${token.position}`
|
|
5326
|
+
);
|
|
5327
|
+
}
|
|
5328
|
+
}
|
|
5329
|
+
parseBinaryExpression(minPrecedence) {
|
|
5330
|
+
let left = this.parsePrimary();
|
|
5331
|
+
while (true) {
|
|
5332
|
+
const token = this.peek();
|
|
5333
|
+
if (token?.kind !== "operator") {
|
|
5334
|
+
return left;
|
|
5335
|
+
}
|
|
5336
|
+
const precedence = getOperatorPrecedence(token.value);
|
|
5337
|
+
if (precedence < minPrecedence) {
|
|
5338
|
+
return left;
|
|
5339
|
+
}
|
|
5340
|
+
const operator = token.value;
|
|
5341
|
+
this.consume();
|
|
5342
|
+
const right = this.parseBinaryExpression(precedence + 1);
|
|
5343
|
+
left = { kind: "binary", operator, left, right };
|
|
5344
|
+
}
|
|
5345
|
+
}
|
|
5346
|
+
parsePrimary() {
|
|
5347
|
+
const token = this.consume();
|
|
5348
|
+
if (token === void 0) {
|
|
5349
|
+
throw new ComputedFormulaParseError("Unexpected end of expression");
|
|
5350
|
+
}
|
|
5351
|
+
switch (token.kind) {
|
|
5352
|
+
case "string":
|
|
5353
|
+
case "number":
|
|
5354
|
+
case "boolean":
|
|
5355
|
+
return { kind: "literal", value: token.value };
|
|
5356
|
+
case "null":
|
|
5357
|
+
return { kind: "literal", value: null };
|
|
5358
|
+
case "identifier":
|
|
5359
|
+
return this.parseIdentifier(token.value);
|
|
5360
|
+
case "punctuation":
|
|
5361
|
+
if (token.value === "(") {
|
|
5362
|
+
const expression = this.parseExpression();
|
|
5363
|
+
this.expectPunctuation(")");
|
|
5364
|
+
return expression;
|
|
5365
|
+
}
|
|
5366
|
+
break;
|
|
5367
|
+
case "operator":
|
|
5368
|
+
if (token.value === "-") {
|
|
5369
|
+
const next = this.consume();
|
|
5370
|
+
if (next?.kind === "number") {
|
|
5371
|
+
return { kind: "literal", value: -next.value };
|
|
5372
|
+
}
|
|
5373
|
+
}
|
|
5374
|
+
break;
|
|
5375
|
+
}
|
|
5376
|
+
throw new ComputedFormulaParseError(
|
|
5377
|
+
`Expected literal, path, call, or parenthesized expression at position ${token.position}`
|
|
5378
|
+
);
|
|
5379
|
+
}
|
|
5380
|
+
parseIdentifier(identifier) {
|
|
5381
|
+
if (this.matchPunctuation("(")) {
|
|
5382
|
+
const args = [];
|
|
5383
|
+
if (!this.matchPunctuation(")")) {
|
|
5384
|
+
do {
|
|
5385
|
+
args.push(this.parseExpression());
|
|
5386
|
+
} while (this.matchPunctuation(","));
|
|
5387
|
+
this.expectPunctuation(")");
|
|
5388
|
+
}
|
|
5389
|
+
return { kind: "call", functionName: identifier.toLowerCase(), args };
|
|
5390
|
+
}
|
|
5391
|
+
const parts = [identifier];
|
|
5392
|
+
while (this.matchPunctuation(".")) {
|
|
5393
|
+
const next = this.consume();
|
|
5394
|
+
if (next?.kind !== "identifier") {
|
|
5395
|
+
const position = next?.position ?? this.previousPosition();
|
|
5396
|
+
throw new ComputedFormulaParseError(
|
|
5397
|
+
`Expected attribute name after path separator at position ${position}`
|
|
5398
|
+
);
|
|
5399
|
+
}
|
|
5400
|
+
parts.push(next.value);
|
|
5401
|
+
}
|
|
5402
|
+
return { kind: "path", parts };
|
|
5403
|
+
}
|
|
5404
|
+
expectPunctuation(value) {
|
|
5405
|
+
if (!this.matchPunctuation(value)) {
|
|
5406
|
+
const token = this.peek();
|
|
5407
|
+
const position = token?.position ?? this.previousPosition();
|
|
5408
|
+
throw new ComputedFormulaParseError(`Expected "${value}" at position ${position}`);
|
|
5409
|
+
}
|
|
5410
|
+
}
|
|
5411
|
+
matchPunctuation(value) {
|
|
5412
|
+
const token = this.peek();
|
|
5413
|
+
if (token?.kind === "punctuation" && token.value === value) {
|
|
5414
|
+
this.consume();
|
|
5415
|
+
return true;
|
|
5416
|
+
}
|
|
5417
|
+
return false;
|
|
5418
|
+
}
|
|
5419
|
+
peek() {
|
|
5420
|
+
return this.tokens[this.position];
|
|
5421
|
+
}
|
|
5422
|
+
consume() {
|
|
5423
|
+
const token = this.tokens[this.position];
|
|
5424
|
+
this.position += 1;
|
|
5425
|
+
return token;
|
|
5426
|
+
}
|
|
5427
|
+
previousPosition() {
|
|
5428
|
+
const previous = this.tokens[this.position - 1];
|
|
5429
|
+
return previous?.position ?? 0;
|
|
5430
|
+
}
|
|
5431
|
+
};
|
|
5432
|
+
function describeToken(token) {
|
|
5433
|
+
switch (token.kind) {
|
|
5434
|
+
case "identifier":
|
|
5435
|
+
case "number":
|
|
5436
|
+
case "string":
|
|
5437
|
+
case "boolean":
|
|
5438
|
+
case "operator":
|
|
5439
|
+
case "punctuation":
|
|
5440
|
+
return `"${String(token.value)}"`;
|
|
5441
|
+
case "null":
|
|
5442
|
+
return '"null"';
|
|
5443
|
+
default: {
|
|
5444
|
+
const exhaustive = token;
|
|
5445
|
+
return exhaustive;
|
|
5446
|
+
}
|
|
5447
|
+
}
|
|
5448
|
+
}
|
|
5449
|
+
function getOperatorPrecedence(operator) {
|
|
5450
|
+
switch (operator) {
|
|
5451
|
+
case "*":
|
|
5452
|
+
case "/":
|
|
5453
|
+
return 3;
|
|
5454
|
+
case "+":
|
|
5455
|
+
case "-":
|
|
5456
|
+
return 2;
|
|
5457
|
+
case ">":
|
|
5458
|
+
case "<":
|
|
5459
|
+
case ">=":
|
|
5460
|
+
case "<=":
|
|
5461
|
+
case "==":
|
|
5462
|
+
case "!=":
|
|
5463
|
+
return 1;
|
|
5464
|
+
default: {
|
|
5465
|
+
const exhaustive = operator;
|
|
5466
|
+
return exhaustive;
|
|
5467
|
+
}
|
|
5468
|
+
}
|
|
5469
|
+
}
|
|
5470
|
+
|
|
5471
|
+
// src/computed/compiler.ts
|
|
5472
|
+
var ComputedFormulaCompileError = class extends Error {
|
|
5473
|
+
constructor(message) {
|
|
5474
|
+
super(message);
|
|
5475
|
+
this.name = "ComputedFormulaCompileError";
|
|
5476
|
+
}
|
|
5477
|
+
};
|
|
5478
|
+
function compileComputedFormula(input) {
|
|
5479
|
+
const ast = parseCompileFormula(input.expression);
|
|
5480
|
+
const inferred = inferNode(ast, input.schema);
|
|
5481
|
+
const inferredReturnType = valueTypeToReturnType(inferred.valueType);
|
|
5482
|
+
if (inferredReturnType !== input.returnType) {
|
|
5483
|
+
throw new ComputedFormulaCompileError(
|
|
5484
|
+
`Computed formula "${input.attributeName}" returns "${inferredReturnType}", expected "${input.returnType}"`
|
|
5485
|
+
);
|
|
5486
|
+
}
|
|
5487
|
+
if (isSelectLikeReturnType(input.returnType) && inferred.optionsSource === void 0) {
|
|
5488
|
+
throw new ComputedFormulaCompileError(
|
|
5489
|
+
`Computed formula "${input.attributeName}" returns "${input.returnType}" without optionsSource`
|
|
5490
|
+
);
|
|
5491
|
+
}
|
|
5492
|
+
const optionsDependencies = inferred.optionsSource === void 0 ? [] : [{ kind: "options", source: inferred.optionsSource }];
|
|
5493
|
+
const dependencies = uniqueDependencies([...inferred.dependencies, ...optionsDependencies]);
|
|
5494
|
+
return {
|
|
5495
|
+
kind: "formula",
|
|
5496
|
+
attributeName: input.attributeName,
|
|
5497
|
+
expression: input.expression,
|
|
5498
|
+
returnType: input.returnType,
|
|
5499
|
+
valueType: inferred.valueType,
|
|
5500
|
+
dependencies,
|
|
5501
|
+
...inferred.optionsSource ? { optionsSource: inferred.optionsSource } : {},
|
|
5502
|
+
...inferred.targetAttributeType ? { targetAttributeType: inferred.targetAttributeType } : {},
|
|
5503
|
+
dependenciesHash: createDependenciesHash({
|
|
5504
|
+
expression: input.expression,
|
|
5505
|
+
returnType: input.returnType,
|
|
5506
|
+
dependencies
|
|
5507
|
+
})
|
|
5508
|
+
};
|
|
5509
|
+
}
|
|
5510
|
+
function assertAcyclicComputedDependencies(nodes) {
|
|
5511
|
+
const graph = new Map(nodes.map((node) => [node.attributeName, node]));
|
|
5512
|
+
const visited = /* @__PURE__ */ new Set();
|
|
5513
|
+
const visit = (attributeName, activePath) => {
|
|
5514
|
+
const cycleStart = activePath.indexOf(attributeName);
|
|
5515
|
+
if (cycleStart !== -1) {
|
|
5516
|
+
const cycle = [...activePath.slice(cycleStart), attributeName];
|
|
5517
|
+
throw new ComputedFormulaCompileError(`Computed field cycle detected: ${cycle.join(" -> ")}`);
|
|
5518
|
+
}
|
|
5519
|
+
if (visited.has(attributeName)) {
|
|
5520
|
+
return;
|
|
5521
|
+
}
|
|
5522
|
+
const node = graph.get(attributeName);
|
|
5523
|
+
if (node === void 0) {
|
|
5524
|
+
return;
|
|
5525
|
+
}
|
|
5526
|
+
const nextPath = [...activePath, attributeName];
|
|
5527
|
+
for (const dependency of node.dependencies) {
|
|
5528
|
+
if (dependency.kind === "computed") {
|
|
5529
|
+
visit(dependency.attributeName, nextPath);
|
|
5530
|
+
}
|
|
5531
|
+
}
|
|
5532
|
+
visited.add(attributeName);
|
|
5533
|
+
};
|
|
5534
|
+
for (const node of nodes) {
|
|
5535
|
+
visit(node.attributeName, []);
|
|
5536
|
+
}
|
|
5537
|
+
}
|
|
5538
|
+
function parseCompileFormula(expression) {
|
|
5539
|
+
try {
|
|
5540
|
+
return parseComputedFormula(expression);
|
|
5541
|
+
} catch (error) {
|
|
5542
|
+
if (error instanceof Error) {
|
|
5543
|
+
throw new ComputedFormulaCompileError(error.message);
|
|
5544
|
+
}
|
|
5545
|
+
throw error;
|
|
5546
|
+
}
|
|
5547
|
+
}
|
|
5548
|
+
function inferNode(node, schema) {
|
|
5549
|
+
switch (node.kind) {
|
|
5550
|
+
case "literal":
|
|
5551
|
+
return {
|
|
5552
|
+
valueType: literalToValueType(node.value),
|
|
5553
|
+
dependencies: [],
|
|
5554
|
+
...typeof node.value === "string" ? { literalString: node.value } : {}
|
|
5555
|
+
};
|
|
5556
|
+
case "path":
|
|
5557
|
+
return inferPath(node.parts, schema);
|
|
5558
|
+
case "call":
|
|
5559
|
+
return inferCall(node.functionName, node.args, schema);
|
|
5560
|
+
case "binary":
|
|
5561
|
+
return inferBinary(
|
|
5562
|
+
node.operator,
|
|
5563
|
+
inferNode(node.left, schema),
|
|
5564
|
+
inferNode(node.right, schema)
|
|
5565
|
+
);
|
|
5566
|
+
default: {
|
|
5567
|
+
const exhaustive = node;
|
|
5568
|
+
return exhaustive;
|
|
5569
|
+
}
|
|
5570
|
+
}
|
|
5571
|
+
}
|
|
5572
|
+
function literalToValueType(value) {
|
|
5573
|
+
switch (typeof value) {
|
|
5574
|
+
case "string":
|
|
5575
|
+
return { kind: "scalar", type: "text" };
|
|
5576
|
+
case "number":
|
|
5577
|
+
return { kind: "scalar", type: "number" };
|
|
5578
|
+
case "boolean":
|
|
5579
|
+
return { kind: "scalar", type: "boolean" };
|
|
5580
|
+
case "object":
|
|
5581
|
+
return { kind: "scalar", type: "text" };
|
|
5582
|
+
default: {
|
|
5583
|
+
const exhaustive = value;
|
|
5584
|
+
return exhaustive;
|
|
5585
|
+
}
|
|
5586
|
+
}
|
|
5587
|
+
}
|
|
5588
|
+
function inferPath(parts, schema) {
|
|
5589
|
+
if (parts.length === 1) {
|
|
5590
|
+
const attributeName = parts[0];
|
|
5591
|
+
const attribute = findAttribute(schema, attributeName);
|
|
5592
|
+
if (attribute === void 0) {
|
|
5593
|
+
throw new ComputedFormulaCompileError(
|
|
5594
|
+
`Unknown attribute "${attributeName}" on object "${schema.objectName}"`
|
|
5595
|
+
);
|
|
5596
|
+
}
|
|
5597
|
+
if (attribute.type === "relation") {
|
|
5598
|
+
return {
|
|
5599
|
+
valueType: relationToValueType(),
|
|
5600
|
+
dependencies: [
|
|
5601
|
+
{
|
|
5602
|
+
kind: "relation",
|
|
5603
|
+
relationName: attribute.name,
|
|
5604
|
+
cardinality: relationCardinality(attribute)
|
|
5605
|
+
}
|
|
5606
|
+
],
|
|
5607
|
+
targetAttributeType: attribute.type
|
|
5608
|
+
};
|
|
5609
|
+
}
|
|
5610
|
+
return {
|
|
5611
|
+
valueType: attributeToValueType(attribute),
|
|
5612
|
+
dependencies: [localDependencyFor(attribute)],
|
|
5613
|
+
optionsSource: optionsSourceForAttribute(schema.objectName, attribute),
|
|
5614
|
+
targetAttributeType: attribute.type
|
|
5615
|
+
};
|
|
5616
|
+
}
|
|
5617
|
+
if (parts.length === 2) {
|
|
5618
|
+
const [relationName, targetAttributeName] = parts;
|
|
5619
|
+
const relation2 = findAttribute(schema, relationName);
|
|
5620
|
+
if (relation2 === void 0) {
|
|
5621
|
+
throw new ComputedFormulaCompileError(
|
|
5622
|
+
`Unknown attribute "${relationName}" on object "${schema.objectName}"`
|
|
5623
|
+
);
|
|
5624
|
+
}
|
|
5625
|
+
if (relation2.type !== "relation") {
|
|
5626
|
+
throw new ComputedFormulaCompileError(
|
|
5627
|
+
`Attribute "${relationName}" on object "${schema.objectName}" is not a relation`
|
|
5628
|
+
);
|
|
5629
|
+
}
|
|
5630
|
+
const targetObjectName = getRelationTargetObjectName(relation2);
|
|
5631
|
+
if (targetObjectName === void 0) {
|
|
5632
|
+
throw new ComputedFormulaCompileError(
|
|
5633
|
+
`Relation "${relationName}" on object "${schema.objectName}" is missing targetObjectName`
|
|
5634
|
+
);
|
|
5635
|
+
}
|
|
5636
|
+
const targetObject = schema.relatedObjects?.[targetObjectName];
|
|
5637
|
+
if (targetObject === void 0) {
|
|
5638
|
+
throw new ComputedFormulaCompileError(`Unknown related object "${targetObjectName}"`);
|
|
5639
|
+
}
|
|
5640
|
+
const targetAttribute = findAttribute(targetObject, targetAttributeName);
|
|
5641
|
+
if (targetAttribute === void 0) {
|
|
5642
|
+
throw new ComputedFormulaCompileError(
|
|
5643
|
+
`Unknown attribute "${targetAttributeName}" on object "${targetObject.objectName}"`
|
|
5644
|
+
);
|
|
5645
|
+
}
|
|
5646
|
+
const targetValueType = attributeToValueType(targetAttribute);
|
|
5647
|
+
return {
|
|
5648
|
+
valueType: relation2.cardinality === "many" ? asCollectionValueType(targetValueType) : targetValueType,
|
|
5649
|
+
dependencies: [
|
|
5650
|
+
{
|
|
5651
|
+
kind: "relation",
|
|
5652
|
+
relationName,
|
|
5653
|
+
targetAttributeName,
|
|
5654
|
+
cardinality: relationCardinality(relation2)
|
|
5655
|
+
}
|
|
5656
|
+
],
|
|
5657
|
+
optionsSource: optionsSourceForAttribute(targetObject.objectName, targetAttribute),
|
|
5658
|
+
targetAttributeType: targetAttribute.type
|
|
5659
|
+
};
|
|
5660
|
+
}
|
|
5661
|
+
throw new ComputedFormulaCompileError("Computed paths support at most one relation hop");
|
|
5662
|
+
}
|
|
5663
|
+
function findAttribute(object2, attributeName) {
|
|
5664
|
+
return object2.attributes.find((attribute) => attribute.name === attributeName);
|
|
5665
|
+
}
|
|
5666
|
+
function getRelationTargetObjectName(attribute) {
|
|
5667
|
+
if (attribute.targetObjectName !== void 0) {
|
|
5668
|
+
return attribute.targetObjectName;
|
|
5669
|
+
}
|
|
5670
|
+
if (attribute.targets?.length === 1) {
|
|
5671
|
+
const [target] = attribute.targets;
|
|
5672
|
+
return target?.object === "*" ? void 0 : target?.object;
|
|
5673
|
+
}
|
|
5674
|
+
return void 0;
|
|
5675
|
+
}
|
|
5676
|
+
function relationCardinality(attribute) {
|
|
5677
|
+
return attribute.cardinality ?? "one";
|
|
5678
|
+
}
|
|
5679
|
+
function localDependencyFor(attribute) {
|
|
5680
|
+
if (attribute.type === "formula" || attribute.type === "rollup") {
|
|
5681
|
+
return { kind: "computed", attributeName: attribute.name };
|
|
5682
|
+
}
|
|
5683
|
+
return { kind: "local", attributeName: attribute.name };
|
|
5684
|
+
}
|
|
5685
|
+
function attributeToValueType(attribute) {
|
|
5686
|
+
if (attribute.type === "formula" || attribute.type === "rollup") {
|
|
5687
|
+
if (attribute.returnType === void 0) {
|
|
5688
|
+
throw new ComputedFormulaCompileError(
|
|
5689
|
+
`Computed attribute "${attribute.name}" is missing returnType`
|
|
5690
|
+
);
|
|
5691
|
+
}
|
|
5692
|
+
return returnTypeToValueType(attribute.returnType);
|
|
5693
|
+
}
|
|
5694
|
+
switch (attribute.type) {
|
|
5695
|
+
case "number":
|
|
5696
|
+
case "currency":
|
|
5697
|
+
return { kind: "scalar", type: "number" };
|
|
5698
|
+
case "checkbox":
|
|
5699
|
+
return { kind: "scalar", type: "boolean" };
|
|
5700
|
+
case "date":
|
|
5701
|
+
return { kind: "scalar", type: "date" };
|
|
5702
|
+
case "select":
|
|
5703
|
+
case "status":
|
|
5704
|
+
return { kind: "scalar", type: "select" };
|
|
5705
|
+
case "multiselect":
|
|
5706
|
+
return { kind: "collection", itemType: "select" };
|
|
5707
|
+
case "text":
|
|
5708
|
+
case "richtext":
|
|
5709
|
+
case "phone":
|
|
5710
|
+
case "file":
|
|
5711
|
+
case "user":
|
|
5712
|
+
case "location":
|
|
5713
|
+
case "relation":
|
|
5714
|
+
case "document":
|
|
5715
|
+
return { kind: "scalar", type: "text" };
|
|
5716
|
+
default: {
|
|
5717
|
+
const exhaustive = attribute.type;
|
|
5718
|
+
return exhaustive;
|
|
5719
|
+
}
|
|
5720
|
+
}
|
|
5721
|
+
}
|
|
5722
|
+
function relationToValueType() {
|
|
5723
|
+
return { kind: "collection", itemType: "text" };
|
|
5724
|
+
}
|
|
5725
|
+
function returnTypeToValueType(returnType) {
|
|
5726
|
+
if (returnType === "multiselect") {
|
|
5727
|
+
return { kind: "collection", itemType: "select" };
|
|
5728
|
+
}
|
|
5729
|
+
return { kind: "scalar", type: returnType };
|
|
5730
|
+
}
|
|
5731
|
+
function asCollectionValueType(valueType) {
|
|
5732
|
+
if (valueType.kind === "collection") {
|
|
5733
|
+
return valueType;
|
|
5734
|
+
}
|
|
5735
|
+
return { kind: "collection", itemType: valueType.type };
|
|
5736
|
+
}
|
|
5737
|
+
function optionsSourceForAttribute(objectName, attribute) {
|
|
5738
|
+
if (attribute.type === "formula" || attribute.type === "rollup") {
|
|
5739
|
+
if (!isSelectLikeReturnType(attribute.returnType)) {
|
|
5740
|
+
return void 0;
|
|
5741
|
+
}
|
|
5742
|
+
return attribute.optionsSource;
|
|
5743
|
+
}
|
|
5744
|
+
if (attribute.type !== "select" && attribute.type !== "status" && attribute.type !== "multiselect") {
|
|
5745
|
+
return void 0;
|
|
5746
|
+
}
|
|
5747
|
+
return attribute.optionsSource ?? {
|
|
5748
|
+
objectName,
|
|
5749
|
+
attributeName: attribute.name,
|
|
5750
|
+
...attribute.id ? { attributeId: attribute.id } : {}
|
|
5751
|
+
};
|
|
5752
|
+
}
|
|
5753
|
+
function inferCall(functionName, args, schema) {
|
|
5754
|
+
if (!isComputedFunctionName(functionName)) {
|
|
5755
|
+
throw new ComputedFormulaCompileError(`Unknown computed function "${functionName}"`);
|
|
5756
|
+
}
|
|
5757
|
+
const signature = COMPUTED_FUNCTIONS[functionName];
|
|
5758
|
+
if (args.length < signature.minArgs || signature.maxArgs !== void 0 && args.length > signature.maxArgs) {
|
|
5759
|
+
const expected = signature.maxArgs === void 0 || signature.maxArgs === signature.minArgs ? String(signature.minArgs) : `${signature.minArgs}-${signature.maxArgs}`;
|
|
5760
|
+
throw new ComputedFormulaCompileError(
|
|
5761
|
+
`Function "${functionName}" expects ${expected} arguments, received ${args.length}`
|
|
5762
|
+
);
|
|
5763
|
+
}
|
|
5764
|
+
const inferredArgs = args.map((arg) => inferNode(arg, schema));
|
|
5765
|
+
return inferFunctionReturn(functionName, inferredArgs);
|
|
5766
|
+
}
|
|
5767
|
+
function inferFunctionReturn(functionName, args) {
|
|
5768
|
+
const dependencies = uniqueDependencies(args.flatMap((arg) => arg.dependencies));
|
|
5769
|
+
switch (functionName) {
|
|
5770
|
+
case "if":
|
|
5771
|
+
return inferIfFunction(args, dependencies);
|
|
5772
|
+
case "concat":
|
|
5773
|
+
ensureNoCollections(args, `Function "${functionName}" does not accept collection values`);
|
|
5774
|
+
return { valueType: { kind: "scalar", type: "text" }, dependencies };
|
|
5775
|
+
case "sum":
|
|
5776
|
+
case "avg":
|
|
5777
|
+
ensureNumericCollectionOrScalar(functionName, args[0]);
|
|
5778
|
+
return { valueType: { kind: "scalar", type: "number" }, dependencies };
|
|
5779
|
+
case "count":
|
|
5780
|
+
case "count_empty":
|
|
5781
|
+
case "count_not_empty":
|
|
5782
|
+
case "count_unique":
|
|
5783
|
+
case "percent_empty":
|
|
5784
|
+
case "percent_not_empty":
|
|
5785
|
+
if (functionName === "count_unique") {
|
|
5786
|
+
ensureUniqueCountSource(args[0]);
|
|
5787
|
+
} else {
|
|
5788
|
+
ensureCollectionSource(functionName, args[0]);
|
|
5789
|
+
}
|
|
5790
|
+
return { valueType: { kind: "scalar", type: "number" }, dependencies };
|
|
5791
|
+
case "min":
|
|
5792
|
+
case "max":
|
|
5793
|
+
ensureMinMaxSource(functionName, args[0]);
|
|
5794
|
+
return {
|
|
5795
|
+
valueType: sourceItemValueType(args[0]),
|
|
5796
|
+
dependencies,
|
|
5797
|
+
optionsSource: args[0].optionsSource,
|
|
5798
|
+
targetAttributeType: args[0].targetAttributeType
|
|
5799
|
+
};
|
|
5800
|
+
case "earliest":
|
|
5801
|
+
case "latest":
|
|
5802
|
+
ensureDateCollectionOrScalar(functionName, args[0]);
|
|
5803
|
+
return {
|
|
5804
|
+
valueType: { kind: "scalar", type: "date" },
|
|
5805
|
+
dependencies,
|
|
5806
|
+
targetAttributeType: args[0].targetAttributeType
|
|
5807
|
+
};
|
|
5808
|
+
case "unique":
|
|
5809
|
+
return inferUniqueFunction(args[0], dependencies);
|
|
5810
|
+
case "first":
|
|
5811
|
+
return inferFirstFunction(args[0], dependencies);
|
|
5812
|
+
default: {
|
|
5813
|
+
const exhaustive = functionName;
|
|
5814
|
+
return exhaustive;
|
|
5815
|
+
}
|
|
5816
|
+
}
|
|
5817
|
+
}
|
|
5818
|
+
function inferIfFunction(args, dependencies) {
|
|
5819
|
+
ensureScalarType(args[0].valueType, "boolean", 'Function "if" condition must be boolean');
|
|
5820
|
+
const thenValue = args[1];
|
|
5821
|
+
const elseValue = args[2];
|
|
5822
|
+
const valueType = resolveIfBranchValueType(thenValue, elseValue);
|
|
5823
|
+
const optionsSource = resolveIfOptionsSource(thenValue, elseValue);
|
|
5824
|
+
if (valueType === void 0) {
|
|
5825
|
+
throw new ComputedFormulaCompileError('Function "if" branches must return the same type');
|
|
5826
|
+
}
|
|
5827
|
+
return {
|
|
5828
|
+
valueType,
|
|
5829
|
+
dependencies,
|
|
5830
|
+
...optionsSource ? { optionsSource } : {},
|
|
5831
|
+
targetAttributeType: thenValue.targetAttributeType ?? elseValue.targetAttributeType
|
|
5832
|
+
};
|
|
5833
|
+
}
|
|
5834
|
+
function inferUniqueFunction(source, dependencies) {
|
|
5835
|
+
if (source.valueType.kind !== "collection" || source.valueType.itemType !== "select") {
|
|
5836
|
+
throw new ComputedFormulaCompileError('Function "unique" expects a select collection');
|
|
5837
|
+
}
|
|
5838
|
+
return {
|
|
5839
|
+
valueType: { kind: "collection", itemType: "select" },
|
|
5840
|
+
dependencies,
|
|
5841
|
+
optionsSource: source.optionsSource,
|
|
5842
|
+
targetAttributeType: source.targetAttributeType
|
|
5843
|
+
};
|
|
5844
|
+
}
|
|
5845
|
+
function inferFirstFunction(source, dependencies) {
|
|
5846
|
+
if (source.valueType.kind !== "collection") {
|
|
5847
|
+
throw new ComputedFormulaCompileError('Function "first" expects a collection');
|
|
5848
|
+
}
|
|
5849
|
+
return {
|
|
5850
|
+
valueType: { kind: "scalar", type: source.valueType.itemType },
|
|
5851
|
+
dependencies,
|
|
5852
|
+
optionsSource: source.optionsSource,
|
|
5853
|
+
targetAttributeType: source.targetAttributeType
|
|
5854
|
+
};
|
|
5855
|
+
}
|
|
5856
|
+
function inferBinary(operator, left, right) {
|
|
5857
|
+
const dependencies = uniqueDependencies([...left.dependencies, ...right.dependencies]);
|
|
5858
|
+
if (operator === "+" || operator === "-" || operator === "*" || operator === "/") {
|
|
5859
|
+
if (left.valueType.kind === "collection" || right.valueType.kind === "collection") {
|
|
5860
|
+
throw new ComputedFormulaCompileError(
|
|
5861
|
+
`Cannot use collection values with arithmetic operator "${operator}"`
|
|
5862
|
+
);
|
|
5863
|
+
}
|
|
5864
|
+
if (operator === "+" && (left.valueType.type === "text" || right.valueType.type === "text")) {
|
|
5865
|
+
return { valueType: { kind: "scalar", type: "text" }, dependencies };
|
|
5866
|
+
}
|
|
5867
|
+
ensureScalarType(left.valueType, "number", `Operator "${operator}" expects number operands`);
|
|
5868
|
+
ensureScalarType(right.valueType, "number", `Operator "${operator}" expects number operands`);
|
|
5869
|
+
return { valueType: { kind: "scalar", type: "number" }, dependencies };
|
|
5870
|
+
}
|
|
5871
|
+
if (left.valueType.kind === "collection" || right.valueType.kind === "collection") {
|
|
5872
|
+
throw new ComputedFormulaCompileError(
|
|
5873
|
+
`Cannot use collection values with comparison operator "${operator}"`
|
|
5874
|
+
);
|
|
5875
|
+
}
|
|
5876
|
+
if (operator === "==" || operator === "!=") {
|
|
5877
|
+
if (left.valueType.type !== right.valueType.type) {
|
|
5878
|
+
throw new ComputedFormulaCompileError(
|
|
5879
|
+
`Operator "${operator}" expects operands with the same scalar type`
|
|
5880
|
+
);
|
|
5881
|
+
}
|
|
5882
|
+
return { valueType: { kind: "scalar", type: "boolean" }, dependencies };
|
|
5883
|
+
}
|
|
5884
|
+
if ((left.valueType.type !== "number" || right.valueType.type !== "number") && (left.valueType.type !== "date" || right.valueType.type !== "date")) {
|
|
5885
|
+
throw new ComputedFormulaCompileError(
|
|
5886
|
+
`Operator "${operator}" expects number or date operands of the same type`
|
|
5887
|
+
);
|
|
5888
|
+
}
|
|
5889
|
+
return { valueType: { kind: "scalar", type: "boolean" }, dependencies };
|
|
5890
|
+
}
|
|
5891
|
+
function ensureNoCollections(args, message) {
|
|
5892
|
+
for (const arg of args) {
|
|
5893
|
+
if (arg.valueType.kind === "collection") {
|
|
5894
|
+
throw new ComputedFormulaCompileError(message);
|
|
5895
|
+
}
|
|
5896
|
+
}
|
|
5897
|
+
}
|
|
5898
|
+
function ensureNumericCollectionOrScalar(functionName, expression) {
|
|
5899
|
+
const type = expression.valueType.kind === "collection" ? expression.valueType.itemType : expression.valueType.type;
|
|
5900
|
+
if (type !== "number") {
|
|
5901
|
+
throw new ComputedFormulaCompileError(`Function "${functionName}" expects number values`);
|
|
5902
|
+
}
|
|
5903
|
+
}
|
|
5904
|
+
function ensureDateCollectionOrScalar(functionName, expression) {
|
|
5905
|
+
const type = expression.valueType.kind === "collection" ? expression.valueType.itemType : expression.valueType.type;
|
|
5906
|
+
if (type !== "date") {
|
|
5907
|
+
throw new ComputedFormulaCompileError(`Function "${functionName}" expects date values`);
|
|
5908
|
+
}
|
|
5909
|
+
}
|
|
5910
|
+
function ensureMinMaxSource(functionName, expression) {
|
|
5911
|
+
if (expression.valueType.kind !== "collection") {
|
|
5912
|
+
throw new ComputedFormulaCompileError(`Function "${functionName}" expects a collection`);
|
|
5913
|
+
}
|
|
5914
|
+
const type = expression.valueType.itemType;
|
|
5915
|
+
if (type !== "number" && type !== "date") {
|
|
5916
|
+
throw new ComputedFormulaCompileError(
|
|
5917
|
+
`Function "${functionName}" expects number or date values`
|
|
5918
|
+
);
|
|
5919
|
+
}
|
|
5920
|
+
}
|
|
5921
|
+
function ensureCollectionSource(functionName, expression) {
|
|
5922
|
+
if (expression.valueType.kind !== "collection") {
|
|
5923
|
+
throw new ComputedFormulaCompileError(`Function "${functionName}" expects a collection`);
|
|
5924
|
+
}
|
|
5925
|
+
}
|
|
5926
|
+
function ensureUniqueCountSource(expression) {
|
|
5927
|
+
if (expression.valueType.kind !== "collection") {
|
|
5928
|
+
throw new ComputedFormulaCompileError('Function "count_unique" expects a collection');
|
|
5929
|
+
}
|
|
5930
|
+
}
|
|
5931
|
+
function ensureScalarType(valueType, expectedType, message) {
|
|
5932
|
+
if (valueType.kind !== "scalar" || valueType.type !== expectedType) {
|
|
5933
|
+
throw new ComputedFormulaCompileError(message);
|
|
5934
|
+
}
|
|
5935
|
+
}
|
|
5936
|
+
function sourceItemValueType(source) {
|
|
5937
|
+
if (source.valueType.kind === "scalar") {
|
|
5938
|
+
return source.valueType;
|
|
5939
|
+
}
|
|
5940
|
+
return { kind: "scalar", type: source.valueType.itemType };
|
|
5941
|
+
}
|
|
5942
|
+
function sameValueType(left, right) {
|
|
5943
|
+
if (left.kind === "scalar") {
|
|
5944
|
+
return right.kind === "scalar" && left.type === right.type;
|
|
5945
|
+
}
|
|
5946
|
+
return right.kind === "collection" && left.itemType === right.itemType;
|
|
5947
|
+
}
|
|
5948
|
+
function resolveIfBranchValueType(thenValue, elseValue) {
|
|
5949
|
+
if (sameValueType(thenValue.valueType, elseValue.valueType)) {
|
|
5950
|
+
return thenValue.valueType;
|
|
5951
|
+
}
|
|
5952
|
+
if (isSelectScalarWithStringLiteral(thenValue, elseValue)) {
|
|
5953
|
+
return thenValue.valueType;
|
|
5954
|
+
}
|
|
5955
|
+
if (isSelectScalarWithStringLiteral(elseValue, thenValue)) {
|
|
5956
|
+
return elseValue.valueType;
|
|
5957
|
+
}
|
|
5958
|
+
return void 0;
|
|
5959
|
+
}
|
|
5960
|
+
function isSelectScalarWithStringLiteral(selectValue, literalValue) {
|
|
5961
|
+
return selectValue.valueType.kind === "scalar" && selectValue.valueType.type === "select" && literalValue.valueType.kind === "scalar" && literalValue.valueType.type === "text" && literalValue.literalString !== void 0;
|
|
5962
|
+
}
|
|
5963
|
+
function resolveIfOptionsSource(thenValue, elseValue) {
|
|
5964
|
+
const thenSource = thenValue.optionsSource;
|
|
5965
|
+
const elseSource = elseValue.optionsSource;
|
|
5966
|
+
if (thenSource !== void 0 && elseSource !== void 0 && !sameOptionsSource(thenSource, elseSource)) {
|
|
5967
|
+
throw new ComputedFormulaCompileError(
|
|
5968
|
+
'Function "if" branches use incompatible options sources'
|
|
5969
|
+
);
|
|
5970
|
+
}
|
|
5971
|
+
return thenSource ?? elseSource;
|
|
5972
|
+
}
|
|
5973
|
+
function sameOptionsSource(left, right) {
|
|
5974
|
+
return left.objectName === right.objectName && left.attributeName === right.attributeName && left.attributeId === right.attributeId;
|
|
5975
|
+
}
|
|
5976
|
+
function isSelectLikeReturnType(returnType) {
|
|
5977
|
+
return returnType === "select" || returnType === "multiselect";
|
|
5978
|
+
}
|
|
5979
|
+
function valueTypeToReturnType(valueType) {
|
|
5980
|
+
if (valueType.kind === "scalar") {
|
|
5981
|
+
return valueType.type;
|
|
5982
|
+
}
|
|
5983
|
+
if (valueType.itemType === "select") {
|
|
5984
|
+
return "multiselect";
|
|
5985
|
+
}
|
|
5986
|
+
throw new ComputedFormulaCompileError(
|
|
5987
|
+
`Collection item type "${valueType.itemType}" cannot be used as a computed return type`
|
|
5988
|
+
);
|
|
5989
|
+
}
|
|
5990
|
+
function uniqueDependencies(dependencies) {
|
|
5991
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5992
|
+
const unique = [];
|
|
5993
|
+
for (const dependency of dependencies) {
|
|
5994
|
+
const key = JSON.stringify(stableNormalize(dependency));
|
|
5995
|
+
if (!seen.has(key)) {
|
|
5996
|
+
seen.add(key);
|
|
5997
|
+
unique.push(dependency);
|
|
5998
|
+
}
|
|
5999
|
+
}
|
|
6000
|
+
return unique;
|
|
6001
|
+
}
|
|
6002
|
+
function createDependenciesHash(input) {
|
|
6003
|
+
const sortedDependencies = input.dependencies.map((dependency) => stableNormalize(dependency)).sort((left, right) => compareStrings(JSON.stringify(left), JSON.stringify(right)));
|
|
6004
|
+
return JSON.stringify(
|
|
6005
|
+
stableNormalize({
|
|
6006
|
+
expression: input.expression,
|
|
6007
|
+
returnType: input.returnType,
|
|
6008
|
+
dependencies: sortedDependencies
|
|
6009
|
+
})
|
|
6010
|
+
);
|
|
6011
|
+
}
|
|
6012
|
+
function stableNormalize(value) {
|
|
6013
|
+
if (Array.isArray(value)) {
|
|
6014
|
+
return value.map((item) => stableNormalize(item));
|
|
6015
|
+
}
|
|
6016
|
+
if (value !== null && typeof value === "object") {
|
|
6017
|
+
return Object.fromEntries(
|
|
6018
|
+
Object.entries(value).filter(([, item]) => item !== void 0).sort(([left], [right]) => compareStrings(left, right)).map(([key, item]) => [key, stableNormalize(item)])
|
|
6019
|
+
);
|
|
6020
|
+
}
|
|
6021
|
+
return value;
|
|
6022
|
+
}
|
|
6023
|
+
function compareStrings(left, right) {
|
|
6024
|
+
if (left < right) {
|
|
6025
|
+
return -1;
|
|
6026
|
+
}
|
|
6027
|
+
if (left > right) {
|
|
6028
|
+
return 1;
|
|
6029
|
+
}
|
|
6030
|
+
return 0;
|
|
6031
|
+
}
|
|
6032
|
+
|
|
6033
|
+
// src/computed/rollup-compiler.ts
|
|
6034
|
+
function rollupToFormulaExpression(attr) {
|
|
6035
|
+
if (attr.function === "count") {
|
|
6036
|
+
return `count(${attr.relationAttribute})`;
|
|
6037
|
+
}
|
|
6038
|
+
return `${rollupFunctionToFormulaFunction(attr.function)}(${attr.relationAttribute}.${attr.targetAttribute})`;
|
|
6039
|
+
}
|
|
6040
|
+
function inferRollupReturnType(attr, schema) {
|
|
6041
|
+
if (attr.function === "count") {
|
|
6042
|
+
return "number";
|
|
6043
|
+
}
|
|
6044
|
+
const targetAttribute = findRollupTargetAttribute(attr, schema);
|
|
6045
|
+
switch (attr.function) {
|
|
6046
|
+
case "countValues":
|
|
6047
|
+
case "countEmpty":
|
|
6048
|
+
case "percentEmpty":
|
|
6049
|
+
case "percentNotEmpty":
|
|
6050
|
+
case "countUniqueValues":
|
|
6051
|
+
return "number";
|
|
6052
|
+
case "sum":
|
|
6053
|
+
case "avg":
|
|
6054
|
+
return "number";
|
|
6055
|
+
case "earliest":
|
|
6056
|
+
case "latest":
|
|
6057
|
+
return "date";
|
|
6058
|
+
case "original":
|
|
6059
|
+
if (!isSelectLikeAttributeType(targetAttribute.type)) {
|
|
6060
|
+
throw new ComputedFormulaCompileError(
|
|
6061
|
+
`Rollup "original" requires a select, status, or multiselect target`
|
|
6062
|
+
);
|
|
6063
|
+
}
|
|
6064
|
+
return "multiselect";
|
|
6065
|
+
default: {
|
|
6066
|
+
const exhaustive = attr.function;
|
|
6067
|
+
return exhaustive;
|
|
6068
|
+
}
|
|
6069
|
+
}
|
|
6070
|
+
}
|
|
6071
|
+
function compileRollupAttribute(attr, schema) {
|
|
6072
|
+
const expression = rollupToFormulaExpression(attr);
|
|
6073
|
+
const returnType = inferRollupReturnType(attr, schema);
|
|
6074
|
+
const plan = compileComputedFormula({
|
|
6075
|
+
attributeName: attr.name,
|
|
6076
|
+
expression,
|
|
6077
|
+
returnType,
|
|
6078
|
+
schema: schemaWithRollupRelationAsMany(attr, schema)
|
|
6079
|
+
});
|
|
6080
|
+
return {
|
|
6081
|
+
...plan,
|
|
6082
|
+
kind: "rollup"
|
|
6083
|
+
};
|
|
6084
|
+
}
|
|
6085
|
+
function schemaWithRollupRelationAsMany(attr, schema) {
|
|
6086
|
+
return {
|
|
6087
|
+
...schema,
|
|
6088
|
+
attributes: schema.attributes.map(
|
|
6089
|
+
(attribute) => attribute.name === attr.relationAttribute && attribute.type === "relation" ? { ...attribute, cardinality: "many" } : attribute
|
|
6090
|
+
)
|
|
6091
|
+
};
|
|
6092
|
+
}
|
|
6093
|
+
function rollupFunctionToFormulaFunction(functionName) {
|
|
6094
|
+
switch (functionName) {
|
|
6095
|
+
case "sum":
|
|
6096
|
+
case "avg":
|
|
6097
|
+
case "earliest":
|
|
6098
|
+
case "latest":
|
|
6099
|
+
return functionName;
|
|
6100
|
+
case "original":
|
|
6101
|
+
return "unique";
|
|
6102
|
+
case "countValues":
|
|
6103
|
+
return "count_not_empty";
|
|
6104
|
+
case "countEmpty":
|
|
6105
|
+
return "count_empty";
|
|
6106
|
+
case "percentEmpty":
|
|
6107
|
+
return "percent_empty";
|
|
6108
|
+
case "percentNotEmpty":
|
|
6109
|
+
return "percent_not_empty";
|
|
6110
|
+
case "countUniqueValues":
|
|
6111
|
+
return "count_unique";
|
|
6112
|
+
default: {
|
|
6113
|
+
const exhaustive = functionName;
|
|
6114
|
+
return exhaustive;
|
|
6115
|
+
}
|
|
6116
|
+
}
|
|
6117
|
+
}
|
|
6118
|
+
function findRollupTargetAttribute(attr, schema) {
|
|
6119
|
+
const relation2 = findAttribute2(schema, attr.relationAttribute);
|
|
6120
|
+
if (relation2 === void 0) {
|
|
6121
|
+
throw new ComputedFormulaCompileError(
|
|
6122
|
+
`Unknown relation attribute "${attr.relationAttribute}" on object "${schema.objectName}"`
|
|
6123
|
+
);
|
|
6124
|
+
}
|
|
6125
|
+
if (relation2.type !== "relation") {
|
|
6126
|
+
throw new ComputedFormulaCompileError(
|
|
6127
|
+
`Attribute "${attr.relationAttribute}" on object "${schema.objectName}" is not a relation`
|
|
6128
|
+
);
|
|
6129
|
+
}
|
|
6130
|
+
const targetObjectName = getRelationTargetObjectName2(relation2);
|
|
6131
|
+
if (targetObjectName === void 0) {
|
|
6132
|
+
throw new ComputedFormulaCompileError(
|
|
6133
|
+
`Relation "${attr.relationAttribute}" on object "${schema.objectName}" is missing targetObjectName`
|
|
6134
|
+
);
|
|
6135
|
+
}
|
|
6136
|
+
const targetObject = schema.relatedObjects?.[targetObjectName];
|
|
6137
|
+
if (targetObject === void 0) {
|
|
6138
|
+
throw new ComputedFormulaCompileError(`Unknown related object "${targetObjectName}"`);
|
|
6139
|
+
}
|
|
6140
|
+
const targetAttribute = findAttribute2(targetObject, attr.targetAttribute);
|
|
6141
|
+
if (targetAttribute === void 0) {
|
|
6142
|
+
throw new ComputedFormulaCompileError(
|
|
6143
|
+
`Unknown attribute "${attr.targetAttribute}" on object "${targetObject.objectName}"`
|
|
6144
|
+
);
|
|
6145
|
+
}
|
|
6146
|
+
return targetAttribute;
|
|
6147
|
+
}
|
|
6148
|
+
function findAttribute2(object2, attributeName) {
|
|
6149
|
+
return object2.attributes.find((attribute) => attribute.name === attributeName);
|
|
6150
|
+
}
|
|
6151
|
+
function getRelationTargetObjectName2(attribute) {
|
|
6152
|
+
if (attribute.targetObjectName !== void 0) {
|
|
6153
|
+
return attribute.targetObjectName;
|
|
6154
|
+
}
|
|
6155
|
+
if (attribute.targets?.length === 1) {
|
|
6156
|
+
const [target] = attribute.targets;
|
|
6157
|
+
return target?.object === "*" ? void 0 : target?.object;
|
|
6158
|
+
}
|
|
6159
|
+
return void 0;
|
|
6160
|
+
}
|
|
6161
|
+
function isSelectLikeAttributeType(type) {
|
|
6162
|
+
return type === "select" || type === "status" || type === "multiselect";
|
|
6163
|
+
}
|
|
6164
|
+
|
|
6165
|
+
// src/computed/evaluator.ts
|
|
6166
|
+
function evaluateAst(node, input) {
|
|
6167
|
+
switch (node.kind) {
|
|
6168
|
+
case "literal":
|
|
6169
|
+
return node.value;
|
|
6170
|
+
case "path":
|
|
6171
|
+
return resolvePath(input, node.parts);
|
|
6172
|
+
case "binary":
|
|
6173
|
+
return evaluateBinary(
|
|
6174
|
+
node.operator,
|
|
6175
|
+
evaluateAst(node.left, input),
|
|
6176
|
+
evaluateAst(node.right, input)
|
|
6177
|
+
);
|
|
6178
|
+
case "call":
|
|
6179
|
+
return evaluateCallNode(node.functionName, node.args, input);
|
|
6180
|
+
default:
|
|
6181
|
+
return unreachable(node);
|
|
6182
|
+
}
|
|
6183
|
+
}
|
|
6184
|
+
function evaluateCallNode(functionName, args, input) {
|
|
6185
|
+
assertComputedFunctionName(functionName);
|
|
6186
|
+
validateArity(functionName, args.length);
|
|
6187
|
+
if (functionName === "if") {
|
|
6188
|
+
return evaluateAst(args[0], input) ? evaluateAst(args[1], input) : evaluateAst(args[2], input);
|
|
6189
|
+
}
|
|
6190
|
+
return evaluateCall(
|
|
6191
|
+
functionName,
|
|
6192
|
+
args.map((arg) => evaluateAst(arg, input))
|
|
6193
|
+
);
|
|
6194
|
+
}
|
|
6195
|
+
function resolvePath(input, parts) {
|
|
6196
|
+
return parts.reduce((value, part) => resolvePathPart(value, part), input);
|
|
6197
|
+
}
|
|
6198
|
+
function resolvePathPart(value, part) {
|
|
6199
|
+
if (Array.isArray(value)) {
|
|
6200
|
+
return value.map((item) => resolvePathPart(item, part));
|
|
6201
|
+
}
|
|
6202
|
+
if (!isPlainRecord(value)) {
|
|
6203
|
+
return null;
|
|
6204
|
+
}
|
|
6205
|
+
if (Object.hasOwn(value, part)) {
|
|
6206
|
+
return value[part] ?? null;
|
|
4660
6207
|
}
|
|
4661
|
-
|
|
4662
|
-
|
|
4663
|
-
|
|
6208
|
+
const nestedValues = value.values;
|
|
6209
|
+
if (isPlainRecord(nestedValues) && Object.hasOwn(nestedValues, part)) {
|
|
6210
|
+
return nestedValues[part] ?? null;
|
|
4664
6211
|
}
|
|
4665
|
-
|
|
4666
|
-
|
|
6212
|
+
return null;
|
|
6213
|
+
}
|
|
6214
|
+
function evaluateBinary(operator, left, right) {
|
|
6215
|
+
switch (operator) {
|
|
6216
|
+
case "+":
|
|
6217
|
+
return add(left, right);
|
|
6218
|
+
case "-":
|
|
6219
|
+
return numericBinary(left, right, (leftNumber, rightNumber) => leftNumber - rightNumber);
|
|
6220
|
+
case "*":
|
|
6221
|
+
return numericBinary(left, right, (leftNumber, rightNumber) => leftNumber * rightNumber);
|
|
6222
|
+
case "/":
|
|
6223
|
+
return numericBinary(left, right, (leftNumber, rightNumber) => leftNumber / rightNumber);
|
|
6224
|
+
case ">":
|
|
6225
|
+
return compareWith(left, right, (result) => result > 0);
|
|
6226
|
+
case "<":
|
|
6227
|
+
return compareWith(left, right, (result) => result < 0);
|
|
6228
|
+
case ">=":
|
|
6229
|
+
return compareWith(left, right, (result) => result >= 0);
|
|
6230
|
+
case "<=":
|
|
6231
|
+
return compareWith(left, right, (result) => result <= 0);
|
|
6232
|
+
case "==":
|
|
6233
|
+
return equalScalar(left, right);
|
|
6234
|
+
case "!=":
|
|
6235
|
+
return !equalScalar(left, right);
|
|
6236
|
+
default:
|
|
6237
|
+
return unreachable(operator);
|
|
4667
6238
|
}
|
|
4668
|
-
return result;
|
|
4669
6239
|
}
|
|
4670
|
-
function
|
|
4671
|
-
|
|
4672
|
-
|
|
4673
|
-
const preservedTabs = getPreservedDetailTabs(view, options);
|
|
4674
|
-
const mergedTabs = mergeDetailTabs(defaultView.config.tabs, preservedTabs);
|
|
4675
|
-
const config = {
|
|
4676
|
-
...defaultView.config,
|
|
4677
|
-
tabs: mergedTabs
|
|
4678
|
-
};
|
|
4679
|
-
const resetView = {
|
|
4680
|
-
...defaultView,
|
|
4681
|
-
config
|
|
4682
|
-
};
|
|
4683
|
-
if (preserveMetadata) {
|
|
4684
|
-
resetView.id = view.id;
|
|
4685
|
-
resetView.name = view.name;
|
|
4686
|
-
resetView.label = view.label;
|
|
4687
|
-
resetView.description = view.description;
|
|
4688
|
-
resetView.icon = view.icon;
|
|
6240
|
+
function add(left, right) {
|
|
6241
|
+
if (typeof left === "string" || typeof right === "string") {
|
|
6242
|
+
return `${left ?? ""}${right ?? ""}`;
|
|
4689
6243
|
}
|
|
4690
|
-
return
|
|
6244
|
+
return numericBinary(left, right, (leftNumber, rightNumber) => leftNumber + rightNumber);
|
|
4691
6245
|
}
|
|
4692
|
-
function
|
|
4693
|
-
const
|
|
4694
|
-
|
|
4695
|
-
|
|
4696
|
-
|
|
4697
|
-
|
|
4698
|
-
|
|
4699
|
-
|
|
4700
|
-
|
|
4701
|
-
resetView.label = view.label;
|
|
4702
|
-
resetView.description = view.description;
|
|
4703
|
-
resetView.icon = view.icon;
|
|
6246
|
+
function compareWith(left, right, predicate) {
|
|
6247
|
+
const result = compare(left, right);
|
|
6248
|
+
return result === null ? null : predicate(result);
|
|
6249
|
+
}
|
|
6250
|
+
function compare(left, right) {
|
|
6251
|
+
const leftNumber = toFiniteNumber(left);
|
|
6252
|
+
const rightNumber = toFiniteNumber(right);
|
|
6253
|
+
if (leftNumber !== null && rightNumber !== null) {
|
|
6254
|
+
return leftNumber - rightNumber;
|
|
4704
6255
|
}
|
|
4705
|
-
|
|
6256
|
+
const leftDate = toDate(left);
|
|
6257
|
+
const rightDate = toDate(right);
|
|
6258
|
+
return leftDate && rightDate ? leftDate.getTime() - rightDate.getTime() : null;
|
|
4706
6259
|
}
|
|
4707
|
-
function
|
|
4708
|
-
if (
|
|
4709
|
-
return
|
|
6260
|
+
function equalScalar(left, right) {
|
|
6261
|
+
if (left === right) {
|
|
6262
|
+
return true;
|
|
4710
6263
|
}
|
|
4711
|
-
if (
|
|
4712
|
-
return
|
|
6264
|
+
if (left === null || left === void 0 || right === null || right === void 0) {
|
|
6265
|
+
return false;
|
|
4713
6266
|
}
|
|
4714
|
-
|
|
6267
|
+
return compare(left, right) === 0;
|
|
4715
6268
|
}
|
|
4716
|
-
|
|
4717
|
-
|
|
4718
|
-
|
|
4719
|
-
if (tabId) {
|
|
4720
|
-
const tab = config.tabs.find((t) => t.id === tabId);
|
|
4721
|
-
if (tab) return tab;
|
|
6269
|
+
function assertComputedFunctionName(name) {
|
|
6270
|
+
if (!isComputedFunctionName(name)) {
|
|
6271
|
+
throw new Error(`Unknown computed function "${name}"`);
|
|
4722
6272
|
}
|
|
4723
|
-
return config.tabs.find((t) => t.default) ?? config.tabs[0];
|
|
4724
6273
|
}
|
|
4725
|
-
|
|
4726
|
-
|
|
4727
|
-
|
|
4728
|
-
|
|
4729
|
-
this.forms = /* @__PURE__ */ new Map();
|
|
4730
|
-
}
|
|
4731
|
-
/**
|
|
4732
|
-
* Register a form definition (or array of definitions).
|
|
4733
|
-
* @throws Error if a form with the same name is already registered.
|
|
4734
|
-
*/
|
|
4735
|
-
register(formOrForms) {
|
|
4736
|
-
const forms = Array.isArray(formOrForms) ? formOrForms : [formOrForms];
|
|
4737
|
-
for (const f of forms) {
|
|
4738
|
-
if (this.forms.has(f.name)) {
|
|
4739
|
-
throw new Error(`[FormRegistry] Duplicate form: "${f.name}"`);
|
|
4740
|
-
}
|
|
4741
|
-
this.forms.set(f.name, f);
|
|
4742
|
-
}
|
|
4743
|
-
return this;
|
|
4744
|
-
}
|
|
4745
|
-
/**
|
|
4746
|
-
* Get a form definition by name.
|
|
4747
|
-
*/
|
|
4748
|
-
get(name) {
|
|
4749
|
-
return this.forms.get(name);
|
|
6274
|
+
function validateArity(functionName, actual) {
|
|
6275
|
+
const { minArgs, maxArgs } = COMPUTED_FUNCTIONS[functionName];
|
|
6276
|
+
if (actual >= minArgs && actual <= (maxArgs ?? Number.POSITIVE_INFINITY)) {
|
|
6277
|
+
return;
|
|
4750
6278
|
}
|
|
4751
|
-
|
|
4752
|
-
|
|
4753
|
-
|
|
4754
|
-
|
|
4755
|
-
|
|
6279
|
+
const range = maxArgs === void 0 ? `at least ${minArgs}` : `between ${minArgs} and ${maxArgs}`;
|
|
6280
|
+
throw new Error(`Function "${functionName}" expects ${range} arguments, received ${actual}`);
|
|
6281
|
+
}
|
|
6282
|
+
function evaluateCall(functionName, args) {
|
|
6283
|
+
switch (functionName) {
|
|
6284
|
+
case "concat":
|
|
6285
|
+
return args.filter((arg) => arg !== null && arg !== void 0).map(String).join("");
|
|
6286
|
+
case "sum":
|
|
6287
|
+
return numericValues(args).reduce((total, value) => total + value, 0);
|
|
6288
|
+
case "avg": {
|
|
6289
|
+
const values = numericValues(args);
|
|
6290
|
+
return values.length > 0 ? values.reduce((total, value) => total + value, 0) / values.length : null;
|
|
6291
|
+
}
|
|
6292
|
+
case "count":
|
|
6293
|
+
return collectionItems(args[0]).length;
|
|
6294
|
+
case "min":
|
|
6295
|
+
return minMax(args, "min");
|
|
6296
|
+
case "max":
|
|
6297
|
+
return minMax(args, "max");
|
|
6298
|
+
case "earliest":
|
|
6299
|
+
return dateMinMax(args, "min");
|
|
6300
|
+
case "latest":
|
|
6301
|
+
return dateMinMax(args, "max");
|
|
6302
|
+
case "unique":
|
|
6303
|
+
return uniqueNonEmpty(collectionItems(args[0]));
|
|
6304
|
+
case "first":
|
|
6305
|
+
return collectionItems(args[0]).find((item) => item !== null && item !== void 0) ?? null;
|
|
6306
|
+
case "percent_empty":
|
|
6307
|
+
return percentByEmptyState(args[0], true);
|
|
6308
|
+
case "percent_not_empty":
|
|
6309
|
+
return percentByEmptyState(args[0], false);
|
|
6310
|
+
case "count_empty":
|
|
6311
|
+
return collectionItems(args[0]).filter(isEmptyValue).length;
|
|
6312
|
+
case "count_not_empty":
|
|
6313
|
+
return collectionItems(args[0]).filter((item) => !isEmptyValue(item)).length;
|
|
6314
|
+
case "count_unique":
|
|
6315
|
+
return uniqueNonEmpty(collectionItems(args[0])).length;
|
|
6316
|
+
default:
|
|
6317
|
+
return unreachable(functionName);
|
|
4756
6318
|
}
|
|
4757
|
-
|
|
4758
|
-
|
|
4759
|
-
|
|
4760
|
-
|
|
4761
|
-
|
|
6319
|
+
}
|
|
6320
|
+
function unreachable(value) {
|
|
6321
|
+
return value;
|
|
6322
|
+
}
|
|
6323
|
+
function numericValues(values) {
|
|
6324
|
+
return flatten(values).map(toFiniteNumber).filter((value) => value !== null);
|
|
6325
|
+
}
|
|
6326
|
+
function numericBinary(left, right, operation) {
|
|
6327
|
+
const leftNumber = toFiniteNumber(left);
|
|
6328
|
+
const rightNumber = toFiniteNumber(right);
|
|
6329
|
+
if (leftNumber === null || rightNumber === null) {
|
|
6330
|
+
return null;
|
|
4762
6331
|
}
|
|
4763
|
-
|
|
4764
|
-
|
|
4765
|
-
var DOS_PROTECTION_LIMITS = {
|
|
4766
|
-
MAX_EXPRESSION_LENGTH: 1e4,
|
|
4767
|
-
MAX_TOKEN_COUNT: 1e3,
|
|
4768
|
-
MAX_NESTING_DEPTH: 50
|
|
4769
|
-
};
|
|
4770
|
-
function createFormulaParser() {
|
|
4771
|
-
const parser = new exprEval.Parser();
|
|
4772
|
-
parser.functions.IF = (condition, thenValue, elseValue) => condition ? thenValue : elseValue;
|
|
4773
|
-
parser.functions.AND = (...args) => args.every(Boolean);
|
|
4774
|
-
parser.functions.OR = (...args) => args.some(Boolean);
|
|
4775
|
-
parser.functions.NOT = (value) => !value;
|
|
4776
|
-
parser.functions.EMPTY = (value) => value === null || value === void 0 || value === "";
|
|
4777
|
-
parser.functions.COALESCE = (...args) => args.find((a) => a != null) ?? null;
|
|
4778
|
-
parser.functions.DEFAULT = (value, defaultValue) => value == null || value === "" ? defaultValue : value;
|
|
4779
|
-
parser.functions.CONCAT = (...args) => args.filter((a) => a != null).map(String).join("");
|
|
4780
|
-
parser.functions.UPPER = (value) => String(value ?? "").toUpperCase();
|
|
4781
|
-
parser.functions.LOWER = (value) => String(value ?? "").toLowerCase();
|
|
4782
|
-
parser.functions.TRIM = (value) => String(value ?? "").trim();
|
|
4783
|
-
parser.functions.LENGTH = (value) => String(value ?? "").length;
|
|
4784
|
-
parser.functions.LEFT = (value, count) => String(value ?? "").slice(0, count);
|
|
4785
|
-
parser.functions.RIGHT = (value, count) => String(value ?? "").slice(-count);
|
|
4786
|
-
parser.functions.REPLACE = (value, search, replacement) => String(value ?? "").split(String(search)).join(replacement);
|
|
4787
|
-
parser.functions.CONTAINS = (value, search) => String(value ?? "").toLowerCase().includes(String(search).toLowerCase());
|
|
4788
|
-
parser.functions.ROUND = (value, decimals = 0) => {
|
|
4789
|
-
if (typeof value !== "number" || Number.isNaN(value)) return null;
|
|
4790
|
-
const factor = 10 ** decimals;
|
|
4791
|
-
return Math.round(value * factor) / factor;
|
|
4792
|
-
};
|
|
4793
|
-
parser.functions.FLOOR = (value) => {
|
|
4794
|
-
if (typeof value !== "number" || Number.isNaN(value)) return null;
|
|
4795
|
-
return Math.floor(value);
|
|
4796
|
-
};
|
|
4797
|
-
parser.functions.CEIL = (value) => {
|
|
4798
|
-
if (typeof value !== "number" || Number.isNaN(value)) return null;
|
|
4799
|
-
return Math.ceil(value);
|
|
4800
|
-
};
|
|
4801
|
-
parser.functions.ABS = (value) => {
|
|
4802
|
-
if (typeof value !== "number" || Number.isNaN(value)) return null;
|
|
4803
|
-
return Math.abs(value);
|
|
4804
|
-
};
|
|
4805
|
-
parser.functions.MIN = (...args) => {
|
|
4806
|
-
const nums = args.filter((n) => typeof n === "number" && !Number.isNaN(n));
|
|
4807
|
-
return nums.length > 0 ? Math.min(...nums) : null;
|
|
4808
|
-
};
|
|
4809
|
-
parser.functions.MAX = (...args) => {
|
|
4810
|
-
const nums = args.filter((n) => typeof n === "number" && !Number.isNaN(n));
|
|
4811
|
-
return nums.length > 0 ? Math.max(...nums) : null;
|
|
4812
|
-
};
|
|
4813
|
-
parser.functions.POW = (base, exponent) => {
|
|
4814
|
-
if (typeof base !== "number" || typeof exponent !== "number") return null;
|
|
4815
|
-
if (exponent > 1e3 || exponent < -1e3) return null;
|
|
4816
|
-
return base ** exponent;
|
|
4817
|
-
};
|
|
4818
|
-
parser.functions.MOD = (a, b) => {
|
|
4819
|
-
if (typeof a !== "number" || typeof b !== "number" || b === 0) return null;
|
|
4820
|
-
return a % b;
|
|
4821
|
-
};
|
|
4822
|
-
parser.functions.NOW = () => (/* @__PURE__ */ new Date()).toISOString();
|
|
4823
|
-
parser.functions.TODAY = () => (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
4824
|
-
parser.functions.YEAR = (value) => {
|
|
4825
|
-
const date2 = parseDate(value);
|
|
4826
|
-
return date2 ? date2.getFullYear() : null;
|
|
4827
|
-
};
|
|
4828
|
-
parser.functions.MONTH = (value) => {
|
|
4829
|
-
const date2 = parseDate(value);
|
|
4830
|
-
return date2 ? date2.getMonth() + 1 : null;
|
|
4831
|
-
};
|
|
4832
|
-
parser.functions.DAY = (value) => {
|
|
4833
|
-
const date2 = parseDate(value);
|
|
4834
|
-
return date2 ? date2.getDate() : null;
|
|
4835
|
-
};
|
|
4836
|
-
parser.functions.DATE_DIFF = (date1, date2, unit = "days") => {
|
|
4837
|
-
const d1 = parseDate(date1);
|
|
4838
|
-
const d2 = parseDate(date2);
|
|
4839
|
-
if (d1 === null || d2 === null) return null;
|
|
4840
|
-
const diffMs = d1.getTime() - d2.getTime();
|
|
4841
|
-
const MS_PER_DAY = 1e3 * 60 * 60 * 24;
|
|
4842
|
-
const conversions = {
|
|
4843
|
-
years: MS_PER_DAY * 365,
|
|
4844
|
-
months: MS_PER_DAY * 30,
|
|
4845
|
-
weeks: MS_PER_DAY * 7,
|
|
4846
|
-
days: MS_PER_DAY,
|
|
4847
|
-
hours: 1e3 * 60 * 60,
|
|
4848
|
-
minutes: 1e3 * 60
|
|
4849
|
-
};
|
|
4850
|
-
const divisor = conversions[unit] ?? MS_PER_DAY;
|
|
4851
|
-
return diffMs / divisor;
|
|
4852
|
-
};
|
|
4853
|
-
return parser;
|
|
6332
|
+
const result = operation(leftNumber, rightNumber);
|
|
6333
|
+
return Number.isFinite(result) ? result : null;
|
|
4854
6334
|
}
|
|
4855
|
-
function
|
|
4856
|
-
|
|
4857
|
-
if (
|
|
4858
|
-
|
|
4859
|
-
const date2 = new Date(value);
|
|
4860
|
-
return Number.isNaN(date2.getTime()) ? null : date2;
|
|
6335
|
+
function minMax(values, mode) {
|
|
6336
|
+
const numbers = numericValues(values);
|
|
6337
|
+
if (numbers.length > 0) {
|
|
6338
|
+
return mode === "min" ? Math.min(...numbers) : Math.max(...numbers);
|
|
4861
6339
|
}
|
|
4862
|
-
return
|
|
6340
|
+
return dateMinMax(values, mode);
|
|
4863
6341
|
}
|
|
4864
|
-
|
|
4865
|
-
|
|
4866
|
-
if (
|
|
4867
|
-
return
|
|
4868
|
-
valid: false,
|
|
4869
|
-
error: `Expression too long (${expression.length} chars, max ${DOS_PROTECTION_LIMITS.MAX_EXPRESSION_LENGTH})`
|
|
4870
|
-
};
|
|
6342
|
+
function dateMinMax(values, mode) {
|
|
6343
|
+
const dates = flatten(values).map(toDate).filter((date2) => date2 !== null);
|
|
6344
|
+
if (dates.length === 0) {
|
|
6345
|
+
return null;
|
|
4871
6346
|
}
|
|
4872
|
-
const
|
|
4873
|
-
|
|
4874
|
-
|
|
4875
|
-
|
|
4876
|
-
|
|
4877
|
-
|
|
6347
|
+
const timestamps = dates.map((date2) => date2.getTime());
|
|
6348
|
+
const timestamp = mode === "min" ? Math.min(...timestamps) : Math.max(...timestamps);
|
|
6349
|
+
return new Date(timestamp).toISOString();
|
|
6350
|
+
}
|
|
6351
|
+
function percentByEmptyState(value, countEmpty) {
|
|
6352
|
+
const items = collectionItems(value);
|
|
6353
|
+
if (items.length === 0) {
|
|
6354
|
+
return 0;
|
|
4878
6355
|
}
|
|
4879
|
-
|
|
4880
|
-
|
|
4881
|
-
|
|
4882
|
-
|
|
4883
|
-
|
|
4884
|
-
|
|
4885
|
-
|
|
4886
|
-
|
|
6356
|
+
const count = items.filter((item) => isEmptyValue(item) === countEmpty).length;
|
|
6357
|
+
return count / items.length;
|
|
6358
|
+
}
|
|
6359
|
+
function uniqueNonEmpty(values) {
|
|
6360
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6361
|
+
const result = [];
|
|
6362
|
+
for (const value of values) {
|
|
6363
|
+
if (isEmptyValue(value)) {
|
|
6364
|
+
continue;
|
|
4887
6365
|
}
|
|
4888
|
-
|
|
4889
|
-
|
|
4890
|
-
|
|
6366
|
+
const key = uniqueKey(value);
|
|
6367
|
+
if (seen.has(key)) {
|
|
6368
|
+
continue;
|
|
6369
|
+
}
|
|
6370
|
+
seen.add(key);
|
|
6371
|
+
result.push(value);
|
|
6372
|
+
}
|
|
6373
|
+
return result;
|
|
6374
|
+
}
|
|
6375
|
+
function collectionItems(value) {
|
|
6376
|
+
if (value === null || value === void 0) {
|
|
6377
|
+
return [];
|
|
4891
6378
|
}
|
|
6379
|
+
return Array.isArray(value) ? flatten(value) : [value];
|
|
4892
6380
|
}
|
|
4893
|
-
function
|
|
4894
|
-
|
|
4895
|
-
return tokens.length;
|
|
6381
|
+
function flatten(values) {
|
|
6382
|
+
return values.flat(Number.POSITIVE_INFINITY);
|
|
4896
6383
|
}
|
|
4897
|
-
function
|
|
4898
|
-
|
|
4899
|
-
|
|
4900
|
-
|
|
4901
|
-
|
|
4902
|
-
|
|
4903
|
-
if (currentDepth > maxDepth) maxDepth = currentDepth;
|
|
4904
|
-
} else if (char === ")") {
|
|
4905
|
-
currentDepth--;
|
|
4906
|
-
}
|
|
6384
|
+
function isEmptyValue(value) {
|
|
6385
|
+
return value === null || value === void 0 || value === "";
|
|
6386
|
+
}
|
|
6387
|
+
function toFiniteNumber(value) {
|
|
6388
|
+
if (typeof value === "number") {
|
|
6389
|
+
return Number.isFinite(value) ? value : null;
|
|
4907
6390
|
}
|
|
4908
|
-
|
|
6391
|
+
if (typeof value === "string" && value.trim() !== "") {
|
|
6392
|
+
const parsed = Number(value);
|
|
6393
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
6394
|
+
}
|
|
6395
|
+
return null;
|
|
4909
6396
|
}
|
|
4910
|
-
function
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
|
|
4914
|
-
|
|
4915
|
-
return null;
|
|
4916
|
-
}
|
|
4917
|
-
const parsed = formulaParser.parse(expression);
|
|
4918
|
-
return parsed.evaluate(values);
|
|
4919
|
-
} catch {
|
|
6397
|
+
function toDate(value) {
|
|
6398
|
+
if (value instanceof Date) {
|
|
6399
|
+
return Number.isNaN(value.getTime()) ? null : value;
|
|
6400
|
+
}
|
|
6401
|
+
if (typeof value !== "string") {
|
|
4920
6402
|
return null;
|
|
4921
6403
|
}
|
|
6404
|
+
const date2 = new Date(value);
|
|
6405
|
+
return Number.isNaN(date2.getTime()) ? null : date2;
|
|
4922
6406
|
}
|
|
4923
|
-
function
|
|
4924
|
-
|
|
4925
|
-
|
|
4926
|
-
|
|
4927
|
-
return { value: null, error: complexityCheck.error };
|
|
4928
|
-
}
|
|
4929
|
-
const parsed = formulaParser.parse(expression);
|
|
4930
|
-
const value = parsed.evaluate(values);
|
|
4931
|
-
return { value };
|
|
4932
|
-
} catch (error) {
|
|
4933
|
-
return { value: null, error: getErrorMessage(error) };
|
|
6407
|
+
function uniqueKey(value) {
|
|
6408
|
+
const numberValue = toFiniteNumber(value);
|
|
6409
|
+
if (numberValue !== null) {
|
|
6410
|
+
return `number:${numberValue}`;
|
|
4934
6411
|
}
|
|
6412
|
+
if (value instanceof Date) {
|
|
6413
|
+
return `date:${value.toISOString()}`;
|
|
6414
|
+
}
|
|
6415
|
+
return `${typeof value}:${JSON.stringify(value)}`;
|
|
4935
6416
|
}
|
|
4936
|
-
function
|
|
6417
|
+
function isPlainRecord(value) {
|
|
6418
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6419
|
+
}
|
|
6420
|
+
function formatComputedResult(value, returnType, decimals) {
|
|
4937
6421
|
if (value === null || value === void 0) return null;
|
|
6422
|
+
const stringifyScalar = (input) => {
|
|
6423
|
+
if (typeof input === "string" || typeof input === "number" || typeof input === "boolean" || input instanceof Date) {
|
|
6424
|
+
return String(input);
|
|
6425
|
+
}
|
|
6426
|
+
return null;
|
|
6427
|
+
};
|
|
4938
6428
|
switch (returnType) {
|
|
4939
6429
|
case "number": {
|
|
4940
6430
|
const num = typeof value === "number" ? value : Number(value);
|
|
@@ -4949,152 +6439,163 @@ function formatFormulaResult(value, returnType, decimals) {
|
|
|
4949
6439
|
return null;
|
|
4950
6440
|
case "text":
|
|
4951
6441
|
return String(value);
|
|
4952
|
-
|
|
4953
|
-
|
|
4954
|
-
|
|
4955
|
-
|
|
4956
|
-
|
|
4957
|
-
|
|
4958
|
-
function validateFormulaExpression(expression) {
|
|
4959
|
-
try {
|
|
4960
|
-
const complexityCheck = validateFormulaComplexity(expression);
|
|
4961
|
-
if (!complexityCheck.valid) return complexityCheck;
|
|
4962
|
-
formulaParser.parse(expression);
|
|
4963
|
-
return { valid: true };
|
|
4964
|
-
} catch (error) {
|
|
4965
|
-
return { valid: false, error: getErrorMessage(error) };
|
|
4966
|
-
}
|
|
4967
|
-
}
|
|
4968
|
-
function extractFormulaVariables(expression) {
|
|
4969
|
-
try {
|
|
4970
|
-
const parsed = formulaParser.parse(expression);
|
|
4971
|
-
return parsed.variables();
|
|
4972
|
-
} catch {
|
|
4973
|
-
return [];
|
|
4974
|
-
}
|
|
4975
|
-
}
|
|
4976
|
-
var RELATION_REF_PATTERN = /\b([a-zA-Z_][a-zA-Z0-9_]*)\.([a-zA-Z_][a-zA-Z0-9_]*)\b/g;
|
|
4977
|
-
function extractRelationReferences(expression) {
|
|
4978
|
-
const regex = new RegExp(RELATION_REF_PATTERN.source, "g");
|
|
4979
|
-
const matches = expression.matchAll(regex);
|
|
4980
|
-
return [...matches].map((m) => m[0]);
|
|
4981
|
-
}
|
|
4982
|
-
function extractRelationNames(expression) {
|
|
4983
|
-
const refs = extractRelationReferences(expression);
|
|
4984
|
-
const names = refs.map((ref) => ref.split(".")[0]);
|
|
4985
|
-
return [...new Set(names)];
|
|
4986
|
-
}
|
|
4987
|
-
function hasRelationReferences(expression) {
|
|
4988
|
-
const regex = new RegExp(RELATION_REF_PATTERN.source);
|
|
4989
|
-
return regex.test(expression);
|
|
4990
|
-
}
|
|
4991
|
-
function flattenRelationsForEval(resolvedRelations) {
|
|
4992
|
-
const result = {};
|
|
4993
|
-
for (const [relationName, values] of Object.entries(resolvedRelations)) {
|
|
4994
|
-
result[relationName] = { ...values };
|
|
4995
|
-
}
|
|
4996
|
-
return result;
|
|
4997
|
-
}
|
|
4998
|
-
|
|
4999
|
-
// src/formula/path-parser.ts
|
|
5000
|
-
var InvalidPathError = class extends Error {
|
|
5001
|
-
constructor(path, segment, reason) {
|
|
5002
|
-
super(`Invalid path "${path}" at "${segment}": ${reason}`);
|
|
5003
|
-
this.path = path;
|
|
5004
|
-
this.segment = segment;
|
|
5005
|
-
this.reason = reason;
|
|
5006
|
-
this.name = "InvalidPathError";
|
|
5007
|
-
}
|
|
5008
|
-
};
|
|
5009
|
-
var MaxDepthExceededError = class extends Error {
|
|
5010
|
-
constructor(path, maxDepth) {
|
|
5011
|
-
super(`Path "${path}" exceeds maximum depth of ${maxDepth}`);
|
|
5012
|
-
this.path = path;
|
|
5013
|
-
this.maxDepth = maxDepth;
|
|
5014
|
-
this.name = "MaxDepthExceededError";
|
|
5015
|
-
}
|
|
5016
|
-
};
|
|
5017
|
-
async function parsePath(path, startSchema, getSchema, maxDepth = 5) {
|
|
5018
|
-
const segments = path.split(".");
|
|
5019
|
-
if (segments.length === 0 || segments.length === 1 && segments[0] === "") {
|
|
5020
|
-
throw new InvalidPathError(path, path, "Path cannot be empty");
|
|
5021
|
-
}
|
|
5022
|
-
if (segments.length > maxDepth) {
|
|
5023
|
-
throw new MaxDepthExceededError(path, maxDepth);
|
|
5024
|
-
}
|
|
5025
|
-
const result = [];
|
|
5026
|
-
let currentSchema = startSchema;
|
|
5027
|
-
for (let i = 0; i < segments.length; i++) {
|
|
5028
|
-
const segmentName = segments[i];
|
|
5029
|
-
const isLastSegment = i === segments.length - 1;
|
|
5030
|
-
const attr = currentSchema.attributes.find((a) => a.name === segmentName);
|
|
5031
|
-
if (!attr) {
|
|
5032
|
-
throw new InvalidPathError(
|
|
5033
|
-
path,
|
|
5034
|
-
segmentName,
|
|
5035
|
-
`Attribute "${segmentName}" not found in object "${currentSchema.name}"`
|
|
5036
|
-
);
|
|
5037
|
-
}
|
|
5038
|
-
if (attr.type === "relation") {
|
|
5039
|
-
const relationAttr = attr;
|
|
5040
|
-
const targetObject = relationAttr.targets[0]?.object;
|
|
5041
|
-
if (!targetObject) {
|
|
5042
|
-
throw new InvalidPathError(path, segmentName, "Relation has no target object");
|
|
5043
|
-
}
|
|
5044
|
-
result.push({
|
|
5045
|
-
name: segmentName,
|
|
5046
|
-
type: "relation",
|
|
5047
|
-
cardinality: relationAttr.cardinality,
|
|
5048
|
-
targetObject
|
|
5049
|
-
});
|
|
5050
|
-
if (!isLastSegment) {
|
|
5051
|
-
const nextSchema = await getSchema(targetObject);
|
|
5052
|
-
if (!nextSchema) {
|
|
5053
|
-
throw new InvalidPathError(
|
|
5054
|
-
path,
|
|
5055
|
-
segmentName,
|
|
5056
|
-
`Target object "${targetObject}" schema not found`
|
|
5057
|
-
);
|
|
6442
|
+
case "select":
|
|
6443
|
+
if (Array.isArray(value)) {
|
|
6444
|
+
for (const item of value) {
|
|
6445
|
+
if (item === null || item === void 0) continue;
|
|
6446
|
+
const formatted = stringifyScalar(item);
|
|
6447
|
+
if (formatted !== null) return formatted;
|
|
5058
6448
|
}
|
|
5059
|
-
|
|
6449
|
+
return null;
|
|
5060
6450
|
}
|
|
5061
|
-
|
|
5062
|
-
|
|
5063
|
-
|
|
5064
|
-
|
|
5065
|
-
|
|
5066
|
-
|
|
5067
|
-
|
|
6451
|
+
return stringifyScalar(value);
|
|
6452
|
+
case "multiselect":
|
|
6453
|
+
if (Array.isArray(value)) {
|
|
6454
|
+
return value.flatMap((item) => {
|
|
6455
|
+
if (item === null || item === void 0) return [];
|
|
6456
|
+
const formatted = stringifyScalar(item);
|
|
6457
|
+
return formatted === null ? [] : [formatted];
|
|
6458
|
+
});
|
|
5068
6459
|
}
|
|
5069
|
-
|
|
6460
|
+
return stringifyScalar(value) === null ? [] : [String(value)];
|
|
6461
|
+
default: {
|
|
6462
|
+
const _exhaustive = returnType;
|
|
6463
|
+
throw new Error(`Unsupported computed return type: ${_exhaustive}`);
|
|
5070
6464
|
}
|
|
5071
6465
|
}
|
|
5072
|
-
return result;
|
|
5073
|
-
}
|
|
5074
|
-
async function validatePath(path, startSchema, getSchema, maxDepth = 5) {
|
|
5075
|
-
try {
|
|
5076
|
-
await parsePath(path, startSchema, getSchema, maxDepth);
|
|
5077
|
-
return true;
|
|
5078
|
-
} catch {
|
|
5079
|
-
return false;
|
|
5080
|
-
}
|
|
5081
6466
|
}
|
|
5082
|
-
function
|
|
5083
|
-
return
|
|
6467
|
+
function evaluateComputedAst(node, input) {
|
|
6468
|
+
return evaluateAst(node, input);
|
|
5084
6469
|
}
|
|
5085
|
-
function
|
|
5086
|
-
return
|
|
6470
|
+
function evaluateComputedFormula(expression, input) {
|
|
6471
|
+
return evaluateAst(parseComputedFormula(expression), input);
|
|
5087
6472
|
}
|
|
5088
|
-
function
|
|
5089
|
-
|
|
5090
|
-
|
|
5091
|
-
|
|
5092
|
-
|
|
5093
|
-
|
|
5094
|
-
|
|
5095
|
-
|
|
6473
|
+
function evaluateComputedFormulaWithResult(expression, input, returnType, decimals) {
|
|
6474
|
+
try {
|
|
6475
|
+
const raw = evaluateAst(parseComputedFormula(expression), input);
|
|
6476
|
+
const value = returnType ? formatComputedResult(raw, returnType, decimals) : raw;
|
|
6477
|
+
return { value };
|
|
6478
|
+
} catch (error) {
|
|
6479
|
+
return { value: null, error: error instanceof Error ? error.message : String(error) };
|
|
6480
|
+
}
|
|
5096
6481
|
}
|
|
5097
6482
|
|
|
6483
|
+
// src/computed/function-metadata.ts
|
|
6484
|
+
var COMPUTED_FUNCTION_METADATA = [
|
|
6485
|
+
{
|
|
6486
|
+
name: "if",
|
|
6487
|
+
signature: "if(condition, then, else)",
|
|
6488
|
+
description: "Conditional value",
|
|
6489
|
+
category: "logic",
|
|
6490
|
+
example: "if(amount > 100, 'big', 'small')"
|
|
6491
|
+
},
|
|
6492
|
+
{
|
|
6493
|
+
name: "concat",
|
|
6494
|
+
signature: "concat(a, b, ...)",
|
|
6495
|
+
description: "Join values into text",
|
|
6496
|
+
category: "text",
|
|
6497
|
+
example: "concat(firstName, ' ', lastName)"
|
|
6498
|
+
},
|
|
6499
|
+
{
|
|
6500
|
+
name: "sum",
|
|
6501
|
+
signature: "sum(collection)",
|
|
6502
|
+
description: "Total of values",
|
|
6503
|
+
category: "number",
|
|
6504
|
+
example: "sum(orders.amount)"
|
|
6505
|
+
},
|
|
6506
|
+
{
|
|
6507
|
+
name: "avg",
|
|
6508
|
+
signature: "avg(collection)",
|
|
6509
|
+
description: "Average of values",
|
|
6510
|
+
category: "number",
|
|
6511
|
+
example: "avg(orders.amount)"
|
|
6512
|
+
},
|
|
6513
|
+
{
|
|
6514
|
+
name: "min",
|
|
6515
|
+
signature: "min(collection)",
|
|
6516
|
+
description: "Smallest value",
|
|
6517
|
+
category: "number",
|
|
6518
|
+
example: "min(orders.amount)"
|
|
6519
|
+
},
|
|
6520
|
+
{
|
|
6521
|
+
name: "max",
|
|
6522
|
+
signature: "max(collection)",
|
|
6523
|
+
description: "Largest value",
|
|
6524
|
+
category: "number",
|
|
6525
|
+
example: "max(orders.amount)"
|
|
6526
|
+
},
|
|
6527
|
+
{
|
|
6528
|
+
name: "count",
|
|
6529
|
+
signature: "count(collection)",
|
|
6530
|
+
description: "Number of items",
|
|
6531
|
+
category: "collection",
|
|
6532
|
+
example: "count(orders)"
|
|
6533
|
+
},
|
|
6534
|
+
{
|
|
6535
|
+
name: "count_empty",
|
|
6536
|
+
signature: "count_empty(collection)",
|
|
6537
|
+
description: "Items with no value",
|
|
6538
|
+
category: "collection",
|
|
6539
|
+
example: "count_empty(orders.note)"
|
|
6540
|
+
},
|
|
6541
|
+
{
|
|
6542
|
+
name: "count_not_empty",
|
|
6543
|
+
signature: "count_not_empty(collection)",
|
|
6544
|
+
description: "Items with a value",
|
|
6545
|
+
category: "collection",
|
|
6546
|
+
example: "count_not_empty(orders.note)"
|
|
6547
|
+
},
|
|
6548
|
+
{
|
|
6549
|
+
name: "count_unique",
|
|
6550
|
+
signature: "count_unique(collection)",
|
|
6551
|
+
description: "Distinct values",
|
|
6552
|
+
category: "collection",
|
|
6553
|
+
example: "count_unique(orders.status)"
|
|
6554
|
+
},
|
|
6555
|
+
{
|
|
6556
|
+
name: "percent_empty",
|
|
6557
|
+
signature: "percent_empty(collection)",
|
|
6558
|
+
description: "Fraction with no value",
|
|
6559
|
+
category: "collection",
|
|
6560
|
+
example: "percent_empty(orders.note)"
|
|
6561
|
+
},
|
|
6562
|
+
{
|
|
6563
|
+
name: "percent_not_empty",
|
|
6564
|
+
signature: "percent_not_empty(collection)",
|
|
6565
|
+
description: "Fraction with a value",
|
|
6566
|
+
category: "collection",
|
|
6567
|
+
example: "percent_not_empty(orders.note)"
|
|
6568
|
+
},
|
|
6569
|
+
{
|
|
6570
|
+
name: "unique",
|
|
6571
|
+
signature: "unique(collection)",
|
|
6572
|
+
description: "Distinct values as a list",
|
|
6573
|
+
category: "collection",
|
|
6574
|
+
example: "unique(orders.status)"
|
|
6575
|
+
},
|
|
6576
|
+
{
|
|
6577
|
+
name: "first",
|
|
6578
|
+
signature: "first(collection)",
|
|
6579
|
+
description: "First non-empty value",
|
|
6580
|
+
category: "collection",
|
|
6581
|
+
example: "first(orders.status)"
|
|
6582
|
+
},
|
|
6583
|
+
{
|
|
6584
|
+
name: "earliest",
|
|
6585
|
+
signature: "earliest(collection)",
|
|
6586
|
+
description: "Earliest date",
|
|
6587
|
+
category: "date",
|
|
6588
|
+
example: "earliest(orders.closeDate)"
|
|
6589
|
+
},
|
|
6590
|
+
{
|
|
6591
|
+
name: "latest",
|
|
6592
|
+
signature: "latest(collection)",
|
|
6593
|
+
description: "Latest date",
|
|
6594
|
+
category: "date",
|
|
6595
|
+
example: "latest(orders.closeDate)"
|
|
6596
|
+
}
|
|
6597
|
+
];
|
|
6598
|
+
|
|
5098
6599
|
// src/filters/operators.ts
|
|
5099
6600
|
var isEmpty = (v) => v === null || v === void 0 || v === "" || Array.isArray(v) && v.length === 0;
|
|
5100
6601
|
var toStr = (v) => v == null ? "" : String(v);
|
|
@@ -5103,7 +6604,7 @@ var toNum = (v) => {
|
|
|
5103
6604
|
const n = typeof v === "number" ? v : Number(v);
|
|
5104
6605
|
return Number.isFinite(n) ? n : null;
|
|
5105
6606
|
};
|
|
5106
|
-
var
|
|
6607
|
+
var toDate2 = (v) => {
|
|
5107
6608
|
if (v == null) return null;
|
|
5108
6609
|
const d = v instanceof Date ? v : new Date(String(v));
|
|
5109
6610
|
return Number.isNaN(d.getTime()) ? null : d;
|
|
@@ -5122,48 +6623,38 @@ function relativeDateBoundary(rel) {
|
|
|
5122
6623
|
var OPERATOR_SPECS = {
|
|
5123
6624
|
// ---- text-style ----
|
|
5124
6625
|
is: {
|
|
5125
|
-
appliesTo: ["text", "
|
|
6626
|
+
appliesTo: ["text", "phone", "status", "select", "date", "formula"],
|
|
5126
6627
|
evaluateInMemory: (v, rv) => toStr(v) === toStr(rv)
|
|
5127
6628
|
},
|
|
5128
6629
|
is_not: {
|
|
5129
|
-
appliesTo: ["text", "
|
|
6630
|
+
appliesTo: ["text", "phone", "status", "select", "date", "formula"],
|
|
5130
6631
|
evaluateInMemory: (v, rv) => toStr(v) !== toStr(rv)
|
|
5131
6632
|
},
|
|
5132
6633
|
contains: {
|
|
5133
|
-
appliesTo: [
|
|
5134
|
-
"text",
|
|
5135
|
-
"textarea",
|
|
5136
|
-
"phone",
|
|
5137
|
-
"location",
|
|
5138
|
-
"user",
|
|
5139
|
-
"multiselect",
|
|
5140
|
-
"relation",
|
|
5141
|
-
"formula"
|
|
5142
|
-
],
|
|
6634
|
+
appliesTo: ["text", "phone", "location", "user", "multiselect", "relation", "formula"],
|
|
5143
6635
|
evaluateInMemory: (v, rv) => {
|
|
5144
6636
|
if (Array.isArray(v)) return v.map(toStr).includes(toStr(rv));
|
|
5145
6637
|
return toStr(v).toLowerCase().includes(toStr(rv).toLowerCase());
|
|
5146
6638
|
}
|
|
5147
6639
|
},
|
|
5148
6640
|
not_contains: {
|
|
5149
|
-
appliesTo: ["text", "
|
|
6641
|
+
appliesTo: ["text", "phone", "user", "multiselect", "relation", "formula"],
|
|
5150
6642
|
evaluateInMemory: (v, rv) => {
|
|
5151
6643
|
if (Array.isArray(v)) return !v.map(toStr).includes(toStr(rv));
|
|
5152
6644
|
return !toStr(v).toLowerCase().includes(toStr(rv).toLowerCase());
|
|
5153
6645
|
}
|
|
5154
6646
|
},
|
|
5155
6647
|
starts_with: {
|
|
5156
|
-
appliesTo: ["text", "
|
|
6648
|
+
appliesTo: ["text", "formula"],
|
|
5157
6649
|
evaluateInMemory: (v, rv) => toStr(v).toLowerCase().startsWith(toStr(rv).toLowerCase())
|
|
5158
6650
|
},
|
|
5159
6651
|
ends_with: {
|
|
5160
|
-
appliesTo: ["text", "
|
|
6652
|
+
appliesTo: ["text", "formula"],
|
|
5161
6653
|
evaluateInMemory: (v, rv) => toStr(v).toLowerCase().endsWith(toStr(rv).toLowerCase())
|
|
5162
6654
|
},
|
|
5163
6655
|
is_empty: {
|
|
5164
6656
|
appliesTo: [
|
|
5165
6657
|
"text",
|
|
5166
|
-
"textarea",
|
|
5167
6658
|
"richtext",
|
|
5168
6659
|
"number",
|
|
5169
6660
|
"phone",
|
|
@@ -5176,7 +6667,6 @@ var OPERATOR_SPECS = {
|
|
|
5176
6667
|
"file",
|
|
5177
6668
|
"user",
|
|
5178
6669
|
"relation",
|
|
5179
|
-
"rating",
|
|
5180
6670
|
"formula",
|
|
5181
6671
|
"document"
|
|
5182
6672
|
],
|
|
@@ -5185,7 +6675,6 @@ var OPERATOR_SPECS = {
|
|
|
5185
6675
|
is_not_empty: {
|
|
5186
6676
|
appliesTo: [
|
|
5187
6677
|
"text",
|
|
5188
|
-
"textarea",
|
|
5189
6678
|
"richtext",
|
|
5190
6679
|
"number",
|
|
5191
6680
|
"phone",
|
|
@@ -5198,7 +6687,6 @@ var OPERATOR_SPECS = {
|
|
|
5198
6687
|
"file",
|
|
5199
6688
|
"user",
|
|
5200
6689
|
"relation",
|
|
5201
|
-
"rating",
|
|
5202
6690
|
"formula",
|
|
5203
6691
|
"document"
|
|
5204
6692
|
],
|
|
@@ -5206,15 +6694,15 @@ var OPERATOR_SPECS = {
|
|
|
5206
6694
|
},
|
|
5207
6695
|
// ---- numeric ----
|
|
5208
6696
|
eq: {
|
|
5209
|
-
appliesTo: ["number", "currency", "
|
|
6697
|
+
appliesTo: ["number", "currency", "formula"],
|
|
5210
6698
|
evaluateInMemory: (v, rv) => toNum(v) === toNum(rv)
|
|
5211
6699
|
},
|
|
5212
6700
|
neq: {
|
|
5213
|
-
appliesTo: ["number", "currency", "
|
|
6701
|
+
appliesTo: ["number", "currency", "formula"],
|
|
5214
6702
|
evaluateInMemory: (v, rv) => toNum(v) !== toNum(rv)
|
|
5215
6703
|
},
|
|
5216
6704
|
lt: {
|
|
5217
|
-
appliesTo: ["number", "currency", "
|
|
6705
|
+
appliesTo: ["number", "currency", "formula"],
|
|
5218
6706
|
evaluateInMemory: (v, rv) => {
|
|
5219
6707
|
const a = toNum(v);
|
|
5220
6708
|
const b = toNum(rv);
|
|
@@ -5222,7 +6710,7 @@ var OPERATOR_SPECS = {
|
|
|
5222
6710
|
}
|
|
5223
6711
|
},
|
|
5224
6712
|
gt: {
|
|
5225
|
-
appliesTo: ["number", "currency", "
|
|
6713
|
+
appliesTo: ["number", "currency", "formula"],
|
|
5226
6714
|
evaluateInMemory: (v, rv) => {
|
|
5227
6715
|
const a = toNum(v);
|
|
5228
6716
|
const b = toNum(rv);
|
|
@@ -5230,7 +6718,7 @@ var OPERATOR_SPECS = {
|
|
|
5230
6718
|
}
|
|
5231
6719
|
},
|
|
5232
6720
|
lte: {
|
|
5233
|
-
appliesTo: ["number", "currency", "
|
|
6721
|
+
appliesTo: ["number", "currency", "formula"],
|
|
5234
6722
|
evaluateInMemory: (v, rv) => {
|
|
5235
6723
|
const a = toNum(v);
|
|
5236
6724
|
const b = toNum(rv);
|
|
@@ -5238,7 +6726,7 @@ var OPERATOR_SPECS = {
|
|
|
5238
6726
|
}
|
|
5239
6727
|
},
|
|
5240
6728
|
gte: {
|
|
5241
|
-
appliesTo: ["number", "currency", "
|
|
6729
|
+
appliesTo: ["number", "currency", "formula"],
|
|
5242
6730
|
evaluateInMemory: (v, rv) => {
|
|
5243
6731
|
const a = toNum(v);
|
|
5244
6732
|
const b = toNum(rv);
|
|
@@ -5258,39 +6746,39 @@ var OPERATOR_SPECS = {
|
|
|
5258
6746
|
before: {
|
|
5259
6747
|
appliesTo: ["date"],
|
|
5260
6748
|
evaluateInMemory: (v, rv) => {
|
|
5261
|
-
const a =
|
|
5262
|
-
const b =
|
|
6749
|
+
const a = toDate2(v);
|
|
6750
|
+
const b = toDate2(rv);
|
|
5263
6751
|
return a !== null && b !== null && a < b;
|
|
5264
6752
|
}
|
|
5265
6753
|
},
|
|
5266
6754
|
after: {
|
|
5267
6755
|
appliesTo: ["date"],
|
|
5268
6756
|
evaluateInMemory: (v, rv) => {
|
|
5269
|
-
const a =
|
|
5270
|
-
const b =
|
|
6757
|
+
const a = toDate2(v);
|
|
6758
|
+
const b = toDate2(rv);
|
|
5271
6759
|
return a !== null && b !== null && a > b;
|
|
5272
6760
|
}
|
|
5273
6761
|
},
|
|
5274
6762
|
on_or_before: {
|
|
5275
6763
|
appliesTo: ["date"],
|
|
5276
6764
|
evaluateInMemory: (v, rv) => {
|
|
5277
|
-
const a =
|
|
5278
|
-
const b =
|
|
6765
|
+
const a = toDate2(v);
|
|
6766
|
+
const b = toDate2(rv);
|
|
5279
6767
|
return a !== null && b !== null && a <= b;
|
|
5280
6768
|
}
|
|
5281
6769
|
},
|
|
5282
6770
|
on_or_after: {
|
|
5283
6771
|
appliesTo: ["date"],
|
|
5284
6772
|
evaluateInMemory: (v, rv) => {
|
|
5285
|
-
const a =
|
|
5286
|
-
const b =
|
|
6773
|
+
const a = toDate2(v);
|
|
6774
|
+
const b = toDate2(rv);
|
|
5287
6775
|
return a !== null && b !== null && a >= b;
|
|
5288
6776
|
}
|
|
5289
6777
|
},
|
|
5290
6778
|
is_within: {
|
|
5291
6779
|
appliesTo: ["date"],
|
|
5292
6780
|
evaluateInMemory: (v, rv) => {
|
|
5293
|
-
const d =
|
|
6781
|
+
const d = toDate2(v);
|
|
5294
6782
|
if (!d) return false;
|
|
5295
6783
|
const rel = rv;
|
|
5296
6784
|
const boundary = relativeDateBoundary(rel);
|
|
@@ -5300,7 +6788,7 @@ var OPERATOR_SPECS = {
|
|
|
5300
6788
|
day_month_eq: {
|
|
5301
6789
|
appliesTo: ["date"],
|
|
5302
6790
|
evaluateInMemory: (v, rv) => {
|
|
5303
|
-
const d =
|
|
6791
|
+
const d = toDate2(v);
|
|
5304
6792
|
if (!d) return false;
|
|
5305
6793
|
const rule = rv;
|
|
5306
6794
|
return d.getMonth() + 1 === rule.month && d.getDate() === rule.day;
|
|
@@ -5398,7 +6886,7 @@ function assertOperatorForType(operator, type, context) {
|
|
|
5398
6886
|
if (!ops?.includes(operator)) {
|
|
5399
6887
|
const validList = ops?.length ? ops.join(", ") : "(no operators registered for this type)";
|
|
5400
6888
|
const summary = `Operator "${operator}" is not valid for ${context} type "${type}". Valid operators: ${validList}.`;
|
|
5401
|
-
throw new
|
|
6889
|
+
throw new chunkMZOEL7FN_js.ValidationError(summary, [
|
|
5402
6890
|
{
|
|
5403
6891
|
path: ["operator"],
|
|
5404
6892
|
message: summary
|
|
@@ -5410,43 +6898,45 @@ function validateQualifiedRule(rule, context) {
|
|
|
5410
6898
|
const rootAttr = context.attributes.find((a) => a.name === rule.attribute);
|
|
5411
6899
|
const systemOps = SYSTEM_FILTER_OPERATORS[rule.attribute];
|
|
5412
6900
|
if (!(rootAttr || systemOps)) {
|
|
5413
|
-
throw new
|
|
6901
|
+
throw new chunkMZOEL7FN_js.ValidationError(`Unknown attribute: ${rule.attribute}`, [
|
|
5414
6902
|
{ path: ["attribute"], message: `Unknown attribute: ${rule.attribute}` }
|
|
5415
6903
|
]);
|
|
5416
6904
|
}
|
|
5417
6905
|
if (!rootAttr) {
|
|
5418
6906
|
if (rule.property) {
|
|
5419
|
-
throw new
|
|
6907
|
+
throw new chunkMZOEL7FN_js.ValidationError(`Cannot qualify system attribute "${rule.attribute}"`, [
|
|
5420
6908
|
{ path: ["property"], message: `"${rule.attribute}" is a system column, not a reference` }
|
|
5421
6909
|
]);
|
|
5422
6910
|
}
|
|
5423
6911
|
if (!systemOps.includes(rule.operator)) {
|
|
5424
6912
|
const summary = `Operator "${rule.operator}" is not valid for system attribute "${rule.attribute}". Valid operators: ${systemOps.join(", ")}.`;
|
|
5425
|
-
throw new
|
|
6913
|
+
throw new chunkMZOEL7FN_js.ValidationError(summary, [{ path: ["operator"], message: summary }]);
|
|
5426
6914
|
}
|
|
5427
6915
|
return;
|
|
5428
6916
|
}
|
|
5429
6917
|
if (!rule.property) {
|
|
5430
|
-
|
|
5431
|
-
|
|
6918
|
+
const ops = getAttributeFilterOperators(rootAttr);
|
|
6919
|
+
if (!ops.includes(rule.operator)) {
|
|
6920
|
+
const validList = ops.length ? ops.join(", ") : "(not filterable)";
|
|
6921
|
+
const summary = `Operator "${rule.operator}" is not valid for attribute "${rootAttr.name}". Valid operators: ${validList}.`;
|
|
6922
|
+
throw new chunkMZOEL7FN_js.ValidationError(summary, [{ path: ["operator"], message: summary }]);
|
|
5432
6923
|
}
|
|
5433
|
-
assertOperatorForType(rule.operator, rootAttr.type, "attribute");
|
|
5434
6924
|
return;
|
|
5435
6925
|
}
|
|
5436
6926
|
if (!REFERENCE_TYPES.has(rootAttr.type)) {
|
|
5437
|
-
throw new
|
|
6927
|
+
throw new chunkMZOEL7FN_js.ValidationError(`Cannot qualify ${rootAttr.type} attribute "${rule.attribute}"`, [
|
|
5438
6928
|
{ path: ["property"], message: `Attribute "${rule.attribute}" is not a reference type` }
|
|
5439
6929
|
]);
|
|
5440
6930
|
}
|
|
5441
6931
|
const propSchema = rootAttr.properties;
|
|
5442
6932
|
if (!propSchema) {
|
|
5443
|
-
throw new
|
|
6933
|
+
throw new chunkMZOEL7FN_js.ValidationError(`Attribute "${rule.attribute}" has no qualifyWith schema`, [
|
|
5444
6934
|
{ path: ["property"], message: `"${rule.attribute}" has no qualifyWith` }
|
|
5445
6935
|
]);
|
|
5446
6936
|
}
|
|
5447
6937
|
const propDef = propSchema.definitions.find((p) => p.name === rule.property);
|
|
5448
6938
|
if (!propDef) {
|
|
5449
|
-
throw new
|
|
6939
|
+
throw new chunkMZOEL7FN_js.ValidationError(
|
|
5450
6940
|
`Property "${rule.property}" not defined on "${rule.attribute}".qualifyWith()`,
|
|
5451
6941
|
[{ path: ["property"], message: `Unknown property: ${rule.property}` }]
|
|
5452
6942
|
);
|
|
@@ -5476,189 +6966,199 @@ Object.defineProperty(exports, "indexBy", {
|
|
|
5476
6966
|
});
|
|
5477
6967
|
Object.defineProperty(exports, "parseAttributeConfig", {
|
|
5478
6968
|
enumerable: true,
|
|
5479
|
-
get: function () { return
|
|
6969
|
+
get: function () { return chunkM5QIBMIH_js.parseAttributeConfig; }
|
|
5480
6970
|
});
|
|
5481
6971
|
Object.defineProperty(exports, "AccessDeniedError", {
|
|
5482
6972
|
enumerable: true,
|
|
5483
|
-
get: function () { return
|
|
6973
|
+
get: function () { return chunkMZOEL7FN_js.AccessDeniedError; }
|
|
5484
6974
|
});
|
|
5485
6975
|
Object.defineProperty(exports, "AttributeInUseError", {
|
|
5486
6976
|
enumerable: true,
|
|
5487
|
-
get: function () { return
|
|
6977
|
+
get: function () { return chunkMZOEL7FN_js.AttributeInUseError; }
|
|
5488
6978
|
});
|
|
5489
6979
|
Object.defineProperty(exports, "AttributeNotFoundError", {
|
|
5490
6980
|
enumerable: true,
|
|
5491
|
-
get: function () { return
|
|
6981
|
+
get: function () { return chunkMZOEL7FN_js.AttributeNotFoundError; }
|
|
5492
6982
|
});
|
|
5493
6983
|
Object.defineProperty(exports, "ChangeTypeNotSupportedError", {
|
|
5494
6984
|
enumerable: true,
|
|
5495
|
-
get: function () { return
|
|
6985
|
+
get: function () { return chunkMZOEL7FN_js.ChangeTypeNotSupportedError; }
|
|
5496
6986
|
});
|
|
5497
6987
|
Object.defineProperty(exports, "ConcurrentModificationError", {
|
|
5498
6988
|
enumerable: true,
|
|
5499
|
-
get: function () { return
|
|
6989
|
+
get: function () { return chunkMZOEL7FN_js.ConcurrentModificationError; }
|
|
5500
6990
|
});
|
|
5501
6991
|
Object.defineProperty(exports, "DestructiveSyncNotAllowedError", {
|
|
5502
6992
|
enumerable: true,
|
|
5503
|
-
get: function () { return
|
|
6993
|
+
get: function () { return chunkMZOEL7FN_js.DestructiveSyncNotAllowedError; }
|
|
5504
6994
|
});
|
|
5505
6995
|
Object.defineProperty(exports, "DuplicateError", {
|
|
5506
6996
|
enumerable: true,
|
|
5507
|
-
get: function () { return
|
|
6997
|
+
get: function () { return chunkMZOEL7FN_js.DuplicateError; }
|
|
5508
6998
|
});
|
|
5509
6999
|
Object.defineProperty(exports, "ForbiddenError", {
|
|
5510
7000
|
enumerable: true,
|
|
5511
|
-
get: function () { return
|
|
7001
|
+
get: function () { return chunkMZOEL7FN_js.ForbiddenError; }
|
|
5512
7002
|
});
|
|
5513
7003
|
Object.defineProperty(exports, "MemoryNotFoundError", {
|
|
5514
7004
|
enumerable: true,
|
|
5515
|
-
get: function () { return
|
|
7005
|
+
get: function () { return chunkMZOEL7FN_js.MemoryNotFoundError; }
|
|
5516
7006
|
});
|
|
5517
7007
|
Object.defineProperty(exports, "MigrationTimeoutError", {
|
|
5518
7008
|
enumerable: true,
|
|
5519
|
-
get: function () { return
|
|
7009
|
+
get: function () { return chunkMZOEL7FN_js.MigrationTimeoutError; }
|
|
5520
7010
|
});
|
|
5521
7011
|
Object.defineProperty(exports, "NotFoundError", {
|
|
5522
7012
|
enumerable: true,
|
|
5523
|
-
get: function () { return
|
|
7013
|
+
get: function () { return chunkMZOEL7FN_js.NotFoundError; }
|
|
5524
7014
|
});
|
|
5525
7015
|
Object.defineProperty(exports, "NotImplementedError", {
|
|
5526
7016
|
enumerable: true,
|
|
5527
|
-
get: function () { return
|
|
7017
|
+
get: function () { return chunkMZOEL7FN_js.NotImplementedError; }
|
|
5528
7018
|
});
|
|
5529
7019
|
Object.defineProperty(exports, "ObjectNotFoundError", {
|
|
5530
7020
|
enumerable: true,
|
|
5531
|
-
get: function () { return
|
|
7021
|
+
get: function () { return chunkMZOEL7FN_js.ObjectNotFoundError; }
|
|
5532
7022
|
});
|
|
5533
7023
|
Object.defineProperty(exports, "ObjectReferencedError", {
|
|
5534
7024
|
enumerable: true,
|
|
5535
|
-
get: function () { return
|
|
7025
|
+
get: function () { return chunkMZOEL7FN_js.ObjectReferencedError; }
|
|
5536
7026
|
});
|
|
5537
7027
|
Object.defineProperty(exports, "OrphanSystemAttributeError", {
|
|
5538
7028
|
enumerable: true,
|
|
5539
|
-
get: function () { return
|
|
7029
|
+
get: function () { return chunkMZOEL7FN_js.OrphanSystemAttributeError; }
|
|
5540
7030
|
});
|
|
5541
7031
|
Object.defineProperty(exports, "ProtectedResourceError", {
|
|
5542
7032
|
enumerable: true,
|
|
5543
|
-
get: function () { return
|
|
7033
|
+
get: function () { return chunkMZOEL7FN_js.ProtectedResourceError; }
|
|
5544
7034
|
});
|
|
5545
7035
|
Object.defineProperty(exports, "ProtectedRoleError", {
|
|
5546
7036
|
enumerable: true,
|
|
5547
|
-
get: function () { return
|
|
7037
|
+
get: function () { return chunkMZOEL7FN_js.ProtectedRoleError; }
|
|
5548
7038
|
});
|
|
5549
7039
|
Object.defineProperty(exports, "RecordNotFoundError", {
|
|
5550
7040
|
enumerable: true,
|
|
5551
|
-
get: function () { return
|
|
7041
|
+
get: function () { return chunkMZOEL7FN_js.RecordNotFoundError; }
|
|
5552
7042
|
});
|
|
5553
7043
|
Object.defineProperty(exports, "RecordReferencedError", {
|
|
5554
7044
|
enumerable: true,
|
|
5555
|
-
get: function () { return
|
|
7045
|
+
get: function () { return chunkMZOEL7FN_js.RecordReferencedError; }
|
|
5556
7046
|
});
|
|
5557
7047
|
Object.defineProperty(exports, "RepositoryError", {
|
|
5558
7048
|
enumerable: true,
|
|
5559
|
-
get: function () { return
|
|
7049
|
+
get: function () { return chunkMZOEL7FN_js.RepositoryError; }
|
|
5560
7050
|
});
|
|
5561
7051
|
Object.defineProperty(exports, "RoleNotFoundError", {
|
|
5562
7052
|
enumerable: true,
|
|
5563
|
-
get: function () { return
|
|
7053
|
+
get: function () { return chunkMZOEL7FN_js.RoleNotFoundError; }
|
|
5564
7054
|
});
|
|
5565
7055
|
Object.defineProperty(exports, "SchemaError", {
|
|
5566
7056
|
enumerable: true,
|
|
5567
|
-
get: function () { return
|
|
7057
|
+
get: function () { return chunkMZOEL7FN_js.SchemaError; }
|
|
5568
7058
|
});
|
|
5569
7059
|
Object.defineProperty(exports, "SchemaErrorCode", {
|
|
5570
7060
|
enumerable: true,
|
|
5571
|
-
get: function () { return
|
|
7061
|
+
get: function () { return chunkMZOEL7FN_js.SchemaErrorCode; }
|
|
5572
7062
|
});
|
|
5573
7063
|
Object.defineProperty(exports, "StorageError", {
|
|
5574
7064
|
enumerable: true,
|
|
5575
|
-
get: function () { return
|
|
7065
|
+
get: function () { return chunkMZOEL7FN_js.StorageError; }
|
|
5576
7066
|
});
|
|
5577
7067
|
Object.defineProperty(exports, "SyncCascadeError", {
|
|
5578
7068
|
enumerable: true,
|
|
5579
|
-
get: function () { return
|
|
7069
|
+
get: function () { return chunkMZOEL7FN_js.SyncCascadeError; }
|
|
5580
7070
|
});
|
|
5581
7071
|
Object.defineProperty(exports, "SyncConflictError", {
|
|
5582
7072
|
enumerable: true,
|
|
5583
|
-
get: function () { return
|
|
7073
|
+
get: function () { return chunkMZOEL7FN_js.SyncConflictError; }
|
|
5584
7074
|
});
|
|
5585
7075
|
Object.defineProperty(exports, "SyncError", {
|
|
5586
7076
|
enumerable: true,
|
|
5587
|
-
get: function () { return
|
|
7077
|
+
get: function () { return chunkMZOEL7FN_js.SyncError; }
|
|
5588
7078
|
});
|
|
5589
7079
|
Object.defineProperty(exports, "SystemEntityImmutableError", {
|
|
5590
7080
|
enumerable: true,
|
|
5591
|
-
get: function () { return
|
|
7081
|
+
get: function () { return chunkMZOEL7FN_js.SystemEntityImmutableError; }
|
|
5592
7082
|
});
|
|
5593
7083
|
Object.defineProperty(exports, "ValidationError", {
|
|
5594
7084
|
enumerable: true,
|
|
5595
|
-
get: function () { return
|
|
7085
|
+
get: function () { return chunkMZOEL7FN_js.ValidationError; }
|
|
5596
7086
|
});
|
|
5597
7087
|
Object.defineProperty(exports, "computeRecordStatus", {
|
|
5598
7088
|
enumerable: true,
|
|
5599
|
-
get: function () { return
|
|
7089
|
+
get: function () { return chunkMZOEL7FN_js.computeRecordStatus; }
|
|
5600
7090
|
});
|
|
5601
7091
|
Object.defineProperty(exports, "createFormAttributeValidator", {
|
|
5602
7092
|
enumerable: true,
|
|
5603
|
-
get: function () { return
|
|
7093
|
+
get: function () { return chunkMZOEL7FN_js.createFormAttributeValidator; }
|
|
5604
7094
|
});
|
|
5605
7095
|
Object.defineProperty(exports, "isAttributeInUseError", {
|
|
5606
7096
|
enumerable: true,
|
|
5607
|
-
get: function () { return
|
|
7097
|
+
get: function () { return chunkMZOEL7FN_js.isAttributeInUseError; }
|
|
5608
7098
|
});
|
|
5609
7099
|
Object.defineProperty(exports, "isNotFoundError", {
|
|
5610
7100
|
enumerable: true,
|
|
5611
|
-
get: function () { return
|
|
7101
|
+
get: function () { return chunkMZOEL7FN_js.isNotFoundError; }
|
|
5612
7102
|
});
|
|
5613
7103
|
Object.defineProperty(exports, "isObjectReferencedError", {
|
|
5614
7104
|
enumerable: true,
|
|
5615
|
-
get: function () { return
|
|
7105
|
+
get: function () { return chunkMZOEL7FN_js.isObjectReferencedError; }
|
|
5616
7106
|
});
|
|
5617
7107
|
Object.defineProperty(exports, "isProtectedResourceError", {
|
|
5618
7108
|
enumerable: true,
|
|
5619
|
-
get: function () { return
|
|
7109
|
+
get: function () { return chunkMZOEL7FN_js.isProtectedResourceError; }
|
|
5620
7110
|
});
|
|
5621
7111
|
Object.defineProperty(exports, "isRecordReferencedError", {
|
|
5622
7112
|
enumerable: true,
|
|
5623
|
-
get: function () { return
|
|
7113
|
+
get: function () { return chunkMZOEL7FN_js.isRecordReferencedError; }
|
|
5624
7114
|
});
|
|
5625
7115
|
Object.defineProperty(exports, "isSchemaError", {
|
|
5626
7116
|
enumerable: true,
|
|
5627
|
-
get: function () { return
|
|
7117
|
+
get: function () { return chunkMZOEL7FN_js.isSchemaError; }
|
|
5628
7118
|
});
|
|
5629
7119
|
Object.defineProperty(exports, "isValidationError", {
|
|
5630
7120
|
enumerable: true,
|
|
5631
|
-
get: function () { return
|
|
7121
|
+
get: function () { return chunkMZOEL7FN_js.isValidationError; }
|
|
7122
|
+
});
|
|
7123
|
+
Object.defineProperty(exports, "rejectUnknownAttributesOrThrow", {
|
|
7124
|
+
enumerable: true,
|
|
7125
|
+
get: function () { return chunkMZOEL7FN_js.rejectUnknownAttributesOrThrow; }
|
|
5632
7126
|
});
|
|
5633
7127
|
Object.defineProperty(exports, "validateDraft", {
|
|
5634
7128
|
enumerable: true,
|
|
5635
|
-
get: function () { return
|
|
7129
|
+
get: function () { return chunkMZOEL7FN_js.validateDraft; }
|
|
5636
7130
|
});
|
|
5637
7131
|
Object.defineProperty(exports, "validateDraftOrThrow", {
|
|
5638
7132
|
enumerable: true,
|
|
5639
|
-
get: function () { return
|
|
7133
|
+
get: function () { return chunkMZOEL7FN_js.validateDraftOrThrow; }
|
|
5640
7134
|
});
|
|
5641
7135
|
Object.defineProperty(exports, "validateObject", {
|
|
5642
7136
|
enumerable: true,
|
|
5643
|
-
get: function () { return
|
|
7137
|
+
get: function () { return chunkMZOEL7FN_js.validateObject; }
|
|
5644
7138
|
});
|
|
5645
7139
|
Object.defineProperty(exports, "validateObjectOrThrow", {
|
|
5646
7140
|
enumerable: true,
|
|
5647
|
-
get: function () { return
|
|
7141
|
+
get: function () { return chunkMZOEL7FN_js.validateObjectOrThrow; }
|
|
5648
7142
|
});
|
|
5649
7143
|
Object.defineProperty(exports, "DEFAULT_VALIDATION_MESSAGES", {
|
|
5650
7144
|
enumerable: true,
|
|
5651
|
-
get: function () { return
|
|
7145
|
+
get: function () { return chunkYKWSHBT5_js.DEFAULT_VALIDATION_MESSAGES; }
|
|
5652
7146
|
});
|
|
5653
7147
|
Object.defineProperty(exports, "formatZodErrors", {
|
|
5654
7148
|
enumerable: true,
|
|
5655
|
-
get: function () { return
|
|
7149
|
+
get: function () { return chunkYKWSHBT5_js.formatZodErrors; }
|
|
5656
7150
|
});
|
|
5657
7151
|
exports.ALLOWED_PROPERTY_TYPES = ALLOWED_PROPERTY_TYPES;
|
|
5658
7152
|
exports.ALL_ACTIONS = ALL_ACTIONS;
|
|
5659
7153
|
exports.ALL_SYSTEM_RESOURCES = ALL_SYSTEM_RESOURCES;
|
|
7154
|
+
exports.ATTRIBUTE_FILTER_OPERATORS = ATTRIBUTE_FILTER_OPERATORS;
|
|
5660
7155
|
exports.ActivityTabConfig = ActivityTabConfig;
|
|
5661
7156
|
exports.BEHAVIOR_PROPERTIES = BEHAVIOR_PROPERTIES;
|
|
7157
|
+
exports.COMPUTED_FUNCTIONS = COMPUTED_FUNCTIONS;
|
|
7158
|
+
exports.COMPUTED_FUNCTION_METADATA = COMPUTED_FUNCTION_METADATA;
|
|
7159
|
+
exports.COMPUTED_FUNCTION_NAMES = COMPUTED_FUNCTION_NAMES;
|
|
7160
|
+
exports.ComputedFormulaCompileError = ComputedFormulaCompileError;
|
|
7161
|
+
exports.ComputedFormulaParseError = ComputedFormulaParseError;
|
|
5662
7162
|
exports.CustomTabConfig = CustomTabConfig;
|
|
5663
7163
|
exports.DB_COLUMN_FIELDS = DB_COLUMN_FIELDS;
|
|
5664
7164
|
exports.DEFAULT_DOCUMENT_SLOT = DEFAULT_DOCUMENT_SLOT;
|
|
@@ -5680,11 +7180,11 @@ exports.FormRowBuilder = FormRowBuilder;
|
|
|
5680
7180
|
exports.FormStepBuilder = FormStepBuilder;
|
|
5681
7181
|
exports.GroupBuilder = GroupBuilder;
|
|
5682
7182
|
exports.IDENTITY_PROPERTIES = IDENTITY_PROPERTIES;
|
|
5683
|
-
exports.
|
|
7183
|
+
exports.LIVE_EVENT_TYPES = LIVE_EVENT_TYPES;
|
|
7184
|
+
exports.LIVE_STREAM_START_CURSOR = LIVE_STREAM_START_CURSOR;
|
|
5684
7185
|
exports.ListViewBuilder = ListViewBuilder;
|
|
5685
7186
|
exports.ListViewTabConfigBuilder = ListViewTabConfigBuilder;
|
|
5686
7187
|
exports.MAX_PRESET_DEPTH = MAX_PRESET_DEPTH;
|
|
5687
|
-
exports.MaxDepthExceededError = MaxDepthExceededError;
|
|
5688
7188
|
exports.NO_VALUE_OPERATORS = NO_VALUE_OPERATORS;
|
|
5689
7189
|
exports.NoopGeocodingAdapter = NoopGeocodingAdapter;
|
|
5690
7190
|
exports.OPERATORS_BY_TYPE = OPERATORS_BY_TYPE;
|
|
@@ -5708,10 +7208,15 @@ exports.USER_STATUSES = USER_STATUSES;
|
|
|
5708
7208
|
exports.accessLevelToActions = accessLevelToActions;
|
|
5709
7209
|
exports.actionsToAccessLevel = actionsToAccessLevel;
|
|
5710
7210
|
exports.applyPipes = applyPipes;
|
|
7211
|
+
exports.assertAcyclicComputedDependencies = assertAcyclicComputedDependencies;
|
|
7212
|
+
exports.assertLiveStreamCursor = assertLiveStreamCursor;
|
|
5711
7213
|
exports.booleanFlag = booleanFlag;
|
|
5712
7214
|
exports.buildPropertySchema = buildPropertySchema;
|
|
5713
7215
|
exports.buildQualifiedAttribute = buildQualifiedAttribute;
|
|
5714
7216
|
exports.checkbox = checkbox;
|
|
7217
|
+
exports.compareLiveStreamCursors = compareLiveStreamCursors;
|
|
7218
|
+
exports.compileComputedFormula = compileComputedFormula;
|
|
7219
|
+
exports.compileRollupAttribute = compileRollupAttribute;
|
|
5715
7220
|
exports.createFlagRegistry = createFlagRegistry;
|
|
5716
7221
|
exports.createFlagService = createFlagService;
|
|
5717
7222
|
exports.createStandardSchemaProps = createStandardSchemaProps;
|
|
@@ -5719,39 +7224,40 @@ exports.currency = currency;
|
|
|
5719
7224
|
exports.date = date;
|
|
5720
7225
|
exports.detailView = detailView;
|
|
5721
7226
|
exports.document = document;
|
|
7227
|
+
exports.evaluateComputedAst = evaluateComputedAst;
|
|
7228
|
+
exports.evaluateComputedFormula = evaluateComputedFormula;
|
|
7229
|
+
exports.evaluateComputedFormulaWithResult = evaluateComputedFormulaWithResult;
|
|
5722
7230
|
exports.evaluateFilterState = evaluateFilterState;
|
|
5723
|
-
exports.evaluateFormula = evaluateFormula;
|
|
5724
|
-
exports.evaluateFormulaAttribute = evaluateFormulaAttribute;
|
|
5725
|
-
exports.evaluateFormulaWithResult = evaluateFormulaWithResult;
|
|
5726
7231
|
exports.extractAttributeNames = extractAttributeNames;
|
|
5727
|
-
exports.extractFormulaVariables = extractFormulaVariables;
|
|
5728
|
-
exports.extractRelationNames = extractRelationNames;
|
|
5729
|
-
exports.extractRelationReferences = extractRelationReferences;
|
|
5730
7232
|
exports.file = file;
|
|
5731
7233
|
exports.flagRegistry = flagRegistry;
|
|
5732
|
-
exports.flattenRelationsForEval = flattenRelationsForEval;
|
|
5733
7234
|
exports.form = form;
|
|
5734
7235
|
exports.formRegistry = formRegistry;
|
|
5735
7236
|
exports.formatAttributeValue = formatAttributeValue;
|
|
5736
|
-
exports.
|
|
7237
|
+
exports.formatComputedResult = formatComputedResult;
|
|
5737
7238
|
exports.formula = formula;
|
|
5738
7239
|
exports.generateDefaultDetailView = generateDefaultDetailView;
|
|
5739
7240
|
exports.generateDefaultListView = generateDefaultListView;
|
|
5740
7241
|
exports.getActiveTab = getActiveTab;
|
|
7242
|
+
exports.getAttributeCapabilities = getAttributeCapabilities;
|
|
7243
|
+
exports.getAttributeFilterOperators = getAttributeFilterOperators;
|
|
5741
7244
|
exports.getErrorMessage = getErrorMessage;
|
|
7245
|
+
exports.getLiveProtocolPayloadChannel = getLiveProtocolPayloadChannel;
|
|
5742
7246
|
exports.getOperatorPolarity = getOperatorPolarity;
|
|
5743
|
-
exports.
|
|
5744
|
-
exports.getRelationPath = getRelationPath;
|
|
5745
|
-
exports.getRollupFilterOperators = getRollupFilterOperators;
|
|
7247
|
+
exports.getRollupFilterOperators = getRollupFilterOperators2;
|
|
5746
7248
|
exports.getSystemAttributeI18nKey = getSystemAttributeI18nKey;
|
|
5747
7249
|
exports.getSystemAttributeList = getSystemAttributeList;
|
|
5748
|
-
exports.getTargetAttributeName = getTargetAttributeName;
|
|
5749
7250
|
exports.group = group;
|
|
5750
7251
|
exports.hasOptions = hasOptions;
|
|
5751
|
-
exports.hasRelationReferences = hasRelationReferences;
|
|
5752
7252
|
exports.inferInverseCardinality = inferInverseCardinality;
|
|
5753
|
-
exports.
|
|
7253
|
+
exports.inferRollupReturnType = inferRollupReturnType;
|
|
7254
|
+
exports.isAttributeFilterable = isAttributeFilterable;
|
|
7255
|
+
exports.isAttributeGroupable = isAttributeGroupable;
|
|
7256
|
+
exports.isAttributeKanbanGroupable = isAttributeKanbanGroupable;
|
|
7257
|
+
exports.isAttributeSearchable = isAttributeSearchable;
|
|
7258
|
+
exports.isAttributeSortable = isAttributeSortable2;
|
|
5754
7259
|
exports.isBilateralRelation = isBilateralRelation;
|
|
7260
|
+
exports.isComputedFunctionName = isComputedFunctionName;
|
|
5755
7261
|
exports.isDefaultRole = isDefaultRole;
|
|
5756
7262
|
exports.isDetailView = isDetailView;
|
|
5757
7263
|
exports.isDocumentAttribute = isDocumentAttribute;
|
|
@@ -5766,13 +7272,23 @@ exports.isFormSlotFieldRef = isFormSlotFieldRef;
|
|
|
5766
7272
|
exports.isFormTextRow = isFormTextRow;
|
|
5767
7273
|
exports.isLabelExpression = isLabelExpression;
|
|
5768
7274
|
exports.isListView = isListView;
|
|
7275
|
+
exports.isLiveErrorPayload = isLiveErrorPayload;
|
|
7276
|
+
exports.isLiveEventEnvelope = isLiveEventEnvelope;
|
|
7277
|
+
exports.isLiveEventPayload = isLiveEventPayload;
|
|
7278
|
+
exports.isLiveEventPayloadForType = isLiveEventPayloadForType;
|
|
7279
|
+
exports.isLiveEventType = isLiveEventType;
|
|
7280
|
+
exports.isLiveGapPayload = isLiveGapPayload;
|
|
7281
|
+
exports.isLiveReplayCompletePayload = isLiveReplayCompletePayload;
|
|
7282
|
+
exports.isLiveStreamCursor = isLiveStreamCursor;
|
|
5769
7283
|
exports.isNoValueOperator = isNoValueOperator;
|
|
5770
7284
|
exports.isNotEmpty = isNotEmpty;
|
|
7285
|
+
exports.isPlainRecord = isPlainRecord;
|
|
5771
7286
|
exports.isRelationGroup = isRelationGroup;
|
|
5772
7287
|
exports.isStandardSchema = isStandardSchema;
|
|
5773
7288
|
exports.isUniversalRelation = isUniversalRelation;
|
|
5774
7289
|
exports.jsonFlag = jsonFlag;
|
|
5775
7290
|
exports.listView = listView;
|
|
7291
|
+
exports.liveChannelKey = liveChannelKey;
|
|
5776
7292
|
exports.location = location;
|
|
5777
7293
|
exports.matchesMime = matchesMime;
|
|
5778
7294
|
exports.multiselect = multiselect;
|
|
@@ -5781,11 +7297,12 @@ exports.normalizeForEdgeRpc = normalizeForEdgeRpc;
|
|
|
5781
7297
|
exports.number = number;
|
|
5782
7298
|
exports.numberFlag = numberFlag;
|
|
5783
7299
|
exports.object = object;
|
|
5784
|
-
exports.
|
|
7300
|
+
exports.parseComputedFormula = parseComputedFormula;
|
|
7301
|
+
exports.parseLiveChannelKey = parseLiveChannelKey;
|
|
7302
|
+
exports.parseLiveSubscribePayload = parseLiveSubscribePayload;
|
|
7303
|
+
exports.parseLiveUnsubscribePayload = parseLiveUnsubscribePayload;
|
|
5785
7304
|
exports.parseQualifiedAttribute = parseQualifiedAttribute;
|
|
5786
|
-
exports.pathHasManyCardinality = pathHasManyCardinality;
|
|
5787
7305
|
exports.phone = phone;
|
|
5788
|
-
exports.rating = rating;
|
|
5789
7306
|
exports.registry = registry;
|
|
5790
7307
|
exports.relation = relation;
|
|
5791
7308
|
exports.relationGroup = relationGroup;
|
|
@@ -5793,16 +7310,14 @@ exports.renderLabelExpression = renderLabelExpression;
|
|
|
5793
7310
|
exports.resetViewToDefault = resetViewToDefault;
|
|
5794
7311
|
exports.richtext = richtext;
|
|
5795
7312
|
exports.rollup = rollup;
|
|
7313
|
+
exports.rollupToFormulaExpression = rollupToFormulaExpression;
|
|
5796
7314
|
exports.select = select;
|
|
5797
7315
|
exports.status = status;
|
|
5798
7316
|
exports.stringFlag = stringFlag;
|
|
5799
7317
|
exports.text = text;
|
|
5800
|
-
exports.textarea = textarea;
|
|
5801
7318
|
exports.toUndefinedIfEmpty = toUndefinedIfEmpty;
|
|
5802
7319
|
exports.user = user;
|
|
5803
7320
|
exports.validateAttributeName = validateAttributeName;
|
|
5804
|
-
exports.validateFormulaExpression = validateFormulaExpression;
|
|
5805
|
-
exports.validatePath = validatePath;
|
|
5806
7321
|
exports.validateQualifiedRule = validateQualifiedRule;
|
|
5807
7322
|
exports.validateSlotAgainstConfig = validateSlotAgainstConfig;
|
|
5808
7323
|
exports.viewRegistry = viewRegistry;
|