@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.
Files changed (151) hide show
  1. package/lib/cn/DivisionCode.js +55 -32
  2. package/lib/cn/DivisionCode.test.js +140 -0
  3. package/lib/cn/Mod11Checksum.js +80 -37
  4. package/lib/cn/Mod31Checksum.js +89 -40
  5. package/lib/cn/ResidentIdentityCardNumber.js +16 -16
  6. package/lib/cn/ResidentIdentityCardNumber.test.js +21 -0
  7. package/lib/cn/UnifiedSocialCreditCode.js +32 -26
  8. package/lib/cn/UnifiedSocialCreditCode.test.js +16 -0
  9. package/lib/cn/formatDate.js +5 -7
  10. package/lib/cn/index.js +0 -1
  11. package/lib/cn/parseSex.js +0 -2
  12. package/lib/cn/types.d.js +0 -2
  13. package/lib/consola/createStandardConsolaReporter.js +18 -0
  14. package/lib/consola/formatLogObject.js +226 -0
  15. package/lib/consola/index.js +2 -0
  16. package/lib/data/formatSort.js +5 -6
  17. package/lib/data/formatSort.test.js +34 -0
  18. package/lib/data/index.js +0 -1
  19. package/lib/data/maybeNumber.js +11 -5
  20. package/lib/data/parseSort.js +28 -22
  21. package/lib/data/parseSort.test.js +188 -0
  22. package/lib/data/resolvePagination.js +27 -16
  23. package/lib/data/resolvePagination.test.js +232 -0
  24. package/lib/data/types.d.js +0 -2
  25. package/lib/index.js +0 -1
  26. package/lib/jsonschema/JsonSchema.js +80 -54
  27. package/lib/jsonschema/JsonSchema.test.js +137 -0
  28. package/lib/jsonschema/index.js +0 -1
  29. package/lib/jsonschema/types.d.js +5 -3
  30. package/lib/meta/defineFileType.js +103 -20
  31. package/lib/meta/defineInit.js +250 -31
  32. package/lib/meta/defineMetadata.js +140 -24
  33. package/lib/meta/defineMetadata.test.js +13 -0
  34. package/lib/meta/index.js +0 -1
  35. package/lib/password/PHC.js +87 -63
  36. package/lib/password/PHC.test.js +539 -0
  37. package/lib/password/Password.js +291 -29
  38. package/lib/password/Password.test.js +362 -0
  39. package/lib/password/createArgon2PasswordAlgorithm.js +191 -35
  40. package/lib/password/createBase64PasswordAlgorithm.js +141 -8
  41. package/lib/password/createBcryptPasswordAlgorithm.js +168 -13
  42. package/lib/password/createPBKDF2PasswordAlgorithm.js +228 -46
  43. package/lib/password/createScryptPasswordAlgorithm.js +211 -55
  44. package/lib/password/index.js +0 -1
  45. package/lib/password/server/index.js +0 -1
  46. package/lib/resource/Identifiable.js +1 -0
  47. package/lib/resource/getTitleOfResource.js +10 -0
  48. package/lib/resource/index.js +1 -0
  49. package/lib/resource/schema/AnyResourceSchema.js +89 -0
  50. package/lib/resource/schema/BaseResourceSchema.js +29 -0
  51. package/lib/resource/schema/ResourceActionType.js +118 -0
  52. package/lib/resource/schema/ResourceStatus.js +93 -0
  53. package/lib/resource/schema/ResourceType.js +24 -0
  54. package/lib/resource/schema/SchemaRegistry.js +38 -0
  55. package/lib/resource/schema/SexType.js +10 -0
  56. package/lib/resource/schema/types.js +89 -0
  57. package/lib/resource/schema/types.test.js +14 -0
  58. package/lib/schema/TypeSchema.d.js +1 -0
  59. package/lib/schema/createSchemaData.js +173 -0
  60. package/lib/schema/findJsonSchemaByPath.js +49 -0
  61. package/lib/schema/getSchemaCache.js +11 -0
  62. package/lib/schema/getSchemaOptions.js +24 -0
  63. package/lib/schema/index.js +5 -0
  64. package/lib/schema/toJsonSchema.js +441 -0
  65. package/lib/schema/toJsonSchema.test.js +27 -0
  66. package/lib/schema/validate.js +124 -0
  67. package/lib/search/AdvanceSearch.js +0 -1
  68. package/lib/search/AdvanceSearch.test.js +435 -0
  69. package/lib/search/formatAdvanceSearch.js +41 -27
  70. package/lib/search/index.js +0 -1
  71. package/lib/search/optimizeAdvanceSearch.js +79 -25
  72. package/lib/search/parseAdvanceSearch.js +5 -5
  73. package/lib/search/parser.d.js +0 -2
  74. package/lib/search/parser.js +97 -74
  75. package/lib/search/types.d.js +0 -2
  76. package/lib/tools/generateSchema.js +201 -0
  77. package/lib/tools/renderJsonSchemaToMarkdownDoc.js +143 -55
  78. package/package.json +30 -9
  79. package/src/consola/createStandardConsolaReporter.ts +31 -0
  80. package/src/consola/formatLogObject.ts +171 -0
  81. package/src/consola/index.ts +2 -0
  82. package/src/data/maybeNumber.ts +5 -1
  83. package/src/data/resolvePagination.test.ts +1 -1
  84. package/src/data/resolvePagination.ts +18 -7
  85. package/src/data/types.d.ts +12 -0
  86. package/src/jsonschema/JsonSchema.test.ts +17 -0
  87. package/src/jsonschema/JsonSchema.ts +4 -4
  88. package/src/jsonschema/types.d.ts +63 -12
  89. package/src/resource/Identifiable.ts +3 -0
  90. package/src/resource/getTitleOfResource.tsx +6 -0
  91. package/src/resource/index.ts +3 -0
  92. package/src/resource/schema/AnyResourceSchema.ts +113 -0
  93. package/src/resource/schema/BaseResourceSchema.ts +32 -0
  94. package/src/resource/schema/ResourceActionType.ts +123 -0
  95. package/src/resource/schema/ResourceStatus.ts +94 -0
  96. package/src/resource/schema/ResourceType.ts +25 -0
  97. package/src/resource/schema/SchemaRegistry.ts +42 -0
  98. package/src/resource/schema/SexType.ts +13 -0
  99. package/src/resource/schema/types.test.ts +18 -0
  100. package/src/resource/schema/types.ts +105 -0
  101. package/src/schema/TypeSchema.d.ts +32 -0
  102. package/src/schema/createSchemaData.ts +81 -0
  103. package/src/schema/findJsonSchemaByPath.ts +37 -0
  104. package/src/schema/getSchemaCache.ts +21 -0
  105. package/src/schema/getSchemaOptions.ts +24 -0
  106. package/src/schema/index.ts +6 -0
  107. package/src/schema/toJsonSchema.test.ts +37 -0
  108. package/src/schema/toJsonSchema.ts +200 -0
  109. package/src/schema/validate.ts +135 -0
  110. package/src/tools/generateSchema.ts +39 -0
  111. package/lib/cn/DivisionCode.js.map +0 -1
  112. package/lib/cn/Mod11Checksum.js.map +0 -1
  113. package/lib/cn/Mod31Checksum.js.map +0 -1
  114. package/lib/cn/ResidentIdentityCardNumber.js.map +0 -1
  115. package/lib/cn/UnifiedSocialCreditCode.js.map +0 -1
  116. package/lib/cn/formatDate.js.map +0 -1
  117. package/lib/cn/index.js.map +0 -1
  118. package/lib/cn/parseSex.js.map +0 -1
  119. package/lib/cn/types.d.js.map +0 -1
  120. package/lib/data/formatSort.js.map +0 -1
  121. package/lib/data/index.js.map +0 -1
  122. package/lib/data/maybeNumber.js.map +0 -1
  123. package/lib/data/parseSort.js.map +0 -1
  124. package/lib/data/resolvePagination.js.map +0 -1
  125. package/lib/data/types.d.js.map +0 -1
  126. package/lib/index.js.map +0 -1
  127. package/lib/jsonschema/JsonSchema.js.map +0 -1
  128. package/lib/jsonschema/index.js.map +0 -1
  129. package/lib/jsonschema/types.d.js.map +0 -1
  130. package/lib/meta/defineFileType.js.map +0 -1
  131. package/lib/meta/defineInit.js.map +0 -1
  132. package/lib/meta/defineMetadata.js.map +0 -1
  133. package/lib/meta/index.js.map +0 -1
  134. package/lib/password/PHC.js.map +0 -1
  135. package/lib/password/Password.js.map +0 -1
  136. package/lib/password/createArgon2PasswordAlgorithm.js.map +0 -1
  137. package/lib/password/createBase64PasswordAlgorithm.js.map +0 -1
  138. package/lib/password/createBcryptPasswordAlgorithm.js.map +0 -1
  139. package/lib/password/createPBKDF2PasswordAlgorithm.js.map +0 -1
  140. package/lib/password/createScryptPasswordAlgorithm.js.map +0 -1
  141. package/lib/password/index.js.map +0 -1
  142. package/lib/password/server/index.js.map +0 -1
  143. package/lib/search/AdvanceSearch.js.map +0 -1
  144. package/lib/search/formatAdvanceSearch.js.map +0 -1
  145. package/lib/search/index.js.map +0 -1
  146. package/lib/search/optimizeAdvanceSearch.js.map +0 -1
  147. package/lib/search/parseAdvanceSearch.js.map +0 -1
  148. package/lib/search/parser.d.js.map +0 -1
  149. package/lib/search/parser.js.map +0 -1
  150. package/lib/search/types.d.js.map +0 -1
  151. 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
