codeforlife 2.6.12 → 2.6.13
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/CHANGELOG.md +7 -0
- package/package.json +1 -1
- package/src/api/index.ts +1 -0
- package/src/api/schemas.ts +117 -0
- package/src/components/form/FirstNameField.tsx +2 -2
- package/src/components/form/TextField.tsx +40 -23
- package/src/utils/api.tsx +6 -1
- package/src/utils/general.ts +5 -0
- package/src/utils/schema.ts +210 -0
- package/codecov.yml +0 -11
- package/src/schemas/user.ts +0 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
## [2.6.13](https://github.com/ocadotechnology/codeforlife-package-javascript/compare/v2.6.12...v2.6.13) (2025-03-25)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* char set validators ([#81](https://github.com/ocadotechnology/codeforlife-package-javascript/issues/81)) ([9ff1f60](https://github.com/ocadotechnology/codeforlife-package-javascript/commit/9ff1f60808f4a50eb2cbcae997de4ae892665c28))
|
|
7
|
+
|
|
1
8
|
## [2.6.12](https://github.com/ocadotechnology/codeforlife-package-javascript/compare/v2.6.11...v2.6.12) (2025-03-20)
|
|
2
9
|
|
|
3
10
|
|
package/package.json
CHANGED
package/src/api/index.ts
CHANGED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import * as yup from "yup"
|
|
2
|
+
|
|
3
|
+
import { UK_COUNTIES, COUNTRY_ISO_CODES } from "../utils/general"
|
|
4
|
+
import type {
|
|
5
|
+
User,
|
|
6
|
+
Teacher,
|
|
7
|
+
Student,
|
|
8
|
+
Class,
|
|
9
|
+
School,
|
|
10
|
+
AuthFactor,
|
|
11
|
+
OtpBypassToken,
|
|
12
|
+
} from "./models"
|
|
13
|
+
import {
|
|
14
|
+
unicodeAlphanumericString,
|
|
15
|
+
uppercaseAsciiAlphanumericString,
|
|
16
|
+
lowercaseAsciiAlphanumericString,
|
|
17
|
+
numericId,
|
|
18
|
+
} from "../utils/schema"
|
|
19
|
+
import { type Schemas } from "../utils/api"
|
|
20
|
+
|
|
21
|
+
// NOTE: do not use .required() here.
|
|
22
|
+
const id = {
|
|
23
|
+
user: numericId(),
|
|
24
|
+
teacher: numericId(),
|
|
25
|
+
student: numericId(),
|
|
26
|
+
school: numericId(),
|
|
27
|
+
klass: uppercaseAsciiAlphanumericString().length(5),
|
|
28
|
+
authFactor: numericId(),
|
|
29
|
+
otpBypassToken: numericId(),
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const _userTeacher: Omit<Schemas<Teacher>, "user"> = {
|
|
33
|
+
id: id.teacher.required(),
|
|
34
|
+
school: id.school,
|
|
35
|
+
is_admin: yup.bool().required(),
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const _userStudent: Omit<Schemas<Student>, "user"> = {
|
|
39
|
+
id: id.student.required(),
|
|
40
|
+
school: id.school.required(),
|
|
41
|
+
klass: id.klass.required(),
|
|
42
|
+
auto_gen_password: yup.string().required(),
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export const user: Schemas<User> = {
|
|
46
|
+
id: id.user.required(),
|
|
47
|
+
requesting_to_join_class: id.klass,
|
|
48
|
+
first_name: unicodeAlphanumericString({
|
|
49
|
+
spaces: true,
|
|
50
|
+
specialChars: "-'",
|
|
51
|
+
})
|
|
52
|
+
.required()
|
|
53
|
+
.max(150),
|
|
54
|
+
last_name: unicodeAlphanumericString({
|
|
55
|
+
spaces: true,
|
|
56
|
+
specialChars: "-'",
|
|
57
|
+
}).max(150),
|
|
58
|
+
last_login: yup.date(),
|
|
59
|
+
email: yup.string().email(),
|
|
60
|
+
password: yup.string().required(),
|
|
61
|
+
is_staff: yup.bool().required(),
|
|
62
|
+
is_active: yup.bool().required(),
|
|
63
|
+
date_joined: yup.date().required(),
|
|
64
|
+
teacher: yup.object(_userTeacher).optional(),
|
|
65
|
+
student: yup.object(_userStudent).optional(),
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export const teacher: Schemas<Teacher> = {
|
|
69
|
+
..._userTeacher,
|
|
70
|
+
user: id.user.required(),
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export const student: Schemas<Student> = {
|
|
74
|
+
..._userStudent,
|
|
75
|
+
user: id.user.required(),
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export const school: Schemas<School> = {
|
|
79
|
+
id: id.school.required(),
|
|
80
|
+
name: unicodeAlphanumericString({
|
|
81
|
+
spaces: true,
|
|
82
|
+
specialChars: "'.",
|
|
83
|
+
})
|
|
84
|
+
.required()
|
|
85
|
+
.max(200),
|
|
86
|
+
country: yup.string().oneOf(COUNTRY_ISO_CODES),
|
|
87
|
+
uk_county: yup.string().oneOf(UK_COUNTIES),
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export const klass: Schemas<Class> = {
|
|
91
|
+
id: id.klass.required(),
|
|
92
|
+
teacher: id.teacher.required(),
|
|
93
|
+
school: id.school.required(),
|
|
94
|
+
name: unicodeAlphanumericString({
|
|
95
|
+
spaces: true,
|
|
96
|
+
specialChars: "-_",
|
|
97
|
+
})
|
|
98
|
+
.required()
|
|
99
|
+
.max(200),
|
|
100
|
+
read_classmates_data: yup.bool().required(),
|
|
101
|
+
receive_requests_until: yup.date(),
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export const authFactor: Schemas<AuthFactor> = {
|
|
105
|
+
id: id.authFactor.required(),
|
|
106
|
+
user: id.user.required(),
|
|
107
|
+
type: yup
|
|
108
|
+
.string()
|
|
109
|
+
.oneOf(["otp"] as const)
|
|
110
|
+
.required(),
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export const otpBypassToken: Schemas<OtpBypassToken> = {
|
|
114
|
+
id: id.otpBypassToken.required(),
|
|
115
|
+
user: id.user.required(),
|
|
116
|
+
token: lowercaseAsciiAlphanumericString().required().length(8),
|
|
117
|
+
}
|
|
@@ -3,7 +3,7 @@ import { InputAdornment } from "@mui/material"
|
|
|
3
3
|
import type { FC } from "react"
|
|
4
4
|
|
|
5
5
|
import TextField, { type TextFieldProps } from "./TextField"
|
|
6
|
-
import {
|
|
6
|
+
import { schemas } from "../../api"
|
|
7
7
|
|
|
8
8
|
export type FirstNameFieldProps = Omit<
|
|
9
9
|
TextFieldProps,
|
|
@@ -20,7 +20,7 @@ const FirstNameField: FC<FirstNameFieldProps> = ({
|
|
|
20
20
|
}) => {
|
|
21
21
|
return (
|
|
22
22
|
<TextField
|
|
23
|
-
schema={
|
|
23
|
+
schema={schemas.user.first_name}
|
|
24
24
|
name={name}
|
|
25
25
|
label={label}
|
|
26
26
|
placeholder={placeholder}
|
|
@@ -4,13 +4,7 @@ import {
|
|
|
4
4
|
} from "@mui/material"
|
|
5
5
|
import { Field, type FieldConfig, type FieldProps } from "formik"
|
|
6
6
|
import { type FC, useState, useEffect } from "react"
|
|
7
|
-
import {
|
|
8
|
-
type ArraySchema,
|
|
9
|
-
type StringSchema,
|
|
10
|
-
type ValidateOptions,
|
|
11
|
-
array as YupArray,
|
|
12
|
-
type Schema,
|
|
13
|
-
} from "yup"
|
|
7
|
+
import { type StringSchema, type ValidateOptions, array as YupArray } from "yup"
|
|
14
8
|
|
|
15
9
|
import { schemaToFieldValidator } from "../../utils/form"
|
|
16
10
|
import { getNestedProperty } from "../../utils/general"
|
|
@@ -52,18 +46,40 @@ const TextField: FC<TextFieldProps> = ({
|
|
|
52
46
|
|
|
53
47
|
const dotPath = name.split(".")
|
|
54
48
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
49
|
+
function buildSchema() {
|
|
50
|
+
// Build a schema for a single string.
|
|
51
|
+
let stringSchema = schema
|
|
52
|
+
// 1: Validate string is required.
|
|
53
|
+
stringSchema = required ? stringSchema.required() : stringSchema.optional()
|
|
54
|
+
// 2: Validate string is dirty.
|
|
55
|
+
if (dirty && !split)
|
|
56
|
+
stringSchema = stringSchema.notOneOf(
|
|
57
|
+
[initialValue as string],
|
|
58
|
+
"cannot be initial value",
|
|
59
|
+
)
|
|
60
|
+
// Return a schema for a single string.
|
|
61
|
+
if (!split) return stringSchema
|
|
62
|
+
|
|
63
|
+
// Build a schema for an array of strings.
|
|
64
|
+
let arraySchema = YupArray().of(stringSchema)
|
|
65
|
+
// 1: Validate array has min one string.
|
|
66
|
+
arraySchema = required
|
|
67
|
+
? arraySchema.required().min(1)
|
|
68
|
+
: arraySchema.optional()
|
|
69
|
+
// 2: Validate array has unique strings.
|
|
70
|
+
if (unique || uniqueCaseInsensitive)
|
|
71
|
+
arraySchema = arraySchema.test({
|
|
60
72
|
message: "cannot have duplicates",
|
|
61
73
|
test: values => {
|
|
62
|
-
if (
|
|
74
|
+
if (
|
|
75
|
+
Array.isArray(values) &&
|
|
76
|
+
values.length >= 2 &&
|
|
77
|
+
values.every(value => typeof value === "string")
|
|
78
|
+
) {
|
|
63
79
|
return (
|
|
64
80
|
new Set(
|
|
65
|
-
uniqueCaseInsensitive
|
|
66
|
-
? values.map(value => value.toLowerCase())
|
|
81
|
+
uniqueCaseInsensitive
|
|
82
|
+
? values.map(value => (value as string).toLowerCase())
|
|
67
83
|
: values,
|
|
68
84
|
).size === values.length
|
|
69
85
|
)
|
|
@@ -72,19 +88,20 @@ const TextField: FC<TextFieldProps> = ({
|
|
|
72
88
|
return true
|
|
73
89
|
},
|
|
74
90
|
})
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
91
|
+
// 3: Validate array is dirty.
|
|
92
|
+
if (dirty)
|
|
93
|
+
arraySchema = arraySchema.notOneOf(
|
|
94
|
+
[initialValue as string[]],
|
|
95
|
+
"cannot be initial value",
|
|
96
|
+
)
|
|
97
|
+
// Return a schema for an array of strings.
|
|
98
|
+
return arraySchema
|
|
80
99
|
}
|
|
81
|
-
if (dirty)
|
|
82
|
-
_schema = _schema.notOneOf([initialValue], "cannot be initial value")
|
|
83
100
|
|
|
84
101
|
const fieldConfig: FieldConfig = {
|
|
85
102
|
name,
|
|
86
103
|
type,
|
|
87
|
-
validate: schemaToFieldValidator(
|
|
104
|
+
validate: schemaToFieldValidator(buildSchema(), validateOptions),
|
|
88
105
|
}
|
|
89
106
|
|
|
90
107
|
const _Field: FC<FieldProps> = ({ form }) => {
|
package/src/utils/api.tsx
CHANGED
|
@@ -7,8 +7,9 @@ import type {
|
|
|
7
7
|
} from "@reduxjs/toolkit/query/react"
|
|
8
8
|
import { type ReactNode } from "react"
|
|
9
9
|
|
|
10
|
-
import SyncError from "../components/SyncError"
|
|
11
10
|
import { type Optional, type Required, getNestedProperty } from "./general"
|
|
11
|
+
import { type SchemaMap } from "./schema"
|
|
12
|
+
import SyncError from "../components/SyncError"
|
|
12
13
|
|
|
13
14
|
// -----------------------------------------------------------------------------
|
|
14
15
|
// Model Types
|
|
@@ -33,6 +34,10 @@ export type Model<Id extends ModelId, MFields extends Fields = Fields> = {
|
|
|
33
34
|
id: Id
|
|
34
35
|
} & Omit<MFields, "id">
|
|
35
36
|
|
|
37
|
+
export type Schemas<M extends Model<any>> = {
|
|
38
|
+
[K in keyof M]-?: SchemaMap<M[K]>
|
|
39
|
+
}
|
|
40
|
+
|
|
36
41
|
export type Result<
|
|
37
42
|
M extends Model<any>,
|
|
38
43
|
MFields extends keyof Omit<M, "id"> = never,
|
package/src/utils/general.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
export type Required<T, K extends keyof T> = { [P in K]-?: T[P] }
|
|
2
2
|
export type Optional<T, K extends keyof T> = Partial<Pick<T, K>>
|
|
3
|
+
export type OptionalPropertyNames<T> = {
|
|
4
|
+
[K in keyof T]-?: {} extends { [P in K]: T[K] } ? K : never
|
|
5
|
+
}[keyof T]
|
|
6
|
+
export type IsOptional<T, K extends keyof T> =
|
|
7
|
+
K extends OptionalPropertyNames<T> ? true : false
|
|
3
8
|
|
|
4
9
|
export function openInNewTab(url: string, target = "_blank"): void {
|
|
5
10
|
window.open(url, target)
|
package/src/utils/schema.ts
CHANGED
|
@@ -9,6 +9,13 @@ import {
|
|
|
9
9
|
type Schema,
|
|
10
10
|
type TypeFromShape,
|
|
11
11
|
type ValidateOptions,
|
|
12
|
+
type StringSchema,
|
|
13
|
+
type NumberSchema,
|
|
14
|
+
type Flags,
|
|
15
|
+
type BooleanSchema,
|
|
16
|
+
type DateSchema,
|
|
17
|
+
string as YupString,
|
|
18
|
+
number as YupNumber,
|
|
12
19
|
} from "yup"
|
|
13
20
|
|
|
14
21
|
export type _<T> = T extends {}
|
|
@@ -30,6 +37,209 @@ export type ObjectSchemaFromShape<Shape extends ObjectShape> = ObjectSchema<
|
|
|
30
37
|
""
|
|
31
38
|
>
|
|
32
39
|
|
|
40
|
+
export type SchemaMap<
|
|
41
|
+
TType,
|
|
42
|
+
TContext = AnyObject,
|
|
43
|
+
TDefault = any,
|
|
44
|
+
TFlags extends Flags = "",
|
|
45
|
+
> =
|
|
46
|
+
NonNullable<TType> extends string
|
|
47
|
+
? StringSchema<
|
|
48
|
+
// @ts-expect-error type is fine
|
|
49
|
+
TType extends undefined ? TType | undefined : TType,
|
|
50
|
+
TContext,
|
|
51
|
+
TDefault,
|
|
52
|
+
TFlags
|
|
53
|
+
>
|
|
54
|
+
: NonNullable<TType> extends number
|
|
55
|
+
? NumberSchema<
|
|
56
|
+
// @ts-expect-error type is fine
|
|
57
|
+
TType extends undefined ? TType | undefined : TType,
|
|
58
|
+
TContext,
|
|
59
|
+
TDefault,
|
|
60
|
+
TFlags
|
|
61
|
+
>
|
|
62
|
+
: NonNullable<TType> extends boolean
|
|
63
|
+
? BooleanSchema<
|
|
64
|
+
// @ts-expect-error type is fine
|
|
65
|
+
TType extends undefined ? TType | undefined : TType,
|
|
66
|
+
TContext,
|
|
67
|
+
TDefault,
|
|
68
|
+
TFlags
|
|
69
|
+
>
|
|
70
|
+
: NonNullable<TType> extends Date
|
|
71
|
+
? DateSchema<
|
|
72
|
+
// @ts-expect-error type is fine
|
|
73
|
+
TType extends undefined ? TType | undefined : TType,
|
|
74
|
+
TContext,
|
|
75
|
+
TDefault,
|
|
76
|
+
TFlags
|
|
77
|
+
>
|
|
78
|
+
: NonNullable<TType> extends object
|
|
79
|
+
? ObjectSchema<
|
|
80
|
+
// @ts-expect-error type is fine
|
|
81
|
+
TType extends undefined ? TType | undefined : TType,
|
|
82
|
+
TContext,
|
|
83
|
+
TDefault,
|
|
84
|
+
TFlags
|
|
85
|
+
>
|
|
86
|
+
: Schema<TType, TContext, TDefault, TFlags>
|
|
87
|
+
|
|
88
|
+
export function numericId(schema: NumberSchema = YupNumber()) {
|
|
89
|
+
return schema.min(1)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// -----------------------------------------------------------------------------
|
|
93
|
+
// Limited Character Sets
|
|
94
|
+
// -----------------------------------------------------------------------------
|
|
95
|
+
|
|
96
|
+
export type MatchesCharSetOptions = Partial<{
|
|
97
|
+
schema: StringSchema
|
|
98
|
+
flags: string
|
|
99
|
+
}>
|
|
100
|
+
|
|
101
|
+
export function matchesCharSet(
|
|
102
|
+
charSet: string,
|
|
103
|
+
message: string,
|
|
104
|
+
options: MatchesCharSetOptions = {},
|
|
105
|
+
) {
|
|
106
|
+
const { schema = YupString(), flags } = options
|
|
107
|
+
|
|
108
|
+
return schema.matches(new RegExp(`^[${charSet}]*$`, flags), message)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export type BuildCharSetOptions = MatchesCharSetOptions &
|
|
112
|
+
Partial<{ spaces: boolean; specialChars: string }>
|
|
113
|
+
|
|
114
|
+
export function buildCharSet(
|
|
115
|
+
charSet: string,
|
|
116
|
+
description: string,
|
|
117
|
+
options: BuildCharSetOptions = {},
|
|
118
|
+
) {
|
|
119
|
+
const { spaces = false, specialChars, ...matchesCharSetOptions } = options
|
|
120
|
+
|
|
121
|
+
let message = `can only contain: ${description}`
|
|
122
|
+
|
|
123
|
+
if (spaces) {
|
|
124
|
+
charSet += " "
|
|
125
|
+
message += ", spaces"
|
|
126
|
+
}
|
|
127
|
+
if (specialChars) {
|
|
128
|
+
charSet += specialChars
|
|
129
|
+
message += `, special characters (${specialChars})`
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return matchesCharSet(charSet, message, matchesCharSetOptions)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export function buildUnicodeCharSet(
|
|
136
|
+
charSet: string,
|
|
137
|
+
description: string,
|
|
138
|
+
options: BuildCharSetOptions = {},
|
|
139
|
+
) {
|
|
140
|
+
let { flags = "u", ...otherOptions } = options
|
|
141
|
+
|
|
142
|
+
if (!flags.includes("u")) flags += "u"
|
|
143
|
+
|
|
144
|
+
return buildCharSet(charSet, description, { flags, ...otherOptions })
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export function asciiAlphaString(options?: BuildCharSetOptions) {
|
|
148
|
+
return buildCharSet("a-zA-Z", "ASCII alpha characters (a-z, A-Z)", options)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export function lowercaseAsciiAlphaString(options?: BuildCharSetOptions) {
|
|
152
|
+
return buildCharSet("a-z", "lowercase ASCII alpha characters (a-z)", options)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export function uppercaseAsciiAlphaString(options?: BuildCharSetOptions) {
|
|
156
|
+
return buildCharSet("A-Z", "uppercase ASCII alpha characters (A-Z)", options)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export function asciiNumericString(options?: BuildCharSetOptions) {
|
|
160
|
+
return buildCharSet("0-9", "ASCII numbers (0-9)", options)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export function asciiAlphanumericString(options?: BuildCharSetOptions) {
|
|
164
|
+
return buildCharSet(
|
|
165
|
+
"a-zA-Z0-9",
|
|
166
|
+
"ASCII alphanumeric characters (a-z, A-Z, 0-9)",
|
|
167
|
+
options,
|
|
168
|
+
)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export function lowercaseAsciiAlphanumericString(
|
|
172
|
+
options?: BuildCharSetOptions,
|
|
173
|
+
) {
|
|
174
|
+
return buildCharSet(
|
|
175
|
+
"a-z0-9",
|
|
176
|
+
"lowercase ASCII alphanumeric characters (a-z, 0-9)",
|
|
177
|
+
options,
|
|
178
|
+
)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export function uppercaseAsciiAlphanumericString(
|
|
182
|
+
options?: BuildCharSetOptions,
|
|
183
|
+
) {
|
|
184
|
+
return buildCharSet(
|
|
185
|
+
"A-Z0-9",
|
|
186
|
+
"uppercase ASCII alphanumeric characters (A-Z, 0-9)",
|
|
187
|
+
options,
|
|
188
|
+
)
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export function unicodeAlphaString(options?: BuildCharSetOptions) {
|
|
192
|
+
return buildUnicodeCharSet("\\p{L}", "unicode alpha characters", options)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export function lowercaseUnicodeAlphaString(options?: BuildCharSetOptions) {
|
|
196
|
+
return buildUnicodeCharSet(
|
|
197
|
+
"\\p{Ll}",
|
|
198
|
+
"lowercase unicode alpha characters",
|
|
199
|
+
options,
|
|
200
|
+
)
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export function uppercaseUnicodeAlphaString(options?: BuildCharSetOptions) {
|
|
204
|
+
return buildUnicodeCharSet(
|
|
205
|
+
"\\p{Lu}",
|
|
206
|
+
"uppercase unicode alpha characters",
|
|
207
|
+
options,
|
|
208
|
+
)
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export function unicodeNumericString(options?: BuildCharSetOptions) {
|
|
212
|
+
return buildUnicodeCharSet("\\p{N}", "unicode numbers", options)
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export function unicodeAlphanumericString(options?: BuildCharSetOptions) {
|
|
216
|
+
return buildUnicodeCharSet(
|
|
217
|
+
"\\p{L}\\p{N}",
|
|
218
|
+
"unicode alphanumeric characters",
|
|
219
|
+
options,
|
|
220
|
+
)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export function lowercaseUnicodeAlphanumericString(
|
|
224
|
+
options?: BuildCharSetOptions,
|
|
225
|
+
) {
|
|
226
|
+
return buildUnicodeCharSet(
|
|
227
|
+
"\\p{Ll}\\p{N}",
|
|
228
|
+
"lowercase unicode alphanumeric characters",
|
|
229
|
+
options,
|
|
230
|
+
)
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export function uppercaseUnicodeAlphanumericString(
|
|
234
|
+
options?: BuildCharSetOptions,
|
|
235
|
+
) {
|
|
236
|
+
return buildUnicodeCharSet(
|
|
237
|
+
"\\p{Lu}\\p{N}",
|
|
238
|
+
"uppercase unicode alphanumeric characters",
|
|
239
|
+
options,
|
|
240
|
+
)
|
|
241
|
+
}
|
|
242
|
+
|
|
33
243
|
// -----------------------------------------------------------------------------
|
|
34
244
|
// Try Validate Sync
|
|
35
245
|
// -----------------------------------------------------------------------------
|
package/codecov.yml
DELETED
package/src/schemas/user.ts
DELETED