@plasius/schema 1.1.0 → 1.2.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/README.md +31 -18
- package/dist/index.cjs +2370 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +627 -0
- package/dist/index.d.ts +627 -0
- package/dist/index.js +2308 -0
- package/dist/index.js.map +1 -0
- package/package.json +18 -6
- package/.eslintrc.cjs +0 -7
- package/.github/workflows/cd.yml +0 -236
- package/.github/workflows/ci.yml +0 -16
- package/.nvmrc +0 -1
- package/.vscode/launch.json +0 -15
- package/CHANGELOG.md +0 -120
- package/CODE_OF_CONDUCT.md +0 -79
- package/CONTRIBUTING.md +0 -201
- package/CONTRIBUTORS.md +0 -27
- package/SECURITY.md +0 -17
- package/docs/adrs/adr-0001: schema.md +0 -45
- package/docs/adrs/adr-template.md +0 -67
- package/legal/CLA-REGISTRY.csv +0 -2
- package/legal/CLA.md +0 -22
- package/legal/CORPORATE_CLA.md +0 -57
- package/legal/INDIVIDUAL_CLA.md +0 -91
- package/sbom.cdx.json +0 -66
- package/src/components.ts +0 -39
- package/src/field.builder.ts +0 -239
- package/src/field.ts +0 -153
- package/src/index.ts +0 -7
- package/src/infer.ts +0 -34
- package/src/pii.ts +0 -165
- package/src/schema.ts +0 -893
- package/src/types.ts +0 -156
- package/src/validation/countryCode.ISO3166.ts +0 -256
- package/src/validation/currencyCode.ISO4217.ts +0 -191
- package/src/validation/dateTime.ISO8601.ts +0 -60
- package/src/validation/email.RFC5322.ts +0 -9
- package/src/validation/generalText.OWASP.ts +0 -39
- package/src/validation/index.ts +0 -13
- package/src/validation/languageCode.BCP47.ts +0 -299
- package/src/validation/name.OWASP.ts +0 -25
- package/src/validation/percentage.ISO80000-1.ts +0 -8
- package/src/validation/phone.E.164.ts +0 -9
- package/src/validation/richtext.OWASP.ts +0 -34
- package/src/validation/url.WHATWG.ts +0 -16
- package/src/validation/user.MS-GOOGLE-APPLE.ts +0 -31
- package/src/validation/uuid.RFC4122.ts +0 -10
- package/src/validation/version.SEMVER2.0.0.ts +0 -10
- package/tests/field.builder.test.ts +0 -81
- package/tests/fields.test.ts +0 -213
- package/tests/pii.test.ts +0 -139
- package/tests/schema.test.ts +0 -501
- package/tests/test-utils.ts +0 -97
- package/tests/validate.test.ts +0 -97
- package/tests/validation.test.ts +0 -98
- package/tsconfig.build.json +0 -19
- package/tsconfig.json +0 -7
- package/tsup.config.ts +0 -10
- package/vitest.config.js +0 -20
package/src/types.ts
DELETED
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
import FieldBuilder from "./field.builder.js";
|
|
2
|
-
import { Infer } from "./infer.js";
|
|
3
|
-
import { PII, PIIAction, PIIClassification, PIILogHandling } from "./pii.js";
|
|
4
|
-
|
|
5
|
-
export type FieldTypeMap = {
|
|
6
|
-
string: string;
|
|
7
|
-
number: number;
|
|
8
|
-
boolean: boolean;
|
|
9
|
-
object: Record<string, unknown>;
|
|
10
|
-
"string[]": string[];
|
|
11
|
-
"number[]": number[];
|
|
12
|
-
"boolean[]": boolean[];
|
|
13
|
-
"object[]": Record<string, unknown>[];
|
|
14
|
-
ref: RefEntityId;
|
|
15
|
-
"ref[]": RefEntityId[];
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
export type FieldType = keyof FieldTypeMap;
|
|
19
|
-
|
|
20
|
-
export type PIIEnforcement = "strict" | "warn" | "none";
|
|
21
|
-
|
|
22
|
-
export interface SchemaOptions {
|
|
23
|
-
version?: string;
|
|
24
|
-
table?: string;
|
|
25
|
-
schemaValidator?: (value: any) => boolean;
|
|
26
|
-
piiEnforcement?: PIIEnforcement; // How should PII be enforced?
|
|
27
|
-
}
|
|
28
|
-
export type SchemaShape = Record<string, FieldBuilder<any>>;
|
|
29
|
-
|
|
30
|
-
export interface FieldDefinition<T = unknown> {
|
|
31
|
-
type: FieldType;
|
|
32
|
-
__valueType?: T;
|
|
33
|
-
optional?: boolean;
|
|
34
|
-
immutable?: boolean;
|
|
35
|
-
description?: string;
|
|
36
|
-
refType?: string;
|
|
37
|
-
version?: string;
|
|
38
|
-
deprecated?: boolean;
|
|
39
|
-
deprecatedVersion?: string;
|
|
40
|
-
system?: boolean;
|
|
41
|
-
autoValidate?: boolean;
|
|
42
|
-
refPolicy?: "eager" | "lazy";
|
|
43
|
-
enum?: string[];
|
|
44
|
-
_shape?: SchemaShape;
|
|
45
|
-
pii?: PII;
|
|
46
|
-
validator?: (value: any) => boolean;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export interface ValidateCompositionOptions {
|
|
50
|
-
resolveEntity: (type: string, id: string) => Promise<any | null>;
|
|
51
|
-
validatorContext?: { visited: Set<string> };
|
|
52
|
-
maxDepth?: number;
|
|
53
|
-
log?: (msg: string) => void; // Optional for trace/debug
|
|
54
|
-
onlyFields?: string[]; // NEW
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export interface Schema<T extends SchemaShape> {
|
|
58
|
-
//// The shape of the schema.
|
|
59
|
-
_shape: T;
|
|
60
|
-
|
|
61
|
-
//// System metadata about the schema
|
|
62
|
-
meta: { entityType: string; version: string };
|
|
63
|
-
|
|
64
|
-
//// Methods for schema validation
|
|
65
|
-
schemaValidator: (entity: Infer<T>) => boolean;
|
|
66
|
-
|
|
67
|
-
// Validate an input object against the schema
|
|
68
|
-
validate: (
|
|
69
|
-
input: unknown,
|
|
70
|
-
existing?: Record<string, any>
|
|
71
|
-
) => ValidationResult<Infer<T>>;
|
|
72
|
-
|
|
73
|
-
// Validate an input object against the schema, with options for composition validation
|
|
74
|
-
validateComposition: (
|
|
75
|
-
entity: Infer<T>,
|
|
76
|
-
options: ValidateCompositionOptions
|
|
77
|
-
) => Promise<void>;
|
|
78
|
-
|
|
79
|
-
//// Optional methods for schema metadata
|
|
80
|
-
|
|
81
|
-
// Get the tableName for this schema
|
|
82
|
-
tableName?: () => string | undefined; // Optional method to get the table name
|
|
83
|
-
|
|
84
|
-
//// 🔒 Optional methods for PII handling
|
|
85
|
-
|
|
86
|
-
// 🔒 Auto-prepare for read (decrypt PII)
|
|
87
|
-
prepareForRead(
|
|
88
|
-
stored: Record<string, any>,
|
|
89
|
-
decryptFn: (value: string) => any | null
|
|
90
|
-
): Record<string, any>;
|
|
91
|
-
|
|
92
|
-
// 🔒 Auto-prepare for storage (encrypt/hash PII)
|
|
93
|
-
prepareForStorage(
|
|
94
|
-
input: Record<string, any>,
|
|
95
|
-
encryptFn: (value: any) => string,
|
|
96
|
-
hashFn: (value: any) => string
|
|
97
|
-
): Record<string, any>;
|
|
98
|
-
|
|
99
|
-
// 🔒 Sanitize data for logging (e.g., redact PII)
|
|
100
|
-
sanitizeForLog(
|
|
101
|
-
data: Record<string, any>,
|
|
102
|
-
pseudonymFn: (value: any) => string
|
|
103
|
-
): Record<string, any>;
|
|
104
|
-
|
|
105
|
-
// 🔒 Get PII audit information
|
|
106
|
-
getPiiAudit(): Array<{
|
|
107
|
-
field: string;
|
|
108
|
-
classification: PIIClassification;
|
|
109
|
-
action: PIIAction;
|
|
110
|
-
logHandling?: PIILogHandling;
|
|
111
|
-
purpose?: string;
|
|
112
|
-
}> | null;
|
|
113
|
-
|
|
114
|
-
// 🔒 Scrub PII for deletion (e.g., clear or hash sensitive data)
|
|
115
|
-
scrubPiiForDelete(stored: Record<string, any>): Record<string, any>;
|
|
116
|
-
|
|
117
|
-
describe(): {
|
|
118
|
-
entityType: string;
|
|
119
|
-
version: string;
|
|
120
|
-
shape: Record<
|
|
121
|
-
string,
|
|
122
|
-
{
|
|
123
|
-
type: FieldType;
|
|
124
|
-
optional: boolean;
|
|
125
|
-
immutable: boolean;
|
|
126
|
-
description: string;
|
|
127
|
-
version: string;
|
|
128
|
-
deprecated: boolean;
|
|
129
|
-
deprecatedVersion: string | null;
|
|
130
|
-
system: boolean;
|
|
131
|
-
enum: string[] | null;
|
|
132
|
-
refType: string | null;
|
|
133
|
-
pii: PII | null;
|
|
134
|
-
}
|
|
135
|
-
>;
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
export type DeepReadonly<T> = {
|
|
140
|
-
readonly [K in keyof T]: T[K] extends object
|
|
141
|
-
? T[K] extends (...args: any[]) => any
|
|
142
|
-
? T[K]
|
|
143
|
-
: DeepReadonly<T[K]>
|
|
144
|
-
: T[K];
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
export interface ValidationResult<T> {
|
|
148
|
-
valid: boolean;
|
|
149
|
-
value?: DeepReadonly<T>;
|
|
150
|
-
errors?: string[];
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
export type RefEntityId<T extends string = string> = {
|
|
154
|
-
type: T;
|
|
155
|
-
id: string;
|
|
156
|
-
};
|
|
@@ -1,256 +0,0 @@
|
|
|
1
|
-
export const isoCountryCodes = new Set([
|
|
2
|
-
"AD",
|
|
3
|
-
"AE",
|
|
4
|
-
"AF",
|
|
5
|
-
"AG",
|
|
6
|
-
"AI",
|
|
7
|
-
"AL",
|
|
8
|
-
"AM",
|
|
9
|
-
"AO",
|
|
10
|
-
"AQ",
|
|
11
|
-
"AR",
|
|
12
|
-
"AS",
|
|
13
|
-
"AT",
|
|
14
|
-
"AU",
|
|
15
|
-
"AW",
|
|
16
|
-
"AX",
|
|
17
|
-
"AZ",
|
|
18
|
-
"BA",
|
|
19
|
-
"BB",
|
|
20
|
-
"BD",
|
|
21
|
-
"BE",
|
|
22
|
-
"BF",
|
|
23
|
-
"BG",
|
|
24
|
-
"BH",
|
|
25
|
-
"BI",
|
|
26
|
-
"BJ",
|
|
27
|
-
"BL",
|
|
28
|
-
"BM",
|
|
29
|
-
"BN",
|
|
30
|
-
"BO",
|
|
31
|
-
"BQ",
|
|
32
|
-
"BR",
|
|
33
|
-
"BS",
|
|
34
|
-
"BT",
|
|
35
|
-
"BV",
|
|
36
|
-
"BW",
|
|
37
|
-
"BY",
|
|
38
|
-
"BZ",
|
|
39
|
-
"CA",
|
|
40
|
-
"CC",
|
|
41
|
-
"CD",
|
|
42
|
-
"CF",
|
|
43
|
-
"CG",
|
|
44
|
-
"CH",
|
|
45
|
-
"CI",
|
|
46
|
-
"CK",
|
|
47
|
-
"CL",
|
|
48
|
-
"CM",
|
|
49
|
-
"CN",
|
|
50
|
-
"CO",
|
|
51
|
-
"CR",
|
|
52
|
-
"CU",
|
|
53
|
-
"CV",
|
|
54
|
-
"CW",
|
|
55
|
-
"CX",
|
|
56
|
-
"CY",
|
|
57
|
-
"CZ",
|
|
58
|
-
"DE",
|
|
59
|
-
"DJ",
|
|
60
|
-
"DK",
|
|
61
|
-
"DM",
|
|
62
|
-
"DO",
|
|
63
|
-
"DZ",
|
|
64
|
-
"EC",
|
|
65
|
-
"EE",
|
|
66
|
-
"EG",
|
|
67
|
-
"EH",
|
|
68
|
-
"ER",
|
|
69
|
-
"ES",
|
|
70
|
-
"ET",
|
|
71
|
-
"FI",
|
|
72
|
-
"FJ",
|
|
73
|
-
"FM",
|
|
74
|
-
"FO",
|
|
75
|
-
"FR",
|
|
76
|
-
"GA",
|
|
77
|
-
"GB",
|
|
78
|
-
"GD",
|
|
79
|
-
"GE",
|
|
80
|
-
"GF",
|
|
81
|
-
"GG",
|
|
82
|
-
"GH",
|
|
83
|
-
"GI",
|
|
84
|
-
"GL",
|
|
85
|
-
"GM",
|
|
86
|
-
"GN",
|
|
87
|
-
"GP",
|
|
88
|
-
"GQ",
|
|
89
|
-
"GR",
|
|
90
|
-
"GT",
|
|
91
|
-
"GU",
|
|
92
|
-
"GW",
|
|
93
|
-
"GY",
|
|
94
|
-
"HK",
|
|
95
|
-
"HM",
|
|
96
|
-
"HN",
|
|
97
|
-
"HR",
|
|
98
|
-
"HT",
|
|
99
|
-
"HU",
|
|
100
|
-
"ID",
|
|
101
|
-
"IE",
|
|
102
|
-
"IL",
|
|
103
|
-
"IM",
|
|
104
|
-
"IN",
|
|
105
|
-
"IO",
|
|
106
|
-
"IQ",
|
|
107
|
-
"IR",
|
|
108
|
-
"IS",
|
|
109
|
-
"IT",
|
|
110
|
-
"JE",
|
|
111
|
-
"JM",
|
|
112
|
-
"JO",
|
|
113
|
-
"JP",
|
|
114
|
-
"KE",
|
|
115
|
-
"KG",
|
|
116
|
-
"KH",
|
|
117
|
-
"KI",
|
|
118
|
-
"KM",
|
|
119
|
-
"KN",
|
|
120
|
-
"KP",
|
|
121
|
-
"KR",
|
|
122
|
-
"KW",
|
|
123
|
-
"KY",
|
|
124
|
-
"KZ",
|
|
125
|
-
"LA",
|
|
126
|
-
"LB",
|
|
127
|
-
"LC",
|
|
128
|
-
"LI",
|
|
129
|
-
"LK",
|
|
130
|
-
"LR",
|
|
131
|
-
"LS",
|
|
132
|
-
"LT",
|
|
133
|
-
"LU",
|
|
134
|
-
"LV",
|
|
135
|
-
"LY",
|
|
136
|
-
"MA",
|
|
137
|
-
"MC",
|
|
138
|
-
"MD",
|
|
139
|
-
"ME",
|
|
140
|
-
"MF",
|
|
141
|
-
"MG",
|
|
142
|
-
"MH",
|
|
143
|
-
"MK",
|
|
144
|
-
"ML",
|
|
145
|
-
"MM",
|
|
146
|
-
"MN",
|
|
147
|
-
"MO",
|
|
148
|
-
"MP",
|
|
149
|
-
"MQ",
|
|
150
|
-
"MR",
|
|
151
|
-
"MS",
|
|
152
|
-
"MT",
|
|
153
|
-
"MU",
|
|
154
|
-
"MV",
|
|
155
|
-
"MW",
|
|
156
|
-
"MX",
|
|
157
|
-
"MY",
|
|
158
|
-
"MZ",
|
|
159
|
-
"NA",
|
|
160
|
-
"NC",
|
|
161
|
-
"NE",
|
|
162
|
-
"NF",
|
|
163
|
-
"NG",
|
|
164
|
-
"NI",
|
|
165
|
-
"NL",
|
|
166
|
-
"NO",
|
|
167
|
-
"NP",
|
|
168
|
-
"NR",
|
|
169
|
-
"NU",
|
|
170
|
-
"NZ",
|
|
171
|
-
"OM",
|
|
172
|
-
"PA",
|
|
173
|
-
"PE",
|
|
174
|
-
"PF",
|
|
175
|
-
"PG",
|
|
176
|
-
"PH",
|
|
177
|
-
"PK",
|
|
178
|
-
"PL",
|
|
179
|
-
"PM",
|
|
180
|
-
"PN",
|
|
181
|
-
"PR",
|
|
182
|
-
"PT",
|
|
183
|
-
"PW",
|
|
184
|
-
"PY",
|
|
185
|
-
"QA",
|
|
186
|
-
"RE",
|
|
187
|
-
"RO",
|
|
188
|
-
"RS",
|
|
189
|
-
"RU",
|
|
190
|
-
"RW",
|
|
191
|
-
"SA",
|
|
192
|
-
"SB",
|
|
193
|
-
"SC",
|
|
194
|
-
"SD",
|
|
195
|
-
"SE",
|
|
196
|
-
"SG",
|
|
197
|
-
"SH",
|
|
198
|
-
"SI",
|
|
199
|
-
"SJ",
|
|
200
|
-
"SK",
|
|
201
|
-
"SL",
|
|
202
|
-
"SM",
|
|
203
|
-
"SN",
|
|
204
|
-
"SO",
|
|
205
|
-
"SR",
|
|
206
|
-
"SS",
|
|
207
|
-
"ST",
|
|
208
|
-
"SV",
|
|
209
|
-
"SX",
|
|
210
|
-
"SY",
|
|
211
|
-
"SZ",
|
|
212
|
-
"TC",
|
|
213
|
-
"TD",
|
|
214
|
-
"TF",
|
|
215
|
-
"TG",
|
|
216
|
-
"TH",
|
|
217
|
-
"TJ",
|
|
218
|
-
"TK",
|
|
219
|
-
"TL",
|
|
220
|
-
"TM",
|
|
221
|
-
"TN",
|
|
222
|
-
"TO",
|
|
223
|
-
"TR",
|
|
224
|
-
"TT",
|
|
225
|
-
"TV",
|
|
226
|
-
"TZ",
|
|
227
|
-
"UA",
|
|
228
|
-
"UG",
|
|
229
|
-
"UM",
|
|
230
|
-
"US",
|
|
231
|
-
"UY",
|
|
232
|
-
"UZ",
|
|
233
|
-
"VA",
|
|
234
|
-
"VC",
|
|
235
|
-
"VE",
|
|
236
|
-
"VG",
|
|
237
|
-
"VI",
|
|
238
|
-
"VN",
|
|
239
|
-
"VU",
|
|
240
|
-
"WF",
|
|
241
|
-
"WS",
|
|
242
|
-
"YE",
|
|
243
|
-
"YT",
|
|
244
|
-
"ZA",
|
|
245
|
-
"ZM",
|
|
246
|
-
"ZW",
|
|
247
|
-
]);
|
|
248
|
-
|
|
249
|
-
/**
|
|
250
|
-
* Validates whether a string is a valid ISO 3166-1 alpha-2 country code.
|
|
251
|
-
* Performs a case-insensitive lookup against a predefined set of known codes.
|
|
252
|
-
*/
|
|
253
|
-
export const validateCountryCode = (value: unknown): boolean => {
|
|
254
|
-
if (typeof value !== "string") return false;
|
|
255
|
-
return isoCountryCodes.has(value.toUpperCase());
|
|
256
|
-
};
|
|
@@ -1,191 +0,0 @@
|
|
|
1
|
-
export const isoCurrencyCodes = new Set([
|
|
2
|
-
"AED",
|
|
3
|
-
"AFN",
|
|
4
|
-
"ALL",
|
|
5
|
-
"AMD",
|
|
6
|
-
"ANG",
|
|
7
|
-
"AOA",
|
|
8
|
-
"ARS",
|
|
9
|
-
"AUD",
|
|
10
|
-
"AWG",
|
|
11
|
-
"AZN",
|
|
12
|
-
"BAM",
|
|
13
|
-
"BBD",
|
|
14
|
-
"BDT",
|
|
15
|
-
"BGN",
|
|
16
|
-
"BHD",
|
|
17
|
-
"BIF",
|
|
18
|
-
"BMD",
|
|
19
|
-
"BND",
|
|
20
|
-
"BOB",
|
|
21
|
-
"BOV",
|
|
22
|
-
"BRL",
|
|
23
|
-
"BSD",
|
|
24
|
-
"BTN",
|
|
25
|
-
"BWP",
|
|
26
|
-
"BYN",
|
|
27
|
-
"BZD",
|
|
28
|
-
"CAD",
|
|
29
|
-
"CDF",
|
|
30
|
-
"CHE",
|
|
31
|
-
"CHF",
|
|
32
|
-
"CHW",
|
|
33
|
-
"CLF",
|
|
34
|
-
"CLP",
|
|
35
|
-
"CNY",
|
|
36
|
-
"COP",
|
|
37
|
-
"COU",
|
|
38
|
-
"CRC",
|
|
39
|
-
"CUC",
|
|
40
|
-
"CUP",
|
|
41
|
-
"CVE",
|
|
42
|
-
"CZK",
|
|
43
|
-
"DJF",
|
|
44
|
-
"DKK",
|
|
45
|
-
"DOP",
|
|
46
|
-
"DZD",
|
|
47
|
-
"EGP",
|
|
48
|
-
"ERN",
|
|
49
|
-
"ETB",
|
|
50
|
-
"EUR",
|
|
51
|
-
"FJD",
|
|
52
|
-
"FKP",
|
|
53
|
-
"GBP",
|
|
54
|
-
"GEL",
|
|
55
|
-
"GHS",
|
|
56
|
-
"GIP",
|
|
57
|
-
"GMD",
|
|
58
|
-
"GNF",
|
|
59
|
-
"GTQ",
|
|
60
|
-
"GYD",
|
|
61
|
-
"HKD",
|
|
62
|
-
"HNL",
|
|
63
|
-
"HRK",
|
|
64
|
-
"HTG",
|
|
65
|
-
"HUF",
|
|
66
|
-
"IDR",
|
|
67
|
-
"ILS",
|
|
68
|
-
"INR",
|
|
69
|
-
"IQD",
|
|
70
|
-
"IRR",
|
|
71
|
-
"ISK",
|
|
72
|
-
"JMD",
|
|
73
|
-
"JOD",
|
|
74
|
-
"JPY",
|
|
75
|
-
"KES",
|
|
76
|
-
"KGS",
|
|
77
|
-
"KHR",
|
|
78
|
-
"KMF",
|
|
79
|
-
"KPW",
|
|
80
|
-
"KRW",
|
|
81
|
-
"KWD",
|
|
82
|
-
"KYD",
|
|
83
|
-
"KZT",
|
|
84
|
-
"LAK",
|
|
85
|
-
"LBP",
|
|
86
|
-
"LKR",
|
|
87
|
-
"LRD",
|
|
88
|
-
"LSL",
|
|
89
|
-
"LYD",
|
|
90
|
-
"MAD",
|
|
91
|
-
"MDL",
|
|
92
|
-
"MGA",
|
|
93
|
-
"MKD",
|
|
94
|
-
"MMK",
|
|
95
|
-
"MNT",
|
|
96
|
-
"MOP",
|
|
97
|
-
"MRU",
|
|
98
|
-
"MUR",
|
|
99
|
-
"MVR",
|
|
100
|
-
"MWK",
|
|
101
|
-
"MXN",
|
|
102
|
-
"MXV",
|
|
103
|
-
"MYR",
|
|
104
|
-
"MZN",
|
|
105
|
-
"NAD",
|
|
106
|
-
"NGN",
|
|
107
|
-
"NIO",
|
|
108
|
-
"NOK",
|
|
109
|
-
"NPR",
|
|
110
|
-
"NZD",
|
|
111
|
-
"OMR",
|
|
112
|
-
"PAB",
|
|
113
|
-
"PEN",
|
|
114
|
-
"PGK",
|
|
115
|
-
"PHP",
|
|
116
|
-
"PKR",
|
|
117
|
-
"PLN",
|
|
118
|
-
"PYG",
|
|
119
|
-
"QAR",
|
|
120
|
-
"RON",
|
|
121
|
-
"RSD",
|
|
122
|
-
"RUB",
|
|
123
|
-
"RWF",
|
|
124
|
-
"SAR",
|
|
125
|
-
"SBD",
|
|
126
|
-
"SCR",
|
|
127
|
-
"SDG",
|
|
128
|
-
"SEK",
|
|
129
|
-
"SGD",
|
|
130
|
-
"SHP",
|
|
131
|
-
"SLL",
|
|
132
|
-
"SOS",
|
|
133
|
-
"SRD",
|
|
134
|
-
"SSP",
|
|
135
|
-
"STN",
|
|
136
|
-
"SVC",
|
|
137
|
-
"SYP",
|
|
138
|
-
"SZL",
|
|
139
|
-
"THB",
|
|
140
|
-
"TJS",
|
|
141
|
-
"TMT",
|
|
142
|
-
"TND",
|
|
143
|
-
"TOP",
|
|
144
|
-
"TRY",
|
|
145
|
-
"TTD",
|
|
146
|
-
"TWD",
|
|
147
|
-
"TZS",
|
|
148
|
-
"UAH",
|
|
149
|
-
"UGX",
|
|
150
|
-
"USD",
|
|
151
|
-
"USN",
|
|
152
|
-
"UYI",
|
|
153
|
-
"UYU",
|
|
154
|
-
"UYW",
|
|
155
|
-
"UZS",
|
|
156
|
-
"VES",
|
|
157
|
-
"VND",
|
|
158
|
-
"VUV",
|
|
159
|
-
"WST",
|
|
160
|
-
"XAF",
|
|
161
|
-
"XAG",
|
|
162
|
-
"XAU",
|
|
163
|
-
"XBA",
|
|
164
|
-
"XBB",
|
|
165
|
-
"XBC",
|
|
166
|
-
"XBD",
|
|
167
|
-
"XCD",
|
|
168
|
-
"XDR",
|
|
169
|
-
"XOF",
|
|
170
|
-
"XPD",
|
|
171
|
-
"XPF",
|
|
172
|
-
"XPT",
|
|
173
|
-
"XSU",
|
|
174
|
-
"XTS",
|
|
175
|
-
"XUA",
|
|
176
|
-
"XXX",
|
|
177
|
-
"YER",
|
|
178
|
-
"ZAR",
|
|
179
|
-
"ZMW",
|
|
180
|
-
"ZWL",
|
|
181
|
-
]);
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* Validates whether a string is a valid ISO 4217 currency code.
|
|
186
|
-
* Performs a case-insensitive lookup against a predefined set of known codes.
|
|
187
|
-
*/
|
|
188
|
-
export const validateCurrencyCode = (value: unknown): boolean => {
|
|
189
|
-
if (typeof value !== "string") return false;
|
|
190
|
-
return isoCurrencyCodes.has(value.toUpperCase());
|
|
191
|
-
};
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Validates whether a string is a properly formatted ISO 8601 datetime, date, or time.
|
|
3
|
-
*
|
|
4
|
-
* @param value - The value to validate.
|
|
5
|
-
* @param options - Optional settings for validation mode.
|
|
6
|
-
* @param options.mode - The mode of validation:
|
|
7
|
-
* - "datetime" (default): Checks full ISO 8601 datetime and equality with toISOString().
|
|
8
|
-
* - "date": Validates that the string is in YYYY-MM-DD format and represents a valid date.
|
|
9
|
-
* - "time": Validates that the string matches a valid HH:MM:SS(.sss)?Z? ISO 8601 time pattern.
|
|
10
|
-
* @returns True if the value is valid according to the specified mode; otherwise, false.
|
|
11
|
-
*/
|
|
12
|
-
export const validateDateTimeISO = (
|
|
13
|
-
value: unknown,
|
|
14
|
-
options?: { mode?: "datetime" | "date" | "time" }
|
|
15
|
-
): boolean => {
|
|
16
|
-
const mode = options?.mode ?? "datetime";
|
|
17
|
-
|
|
18
|
-
if (typeof value !== "string") return false;
|
|
19
|
-
|
|
20
|
-
if (mode === "datetime") {
|
|
21
|
-
// Strict ISO 8601 date-time: YYYY-MM-DDTHH:mm:ss(.fraction)?(Z|±HH:MM)
|
|
22
|
-
const isoDateTimeRegex =
|
|
23
|
-
/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?(?:Z|[+-]\d{2}:\d{2})$/;
|
|
24
|
-
|
|
25
|
-
if (!isoDateTimeRegex.test(value)) return false;
|
|
26
|
-
|
|
27
|
-
const date = new Date(value);
|
|
28
|
-
if (Number.isNaN(date.getTime())) return false;
|
|
29
|
-
|
|
30
|
-
// We purposely do NOT require `value === date.toISOString()` because
|
|
31
|
-
// valid ISO8601 inputs may include offsets (e.g., "+01:00") or omit
|
|
32
|
-
// milliseconds, both of which produce a different canonical ISO string.
|
|
33
|
-
// The regex ensures strict shape; Date parsing ensures it is a real moment.
|
|
34
|
-
return true;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
if (mode === "date") {
|
|
38
|
-
// YYYY-MM-DD format
|
|
39
|
-
const dateRegex = /^\d{4}-\d{2}-\d{2}$/;
|
|
40
|
-
if (!dateRegex.test(value)) return false;
|
|
41
|
-
const date = new Date(value);
|
|
42
|
-
if (isNaN(date.getTime())) return false;
|
|
43
|
-
// Ensure the date parts match exactly (to avoid 2023-02-30 being accepted)
|
|
44
|
-
const [year, month, day] = value.split("-").map(Number);
|
|
45
|
-
return (
|
|
46
|
-
date.getUTCFullYear() === year &&
|
|
47
|
-
date.getUTCMonth() + 1 === month &&
|
|
48
|
-
date.getUTCDate() === day
|
|
49
|
-
);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
if (mode === "time") {
|
|
53
|
-
// HH:MM:SS(.sss)?Z?
|
|
54
|
-
// Hours: 00-23, Minutes: 00-59, Seconds: 00-59, optional fractional seconds, optional Z
|
|
55
|
-
const timeRegex = /^([01]\d|2[0-3]):[0-5]\d:[0-5]\d(\.\d+)?Z?$/;
|
|
56
|
-
return timeRegex.test(value);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
return false;
|
|
60
|
-
};
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Validates an email string using a simplified RFC 5322-compliant regex.
|
|
3
|
-
* Returns true if the input is a string in the format `name@domain.tld`.
|
|
4
|
-
*/
|
|
5
|
-
export const validateEmail = (value: unknown): boolean => {
|
|
6
|
-
if (typeof value !== "string") return false;
|
|
7
|
-
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
8
|
-
return emailRegex.test(value);
|
|
9
|
-
};
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Validates that a text field is safe for storage (per OWASP guidelines).
|
|
3
|
-
* Applies to general text fields: names, descriptions, titles, etc.
|
|
4
|
-
* Global Standard: OWASP Input Validation Cheat Sheet (2024)
|
|
5
|
-
*/
|
|
6
|
-
export function validateSafeText(value: unknown): boolean {
|
|
7
|
-
if (typeof value !== "string") return false;
|
|
8
|
-
|
|
9
|
-
// Trimmed version should not be empty
|
|
10
|
-
const trimmed = value.trim();
|
|
11
|
-
if (trimmed.length === 0) return false;
|
|
12
|
-
|
|
13
|
-
// Reject control chars
|
|
14
|
-
for (let i = 0; i < trimmed.length; i++) {
|
|
15
|
-
const code = trimmed.codePointAt(i);
|
|
16
|
-
if (code !== undefined && (code >= 0x00 && code <= 0x1F || code === 0x7F)) {
|
|
17
|
-
return false;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// Reject dangerous characters
|
|
22
|
-
if (/['"<>\\{}();]/.test(trimmed)) return false;
|
|
23
|
-
|
|
24
|
-
// Reject SQL-style injection patterns
|
|
25
|
-
if (
|
|
26
|
-
/(--|\b(SELECT|UPDATE|DELETE|INSERT|DROP|ALTER|EXEC|UNION|GRANT|REVOKE)\b|\/\*|\*\/|@@)/i.test(
|
|
27
|
-
trimmed
|
|
28
|
-
)
|
|
29
|
-
)
|
|
30
|
-
return false;
|
|
31
|
-
|
|
32
|
-
// Reject null char
|
|
33
|
-
if (trimmed.includes("\u0000")) return false;
|
|
34
|
-
|
|
35
|
-
// Optional: limit length
|
|
36
|
-
if (trimmed.length > 1024) return false;
|
|
37
|
-
|
|
38
|
-
return true;
|
|
39
|
-
}
|