@jskit-ai/kernel 0.1.54 → 0.1.56
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/package.json +3 -2
- package/server/actions/ActionRuntimeServiceProvider.test.js +23 -15
- package/server/http/lib/kernel.test.js +447 -0
- package/server/http/lib/routeRegistration.js +236 -15
- package/server/http/lib/routeTransport.js +126 -0
- package/server/http/lib/routeValidator.js +133 -198
- package/server/http/lib/routeValidator.test.js +385 -278
- package/server/http/lib/router.js +17 -2
- package/server/platform/providerRuntime.test.js +7 -7
- package/server/runtime/bootBootstrapRoutes.js +2 -18
- package/server/runtime/bootBootstrapRoutes.test.js +5 -14
- package/server/runtime/fastifyBootstrap.js +119 -0
- package/server/runtime/fastifyBootstrap.test.js +119 -1
- package/server/runtime/moduleConfig.js +32 -62
- package/server/runtime/moduleConfig.test.js +48 -24
- package/server/support/pageTargets.js +15 -9
- package/server/support/pageTargets.test.js +1 -1
- package/shared/actions/actionContributorHelpers.js +5 -11
- package/shared/actions/actionDefinitions.js +37 -150
- package/shared/actions/actionDefinitions.test.js +117 -136
- package/shared/actions/policies.js +25 -169
- package/shared/actions/policies.test.js +76 -87
- package/shared/actions/registry.test.js +24 -50
- package/shared/support/crudFieldContract.js +322 -0
- package/shared/support/crudFieldContract.test.js +67 -0
- package/shared/support/crudListFilters.js +582 -38
- package/shared/support/crudListFilters.test.js +178 -8
- package/shared/support/crudLookup.js +14 -7
- package/shared/support/crudLookup.test.js +91 -66
- package/shared/support/shellLayoutTargets.test.js +1 -1
- package/shared/validators/composeSchemaDefinitions.js +53 -0
- package/shared/validators/composeSchemaDefinitions.test.js +156 -0
- package/shared/validators/createCursorListValidator.js +22 -35
- package/shared/validators/createCursorListValidator.test.js +22 -23
- package/shared/validators/cursorPaginationQueryValidator.js +14 -24
- package/shared/validators/cursorPaginationQueryValidator.test.js +18 -8
- package/shared/validators/htmlTimeSchemas.js +6 -4
- package/shared/validators/index.js +15 -7
- package/shared/validators/jsonRestSchemaSupport.js +139 -0
- package/shared/validators/mergeObjectSchemas.js +44 -6
- package/shared/validators/mergeObjectSchemas.test.js +60 -35
- package/shared/validators/recordIdParamsValidator.js +19 -52
- package/shared/validators/recordIdParamsValidator.test.js +13 -8
- package/shared/validators/resourceRequiredMetadata.js +3 -3
- package/shared/validators/resourceRequiredMetadata.test.js +29 -16
- package/shared/validators/schemaDefinitions.js +126 -0
- package/shared/validators/schemaDefinitions.test.js +51 -0
- package/shared/validators/schemaPayloadValidation.js +65 -0
- package/test/barrelExposure.test.js +30 -0
- package/test/routeInputContractGuard.test.js +10 -6
- package/shared/validators/mergeValidators.js +0 -89
- package/shared/validators/mergeValidators.test.js +0 -116
- package/shared/validators/nestValidator.js +0 -53
- package/shared/validators/nestValidator.test.js +0 -60
- package/shared/validators/settingsFieldNormalization.js +0 -40
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { deepFreeze } from "./deepFreeze.js";
|
|
2
2
|
import {
|
|
3
|
+
normalizeBoolean,
|
|
4
|
+
normalizeCanonicalRecordIdText,
|
|
5
|
+
normalizeUniqueTextList,
|
|
3
6
|
normalizeText,
|
|
4
7
|
normalizeObject
|
|
5
8
|
} from "./normalize.js";
|
|
@@ -43,6 +46,563 @@ const CRUD_LIST_FILTER_PRESENCE_OPTIONS = Object.freeze([
|
|
|
43
46
|
label: "Missing"
|
|
44
47
|
})
|
|
45
48
|
]);
|
|
49
|
+
const CRUD_LIST_FILTER_INVALID_VALUES_REJECT = "reject";
|
|
50
|
+
const CRUD_LIST_FILTER_INVALID_VALUES_DISCARD = "discard";
|
|
51
|
+
const CRUD_LIST_FILTER_INVALID_VALUES_MODES = Object.freeze([
|
|
52
|
+
CRUD_LIST_FILTER_INVALID_VALUES_REJECT,
|
|
53
|
+
CRUD_LIST_FILTER_INVALID_VALUES_DISCARD
|
|
54
|
+
]);
|
|
55
|
+
const INVALID_CRUD_LIST_FILTER_QUERY_VALUE = Symbol("invalidCrudListFilterQueryValue");
|
|
56
|
+
const DATE_FILTER_PATTERN = /^\d{4}-\d{2}-\d{2}$/u;
|
|
57
|
+
|
|
58
|
+
function parseCrudListRangeQueryExpression(value = null) {
|
|
59
|
+
const sourceValue = Array.isArray(value) ? value[0] : value;
|
|
60
|
+
if (sourceValue == null) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const normalized = typeof sourceValue === "number"
|
|
65
|
+
? String(sourceValue)
|
|
66
|
+
: normalizeText(sourceValue);
|
|
67
|
+
if (!normalized) {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const separatorIndex = normalized.indexOf("..");
|
|
72
|
+
if (separatorIndex < 0) {
|
|
73
|
+
return deepFreeze({
|
|
74
|
+
exact: true,
|
|
75
|
+
start: normalized,
|
|
76
|
+
end: normalized
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const start = normalizeText(normalized.slice(0, separatorIndex));
|
|
81
|
+
const end = normalizeText(normalized.slice(separatorIndex + 2));
|
|
82
|
+
if (!start && !end) {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return deepFreeze({
|
|
87
|
+
exact: false,
|
|
88
|
+
start,
|
|
89
|
+
end
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function formatCrudListRangeQueryExpression(startValue = "", endValue = "", { collapseExact = false } = {}) {
|
|
94
|
+
const start = normalizeText(startValue);
|
|
95
|
+
const end = normalizeText(endValue);
|
|
96
|
+
if (!start && !end) {
|
|
97
|
+
return "";
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (collapseExact === true && start && end && start === end) {
|
|
101
|
+
return start;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return `${start}..${end}`;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function normalizeCrudListFilterInvalidValues(value = "") {
|
|
108
|
+
const normalized = normalizeText(value).toLowerCase();
|
|
109
|
+
if (CRUD_LIST_FILTER_INVALID_VALUES_MODES.includes(normalized)) {
|
|
110
|
+
return normalized;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
throw new TypeError(
|
|
114
|
+
`Unsupported CRUD list filter invalidValues mode "${value}". Expected one of: ${CRUD_LIST_FILTER_INVALID_VALUES_MODES.join(", ")}.`
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function firstCrudListFilterValue(value) {
|
|
119
|
+
return Array.isArray(value) ? value[0] : value;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function isPrimitiveCrudListFilterInput(value) {
|
|
123
|
+
const valueType = typeof value;
|
|
124
|
+
return valueType === "string" || valueType === "number" || valueType === "boolean";
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function isPrimitiveOrPrimitiveArrayCrudListFilterInput(value) {
|
|
128
|
+
if (Array.isArray(value)) {
|
|
129
|
+
return value.every((entry) => isPrimitiveCrudListFilterInput(entry));
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return isPrimitiveCrudListFilterInput(value);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function normalizeDateFilterText(value) {
|
|
136
|
+
const normalized = normalizeText(firstCrudListFilterValue(value));
|
|
137
|
+
if (!normalized || !DATE_FILTER_PATTERN.test(normalized)) {
|
|
138
|
+
return "";
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return normalized;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function normalizeCanonicalRecordIdList(value) {
|
|
145
|
+
return normalizeUniqueTextList(value, {
|
|
146
|
+
acceptSingle: true
|
|
147
|
+
})
|
|
148
|
+
.map((entry) => normalizeCanonicalRecordIdText(entry, { fallback: "" }))
|
|
149
|
+
.filter(Boolean);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function normalizeFiniteFilterNumber(value) {
|
|
153
|
+
if (value == null || value === "") {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const normalized = typeof value === "number"
|
|
158
|
+
? value
|
|
159
|
+
: Number(normalizeText(firstCrudListFilterValue(value)));
|
|
160
|
+
return Number.isFinite(normalized) ? normalized : null;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function normalizeAllowedFilterTextValue(value, allowedValues = new Set()) {
|
|
164
|
+
const normalized = normalizeText(firstCrudListFilterValue(value));
|
|
165
|
+
if (!normalized || !allowedValues.has(normalized)) {
|
|
166
|
+
return "";
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return normalized;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function normalizeAllowedFilterTextValues(value, allowedValues = new Set()) {
|
|
173
|
+
return normalizeUniqueTextList(value, {
|
|
174
|
+
acceptSingle: true
|
|
175
|
+
}).filter((entry) => allowedValues.has(entry));
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function resolveCrudListFilterAllowedValues(filter = {}) {
|
|
179
|
+
return new Set(
|
|
180
|
+
(Array.isArray(filter.options) ? filter.options : [])
|
|
181
|
+
.map((entry) => normalizeText(entry?.value))
|
|
182
|
+
.filter(Boolean)
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function normalizeCrudListDateRangeUiValue(rawValue) {
|
|
187
|
+
const source = rawValue && typeof rawValue === "object" && !Array.isArray(rawValue)
|
|
188
|
+
? rawValue
|
|
189
|
+
: null;
|
|
190
|
+
if (source) {
|
|
191
|
+
return {
|
|
192
|
+
from: normalizeDateFilterText(source.from),
|
|
193
|
+
to: normalizeDateFilterText(source.to)
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const parsed = parseCrudListRangeQueryExpression(rawValue);
|
|
198
|
+
return {
|
|
199
|
+
from: normalizeDateFilterText(parsed?.start),
|
|
200
|
+
to: normalizeDateFilterText(parsed?.end)
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function normalizeCrudListNumberRangeUiValue(rawValue) {
|
|
205
|
+
const source = rawValue && typeof rawValue === "object" && !Array.isArray(rawValue)
|
|
206
|
+
? rawValue
|
|
207
|
+
: null;
|
|
208
|
+
if (source) {
|
|
209
|
+
return {
|
|
210
|
+
min: normalizeText(source.min),
|
|
211
|
+
max: normalizeText(source.max)
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const parsed = parseCrudListRangeQueryExpression(rawValue);
|
|
216
|
+
return {
|
|
217
|
+
min: normalizeText(parsed?.start),
|
|
218
|
+
max: normalizeText(parsed?.end)
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function createCrudListFilterInitialValue(filter = {}) {
|
|
223
|
+
if (normalizeCrudListFilterType(filter.type) === CRUD_LIST_FILTER_TYPE_FLAG) {
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
if (isCrudListFilterMultiValue(filter)) {
|
|
227
|
+
return [];
|
|
228
|
+
}
|
|
229
|
+
if (normalizeCrudListFilterType(filter.type) === CRUD_LIST_FILTER_TYPE_DATE_RANGE) {
|
|
230
|
+
return {
|
|
231
|
+
from: "",
|
|
232
|
+
to: ""
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
if (normalizeCrudListFilterType(filter.type) === CRUD_LIST_FILTER_TYPE_NUMBER_RANGE) {
|
|
236
|
+
return {
|
|
237
|
+
min: "",
|
|
238
|
+
max: ""
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return "";
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function isCrudListFilterMultiValue(filter = {}) {
|
|
246
|
+
const type = normalizeCrudListFilterType(filter.type);
|
|
247
|
+
return type === CRUD_LIST_FILTER_TYPE_ENUM_MANY || type === CRUD_LIST_FILTER_TYPE_RECORD_ID_MANY;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function isCrudListFilterStructuredValue(filter = {}) {
|
|
251
|
+
const type = normalizeCrudListFilterType(filter.type);
|
|
252
|
+
return type === CRUD_LIST_FILTER_TYPE_DATE_RANGE || type === CRUD_LIST_FILTER_TYPE_NUMBER_RANGE;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function normalizeCrudListFilterUiValue(filter = {}, rawValue) {
|
|
256
|
+
const type = normalizeCrudListFilterType(filter.type);
|
|
257
|
+
|
|
258
|
+
if (type === CRUD_LIST_FILTER_TYPE_FLAG) {
|
|
259
|
+
return rawValue === true;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
if (type === CRUD_LIST_FILTER_TYPE_ENUM) {
|
|
263
|
+
return normalizeAllowedFilterTextValue(rawValue, resolveCrudListFilterAllowedValues(filter));
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
if (type === CRUD_LIST_FILTER_TYPE_PRESENCE) {
|
|
267
|
+
return normalizeAllowedFilterTextValue(rawValue, new Set([
|
|
268
|
+
CRUD_LIST_FILTER_PRESENCE_PRESENT,
|
|
269
|
+
CRUD_LIST_FILTER_PRESENCE_MISSING
|
|
270
|
+
]));
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if (type === CRUD_LIST_FILTER_TYPE_ENUM_MANY) {
|
|
274
|
+
return normalizeAllowedFilterTextValues(rawValue, resolveCrudListFilterAllowedValues(filter));
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (type === CRUD_LIST_FILTER_TYPE_RECORD_ID) {
|
|
278
|
+
return normalizeCanonicalRecordIdText(rawValue, { fallback: "" }) || "";
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (type === CRUD_LIST_FILTER_TYPE_RECORD_ID_MANY) {
|
|
282
|
+
return normalizeCanonicalRecordIdList(rawValue);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
if (type === CRUD_LIST_FILTER_TYPE_DATE) {
|
|
286
|
+
return normalizeDateFilterText(rawValue);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (type === CRUD_LIST_FILTER_TYPE_DATE_RANGE) {
|
|
290
|
+
return normalizeCrudListDateRangeUiValue(rawValue);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (type === CRUD_LIST_FILTER_TYPE_NUMBER_RANGE) {
|
|
294
|
+
return normalizeCrudListNumberRangeUiValue(rawValue);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
return normalizeText(rawValue);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function matchCrudListFilterValues(currentValue, expectedValue) {
|
|
301
|
+
const currentList = Array.isArray(currentValue) ? [...currentValue].sort() : [];
|
|
302
|
+
const expectedList = Array.isArray(expectedValue) ? [...expectedValue].sort() : [];
|
|
303
|
+
if (currentList.length !== expectedList.length) {
|
|
304
|
+
return false;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return currentList.every((entry, index) => entry === expectedList[index]);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function areCrudListFilterUiValuesEqual(filter = {}, currentValue, expectedValue) {
|
|
311
|
+
const normalizedCurrentValue = normalizeCrudListFilterUiValue(filter, currentValue);
|
|
312
|
+
const normalizedExpectedValue = normalizeCrudListFilterUiValue(filter, expectedValue);
|
|
313
|
+
|
|
314
|
+
if (isCrudListFilterMultiValue(filter)) {
|
|
315
|
+
return matchCrudListFilterValues(normalizedCurrentValue, normalizedExpectedValue);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
if (normalizeCrudListFilterType(filter.type) === CRUD_LIST_FILTER_TYPE_DATE_RANGE) {
|
|
319
|
+
return (
|
|
320
|
+
normalizeText(normalizedCurrentValue?.from) === normalizeText(normalizedExpectedValue?.from) &&
|
|
321
|
+
normalizeText(normalizedCurrentValue?.to) === normalizeText(normalizedExpectedValue?.to)
|
|
322
|
+
);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if (normalizeCrudListFilterType(filter.type) === CRUD_LIST_FILTER_TYPE_NUMBER_RANGE) {
|
|
326
|
+
return (
|
|
327
|
+
normalizeText(normalizedCurrentValue?.min) === normalizeText(normalizedExpectedValue?.min) &&
|
|
328
|
+
normalizeText(normalizedCurrentValue?.max) === normalizeText(normalizedExpectedValue?.max)
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
return normalizedCurrentValue === normalizedExpectedValue;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function formatCrudListFilterQueryValue(filter = {}, value) {
|
|
336
|
+
const type = normalizeCrudListFilterType(filter.type);
|
|
337
|
+
|
|
338
|
+
if (type === CRUD_LIST_FILTER_TYPE_DATE_RANGE) {
|
|
339
|
+
return formatCrudListRangeQueryExpression(value?.from, value?.to, {
|
|
340
|
+
collapseExact: true
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
if (type === CRUD_LIST_FILTER_TYPE_NUMBER_RANGE) {
|
|
345
|
+
return formatCrudListRangeQueryExpression(value?.min, value?.max, {
|
|
346
|
+
collapseExact: true
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
return value;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
function rejectInvalidCrudListFilterValue({ invalidValues = CRUD_LIST_FILTER_INVALID_VALUES_REJECT } = {}) {
|
|
354
|
+
return invalidValues === CRUD_LIST_FILTER_INVALID_VALUES_DISCARD
|
|
355
|
+
? null
|
|
356
|
+
: INVALID_CRUD_LIST_FILTER_QUERY_VALUE;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
function normalizeCrudListDateRangeQueryValue(value) {
|
|
360
|
+
const parsed = parseCrudListRangeQueryExpression(firstCrudListFilterValue(value));
|
|
361
|
+
if (!parsed) {
|
|
362
|
+
return undefined;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
const from = normalizeDateFilterText(parsed.start);
|
|
366
|
+
const to = normalizeDateFilterText(parsed.end);
|
|
367
|
+
|
|
368
|
+
if (parsed.exact) {
|
|
369
|
+
if (!from) {
|
|
370
|
+
return undefined;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
return {
|
|
374
|
+
from,
|
|
375
|
+
to: from
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
if (!from && !to) {
|
|
380
|
+
return undefined;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
return {
|
|
384
|
+
...(from ? { from } : {}),
|
|
385
|
+
...(to ? { to } : {})
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
function normalizeCrudListNumberRangeQueryValue(value) {
|
|
390
|
+
const parsed = parseCrudListRangeQueryExpression(firstCrudListFilterValue(value));
|
|
391
|
+
if (!parsed) {
|
|
392
|
+
return undefined;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
const min = normalizeFiniteFilterNumber(parsed.start);
|
|
396
|
+
const max = normalizeFiniteFilterNumber(parsed.end);
|
|
397
|
+
|
|
398
|
+
if (parsed.exact) {
|
|
399
|
+
if (min == null) {
|
|
400
|
+
return undefined;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
return {
|
|
404
|
+
min,
|
|
405
|
+
max: min
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
if (min == null && max == null) {
|
|
410
|
+
return undefined;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
return {
|
|
414
|
+
...(min != null ? { min } : {}),
|
|
415
|
+
...(max != null ? { max } : {})
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
function parseCrudListFilterQueryValue(
|
|
420
|
+
filter = {},
|
|
421
|
+
value,
|
|
422
|
+
{ invalidValues = CRUD_LIST_FILTER_INVALID_VALUES_REJECT } = {}
|
|
423
|
+
) {
|
|
424
|
+
const type = normalizeCrudListFilterType(filter.type);
|
|
425
|
+
const allowedValues = resolveCrudListFilterAllowedValues(filter);
|
|
426
|
+
|
|
427
|
+
if (type === CRUD_LIST_FILTER_TYPE_FLAG) {
|
|
428
|
+
if (value !== null && value !== "" && !isPrimitiveCrudListFilterInput(value)) {
|
|
429
|
+
return rejectInvalidCrudListFilterValue({ invalidValues });
|
|
430
|
+
}
|
|
431
|
+
} else if (isCrudListFilterMultiValue(filter)) {
|
|
432
|
+
if (!isPrimitiveOrPrimitiveArrayCrudListFilterInput(value)) {
|
|
433
|
+
return rejectInvalidCrudListFilterValue({ invalidValues });
|
|
434
|
+
}
|
|
435
|
+
} else if (!isPrimitiveCrudListFilterInput(value)) {
|
|
436
|
+
return rejectInvalidCrudListFilterValue({ invalidValues });
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
if (type === CRUD_LIST_FILTER_TYPE_FLAG) {
|
|
440
|
+
if (value === null || value === "") {
|
|
441
|
+
return true;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
try {
|
|
445
|
+
return normalizeBoolean(firstCrudListFilterValue(value));
|
|
446
|
+
} catch {
|
|
447
|
+
return rejectInvalidCrudListFilterValue({ invalidValues });
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
if (type === CRUD_LIST_FILTER_TYPE_ENUM) {
|
|
452
|
+
return normalizeAllowedFilterTextValue(value, allowedValues) || rejectInvalidCrudListFilterValue({ invalidValues });
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
if (type === CRUD_LIST_FILTER_TYPE_PRESENCE) {
|
|
456
|
+
return normalizeAllowedFilterTextValue(value, new Set([
|
|
457
|
+
CRUD_LIST_FILTER_PRESENCE_PRESENT,
|
|
458
|
+
CRUD_LIST_FILTER_PRESENCE_MISSING
|
|
459
|
+
])) || rejectInvalidCrudListFilterValue({ invalidValues });
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
if (type === CRUD_LIST_FILTER_TYPE_ENUM_MANY) {
|
|
463
|
+
const values = Array.isArray(value) ? value : [value];
|
|
464
|
+
const normalizedValues = normalizeAllowedFilterTextValues(value, allowedValues);
|
|
465
|
+
|
|
466
|
+
if (invalidValues === CRUD_LIST_FILTER_INVALID_VALUES_DISCARD) {
|
|
467
|
+
return normalizedValues.length > 0 ? normalizedValues : null;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
return normalizedValues.length > 0 && normalizedValues.length === values.length
|
|
471
|
+
? normalizedValues
|
|
472
|
+
: INVALID_CRUD_LIST_FILTER_QUERY_VALUE;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
if (type === CRUD_LIST_FILTER_TYPE_RECORD_ID) {
|
|
476
|
+
return normalizeCanonicalRecordIdText(firstCrudListFilterValue(value), {
|
|
477
|
+
fallback: ""
|
|
478
|
+
}) || rejectInvalidCrudListFilterValue({ invalidValues });
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
if (type === CRUD_LIST_FILTER_TYPE_RECORD_ID_MANY) {
|
|
482
|
+
const values = Array.isArray(value) ? value : [value];
|
|
483
|
+
const normalizedValues = normalizeCanonicalRecordIdList(value);
|
|
484
|
+
|
|
485
|
+
if (invalidValues === CRUD_LIST_FILTER_INVALID_VALUES_DISCARD) {
|
|
486
|
+
return normalizedValues.length > 0 ? normalizedValues : null;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
return normalizedValues.length > 0 && normalizedValues.length === values.length
|
|
490
|
+
? normalizedValues
|
|
491
|
+
: INVALID_CRUD_LIST_FILTER_QUERY_VALUE;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
if (type === CRUD_LIST_FILTER_TYPE_DATE) {
|
|
495
|
+
return normalizeDateFilterText(value) || rejectInvalidCrudListFilterValue({ invalidValues });
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
if (type === CRUD_LIST_FILTER_TYPE_DATE_RANGE) {
|
|
499
|
+
const normalized = normalizeCrudListDateRangeQueryValue(value);
|
|
500
|
+
if (invalidValues === CRUD_LIST_FILTER_INVALID_VALUES_DISCARD) {
|
|
501
|
+
return normalized || null;
|
|
502
|
+
}
|
|
503
|
+
return normalized || INVALID_CRUD_LIST_FILTER_QUERY_VALUE;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
if (type === CRUD_LIST_FILTER_TYPE_NUMBER_RANGE) {
|
|
507
|
+
const normalized = normalizeCrudListNumberRangeQueryValue(value);
|
|
508
|
+
if (invalidValues === CRUD_LIST_FILTER_INVALID_VALUES_DISCARD) {
|
|
509
|
+
return normalized || null;
|
|
510
|
+
}
|
|
511
|
+
return normalized || INVALID_CRUD_LIST_FILTER_QUERY_VALUE;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
return INVALID_CRUD_LIST_FILTER_QUERY_VALUE;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
function hasCrudListFilterUiValue(filter = {}, rawValue) {
|
|
518
|
+
const normalizedValue = normalizeCrudListFilterUiValue(filter, rawValue);
|
|
519
|
+
const type = normalizeCrudListFilterType(filter.type);
|
|
520
|
+
|
|
521
|
+
if (type === CRUD_LIST_FILTER_TYPE_FLAG) {
|
|
522
|
+
return normalizedValue === true;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
if (isCrudListFilterMultiValue(filter)) {
|
|
526
|
+
return Array.isArray(normalizedValue) && normalizedValue.length > 0;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
if (type === CRUD_LIST_FILTER_TYPE_DATE_RANGE) {
|
|
530
|
+
return Boolean(normalizeText(normalizedValue?.from) || normalizeText(normalizedValue?.to));
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
if (type === CRUD_LIST_FILTER_TYPE_NUMBER_RANGE) {
|
|
534
|
+
return Boolean(normalizeText(normalizedValue?.min) || normalizeText(normalizedValue?.max));
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
return Boolean(normalizeText(normalizedValue));
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
function listCrudListFilterChipValues(filter = {}, rawValue) {
|
|
541
|
+
const normalizedValue = normalizeCrudListFilterUiValue(filter, rawValue);
|
|
542
|
+
|
|
543
|
+
if (!hasCrudListFilterUiValue(filter, normalizedValue)) {
|
|
544
|
+
return [];
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
if (isCrudListFilterMultiValue(filter)) {
|
|
548
|
+
return Array.isArray(normalizedValue) ? normalizedValue : [];
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
return [normalizedValue];
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
function formatCrudListFilterDefaultChipLabel(filter = {}, rawValue, { resolveAtomicValue = null } = {}) {
|
|
555
|
+
const normalizedValue = normalizeCrudListFilterUiValue(filter, rawValue);
|
|
556
|
+
const type = normalizeCrudListFilterType(filter.type);
|
|
557
|
+
const resolveAtomicLabel = typeof resolveAtomicValue === "function"
|
|
558
|
+
? resolveAtomicValue
|
|
559
|
+
: (value) => String(value || "");
|
|
560
|
+
|
|
561
|
+
if (type === CRUD_LIST_FILTER_TYPE_FLAG) {
|
|
562
|
+
return filter.label;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
if (
|
|
566
|
+
type === CRUD_LIST_FILTER_TYPE_ENUM ||
|
|
567
|
+
type === CRUD_LIST_FILTER_TYPE_PRESENCE ||
|
|
568
|
+
type === CRUD_LIST_FILTER_TYPE_RECORD_ID
|
|
569
|
+
) {
|
|
570
|
+
return `${filter.label}: ${resolveAtomicLabel(normalizedValue, filter)}`;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
if (type === CRUD_LIST_FILTER_TYPE_ENUM_MANY || type === CRUD_LIST_FILTER_TYPE_RECORD_ID_MANY) {
|
|
574
|
+
const atomicValue = Array.isArray(normalizedValue)
|
|
575
|
+
? normalizedValue[0] || ""
|
|
576
|
+
: normalizeText(normalizedValue);
|
|
577
|
+
return `${filter.label}: ${resolveAtomicLabel(atomicValue, filter)}`;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
if (type === CRUD_LIST_FILTER_TYPE_DATE) {
|
|
581
|
+
return `${filter.label}: ${normalizedValue}`;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
if (type === CRUD_LIST_FILTER_TYPE_DATE_RANGE) {
|
|
585
|
+
if (normalizedValue?.from && normalizedValue?.to) {
|
|
586
|
+
return `${filter.label}: ${normalizedValue.from} to ${normalizedValue.to}`;
|
|
587
|
+
}
|
|
588
|
+
if (normalizedValue?.from) {
|
|
589
|
+
return `${filter.label}: from ${normalizedValue.from}`;
|
|
590
|
+
}
|
|
591
|
+
return `${filter.label}: to ${normalizedValue?.to || ""}`;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
if (type === CRUD_LIST_FILTER_TYPE_NUMBER_RANGE) {
|
|
595
|
+
if (normalizedValue?.min && normalizedValue?.max) {
|
|
596
|
+
return `${filter.label}: ${normalizedValue.min} to ${normalizedValue.max}`;
|
|
597
|
+
}
|
|
598
|
+
if (normalizedValue?.min) {
|
|
599
|
+
return `${filter.label}: min ${normalizedValue.min}`;
|
|
600
|
+
}
|
|
601
|
+
return `${filter.label}: max ${normalizedValue?.max || ""}`;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
return filter.label;
|
|
605
|
+
}
|
|
46
606
|
|
|
47
607
|
function normalizeCrudListFilterType(value = "") {
|
|
48
608
|
const normalized = normalizeText(value);
|
|
@@ -149,20 +709,6 @@ function resolveCrudListFilterOptionSet(rawDefinition = {}, type = "") {
|
|
|
149
709
|
}
|
|
150
710
|
|
|
151
711
|
function resolveCrudListFilterQueryKeys(definition = {}) {
|
|
152
|
-
const type = normalizeCrudListFilterType(definition.type);
|
|
153
|
-
if (type === CRUD_LIST_FILTER_TYPE_DATE_RANGE) {
|
|
154
|
-
return Object.freeze([
|
|
155
|
-
normalizeText(definition.fromKey),
|
|
156
|
-
normalizeText(definition.toKey)
|
|
157
|
-
].filter(Boolean));
|
|
158
|
-
}
|
|
159
|
-
if (type === CRUD_LIST_FILTER_TYPE_NUMBER_RANGE) {
|
|
160
|
-
return Object.freeze([
|
|
161
|
-
normalizeText(definition.minKey),
|
|
162
|
-
normalizeText(definition.maxKey)
|
|
163
|
-
].filter(Boolean));
|
|
164
|
-
}
|
|
165
|
-
|
|
166
712
|
return Object.freeze([normalizeText(definition.queryKey)].filter(Boolean));
|
|
167
713
|
}
|
|
168
714
|
|
|
@@ -201,33 +747,15 @@ function normalizeCrudListFilterDefinition(rawKey = "", rawDefinition = null) {
|
|
|
201
747
|
: null;
|
|
202
748
|
|
|
203
749
|
if (type === CRUD_LIST_FILTER_TYPE_DATE_RANGE) {
|
|
204
|
-
|
|
205
|
-
key
|
|
206
|
-
|
|
207
|
-
label,
|
|
208
|
-
fromKey: normalizeText(source.fromKey) || `${key}From`,
|
|
209
|
-
toKey: normalizeText(source.toKey) || `${key}To`,
|
|
210
|
-
options,
|
|
211
|
-
lookup,
|
|
212
|
-
chipLabel,
|
|
213
|
-
ui,
|
|
214
|
-
meta
|
|
215
|
-
});
|
|
750
|
+
if (normalizeText(source.fromKey) || normalizeText(source.toKey)) {
|
|
751
|
+
throw new TypeError(`CRUD list filter "${key}" uses unsupported split range keys. Use queryKey.`);
|
|
752
|
+
}
|
|
216
753
|
}
|
|
217
754
|
|
|
218
755
|
if (type === CRUD_LIST_FILTER_TYPE_NUMBER_RANGE) {
|
|
219
|
-
|
|
220
|
-
key
|
|
221
|
-
|
|
222
|
-
label,
|
|
223
|
-
minKey: normalizeText(source.minKey) || `${key}Min`,
|
|
224
|
-
maxKey: normalizeText(source.maxKey) || `${key}Max`,
|
|
225
|
-
options,
|
|
226
|
-
lookup,
|
|
227
|
-
chipLabel,
|
|
228
|
-
ui,
|
|
229
|
-
meta
|
|
230
|
-
});
|
|
756
|
+
if (normalizeText(source.minKey) || normalizeText(source.maxKey)) {
|
|
757
|
+
throw new TypeError(`CRUD list filter "${key}" uses unsupported split range keys. Use queryKey.`);
|
|
758
|
+
}
|
|
231
759
|
}
|
|
232
760
|
|
|
233
761
|
return Object.freeze({
|
|
@@ -288,7 +816,23 @@ export {
|
|
|
288
816
|
CRUD_LIST_FILTER_PRESENCE_PRESENT,
|
|
289
817
|
CRUD_LIST_FILTER_PRESENCE_MISSING,
|
|
290
818
|
CRUD_LIST_FILTER_PRESENCE_OPTIONS,
|
|
819
|
+
CRUD_LIST_FILTER_INVALID_VALUES_REJECT,
|
|
820
|
+
CRUD_LIST_FILTER_INVALID_VALUES_DISCARD,
|
|
821
|
+
INVALID_CRUD_LIST_FILTER_QUERY_VALUE,
|
|
822
|
+
normalizeCrudListFilterInvalidValues,
|
|
823
|
+
parseCrudListRangeQueryExpression,
|
|
824
|
+
formatCrudListRangeQueryExpression,
|
|
291
825
|
defineCrudListFilters,
|
|
826
|
+
createCrudListFilterInitialValue,
|
|
827
|
+
isCrudListFilterMultiValue,
|
|
828
|
+
isCrudListFilterStructuredValue,
|
|
829
|
+
normalizeCrudListFilterUiValue,
|
|
830
|
+
areCrudListFilterUiValuesEqual,
|
|
831
|
+
hasCrudListFilterUiValue,
|
|
832
|
+
listCrudListFilterChipValues,
|
|
833
|
+
formatCrudListFilterDefaultChipLabel,
|
|
834
|
+
formatCrudListFilterQueryValue,
|
|
835
|
+
parseCrudListFilterQueryValue,
|
|
292
836
|
resolveCrudListFilterQueryKeys,
|
|
293
837
|
resolveCrudListFilterOptionLabel
|
|
294
838
|
};
|