+ }
@@ -0,0 +1,2 @@
1
+ export { formatLogObject } from './formatLogObject';
2
+ export { createStandardConsolaReporter } from './createStandardConsolaReporter';
@@ -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
- if (v === '') {
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' }, { maxPageSize: 50 })).toMatchObject({ pageSize: 50, limit: 50 });
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 { clamp, mapValues, omitBy, pick } from 'es-toolkit';
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
- maxPageSize?: number;
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 ??= 20;
42
- pageSize = clamp(pageSize, 1, options.maxPageSize ?? 100);
42
+ pageSize ??= resolvePagination.pageSize;
43
43
 
44
- let { pageNumber = 1, pageIndex, limit, offset } = out;
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
- limit = Math.max(1, limit ?? pageSize);
49
- offset = Math.max(0, offset ?? pageSize * pageIndex);
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;
@@ -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 const createAjv = _createAjv;
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.select() }, (v) => v)
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
- | 'http://json-schema.org/draft-07/schema#' //
23
- | 'http://json-schema.org/draft-07/hyper-schema#';
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
- export type JsonSchemaDef = {
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
- additionalProperties?: JsonSchemaDef;
109
- dependencies?: { [key: string]: JsonSchemaDef | string[] };
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,3 @@
1
+ export type Identifiable = {
2
+ id: string;
3
+ };
@@ -0,0 +1,6 @@
1
+ export function getTitleOfResource(res?: any): string | undefined {
2
+ if (res && typeof res === 'object') {
3
+ return res.displayName || res.title || res.fullName || res.loginName || res.topic || res.code;
4
+ }
5
+ return undefined;
6
+ }
@@ -0,0 +1,3 @@
1
+ export { type AnyResource } from './schema/AnyResourceSchema';
2
+ export { type Identifiable } from './Identifiable';
3
+ export { getTitleOfResource } from './getTitleOfResource';
@@ -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
+ });