@wener/common 1.0.4 → 2.0.1
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/lib/cn/DivisionCode.js +55 -32
- package/lib/cn/DivisionCode.test.js +140 -0
- package/lib/cn/Mod11Checksum.js +80 -37
- package/lib/cn/Mod31Checksum.js +89 -40
- package/lib/cn/ResidentIdentityCardNumber.js +16 -16
- package/lib/cn/ResidentIdentityCardNumber.test.js +21 -0
- package/lib/cn/UnifiedSocialCreditCode.js +32 -26
- package/lib/cn/UnifiedSocialCreditCode.test.js +16 -0
- package/lib/cn/formatDate.js +5 -7
- package/lib/cn/index.js +0 -1
- package/lib/cn/parseSex.js +0 -2
- package/lib/cn/types.d.js +0 -2
- package/lib/consola/createStandardConsolaReporter.js +18 -0
- package/lib/consola/formatLogObject.js +226 -0
- package/lib/consola/index.js +2 -0
- package/lib/data/formatSort.js +5 -6
- package/lib/data/formatSort.test.js +34 -0
- package/lib/data/index.js +0 -1
- package/lib/data/maybeNumber.js +11 -5
- package/lib/data/parseSort.js +28 -22
- package/lib/data/parseSort.test.js +188 -0
- package/lib/data/resolvePagination.js +27 -16
- package/lib/data/resolvePagination.test.js +232 -0
- package/lib/data/types.d.js +0 -2
- package/lib/index.js +0 -1
- package/lib/jsonschema/JsonSchema.js +80 -54
- package/lib/jsonschema/JsonSchema.test.js +137 -0
- package/lib/jsonschema/index.js +0 -1
- package/lib/jsonschema/types.d.js +5 -3
- package/lib/meta/defineFileType.js +103 -20
- package/lib/meta/defineInit.js +250 -31
- package/lib/meta/defineMetadata.js +140 -24
- package/lib/meta/defineMetadata.test.js +13 -0
- package/lib/meta/index.js +0 -1
- package/lib/password/PHC.js +87 -63
- package/lib/password/PHC.test.js +539 -0
- package/lib/password/Password.js +291 -29
- package/lib/password/Password.test.js +362 -0
- package/lib/password/createArgon2PasswordAlgorithm.js +191 -35
- package/lib/password/createBase64PasswordAlgorithm.js +141 -8
- package/lib/password/createBcryptPasswordAlgorithm.js +168 -13
- package/lib/password/createPBKDF2PasswordAlgorithm.js +228 -46
- package/lib/password/createScryptPasswordAlgorithm.js +211 -55
- package/lib/password/index.js +0 -1
- package/lib/password/server/index.js +0 -1
- package/lib/resource/Identifiable.js +1 -0
- package/lib/resource/getTitleOfResource.js +10 -0
- package/lib/resource/index.js +1 -0
- package/lib/resource/schema/AnyResourceSchema.js +89 -0
- package/lib/resource/schema/BaseResourceSchema.js +29 -0
- package/lib/resource/schema/ResourceActionType.js +118 -0
- package/lib/resource/schema/ResourceStatus.js +93 -0
- package/lib/resource/schema/ResourceType.js +24 -0
- package/lib/resource/schema/SchemaRegistry.js +38 -0
- package/lib/resource/schema/SexType.js +10 -0
- package/lib/resource/schema/types.js +89 -0
- package/lib/resource/schema/types.test.js +14 -0
- package/lib/schema/TypeSchema.d.js +1 -0
- package/lib/schema/createSchemaData.js +173 -0
- package/lib/schema/findJsonSchemaByPath.js +49 -0
- package/lib/schema/getSchemaCache.js +11 -0
- package/lib/schema/getSchemaOptions.js +24 -0
- package/lib/schema/index.js +5 -0
- package/lib/schema/toJsonSchema.js +441 -0
- package/lib/schema/toJsonSchema.test.js +27 -0
- package/lib/schema/validate.js +124 -0
- package/lib/search/AdvanceSearch.js +0 -1
- package/lib/search/AdvanceSearch.test.js +435 -0
- package/lib/search/formatAdvanceSearch.js +41 -27
- package/lib/search/index.js +0 -1
- package/lib/search/optimizeAdvanceSearch.js +79 -25
- package/lib/search/parseAdvanceSearch.js +5 -5
- package/lib/search/parser.d.js +0 -2
- package/lib/search/parser.js +97 -74
- package/lib/search/types.d.js +0 -2
- package/lib/tools/generateSchema.js +201 -0
- package/lib/tools/renderJsonSchemaToMarkdownDoc.js +143 -55
- package/package.json +30 -9
- package/src/consola/createStandardConsolaReporter.ts +31 -0
- package/src/consola/formatLogObject.ts +171 -0
- package/src/consola/index.ts +2 -0
- package/src/data/maybeNumber.ts +5 -1
- package/src/data/resolvePagination.test.ts +1 -1
- package/src/data/resolvePagination.ts +18 -7
- package/src/data/types.d.ts +12 -0
- package/src/jsonschema/JsonSchema.test.ts +17 -0
- package/src/jsonschema/JsonSchema.ts +4 -4
- package/src/jsonschema/types.d.ts +63 -12
- package/src/resource/Identifiable.ts +3 -0
- package/src/resource/getTitleOfResource.tsx +6 -0
- package/src/resource/index.ts +3 -0
- package/src/resource/schema/AnyResourceSchema.ts +113 -0
- package/src/resource/schema/BaseResourceSchema.ts +32 -0
- package/src/resource/schema/ResourceActionType.ts +123 -0
- package/src/resource/schema/ResourceStatus.ts +94 -0
- package/src/resource/schema/ResourceType.ts +25 -0
- package/src/resource/schema/SchemaRegistry.ts +42 -0
- package/src/resource/schema/SexType.ts +13 -0
- package/src/resource/schema/types.test.ts +18 -0
- package/src/resource/schema/types.ts +105 -0
- package/src/schema/TypeSchema.d.ts +32 -0
- package/src/schema/createSchemaData.ts +81 -0
- package/src/schema/findJsonSchemaByPath.ts +37 -0
- package/src/schema/getSchemaCache.ts +21 -0
- package/src/schema/getSchemaOptions.ts +24 -0
- package/src/schema/index.ts +6 -0
- package/src/schema/toJsonSchema.test.ts +37 -0
- package/src/schema/toJsonSchema.ts +200 -0
- package/src/schema/validate.ts +135 -0
- package/src/tools/generateSchema.ts +39 -0
- package/lib/cn/DivisionCode.js.map +0 -1
- package/lib/cn/Mod11Checksum.js.map +0 -1
- package/lib/cn/Mod31Checksum.js.map +0 -1
- package/lib/cn/ResidentIdentityCardNumber.js.map +0 -1
- package/lib/cn/UnifiedSocialCreditCode.js.map +0 -1
- package/lib/cn/formatDate.js.map +0 -1
- package/lib/cn/index.js.map +0 -1
- package/lib/cn/parseSex.js.map +0 -1
- package/lib/cn/types.d.js.map +0 -1
- package/lib/data/formatSort.js.map +0 -1
- package/lib/data/index.js.map +0 -1
- package/lib/data/maybeNumber.js.map +0 -1
- package/lib/data/parseSort.js.map +0 -1
- package/lib/data/resolvePagination.js.map +0 -1
- package/lib/data/types.d.js.map +0 -1
- package/lib/index.js.map +0 -1
- package/lib/jsonschema/JsonSchema.js.map +0 -1
- package/lib/jsonschema/index.js.map +0 -1
- package/lib/jsonschema/types.d.js.map +0 -1
- package/lib/meta/defineFileType.js.map +0 -1
- package/lib/meta/defineInit.js.map +0 -1
- package/lib/meta/defineMetadata.js.map +0 -1
- package/lib/meta/index.js.map +0 -1
- package/lib/password/PHC.js.map +0 -1
- package/lib/password/Password.js.map +0 -1
- package/lib/password/createArgon2PasswordAlgorithm.js.map +0 -1
- package/lib/password/createBase64PasswordAlgorithm.js.map +0 -1
- package/lib/password/createBcryptPasswordAlgorithm.js.map +0 -1
- package/lib/password/createPBKDF2PasswordAlgorithm.js.map +0 -1
- package/lib/password/createScryptPasswordAlgorithm.js.map +0 -1
- package/lib/password/index.js.map +0 -1
- package/lib/password/server/index.js.map +0 -1
- package/lib/search/AdvanceSearch.js.map +0 -1
- package/lib/search/formatAdvanceSearch.js.map +0 -1
- package/lib/search/index.js.map +0 -1
- package/lib/search/optimizeAdvanceSearch.js.map +0 -1
- package/lib/search/parseAdvanceSearch.js.map +0 -1
- package/lib/search/parser.d.js.map +0 -1
- package/lib/search/parser.js.map +0 -1
- package/lib/search/types.d.js.map +0 -1
- package/lib/tools/renderJsonSchemaToMarkdownDoc.js.map +0 -1
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import colors from 'chalk';
|
|
2
|
+
import type { ConsolaOptions, FormatOptions, LogObject, LogType } from 'consola/core';
|
|
3
|
+
import dayjs from 'dayjs';
|
|
4
|
+
import { isDevelopment } from 'std-env';
|
|
5
|
+
|
|
6
|
+
const levelColors: Record<LogType, (str: string) => string> = {
|
|
7
|
+
trace: colors.gray,
|
|
8
|
+
debug: colors.cyan,
|
|
9
|
+
info: colors.blueBright,
|
|
10
|
+
warn: colors.yellow,
|
|
11
|
+
error: colors.red,
|
|
12
|
+
fatal: colors.bgRed.white,
|
|
13
|
+
silent: colors.white,
|
|
14
|
+
log: colors.white,
|
|
15
|
+
success: colors.green,
|
|
16
|
+
fail: colors.red,
|
|
17
|
+
ready: colors.green,
|
|
18
|
+
start: colors.cyan,
|
|
19
|
+
box: colors.white,
|
|
20
|
+
verbose: colors.green,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const levelShort: Record<LogType, string> = {
|
|
24
|
+
trace: 'TRAC',
|
|
25
|
+
debug: 'DEBG',
|
|
26
|
+
info: 'INFO',
|
|
27
|
+
warn: 'WARN',
|
|
28
|
+
error: 'ERRO',
|
|
29
|
+
fatal: 'FATL',
|
|
30
|
+
silent: 'SLNT',
|
|
31
|
+
log: 'LOG ', // Added space to make it 4 characters
|
|
32
|
+
success: 'SUCC',
|
|
33
|
+
fail: 'FAIL',
|
|
34
|
+
ready: 'READ',
|
|
35
|
+
start: 'STRT',
|
|
36
|
+
box: 'BOX ', // Added space to make it 4 characters
|
|
37
|
+
verbose: 'VERB',
|
|
38
|
+
};
|
|
39
|
+
const start = Date.now();
|
|
40
|
+
|
|
41
|
+
export function formatLogObject(
|
|
42
|
+
o: LogObject,
|
|
43
|
+
ctx: {
|
|
44
|
+
options: ConsolaOptions;
|
|
45
|
+
},
|
|
46
|
+
) {
|
|
47
|
+
let { date, type, tag } = o;
|
|
48
|
+
type = type === 'log' ? 'info' : type;
|
|
49
|
+
|
|
50
|
+
const color = levelColors[type] || colors.white;
|
|
51
|
+
const levelText = levelShort[type] || type.toUpperCase().slice(0, 4); // Get first 4 chars, consistent uppercase
|
|
52
|
+
|
|
53
|
+
let line = '';
|
|
54
|
+
let out: string[] = [];
|
|
55
|
+
|
|
56
|
+
// Timestamp
|
|
57
|
+
if (isDevelopment) {
|
|
58
|
+
// process.hrtime.bigint()
|
|
59
|
+
let diff = (date.getTime() - start) / 1000;
|
|
60
|
+
|
|
61
|
+
out.push(colors.gray(diff.toFixed(3).padStart(7, ' ')));
|
|
62
|
+
} else {
|
|
63
|
+
out.push(colors.gray(dayjs(date).format('YYYY-MM-DD HH:mm:ss.SSS')));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Log Level (colored)
|
|
67
|
+
// Pad to 4 characters
|
|
68
|
+
out.push(color(levelText.padEnd(4, ' ')));
|
|
69
|
+
|
|
70
|
+
if (tag) {
|
|
71
|
+
out.push(colors.yellow(`[${tag}]`)); // Added color for tag
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
{
|
|
75
|
+
const [message, ...additional] = formatArgs(o.args, ctx).split('\n');
|
|
76
|
+
|
|
77
|
+
out.push(characterFormat(message));
|
|
78
|
+
|
|
79
|
+
line = out.join(' ');
|
|
80
|
+
if (additional.length > 0) {
|
|
81
|
+
line += characterFormat(additional.join('\n'));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (type === 'trace') {
|
|
85
|
+
const _err = new Error('Trace: ' + o.message);
|
|
86
|
+
line += formatStack(_err.stack || '', _err.message);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// if (!message && typeof args[0] === 'string') {
|
|
91
|
+
// message = args.shift();
|
|
92
|
+
// }
|
|
93
|
+
// if (message) {
|
|
94
|
+
// out.push(message);
|
|
95
|
+
// }
|
|
96
|
+
// if (args.length) {
|
|
97
|
+
// out.push(...args.map((a) => (typeof a === 'string' ? a : JSON.stringify(a)))); // Handle non-string args
|
|
98
|
+
// }
|
|
99
|
+
|
|
100
|
+
// todo format error
|
|
101
|
+
// https://github.com/unjs/consola/blob/main/src/reporters/fancy.ts
|
|
102
|
+
|
|
103
|
+
// return out.join(' '); // Increased spacing for better readability
|
|
104
|
+
return line;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function characterFormat(str: string) {
|
|
108
|
+
return (
|
|
109
|
+
str
|
|
110
|
+
// highlight backticks
|
|
111
|
+
.replace(/`([^`]+)`/gm, (_, m) => colors.cyan(m))
|
|
112
|
+
// underline underscores
|
|
113
|
+
.replace(/\s+_([^_]+)_\s+/gm, (_, m) => ` ${colors.underline(m)} `)
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function parseStack(stack: string, message: string) {
|
|
118
|
+
// const cwd = process.cwd() + sep;
|
|
119
|
+
|
|
120
|
+
const lines = stack
|
|
121
|
+
.split('\n')
|
|
122
|
+
.splice(message.split('\n').length)
|
|
123
|
+
.map(
|
|
124
|
+
(l) => l.trim().replace('file://', ''),
|
|
125
|
+
// .replace(cwd, '')
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
return lines;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function formatStack(stack: string, message: string, opts?: FormatOptions) {
|
|
132
|
+
const indent = ' '.repeat((opts?.errorLevel || 0) + 1);
|
|
133
|
+
return (
|
|
134
|
+
`\n${indent}`
|
|
135
|
+
+ parseStack(stack, message)
|
|
136
|
+
.map(
|
|
137
|
+
(line) =>
|
|
138
|
+
' ' + line.replace(/^at +/, (m) => colors.gray(m)).replace(/\((.+)\)/, (_, m) => `(${colors.cyan(m)})`),
|
|
139
|
+
)
|
|
140
|
+
.join(`\n${indent}`)
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function formatArgs(args: any[], opts: FormatOptions) {
|
|
145
|
+
const _args = args.map((arg) => {
|
|
146
|
+
if (arg && typeof arg.stack === 'string') {
|
|
147
|
+
return formatError(arg, opts);
|
|
148
|
+
}
|
|
149
|
+
return arg;
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
// Only supported with Node >= 10
|
|
153
|
+
// https://nodejs.org/api/util.html#util_util_inspect_object_options
|
|
154
|
+
return formatWithOptions(opts, ..._args);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function formatWithOptions(o: any, ...params: any[]) {
|
|
158
|
+
// import { formatWithOptions } from 'node:util';
|
|
159
|
+
return params.join(' ');
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function formatError(err: any, opts: FormatOptions): string {
|
|
163
|
+
const message = err.message ?? formatWithOptions(opts, err);
|
|
164
|
+
const stack = err.stack ? formatStack(err.stack, message, opts) : '';
|
|
165
|
+
|
|
166
|
+
const level = opts?.errorLevel || 0;
|
|
167
|
+
const causedPrefix = level > 0 ? `${' '.repeat(level)}[cause]: ` : '';
|
|
168
|
+
const causedError = err.cause ? '\n\n' + formatError(err.cause, { ...opts, errorLevel: level + 1 }) : '';
|
|
169
|
+
|
|
170
|
+
return causedPrefix + message + '\n' + stack + causedError;
|
|
171
|
+
}
|
package/src/data/maybeNumber.ts
CHANGED
|
@@ -9,9 +9,13 @@ export function maybeNumber(v: MaybeNumber) {
|
|
|
9
9
|
case 'number':
|
|
10
10
|
return v;
|
|
11
11
|
case 'bigint':
|
|
12
|
+
if (v > BigInt(Number.MAX_SAFE_INTEGER) || v < BigInt(Number.MIN_SAFE_INTEGER)) {
|
|
13
|
+
throw new Error(`bigint out of range`);
|
|
14
|
+
}
|
|
12
15
|
return Number(v);
|
|
13
16
|
case 'string':
|
|
14
|
-
|
|
17
|
+
v = v.trim();
|
|
18
|
+
if (!v) {
|
|
15
19
|
return undefined;
|
|
16
20
|
}
|
|
17
21
|
}
|
|
@@ -53,6 +53,6 @@ test(resolvePagination.name, () => {
|
|
|
53
53
|
});
|
|
54
54
|
|
|
55
55
|
test(`${resolvePagination.name} with options`, () => {
|
|
56
|
-
expect(resolvePagination({ pageSize: '200' }, {
|
|
56
|
+
expect(resolvePagination({ pageSize: '200' }, { maxLimit: 50 })).toMatchObject({ pageSize: 50, limit: 50 });
|
|
57
57
|
expect(resolvePagination({}, { pageSize: 30 })).toMatchObject({ pageSize: 30, limit: 30 });
|
|
58
58
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { maybeFunction, type MaybeFunction } from '@wener/utils';
|
|
2
|
-
import {
|
|
2
|
+
import { mapValues, omitBy, pick } from 'es-toolkit';
|
|
3
3
|
import { maybeNumber, type MaybeNumber } from './maybeNumber';
|
|
4
4
|
|
|
5
5
|
export type PaginationInput = {
|
|
@@ -22,7 +22,8 @@ export function resolvePagination(
|
|
|
22
22
|
page: PaginationInput,
|
|
23
23
|
options: {
|
|
24
24
|
pageSize?: MaybeFunction<number, [number | undefined]>;
|
|
25
|
-
|
|
25
|
+
maxLimit?: number;
|
|
26
|
+
maxOffset?: number;
|
|
26
27
|
} = {},
|
|
27
28
|
): ResolvedPagination {
|
|
28
29
|
let out = omitBy(
|
|
@@ -38,15 +39,23 @@ export function resolvePagination(
|
|
|
38
39
|
if (options.pageSize) {
|
|
39
40
|
pageSize = maybeFunction(options.pageSize, pageSize);
|
|
40
41
|
}
|
|
41
|
-
pageSize ??=
|
|
42
|
-
pageSize = clamp(pageSize, 1, options.maxPageSize ?? 100);
|
|
42
|
+
pageSize ??= resolvePagination.pageSize;
|
|
43
43
|
|
|
44
|
-
let { pageNumber = 1, pageIndex
|
|
44
|
+
let { pageNumber = 1, pageIndex } = out;
|
|
45
45
|
// page index over page number
|
|
46
46
|
pageNumber = Math.max(pageNumber, 1);
|
|
47
47
|
pageIndex = Math.max(pageIndex ?? pageNumber - 1, 0);
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
|
|
49
|
+
let { limit = pageSize, offset = pageSize * pageIndex } = out;
|
|
50
|
+
limit = Math.max(1, limit);
|
|
51
|
+
offset = Math.max(0, offset);
|
|
52
|
+
|
|
53
|
+
if (options.maxLimit) {
|
|
54
|
+
limit = Math.min(limit, options.maxLimit);
|
|
55
|
+
}
|
|
56
|
+
if (options.maxOffset) {
|
|
57
|
+
offset = Math.min(offset, options.maxOffset);
|
|
58
|
+
}
|
|
50
59
|
|
|
51
60
|
pageSize = limit;
|
|
52
61
|
pageIndex = Math.floor(offset / pageSize);
|
|
@@ -58,3 +67,5 @@ export function resolvePagination(
|
|
|
58
67
|
pageIndex,
|
|
59
68
|
};
|
|
60
69
|
}
|
|
70
|
+
|
|
71
|
+
resolvePagination.pageSize = 20;
|
package/src/data/types.d.ts
CHANGED
|
@@ -31,3 +31,15 @@ type ListResult<T = any> = {
|
|
|
31
31
|
/** Total number of items */
|
|
32
32
|
total: number;
|
|
33
33
|
};
|
|
34
|
+
|
|
35
|
+
type PageInfo = {
|
|
36
|
+
/** Whether there are more items */
|
|
37
|
+
hasNextPage: boolean;
|
|
38
|
+
/** Whether there are previous items */
|
|
39
|
+
hasPreviousPage: boolean;
|
|
40
|
+
|
|
41
|
+
// /** Start cursor */
|
|
42
|
+
// startCursor?: string;
|
|
43
|
+
// /** End cursor */
|
|
44
|
+
// endCursor?: string;
|
|
45
|
+
};
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import type { TObject } from '@sinclair/typebox';
|
|
2
|
+
import type { AnySchemaObject, JSONSchemaType } from 'ajv';
|
|
1
3
|
import { describe, expect, it } from 'vitest';
|
|
2
4
|
import { JsonSchema } from './JsonSchema';
|
|
5
|
+
import type { JsonSchemaDef } from './types';
|
|
3
6
|
|
|
4
7
|
describe('jsonschema', () => {
|
|
5
8
|
it('should create from schema', () => {
|
|
@@ -15,4 +18,18 @@ describe('jsonschema', () => {
|
|
|
15
18
|
expect(JsonSchema.create(a)).toEqual(b);
|
|
16
19
|
}
|
|
17
20
|
});
|
|
21
|
+
|
|
22
|
+
it('should match typebox types', () => {
|
|
23
|
+
let a: JsonSchemaDef | undefined;
|
|
24
|
+
let b: TObject<{}> | undefined;
|
|
25
|
+
a = b;
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('should match ajv types', () => {
|
|
29
|
+
let a: JsonSchemaDef | undefined;
|
|
30
|
+
let b: AnySchemaObject | undefined;
|
|
31
|
+
let c: JSONSchemaType<{}> | undefined;
|
|
32
|
+
a = b;
|
|
33
|
+
// a = c;
|
|
34
|
+
});
|
|
18
35
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Static, TSchema } from '@sinclair/typebox';
|
|
2
2
|
import Ajv, { type ErrorObject, type Options } from 'ajv';
|
|
3
3
|
import addFormats from 'ajv-formats';
|
|
4
|
-
import localize from 'ajv-i18n/localize/zh';
|
|
4
|
+
// import localize from 'ajv-i18n/localize/zh';
|
|
5
5
|
import addKeywords from 'ajv-keywords';
|
|
6
6
|
import { isNil } from 'es-toolkit';
|
|
7
7
|
import { match, P } from 'ts-pattern';
|
|
@@ -45,7 +45,7 @@ function validate({ schema, data, mutate, clone, ajv }: ValidateOptions & { sche
|
|
|
45
45
|
|
|
46
46
|
const valid = validate(data);
|
|
47
47
|
const errors = validate.errors;
|
|
48
|
-
localize(errors);
|
|
48
|
+
// localize(errors);
|
|
49
49
|
|
|
50
50
|
return { data, success: valid, message: ajv.errorsText(errors), errors: errors };
|
|
51
51
|
}
|
|
@@ -55,7 +55,7 @@ type TypeOfSchema<S> = S extends TSchema ? Static<S> : any;
|
|
|
55
55
|
export namespace JsonSchema {
|
|
56
56
|
export let schemas: JsonSchemaDef[] = [];
|
|
57
57
|
|
|
58
|
-
export
|
|
58
|
+
export let createAjv = _createAjv;
|
|
59
59
|
|
|
60
60
|
export function addSchema(
|
|
61
61
|
schema: JsonSchemaDef,
|
|
@@ -113,7 +113,7 @@ export namespace JsonSchema {
|
|
|
113
113
|
// will not ensure value match the rule
|
|
114
114
|
return match(schema as JsonSchemaDef)
|
|
115
115
|
.returnType<any>()
|
|
116
|
-
.with({ const: P.
|
|
116
|
+
.with({ const: P.nonNullable }, (v) => v)
|
|
117
117
|
.with({ default: P.select() }, (v) => v)
|
|
118
118
|
.with({ anyOf: P.nonNullable }, (schema) => {
|
|
119
119
|
return create(schema.anyOf[0]);
|
|
@@ -5,7 +5,8 @@ type JsonSchemaTypeName =
|
|
|
5
5
|
| 'boolean'
|
|
6
6
|
| 'object'
|
|
7
7
|
| 'array'
|
|
8
|
-
| 'null'
|
|
8
|
+
| 'null'
|
|
9
|
+
| undefined;
|
|
9
10
|
|
|
10
11
|
type JsonValue =
|
|
11
12
|
| string //
|
|
@@ -13,15 +14,21 @@ type JsonValue =
|
|
|
13
14
|
| boolean
|
|
14
15
|
| { [key: string]: JsonValue }
|
|
15
16
|
| JsonValue[]
|
|
16
|
-
| null
|
|
17
|
+
| null
|
|
18
|
+
| undefined; //
|
|
17
19
|
|
|
18
20
|
type JsonSchemaVersion =
|
|
19
21
|
| 'http://json-schema.org/schema' // latest
|
|
22
|
+
//
|
|
20
23
|
| 'https://json-schema.org/draft/2020-12/schema'
|
|
24
|
+
| 'https://json-schema.org/draft/2019-09/schema'
|
|
21
25
|
// draft-07
|
|
22
|
-
| '
|
|
23
|
-
| 'http://json-schema.org/draft-07/
|
|
24
|
-
|
|
26
|
+
| 'https://json-schema.org/draft-07/schema'
|
|
27
|
+
| 'http://json-schema.org/draft-07/schema'
|
|
28
|
+
| 'https://json-schema.org/draft-07/hyper-schema'
|
|
29
|
+
| 'http://json-schema.org/draft-07/hyper-schema'
|
|
30
|
+
//
|
|
31
|
+
| 'https://json-schema.org/draft-04/schema';
|
|
25
32
|
type JsonSchemaFormatName =
|
|
26
33
|
//
|
|
27
34
|
| 'date-time'
|
|
@@ -47,7 +54,12 @@ type JsonSchemaFormatName =
|
|
|
47
54
|
| 'json-pointer'
|
|
48
55
|
| 'relative-json-pointer';
|
|
49
56
|
|
|
50
|
-
|
|
57
|
+
/**
|
|
58
|
+
* JSON Schema Definition
|
|
59
|
+
*
|
|
60
|
+
* @see https://json-schema.org/specification-links.html
|
|
61
|
+
*/
|
|
62
|
+
export type JsonSchemaDef<I = any, O = I> = {
|
|
51
63
|
$id?: string;
|
|
52
64
|
$ref?: string;
|
|
53
65
|
/**
|
|
@@ -67,7 +79,7 @@ export type JsonSchemaDef = {
|
|
|
67
79
|
$defs?: { [key: string]: JsonSchemaDef };
|
|
68
80
|
|
|
69
81
|
type?: JsonSchemaTypeName | JsonSchemaTypeName[];
|
|
70
|
-
enum?: JsonValue
|
|
82
|
+
enum?: JsonValue;
|
|
71
83
|
const?: JsonValue;
|
|
72
84
|
|
|
73
85
|
//region Numeric Validation
|
|
@@ -90,12 +102,23 @@ export type JsonSchemaDef = {
|
|
|
90
102
|
|
|
91
103
|
//region Array Validation
|
|
92
104
|
|
|
105
|
+
/**
|
|
106
|
+
* schema for array items
|
|
107
|
+
* @see https://json-schema.org/understanding-json-schema/reference/array.html#items
|
|
108
|
+
*/
|
|
93
109
|
items?: JsonSchemaDef | JsonSchemaDef[];
|
|
94
|
-
additionalItems?: JsonSchemaDef;
|
|
110
|
+
additionalItems?: JsonSchemaDef | boolean;
|
|
95
111
|
maxItems?: number;
|
|
96
112
|
minItems?: number;
|
|
97
113
|
uniqueItems?: boolean;
|
|
98
114
|
contains?: JsonSchemaDef;
|
|
115
|
+
|
|
116
|
+
//region Draft 2022-12
|
|
117
|
+
minContains?: number;
|
|
118
|
+
maxContains?: number;
|
|
119
|
+
prefixItems?: JsonSchemaDef[];
|
|
120
|
+
//endregion
|
|
121
|
+
|
|
99
122
|
//endregion
|
|
100
123
|
|
|
101
124
|
//region Object Validation
|
|
@@ -105,9 +128,25 @@ export type JsonSchemaDef = {
|
|
|
105
128
|
required?: string[];
|
|
106
129
|
properties?: { [key: string]: JsonSchemaDef };
|
|
107
130
|
patternProperties?: { [key: string]: JsonSchemaDef };
|
|
108
|
-
|
|
109
|
-
|
|
131
|
+
/**
|
|
132
|
+
* Replaced by {@link unevaluatedProperties} in Draft 2019-09+
|
|
133
|
+
*/
|
|
134
|
+
additionalProperties?: JsonSchemaDef | boolean; // true = {}
|
|
135
|
+
/**
|
|
136
|
+
* Superseded by {@link dependentSchemas}, {@link dependentRequired} in Draft 2019-09+
|
|
137
|
+
*/
|
|
138
|
+
dependencies?: { [key: string]: undefined | JsonSchemaDef | string[] };
|
|
110
139
|
propertyNames?: JsonSchemaDef;
|
|
140
|
+
|
|
141
|
+
//region Draft 2019-09+
|
|
142
|
+
|
|
143
|
+
dependentSchemas?: { [key: string]: JsonSchemaDef };
|
|
144
|
+
dependentRequired?: { [key: string]: string[] };
|
|
145
|
+
unevaluatedProperties?: JsonSchemaDef | boolean;
|
|
146
|
+
unevaluatedItems?: JsonSchemaDef | boolean;
|
|
147
|
+
|
|
148
|
+
//endregion
|
|
149
|
+
|
|
111
150
|
//endregion
|
|
112
151
|
|
|
113
152
|
//region Conditional
|
|
@@ -120,9 +159,9 @@ export type JsonSchemaDef = {
|
|
|
120
159
|
|
|
121
160
|
//region Boolean Logic
|
|
122
161
|
|
|
123
|
-
allOf?: JsonSchemaDef[];
|
|
162
|
+
allOf?: JsonSchemaDef[]; // extends, merge
|
|
124
163
|
anyOf?: JsonSchemaDef[];
|
|
125
|
-
oneOf?: JsonSchemaDef[];
|
|
164
|
+
oneOf?: JsonSchemaDef[]; // polymorphism, enum
|
|
126
165
|
not?: JsonSchemaDef;
|
|
127
166
|
//endregion
|
|
128
167
|
|
|
@@ -159,5 +198,17 @@ export type JsonSchemaDef = {
|
|
|
159
198
|
|
|
160
199
|
//endregion
|
|
161
200
|
|
|
201
|
+
//region OpenAPI Spec
|
|
202
|
+
|
|
162
203
|
nullable?: boolean;
|
|
204
|
+
|
|
205
|
+
discriminator?: {
|
|
206
|
+
propertyName: string;
|
|
207
|
+
mapping?: { [key: string]: string };
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
//endregion
|
|
211
|
+
|
|
212
|
+
// [key: `x-${string}`]: JsonValue;
|
|
213
|
+
[key: string]: JsonValue;
|
|
163
214
|
};
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { z } from 'zod/v4';
|
|
2
|
+
import { SexTypeSchema } from './SexType';
|
|
3
|
+
import { rz } from './types';
|
|
4
|
+
|
|
5
|
+
export type AnyResource = z.infer<typeof AnyResourceSchema>;
|
|
6
|
+
export const AnyResourceSchema = z
|
|
7
|
+
.object({
|
|
8
|
+
id: rz.resourceId.readonly().describe('ID'),
|
|
9
|
+
uid: z.guid().nullish().readonly().describe('唯一ID'),
|
|
10
|
+
tid: rz.resourceId.nullish().readonly().describe('租户ID'),
|
|
11
|
+
sid: z.coerce.number().nullish().readonly().describe('序号'),
|
|
12
|
+
eid: z.string().nullish().readonly().describe('外部ID'),
|
|
13
|
+
cid: z.string().nullish().readonly().describe('服务商ID'),
|
|
14
|
+
rid: z.string().nullish().readonly().describe('服务商资源ID'),
|
|
15
|
+
|
|
16
|
+
code: z.string().nullish().describe('编号'),
|
|
17
|
+
|
|
18
|
+
fullName: rz.friendlyName.optional().describe('名称'),
|
|
19
|
+
displayName: rz.friendlyName.nullish().describe('显示名'),
|
|
20
|
+
loginName: rz.loginName.nullish().describe('登录名'),
|
|
21
|
+
email: z.string().nullish().describe('邮箱'),
|
|
22
|
+
emailVerifiedAt: rz.date.nullish().describe('邮箱验证时间'),
|
|
23
|
+
phoneNumber: rz.phoneNumber.nullish().describe('电话号码'),
|
|
24
|
+
phoneNumberVerifiedAt: rz.date.nullish().describe('电话号码验证时间'),
|
|
25
|
+
|
|
26
|
+
jobNumber: z.string().nullish().describe('工号'),
|
|
27
|
+
jobTitle: z.string().nullish().describe('职位'),
|
|
28
|
+
birthDate: rz.date.nullish().describe('出生日期'),
|
|
29
|
+
|
|
30
|
+
password: rz.password.nullish().describe('密码'),
|
|
31
|
+
sex: SexTypeSchema.nullish().describe('性别'),
|
|
32
|
+
|
|
33
|
+
contactName: rz.friendlyName.nullish().describe('联系人'),
|
|
34
|
+
contactPhone: rz.phoneNumber.nullish().describe('联系电话'),
|
|
35
|
+
contactEmail: z.string().nullish().describe('联系邮箱'),
|
|
36
|
+
contactAddress: z.string().nullish().describe('联系地址'),
|
|
37
|
+
alternativeName: rz.friendlyName.nullish().describe('备用联系人'),
|
|
38
|
+
alternativeEmail: z.string().nullish().describe('备用联系邮箱'),
|
|
39
|
+
alternativePhone: rz.phoneNumber.nullish().describe('备用联系电话'),
|
|
40
|
+
alternativeAddress: z.string().nullish().describe('备用联系地址'),
|
|
41
|
+
|
|
42
|
+
title: z.string().trim().nullish().describe('标题'),
|
|
43
|
+
description: z.string().trim().nullish().describe('描述'),
|
|
44
|
+
topic: z.string().nullish().describe('主题'),
|
|
45
|
+
summary: z.string().nullish().describe('摘要'),
|
|
46
|
+
|
|
47
|
+
avatarUrl: z.string().nullish().describe('头像'),
|
|
48
|
+
photoUrl: z.string().nullish().describe('照片'),
|
|
49
|
+
imageUrl: z.string().nullish().describe('图片'),
|
|
50
|
+
bannerUrl: z.string().nullish().describe('横幅图片'),
|
|
51
|
+
|
|
52
|
+
type: z.string().nullish().describe('类型'),
|
|
53
|
+
data: z.record(z.string(), z.any()).nullish().describe('数据'),
|
|
54
|
+
|
|
55
|
+
licenseNumber: z.string().nullish().describe('证件号码'),
|
|
56
|
+
licenseStartDate: rz.date.nullish().describe('证件有效期开始日期'),
|
|
57
|
+
licenseEndDate: rz.date.nullish().describe('证件有效期结束日期'),
|
|
58
|
+
licenseAddress: z.string().nullish().describe('证件地址'),
|
|
59
|
+
licenseFrontUrl: z.string().nullish().describe('证件正面照片'),
|
|
60
|
+
licenseBackUrl: z.string().nullish().describe('证件背面照片'),
|
|
61
|
+
|
|
62
|
+
address: z.string().nullish().describe('地址'),
|
|
63
|
+
divisionCode: z.string().nullish().describe('行政区划代码'),
|
|
64
|
+
province: z.string().nullish().describe('省'),
|
|
65
|
+
city: z.string().nullish().describe('市'),
|
|
66
|
+
|
|
67
|
+
startDate: rz.date.nullish().describe('开始日期'),
|
|
68
|
+
endDate: rz.date.nullish().describe('结束日期'),
|
|
69
|
+
startTime: rz.dateTime.nullish().describe('开始时间'),
|
|
70
|
+
endTime: rz.dateTime.nullish().describe('结束时间'),
|
|
71
|
+
|
|
72
|
+
userId: rz.resourceIdOf('User').nullish(),
|
|
73
|
+
entityId: rz.resourceId.nullish().describe('关联的实体'),
|
|
74
|
+
entityType: z.string().nullish().describe('关联的实体类型'),
|
|
75
|
+
customerId: rz.resourceId.nullish().describe('客户'),
|
|
76
|
+
customerType: z.string().nullish().describe('客户类型'),
|
|
77
|
+
contactId: rz.resourceId.nullish().describe('联系人'),
|
|
78
|
+
accountId: rz.resourceId.nullish().describe('账户'),
|
|
79
|
+
|
|
80
|
+
ownerId: rz.resourceId.nullish(),
|
|
81
|
+
ownerType: z.string().nullish().describe('负责人类型'),
|
|
82
|
+
owningUserId: rz.resourceId.nullish().describe('负责人'),
|
|
83
|
+
|
|
84
|
+
parentId: rz.resourceId.nullish().describe('父级'),
|
|
85
|
+
|
|
86
|
+
metadata: z.record(z.string(), z.any()).nullish().describe('元数据'),
|
|
87
|
+
tags: z.array(z.string()).nullish().describe('标记'),
|
|
88
|
+
notes: z.string().nullish().describe('备注'),
|
|
89
|
+
|
|
90
|
+
state: z.string().nullish().describe('系统状态'),
|
|
91
|
+
status: z.string().nullish().describe('状态'),
|
|
92
|
+
statusReason: z.string().nullish().describe('状态原因'),
|
|
93
|
+
statusUpdatedAt: rz.dateTime.nullish().describe('状态更新时间'),
|
|
94
|
+
statusUpdatedById: rz.resourceId.nullish().describe('状态更新人'),
|
|
95
|
+
|
|
96
|
+
createdAt: rz.dateTime.nullish().readonly().describe('创建时间'),
|
|
97
|
+
updatedAt: rz.dateTime.nullish().readonly().describe('更新时间'),
|
|
98
|
+
deletedAt: rz.dateTime.nullish().readonly().describe('删除时间'),
|
|
99
|
+
|
|
100
|
+
createdById: z.string().nullish().readonly().describe('创建人'),
|
|
101
|
+
updatedById: z.string().nullish().readonly().describe('更新人'),
|
|
102
|
+
deletedById: z.string().nullish().readonly().describe('删除人'),
|
|
103
|
+
|
|
104
|
+
attributes: z.record(z.string(), z.any()).nullish().describe('自定义属性'),
|
|
105
|
+
properties: z.record(z.string(), z.any()).nullish().describe('属性'),
|
|
106
|
+
extensions: z.record(z.string(), z.any()).nullish().describe('扩展属性'),
|
|
107
|
+
|
|
108
|
+
__typename: z.coerce.string().nullish().describe('类型'),
|
|
109
|
+
})
|
|
110
|
+
.meta({
|
|
111
|
+
title: 'AnyResource',
|
|
112
|
+
description: '任意资源',
|
|
113
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { AnyResourceSchema } from './AnyResourceSchema';
|
|
2
|
+
|
|
3
|
+
export const BaseResourceSchema = AnyResourceSchema.pick({
|
|
4
|
+
id: true,
|
|
5
|
+
uid: true,
|
|
6
|
+
tid: true,
|
|
7
|
+
eid: true,
|
|
8
|
+
|
|
9
|
+
state: true,
|
|
10
|
+
status: true,
|
|
11
|
+
statusReason: true,
|
|
12
|
+
statusUpdatedAt: true,
|
|
13
|
+
statusUpdatedById: true,
|
|
14
|
+
|
|
15
|
+
createdAt: true,
|
|
16
|
+
updatedAt: true,
|
|
17
|
+
deletedAt: true,
|
|
18
|
+
createdById: true,
|
|
19
|
+
updatedById: true,
|
|
20
|
+
deletedById: true,
|
|
21
|
+
attributes: true,
|
|
22
|
+
properties: true,
|
|
23
|
+
}).required({
|
|
24
|
+
uid: true,
|
|
25
|
+
tid: true,
|
|
26
|
+
state: true,
|
|
27
|
+
status: true,
|
|
28
|
+
createdAt: true,
|
|
29
|
+
updatedAt: true,
|
|
30
|
+
attributes: true,
|
|
31
|
+
properties: true,
|
|
32
|
+
});
|