@ciwergrp/nuxid 1.6.5 → 1.7.0
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/module.json +1 -1
- package/dist/runtime/form.js +42 -3
- package/dist/runtime/validator.d.ts +1 -1
- package/dist/runtime/validator.js +71 -5
- package/docs/.docs/dependencies.md +52 -0
- package/package.json +1 -1
package/dist/module.json
CHANGED
package/dist/runtime/form.js
CHANGED
|
@@ -2,6 +2,45 @@ import { reactive, ref, toRaw } from "vue";
|
|
|
2
2
|
function normalizeStringWhitespace(value) {
|
|
3
3
|
return value.trim().replace(/\s{2,}/g, " ");
|
|
4
4
|
}
|
|
5
|
+
function isPlainObject(value) {
|
|
6
|
+
return value !== null && Object.prototype.toString.call(value) === "[object Object]";
|
|
7
|
+
}
|
|
8
|
+
function flattenErrorPaths(value, currentPath = "", target = {}) {
|
|
9
|
+
if (Array.isArray(value)) {
|
|
10
|
+
const hasObjectItems = value.some((item) => item !== null && typeof item === "object");
|
|
11
|
+
if (!hasObjectItems) {
|
|
12
|
+
if (currentPath) {
|
|
13
|
+
target[currentPath] = value;
|
|
14
|
+
}
|
|
15
|
+
return target;
|
|
16
|
+
}
|
|
17
|
+
value.forEach((item, index) => {
|
|
18
|
+
const nextPath = currentPath ? `${currentPath}.${index}` : `${index}`;
|
|
19
|
+
flattenErrorPaths(item, nextPath, target);
|
|
20
|
+
});
|
|
21
|
+
return target;
|
|
22
|
+
}
|
|
23
|
+
if (isPlainObject(value)) {
|
|
24
|
+
Object.keys(value).forEach((key) => {
|
|
25
|
+
const nextPath = currentPath ? `${currentPath}.${key}` : key;
|
|
26
|
+
flattenErrorPaths(value[key], nextPath, target);
|
|
27
|
+
});
|
|
28
|
+
return target;
|
|
29
|
+
}
|
|
30
|
+
if (currentPath) {
|
|
31
|
+
target[currentPath] = value;
|
|
32
|
+
}
|
|
33
|
+
return target;
|
|
34
|
+
}
|
|
35
|
+
function withDotPathErrors(errors) {
|
|
36
|
+
if (!isPlainObject(errors) && !Array.isArray(errors)) {
|
|
37
|
+
return errors;
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
...errors,
|
|
41
|
+
...flattenErrorPaths(errors)
|
|
42
|
+
};
|
|
43
|
+
}
|
|
5
44
|
function normalizeValue(value) {
|
|
6
45
|
if (typeof value === "string") {
|
|
7
46
|
return normalizeStringWhitespace(value);
|
|
@@ -121,11 +160,11 @@ export default function useHttp(initialData, formOptions) {
|
|
|
121
160
|
if (context.response._data && typeof context.response._data === "object") {
|
|
122
161
|
const responseData = context.response._data;
|
|
123
162
|
if (responseData.errors) {
|
|
124
|
-
form.errors = responseData.errors;
|
|
163
|
+
form.errors = withDotPathErrors(responseData.errors);
|
|
125
164
|
} else if (responseData.message || responseData.error) {
|
|
126
|
-
form.errors = responseData;
|
|
165
|
+
form.errors = withDotPathErrors(responseData);
|
|
127
166
|
} else {
|
|
128
|
-
form.errors = responseData;
|
|
167
|
+
form.errors = withDotPathErrors(responseData);
|
|
129
168
|
}
|
|
130
169
|
} else {
|
|
131
170
|
form.errors = {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { FormRules } from 'element-plus';
|
|
2
|
-
export type ValidationRule = 'required' | 'string' | 'nullable' | 'email' | 'accepted' | `accepted_if:${string}` | 'boolean' | 'filled' | 'present' | `required_if:${string}` | `required_unless:${string}` | `required_with:${string}` | `required_with_all:${string}` | `required_without:${string}` | `required_without_all:${string}` | 'prohibited' | `prohibited_if:${string}` | `prohibited_unless:${string}` | `prohibits:${string}` | `min:${number}` | `max:${number}` | `between:${number},${number}` | `size:${number}` | `same:${string}` | 'alpha' | 'alpha_num' | 'alpha_dash' | 'alphanumeric_space' | 'alpha_space' | 'numeric' | 'integer' | 'confirmed' | `digits:${number}` | `digits_between:${number},${number}` | 'bail' | `different:${string}` | `gt:${string}` | `gte:${string}` | `lt:${string}` | `lte:${string}` | `starts_with:${string}` | `ends_with:${string}` | `regex:${string}` | `date_format:${string}` | 'date' | `before:${string}` | `after:${string}` | `before_or_equal:${string}` | `after_or_equal:${string}` | 'array' | 'distinct' | `in_array:${string}` | `required_array_keys:${string}` | 'list' | 'url' | 'ip' | 'hex_color' | `in:${string}` | `multiple_of:${number}` | `decimal:${number}` | `decimal:${number},${number}` | 'lowercase' | 'uppercase' | 'uuid' | 'ulid' | `timezone:${string}` | 'timezone';
|
|
2
|
+
export type ValidationRule = 'required' | 'time' | 'string' | 'nullable' | 'email' | 'accepted' | `accepted_if:${string}` | 'boolean' | 'filled' | 'present' | `required_if:${string}` | `required_unless:${string}` | `required_with:${string}` | `required_with_all:${string}` | `required_without:${string}` | `required_without_all:${string}` | 'prohibited' | `prohibited_if:${string}` | `prohibited_unless:${string}` | `prohibits:${string}` | `min:${number}` | `max:${number}` | `between:${number},${number}` | `size:${number}` | `same:${string}` | 'alpha' | 'alpha_num' | 'alpha_dash' | 'alphanumeric_space' | 'alpha_space' | 'numeric' | 'integer' | 'confirmed' | `digits:${number}` | `digits_between:${number},${number}` | 'bail' | `different:${string}` | `gt:${string}` | `gte:${string}` | `lt:${string}` | `lte:${string}` | `starts_with:${string}` | `ends_with:${string}` | `regex:${string}` | `date_format:${string}` | 'date' | `before:${string}` | `after:${string}` | `before_or_equal:${string}` | `after_or_equal:${string}` | 'array' | 'distinct' | `in_array:${string}` | `required_array_keys:${string}` | 'list' | 'url' | 'ip' | 'hex_color' | `in:${string}` | `multiple_of:${number}` | `decimal:${number}` | `decimal:${number},${number}` | 'lowercase' | 'uppercase' | 'uuid' | 'ulid' | `timezone:${string}` | 'timezone';
|
|
3
3
|
export interface ValidationOptions {
|
|
4
4
|
messages?: Record<string, string>;
|
|
5
5
|
attributes?: Record<string, string>;
|
|
@@ -84,6 +84,55 @@ const isAcceptedValue = (value) => {
|
|
|
84
84
|
const isBooleanValue = (value) => {
|
|
85
85
|
return value === true || value === false || value === 0 || value === 1 || value === "0" || value === "1";
|
|
86
86
|
};
|
|
87
|
+
const parseTimeToMinutes = (value) => {
|
|
88
|
+
if (typeof value !== "string") {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
const match = value.match(/^([01]\d|2[0-3]):([0-5]\d)$/);
|
|
92
|
+
if (!match) {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
const hours = Number(match[1]);
|
|
96
|
+
const minutes = Number(match[2]);
|
|
97
|
+
return hours * 60 + minutes;
|
|
98
|
+
};
|
|
99
|
+
const parseDateToTimestamp = (value) => {
|
|
100
|
+
if (typeof value !== "string") {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(value)) {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
const date = /* @__PURE__ */ new Date(`${value}T00:00:00.000Z`);
|
|
107
|
+
if (Number.isNaN(date.getTime()) || date.toISOString().slice(0, 10) !== value) {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
return date.getTime();
|
|
111
|
+
};
|
|
112
|
+
const compareFieldValues = (left, right, compare) => {
|
|
113
|
+
const leftTime = parseTimeToMinutes(left);
|
|
114
|
+
const rightTime = parseTimeToMinutes(right);
|
|
115
|
+
if (leftTime !== null || rightTime !== null) {
|
|
116
|
+
if (leftTime === null || rightTime === null) {
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
return compare(leftTime, rightTime);
|
|
120
|
+
}
|
|
121
|
+
const leftDate = parseDateToTimestamp(left);
|
|
122
|
+
const rightDate = parseDateToTimestamp(right);
|
|
123
|
+
if (leftDate !== null || rightDate !== null) {
|
|
124
|
+
if (leftDate === null || rightDate === null) {
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
return compare(leftDate, rightDate);
|
|
128
|
+
}
|
|
129
|
+
const leftNumber = Number(left);
|
|
130
|
+
const rightNumber = Number(right);
|
|
131
|
+
if (Number.isNaN(leftNumber) || Number.isNaN(rightNumber)) {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
return compare(leftNumber, rightNumber);
|
|
135
|
+
};
|
|
87
136
|
const createDateFormatValidator = (format, message) => {
|
|
88
137
|
const tokenMap = {
|
|
89
138
|
Y: "\\d{4}",
|
|
@@ -119,6 +168,17 @@ const createDateFormatValidator = (format, message) => {
|
|
|
119
168
|
callback();
|
|
120
169
|
};
|
|
121
170
|
};
|
|
171
|
+
const createTimeValidator = (message) => {
|
|
172
|
+
return (_rule, value, callback) => {
|
|
173
|
+
if (isEmptyValue(value)) {
|
|
174
|
+
return callback();
|
|
175
|
+
}
|
|
176
|
+
if (parseTimeToMinutes(value) === null) {
|
|
177
|
+
return callback(new Error(message));
|
|
178
|
+
}
|
|
179
|
+
callback();
|
|
180
|
+
};
|
|
181
|
+
};
|
|
122
182
|
const createPatternValidator = (pattern, message) => {
|
|
123
183
|
return (_rule, value, callback) => {
|
|
124
184
|
if (value && !pattern.test(value)) {
|
|
@@ -148,7 +208,7 @@ const createIntegerValidator = (message) => {
|
|
|
148
208
|
};
|
|
149
209
|
const createComparisonValidator = (otherField, formState, message, compare) => {
|
|
150
210
|
return (_rule, value, callback) => {
|
|
151
|
-
const otherValue = formState
|
|
211
|
+
const otherValue = getNestedValue(formState, otherField);
|
|
152
212
|
if (value && otherValue && !compare(value, otherValue)) {
|
|
153
213
|
callback(new Error(message));
|
|
154
214
|
} else {
|
|
@@ -599,6 +659,12 @@ export const createValidationRules = (definitions, formState, options = {}) => {
|
|
|
599
659
|
trigger: pickTrigger(fieldName, "blur")
|
|
600
660
|
});
|
|
601
661
|
break;
|
|
662
|
+
case "time":
|
|
663
|
+
fieldRules.push({
|
|
664
|
+
validator: createTimeValidator(getMessage(fieldName, "time", `${attributeName} must be a valid time in HH:mm format`)),
|
|
665
|
+
trigger: pickTrigger(fieldName, "blur")
|
|
666
|
+
});
|
|
667
|
+
break;
|
|
602
668
|
case "nullable":
|
|
603
669
|
break;
|
|
604
670
|
case "email":
|
|
@@ -1144,7 +1210,7 @@ export const createValidationRules = (definitions, formState, options = {}) => {
|
|
|
1144
1210
|
}
|
|
1145
1211
|
const gtOtherAttr = attributes[firstParam] || firstParam;
|
|
1146
1212
|
fieldRules.push({
|
|
1147
|
-
validator: createComparisonValidator(firstParam, dataToValidate, getMessage(fieldName, "gt", `${attributeName} must be greater than ${gtOtherAttr}`), (a, b) =>
|
|
1213
|
+
validator: createComparisonValidator(firstParam, dataToValidate, getMessage(fieldName, "gt", `${attributeName} must be greater than ${gtOtherAttr}`), (a, b) => compareFieldValues(a, b, (x, y) => x > y)),
|
|
1148
1214
|
trigger: pickTrigger(fieldName, "blur")
|
|
1149
1215
|
});
|
|
1150
1216
|
break;
|
|
@@ -1155,7 +1221,7 @@ export const createValidationRules = (definitions, formState, options = {}) => {
|
|
|
1155
1221
|
}
|
|
1156
1222
|
const gteOtherAttr = attributes[firstParam] || firstParam;
|
|
1157
1223
|
fieldRules.push({
|
|
1158
|
-
validator: createComparisonValidator(firstParam, dataToValidate, getMessage(fieldName, "gte", `${attributeName} must be greater than or equal to ${gteOtherAttr}`), (a, b) =>
|
|
1224
|
+
validator: createComparisonValidator(firstParam, dataToValidate, getMessage(fieldName, "gte", `${attributeName} must be greater than or equal to ${gteOtherAttr}`), (a, b) => compareFieldValues(a, b, (x, y) => x >= y)),
|
|
1159
1225
|
trigger: "blur"
|
|
1160
1226
|
});
|
|
1161
1227
|
break;
|
|
@@ -1166,7 +1232,7 @@ export const createValidationRules = (definitions, formState, options = {}) => {
|
|
|
1166
1232
|
}
|
|
1167
1233
|
const ltOtherAttr = attributes[firstParam] || firstParam;
|
|
1168
1234
|
fieldRules.push({
|
|
1169
|
-
validator: createComparisonValidator(firstParam, dataToValidate, getMessage(fieldName, "lt", `${attributeName} must be less than ${ltOtherAttr}`), (a, b) =>
|
|
1235
|
+
validator: createComparisonValidator(firstParam, dataToValidate, getMessage(fieldName, "lt", `${attributeName} must be less than ${ltOtherAttr}`), (a, b) => compareFieldValues(a, b, (x, y) => x < y)),
|
|
1170
1236
|
trigger: "blur"
|
|
1171
1237
|
});
|
|
1172
1238
|
break;
|
|
@@ -1177,7 +1243,7 @@ export const createValidationRules = (definitions, formState, options = {}) => {
|
|
|
1177
1243
|
}
|
|
1178
1244
|
const lteOtherAttr = attributes[firstParam] || firstParam;
|
|
1179
1245
|
fieldRules.push({
|
|
1180
|
-
validator: createComparisonValidator(firstParam, dataToValidate, getMessage(fieldName, "lte", `${attributeName} must be less than or equal to ${lteOtherAttr}`), (a, b) =>
|
|
1246
|
+
validator: createComparisonValidator(firstParam, dataToValidate, getMessage(fieldName, "lte", `${attributeName} must be less than or equal to ${lteOtherAttr}`), (a, b) => compareFieldValues(a, b, (x, y) => x <= y)),
|
|
1181
1247
|
trigger: "blur"
|
|
1182
1248
|
});
|
|
1183
1249
|
break;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Dependencies
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Dependencies
|
|
6
|
+
|
|
7
|
+
This page documents dependencies introduced by `@ciwergrp/nuxid`.
|
|
8
|
+
|
|
9
|
+
## Does this already exist?
|
|
10
|
+
|
|
11
|
+
Not as a dedicated page/menu.
|
|
12
|
+
|
|
13
|
+
- Existing docs under [Third-Party](/built-in-modules/) explain some integrations.
|
|
14
|
+
- This page is the complete dependency inventory.
|
|
15
|
+
|
|
16
|
+
## Package Dependencies (installed with `@ciwergrp/nuxid`)
|
|
17
|
+
|
|
18
|
+
When you install `@ciwergrp/nuxid`, these direct package dependencies are installed:
|
|
19
|
+
|
|
20
|
+
- `@element-plus/nuxt`
|
|
21
|
+
- `@formatjs/intl`
|
|
22
|
+
- `@inquirer/prompts`
|
|
23
|
+
- `@nuxt/icon`
|
|
24
|
+
- `@nuxt/kit`
|
|
25
|
+
- `@sentry/nuxt`
|
|
26
|
+
- `@sindresorhus/slugify`
|
|
27
|
+
- `@sindresorhus/transliterate`
|
|
28
|
+
- `@tailwindcss/vite`
|
|
29
|
+
- `@vueuse/nuxt`
|
|
30
|
+
- `date-fns`
|
|
31
|
+
- `element-plus`
|
|
32
|
+
- `hls.js`
|
|
33
|
+
- `lodash-es`
|
|
34
|
+
- `pinia`
|
|
35
|
+
- `socket.io-client`
|
|
36
|
+
- `tailwindcss`
|
|
37
|
+
- `vite`
|
|
38
|
+
|
|
39
|
+
## Nuxt Module Dependencies Added to Your App
|
|
40
|
+
|
|
41
|
+
Nuxid adds/integrates Nuxt modules as follows:
|
|
42
|
+
|
|
43
|
+
- Always declared module dependency: `@sentry/nuxt` (via `moduleDependencies`)
|
|
44
|
+
- Conditionally installed when enabled in `nuxid` options:
|
|
45
|
+
- `@nuxt/icon` (`nuxid.icon`)
|
|
46
|
+
- `@element-plus/nuxt` (`nuxid.elementPlus`)
|
|
47
|
+
- `@vueuse/nuxt` (`nuxid.vueuse`)
|
|
48
|
+
|
|
49
|
+
## Notes
|
|
50
|
+
|
|
51
|
+
- Enabling `nuxid.pinia` integrates Pinia runtime/composables but does not call `installModule('@pinia/nuxt')`.
|
|
52
|
+
- Versions follow this repository's `package.json`.
|