@tstdl/base 0.93.51 → 0.93.53
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/browser/utils.js +2 -2
- package/object-storage/google/google.object-storage-provider.d.ts +44 -0
- package/object-storage/google/google.object-storage-provider.js +81 -0
- package/object-storage/google/google.object-storage.d.ts +32 -0
- package/object-storage/google/google.object-storage.js +220 -0
- package/object-storage/google/google.object.d.ts +16 -0
- package/object-storage/google/google.object.js +69 -0
- package/object-storage/google/index.d.ts +3 -0
- package/object-storage/google/index.js +3 -0
- package/object-storage/object-storage.d.ts +7 -4
- package/object-storage/s3/s3.object-storage-provider.js +1 -1
- package/object-storage/s3/s3.object-storage.d.ts +3 -3
- package/object-storage/s3/s3.object-storage.js +21 -11
- package/object-storage/s3/s3.object.d.ts +1 -1
- package/object-storage/s3/s3.object.js +1 -1
- package/orm/sqls.js +3 -3
- package/package.json +2 -2
- package/schema/converters/zod-converter.d.ts +8 -2
- package/schema/converters/zod-converter.js +182 -136
- package/schema/schemas/bigint.js +2 -2
- package/schema/schemas/boolean.d.ts +1 -0
- package/schema/schemas/boolean.js +16 -16
- package/schema/schemas/constraint.d.ts +19 -0
- package/schema/schemas/constraint.js +36 -0
- package/schema/schemas/index.d.ts +1 -0
- package/schema/schemas/index.js +1 -0
- package/schema/schemas/object.d.ts +3 -1
- package/schema/schemas/object.js +54 -28
- package/schema/schemas/simple.d.ts +6 -1
- package/schema/schemas/simple.js +11 -2
- package/schema/schemas/string.d.ts +4 -2
- package/schema/schemas/string.js +10 -5
- package/schema/schemas/transform.d.ts +4 -0
- package/schema/schemas/transform.js +4 -0
- package/schema/schemas/uint8-array.d.ts +1 -0
- package/schema/schemas/uint8-array.js +10 -7
- package/templates/template.model.js +2 -2
- package/test6.js +112 -29
- package/types/types.d.ts +1 -1
- package/utils/base64.js +1 -2
- package/utils/encoding.js +2 -5
- package/utils/format-error.js +1 -1
- package/utils/object/object.js +7 -1
- package/utils/z-base32.js +2 -4
package/schema/schemas/object.js
CHANGED
|
@@ -2,8 +2,8 @@ import { createDecorator, reflectionRegistry } from '../../reflection/index.js';
|
|
|
2
2
|
import { SchemaError } from '../../schema/schema.error.js';
|
|
3
3
|
import { toArray } from '../../utils/array/array.js';
|
|
4
4
|
import { memoizeSingle } from '../../utils/function/memoize.js';
|
|
5
|
-
import { filterObject, fromEntries, mapObjectValues,
|
|
6
|
-
import { assert, isArray, isDefined, isFunction,
|
|
5
|
+
import { filterObject, fromEntries, mapObjectValues, objectEntries } from '../../utils/object/object.js';
|
|
6
|
+
import { assert, isArray, isDefined, isFunction, isNotFunction, isNotNull, isNotNullOrUndefined, isNull, isObject, isUndefined } from '../../utils/type-guards.js';
|
|
7
7
|
import { typeOf } from '../../utils/type-of.js';
|
|
8
8
|
import { Class, PropertySchema } from '../decorators/index.js';
|
|
9
9
|
import { schemaReflectionDataToSchema } from '../decorators/utils.js';
|
|
@@ -13,8 +13,10 @@ import { getFunctionSchemaFromReflection } from './function.js';
|
|
|
13
13
|
import { optional } from './optional.js';
|
|
14
14
|
export const tryGetSchemaFromReflection = memoizeSingle(_tryGetSchemaFromReflection, { weak: true });
|
|
15
15
|
export class ObjectSchema extends Schema {
|
|
16
|
-
|
|
16
|
+
propertyEntries;
|
|
17
|
+
knownPropertyKeys;
|
|
17
18
|
allowUnknownProperties;
|
|
19
|
+
resultValueConstructor;
|
|
18
20
|
name;
|
|
19
21
|
properties;
|
|
20
22
|
mask;
|
|
@@ -33,7 +35,9 @@ export class ObjectSchema extends Schema {
|
|
|
33
35
|
this.maximumPropertiesCount = options.maximumPropertiesCount ?? null;
|
|
34
36
|
this.factory = options.factory ?? null;
|
|
35
37
|
this.allowUnknownProperties = isNotNull(this.unknownProperties) || isNotNull(this.unknownPropertiesKey);
|
|
36
|
-
this.
|
|
38
|
+
this.propertyEntries = objectEntries(this.properties);
|
|
39
|
+
this.knownPropertyKeys = new Set(this.propertyEntries.map((entry) => entry[0]));
|
|
40
|
+
this.resultValueConstructor = (isNotNullOrUndefined(options.factory) && isNotFunction(options.factory)) ? () => new options.factory.type() : () => ({});
|
|
37
41
|
this.name = options.name ?? 'Object';
|
|
38
42
|
}
|
|
39
43
|
_test(value, path, options) {
|
|
@@ -41,41 +45,63 @@ export class ObjectSchema extends Schema {
|
|
|
41
45
|
return { valid: false, error: SchemaError.expectedButGot('object', typeOf(value), path) };
|
|
42
46
|
}
|
|
43
47
|
const mask = this.mask ?? options.mask ?? false;
|
|
44
|
-
const resultValue =
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
+
const resultValue = this.resultValueConstructor();
|
|
49
|
+
const requiresInputKeyScan = isNotNull(this.minimumPropertiesCount) || isNotNull(this.maximumPropertiesCount) || (!mask && !this.allowUnknownProperties) || this.allowUnknownProperties;
|
|
50
|
+
let inputKeys = null;
|
|
51
|
+
let inputKeyCount = 0;
|
|
52
|
+
if (requiresInputKeyScan) {
|
|
53
|
+
inputKeys = Object.keys(value);
|
|
54
|
+
inputKeyCount = inputKeys.length;
|
|
48
55
|
}
|
|
49
|
-
if (isNotNull(this.
|
|
50
|
-
return { valid: false, error: new SchemaError(`Object has too
|
|
56
|
+
if (isNotNull(this.minimumPropertiesCount) && (inputKeyCount < this.minimumPropertiesCount)) {
|
|
57
|
+
return { valid: false, error: new SchemaError(`Object has too few properties (minimum ${this.minimumPropertiesCount}, got ${inputKeyCount}).`, { path, fast: options.fastErrors }) };
|
|
51
58
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
if ((unknownValuePropertyKeys.size > 0) && !mask && !this.allowUnknownProperties) {
|
|
55
|
-
return { valid: false, error: new SchemaError('Unexpected property', { path: path.add(unknownValuePropertyKeys.values().next().value), fast: options.fastErrors }) };
|
|
59
|
+
if (isNotNull(this.maximumPropertiesCount) && (inputKeyCount > this.maximumPropertiesCount)) {
|
|
60
|
+
return { valid: false, error: new SchemaError(`Object has too many properties (maximum ${this.maximumPropertiesCount}, got ${inputKeyCount}).`, { path, fast: options.fastErrors }) };
|
|
56
61
|
}
|
|
57
|
-
|
|
58
|
-
|
|
62
|
+
let matchedInputKeysCount = 0;
|
|
63
|
+
const propertyEntriesLength = this.propertyEntries.length;
|
|
64
|
+
for (let i = 0; i < propertyEntriesLength; i++) {
|
|
65
|
+
const entry = this.propertyEntries[i];
|
|
66
|
+
const key = entry[0];
|
|
67
|
+
const propertySchema = entry[1];
|
|
68
|
+
if (requiresInputKeyScan && Object.hasOwn(value, key)) {
|
|
69
|
+
matchedInputKeysCount++;
|
|
70
|
+
}
|
|
71
|
+
const propertyResult = propertySchema._test(value[key], path.add(key), options);
|
|
59
72
|
if (!propertyResult.valid) {
|
|
60
73
|
return propertyResult;
|
|
61
74
|
}
|
|
62
75
|
resultValue[key] = propertyResult.value;
|
|
63
76
|
}
|
|
64
|
-
if (
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
77
|
+
if (requiresInputKeyScan && (matchedInputKeysCount < inputKeyCount)) {
|
|
78
|
+
if (mask && !this.allowUnknownProperties) {
|
|
79
|
+
// Do nothing (Masking)
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
const keyLength = inputKeys.length;
|
|
83
|
+
for (let i = 0; i < keyLength; i++) {
|
|
84
|
+
const key = inputKeys[i];
|
|
85
|
+
if (this.knownPropertyKeys.has(key)) {
|
|
70
86
|
continue;
|
|
71
87
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
88
|
+
const propertyPath = path.add(key);
|
|
89
|
+
if (!this.allowUnknownProperties) {
|
|
90
|
+
return { valid: false, error: new SchemaError('Unexpected property', { path: propertyPath, fast: options.fastErrors }) };
|
|
91
|
+
}
|
|
92
|
+
const keyResult = this.unknownPropertiesKey?._test(key, propertyPath, options) ?? { valid: true };
|
|
93
|
+
if (!keyResult.valid) {
|
|
94
|
+
if (mask) {
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
return { valid: false, error: new SchemaError('Invalid property key.', { path: propertyPath, inner: keyResult.error, fast: options.fastErrors }) };
|
|
98
|
+
}
|
|
99
|
+
const innerValueResult = this.unknownProperties?._test(value[key], propertyPath, options) ?? { valid: true, value: value[key] };
|
|
100
|
+
if (!innerValueResult.valid) {
|
|
101
|
+
return innerValueResult;
|
|
102
|
+
}
|
|
103
|
+
resultValue[key] = innerValueResult.value;
|
|
77
104
|
}
|
|
78
|
-
resultValue[key] = innerValueResult.value;
|
|
79
105
|
}
|
|
80
106
|
}
|
|
81
107
|
const testResultValue = isFunction(this.factory) ? this.factory(resultValue) : resultValue;
|
|
@@ -6,7 +6,7 @@ export type SimpleSchemaOptions<T> = SchemaOptions<T> & Coercible;
|
|
|
6
6
|
type SimpleSchemaRefinements<T> = {
|
|
7
7
|
coercers?: SimpleSchemaCoercers<T>;
|
|
8
8
|
constraints?: (SimpleSchemaConstraint<T> | null)[];
|
|
9
|
-
transformers?: SimpleSchemaTransformer<T>;
|
|
9
|
+
transformers?: OneOrMany<SimpleSchemaTransformer<T> | null>;
|
|
10
10
|
gotValueFormatter?: SimpleSchemaGotValueFormatter;
|
|
11
11
|
};
|
|
12
12
|
export type SimpleSchemaCoercers<T> = {
|
|
@@ -23,6 +23,11 @@ export type SimpleSchemaGotValueFormatter = (value: unknown) => string;
|
|
|
23
23
|
export type SimpleSchemaTransformer<T> = (value: T) => T;
|
|
24
24
|
export declare abstract class SimpleSchema<T> extends Schema<T> {
|
|
25
25
|
#private;
|
|
26
|
+
/**
|
|
27
|
+
* Whether to coerce wrong input into desired output if possible.
|
|
28
|
+
* If null, uses the option at validation time.
|
|
29
|
+
*/
|
|
30
|
+
readonly coerce: boolean | null;
|
|
26
31
|
constructor(expected: OneOrMany<string | AbstractConstructor>, guardFn: (value: any) => value is T, options?: SimpleSchemaOptions<T>, refinements?: SimpleSchemaRefinements<T>);
|
|
27
32
|
_test(value: any, path: JsonPath, options: SchemaTestOptions): SchemaTestResult<T>;
|
|
28
33
|
}
|
package/schema/schemas/simple.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { toArray } from '../../utils/array/array.js';
|
|
1
2
|
import { isDefined, isError, isNotNull, isNull, isUndefined } from '../../utils/type-guards.js';
|
|
2
3
|
import { typeOf } from '../../utils/type-of.js';
|
|
3
4
|
import { SchemaError } from '../schema.error.js';
|
|
@@ -10,6 +11,11 @@ export class SimpleSchema extends Schema {
|
|
|
10
11
|
#constraints;
|
|
11
12
|
#transformers;
|
|
12
13
|
#gotValueFormatter;
|
|
14
|
+
/**
|
|
15
|
+
* Whether to coerce wrong input into desired output if possible.
|
|
16
|
+
* If null, uses the option at validation time.
|
|
17
|
+
*/
|
|
18
|
+
coerce;
|
|
13
19
|
constructor(expected, guardFn, options = {}, refinements = {}) {
|
|
14
20
|
super(options);
|
|
15
21
|
this.#expected = expected;
|
|
@@ -17,8 +23,9 @@ export class SimpleSchema extends Schema {
|
|
|
17
23
|
this.#options = options;
|
|
18
24
|
this.#coercers = refinements.coercers ?? {};
|
|
19
25
|
this.#constraints = refinements.constraints?.filter(isNotNull) ?? [];
|
|
20
|
-
this.#transformers = refinements.transformers
|
|
26
|
+
this.#transformers = isDefined(refinements.transformers) ? toArray(refinements.transformers).filter(isNotNull) : [];
|
|
21
27
|
this.#gotValueFormatter = refinements.gotValueFormatter ?? typeOf;
|
|
28
|
+
this.coerce = options.coerce ?? null;
|
|
22
29
|
}
|
|
23
30
|
_test(value, path, options) {
|
|
24
31
|
let result;
|
|
@@ -45,6 +52,9 @@ export class SimpleSchema extends Schema {
|
|
|
45
52
|
if (isUndefined(result)) {
|
|
46
53
|
return { valid: false, error: SchemaError.expectedButGot(this.#expected, this.#gotValueFormatter(value), path, { fast: options.fastErrors }) };
|
|
47
54
|
}
|
|
55
|
+
for (const transformer of this.#transformers) {
|
|
56
|
+
result.value = transformer(result.value);
|
|
57
|
+
}
|
|
48
58
|
for (const constraint of this.#constraints) {
|
|
49
59
|
const constraintResult = constraint(result.value);
|
|
50
60
|
if (!constraintResult.success) {
|
|
@@ -54,7 +64,6 @@ export class SimpleSchema extends Schema {
|
|
|
54
64
|
return { valid: false, error: new SchemaError(`Constraint validation failed: ${constraintResult.error}`, { path, fast: options.fastErrors }) };
|
|
55
65
|
}
|
|
56
66
|
}
|
|
57
|
-
result.value = this.#transformers(result.value);
|
|
58
67
|
return result;
|
|
59
68
|
}
|
|
60
69
|
}
|
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
import { type SchemaDecoratorOptions, type SchemaPropertyDecorator } from '../decorators/index.js';
|
|
2
2
|
import { SimpleSchema, type SimpleSchemaOptions } from './simple.js';
|
|
3
3
|
export type StringSchemaOptions = SimpleSchemaOptions<string> & {
|
|
4
|
-
|
|
4
|
+
trim?: boolean;
|
|
5
5
|
lowercase?: boolean;
|
|
6
|
+
pattern?: RegExp | string;
|
|
6
7
|
minimumLength?: number;
|
|
7
8
|
maximumLength?: number;
|
|
8
9
|
};
|
|
9
10
|
export declare class StringSchema extends SimpleSchema<string> {
|
|
10
11
|
readonly name: string;
|
|
11
|
-
readonly
|
|
12
|
+
readonly trim: boolean;
|
|
12
13
|
readonly lowercase: boolean;
|
|
13
14
|
readonly minimumLength: number | null;
|
|
14
15
|
readonly maximumLength: number | null;
|
|
16
|
+
readonly pattern: RegExp | null;
|
|
15
17
|
constructor(options?: StringSchemaOptions);
|
|
16
18
|
}
|
|
17
19
|
export declare function string(options?: StringSchemaOptions): StringSchema;
|
package/schema/schemas/string.js
CHANGED
|
@@ -3,10 +3,11 @@ import { PropertySchema } from '../decorators/index.js';
|
|
|
3
3
|
import { SimpleSchema } from './simple.js';
|
|
4
4
|
export class StringSchema extends SimpleSchema {
|
|
5
5
|
name = 'string';
|
|
6
|
-
|
|
6
|
+
trim;
|
|
7
7
|
lowercase;
|
|
8
8
|
minimumLength;
|
|
9
9
|
maximumLength;
|
|
10
|
+
pattern;
|
|
10
11
|
constructor(options) {
|
|
11
12
|
super('string', isString, options, {
|
|
12
13
|
coercers: {
|
|
@@ -15,17 +16,20 @@ export class StringSchema extends SimpleSchema {
|
|
|
15
16
|
bigint: (value) => ({ success: true, value: globalThis.String(value), valid: true }),
|
|
16
17
|
},
|
|
17
18
|
constraints: [
|
|
18
|
-
isDefined(options?.pattern)
|
|
19
|
-
? (value) => this.pattern.test(value) ? ({ success: true }) : ({ success: false, error: 'Value did not match pattern.' })
|
|
20
|
-
: null,
|
|
21
19
|
isDefined(options?.minimumLength)
|
|
22
20
|
? (value) => (value.length >= this.minimumLength) ? ({ success: true }) : ({ success: false, error: `String length is less than minimum length of ${this.minimumLength}.` })
|
|
23
21
|
: null,
|
|
24
22
|
isDefined(options?.maximumLength)
|
|
25
23
|
? (value) => (value.length <= this.maximumLength) ? ({ success: true }) : ({ success: false, error: `String length exceeds maximum length of ${this.maximumLength}.` })
|
|
26
24
|
: null,
|
|
25
|
+
isDefined(options?.pattern)
|
|
26
|
+
? (value) => this.pattern.test(value) ? ({ success: true }) : ({ success: false, error: 'Value did not match pattern.' })
|
|
27
|
+
: null,
|
|
28
|
+
],
|
|
29
|
+
transformers: [
|
|
30
|
+
(options?.trim ?? false) ? (value) => value.trim() : null,
|
|
31
|
+
(options?.lowercase ?? false) ? (value) => value.toLowerCase() : null,
|
|
27
32
|
],
|
|
28
|
-
transformers: (options?.lowercase ?? false) ? (value) => value.toLowerCase() : undefined,
|
|
29
33
|
});
|
|
30
34
|
this.pattern = isString(options?.pattern)
|
|
31
35
|
? RegExp(options.pattern, 'u')
|
|
@@ -33,6 +37,7 @@ export class StringSchema extends SimpleSchema {
|
|
|
33
37
|
? options.pattern
|
|
34
38
|
: null;
|
|
35
39
|
this.lowercase = options?.lowercase ?? false;
|
|
40
|
+
this.trim = options?.trim ?? false;
|
|
36
41
|
this.minimumLength = options?.minimumLength ?? null;
|
|
37
42
|
this.maximumLength = options?.maximumLength ?? null;
|
|
38
43
|
}
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import type { JsonPath } from '../../json-path/json-path.js';
|
|
2
2
|
import { Schema, type SchemaTestable, type SchemaTestOptions, type SchemaTestResult } from '../schema.js';
|
|
3
|
+
/**
|
|
4
|
+
* Schema that transforms input of type I to output of type O.
|
|
5
|
+
* Validates input using the provided schema before transformation.
|
|
6
|
+
*/
|
|
3
7
|
export declare class TransformSchema<I, O> extends Schema<O> {
|
|
4
8
|
readonly name: string;
|
|
5
9
|
readonly schema: Schema<I>;
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { lazyProperty } from '../../utils/object/lazy-property.js';
|
|
2
2
|
import { Schema } from '../schema.js';
|
|
3
3
|
import { schemaTestableToSchema } from '../testable.js';
|
|
4
|
+
/**
|
|
5
|
+
* Schema that transforms input of type I to output of type O.
|
|
6
|
+
* Validates input using the provided schema before transformation.
|
|
7
|
+
*/
|
|
4
8
|
export class TransformSchema extends Schema {
|
|
5
9
|
name;
|
|
6
10
|
schema;
|
|
@@ -14,6 +14,7 @@ export declare class Uint8ArraySchema extends SimpleSchema<Uint8Array> {
|
|
|
14
14
|
readonly maximumLength: number | null;
|
|
15
15
|
readonly coerceType: 'base64' | 'base64url' | 'zbase32' | 'hex' | null;
|
|
16
16
|
constructor(options?: Uint8ArraySchemaOptions);
|
|
17
|
+
static coerce(value: string, type: 'base64' | 'base64url' | 'zbase32' | 'hex'): Uint8Array;
|
|
17
18
|
}
|
|
18
19
|
export declare function uint8Array(options?: Uint8ArraySchemaOptions): Uint8ArraySchema;
|
|
19
20
|
export declare function Uint8ArrayProperty(options?: Uint8ArraySchemaOptions & SchemaDecoratorOptions): SchemaPropertyDecorator;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { match } from 'ts-pattern';
|
|
2
1
|
import { AssertionError } from '../../errors/index.js';
|
|
3
2
|
import { decodeBase64, decodeBase64Url } from '../../utils/base64.js';
|
|
4
3
|
import { decodeHex } from '../../utils/encoding.js';
|
|
@@ -18,12 +17,7 @@ export class Uint8ArraySchema extends SimpleSchema {
|
|
|
18
17
|
string: isDefined(options?.coerceType)
|
|
19
18
|
? (value, path, options) => {
|
|
20
19
|
try {
|
|
21
|
-
const coerced =
|
|
22
|
-
.with('base64', () => decodeBase64(value))
|
|
23
|
-
.with('base64url', () => decodeBase64Url(value))
|
|
24
|
-
.with('zbase32', () => zBase32Decode(value))
|
|
25
|
-
.with('hex', () => decodeHex(value))
|
|
26
|
-
.exhaustive();
|
|
20
|
+
const coerced = Uint8ArraySchema.coerce(value, this.coerceType);
|
|
27
21
|
return { success: true, valid: true, value: coerced };
|
|
28
22
|
}
|
|
29
23
|
catch (error) {
|
|
@@ -44,6 +38,15 @@ export class Uint8ArraySchema extends SimpleSchema {
|
|
|
44
38
|
this.maximumLength = options?.maximumLength ?? null;
|
|
45
39
|
this.coerceType = options?.coerceType ?? null;
|
|
46
40
|
}
|
|
41
|
+
static coerce(value, type) {
|
|
42
|
+
switch (type) {
|
|
43
|
+
case 'base64': return decodeBase64(value);
|
|
44
|
+
case 'base64url': return decodeBase64Url(value);
|
|
45
|
+
case 'zbase32': return zBase32Decode(value);
|
|
46
|
+
case 'hex': return decodeHex(value);
|
|
47
|
+
default: throw new AssertionError(`Unsupported coerce type: ${type}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
47
50
|
}
|
|
48
51
|
export function uint8Array(options) {
|
|
49
52
|
return new Uint8ArraySchema(options);
|
package/test6.js
CHANGED
|
@@ -1,38 +1,121 @@
|
|
|
1
|
-
|
|
2
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
-
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
-
};
|
|
1
|
+
import z from 'zod';
|
|
7
2
|
import { Application } from './application/application.js';
|
|
8
3
|
import { provideModule, provideSignalHandler } from './application/providers.js';
|
|
9
|
-
import { NotImplementedError } from './errors/not-implemented.error.js';
|
|
10
|
-
import { Singleton } from './injector/decorators.js';
|
|
11
|
-
import { inject } from './injector/index.js';
|
|
12
|
-
import { injectionToken } from './injector/token.js';
|
|
13
4
|
import { PrettyPrintLogFormatter, provideConsoleLogTransport } from './logger/index.js';
|
|
14
|
-
import {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
5
|
+
import { array, boolean, literal, number, object, optional, string, union } from './schema/index.js';
|
|
6
|
+
import { benchmark } from './utils/benchmark.js';
|
|
7
|
+
function logResult(name, tstdl, zod) {
|
|
8
|
+
const faster = tstdl.millisecondsPerOperation < zod.millisecondsPerOperation ? 'TSTDL' : 'Zod';
|
|
9
|
+
const factor = tstdl.millisecondsPerOperation < zod.millisecondsPerOperation ? zod.millisecondsPerOperation / tstdl.millisecondsPerOperation : tstdl.millisecondsPerOperation / zod.millisecondsPerOperation;
|
|
10
|
+
console.log(`\n--- ${name} ---`);
|
|
11
|
+
console.log(`TSTDL: ${tstdl.operationsPerMillisecond.toFixed(2)} ops/ms`);
|
|
12
|
+
console.log(`Zod: ${zod.operationsPerMillisecond.toFixed(2)} ops/ms`);
|
|
13
|
+
console.log(`Result: ${faster} is ${factor.toFixed(2)}x faster`);
|
|
14
|
+
}
|
|
15
|
+
// --- SETUP SCHEMAS ---
|
|
16
|
+
// 1. Primitive Schema
|
|
17
|
+
const tstdlString = string();
|
|
18
|
+
const zodString = z.string();
|
|
19
|
+
// 2. Simple Object
|
|
20
|
+
const tstdlUser = object({
|
|
21
|
+
name: string(),
|
|
22
|
+
age: number(),
|
|
23
|
+
isActive: boolean(),
|
|
24
|
+
});
|
|
25
|
+
const zodUser = z.object({
|
|
26
|
+
name: z.string(),
|
|
27
|
+
age: z.number(),
|
|
28
|
+
isActive: z.boolean(),
|
|
29
|
+
});
|
|
30
|
+
// 3. Complex Nested Schema
|
|
31
|
+
const tstdlComplex = object({
|
|
32
|
+
id: number(),
|
|
33
|
+
tags: array(string()),
|
|
34
|
+
metadata: optional(object({
|
|
35
|
+
createdAt: number(),
|
|
36
|
+
source: union(literal('web'), literal('mobile')),
|
|
37
|
+
})),
|
|
38
|
+
});
|
|
39
|
+
const zodComplex = z.object({
|
|
40
|
+
id: z.number(),
|
|
41
|
+
tags: z.array(z.string()),
|
|
42
|
+
metadata: z.object({
|
|
43
|
+
createdAt: z.number(),
|
|
44
|
+
source: z.union([z.literal('web'), z.literal('mobile')]),
|
|
45
|
+
}).optional(),
|
|
46
|
+
});
|
|
47
|
+
// 4. Large Array
|
|
48
|
+
const tstdlArray = array(number());
|
|
49
|
+
const zodArray = z.array(z.number());
|
|
50
|
+
// --- DATA ---
|
|
51
|
+
const validString = 'Hello World';
|
|
52
|
+
const validUser = {
|
|
53
|
+
name: 'Alice',
|
|
54
|
+
age: 30,
|
|
55
|
+
isActive: true,
|
|
56
|
+
};
|
|
57
|
+
const validComplex = {
|
|
58
|
+
id: 101,
|
|
59
|
+
tags: ['admin', 'staff', 'verified'],
|
|
60
|
+
metadata: {
|
|
61
|
+
createdAt: 1678900000,
|
|
62
|
+
source: 'web',
|
|
63
|
+
},
|
|
19
64
|
};
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
})
|
|
26
|
-
], Foo);
|
|
27
|
-
let Bar = class Bar {
|
|
28
|
-
myInteger = inject(MY_INTEGER);
|
|
65
|
+
const largeArrayData = Array.from({ length: 1000 }, (_, i) => i);
|
|
66
|
+
const invalidUser = {
|
|
67
|
+
name: 'Bob',
|
|
68
|
+
age: '30', // Invalid: string instead of number
|
|
69
|
+
isActive: true,
|
|
29
70
|
};
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
71
|
+
// --- RUN BENCHMARKS ---
|
|
72
|
+
console.log('Starting Benchmarks...');
|
|
73
|
+
// 1. Primitive Parsing
|
|
74
|
+
const ITERATIONS_PRIMITIVE = 1_000_000;
|
|
75
|
+
const zodPrim = benchmark(ITERATIONS_PRIMITIVE, () => zodString.parse(validString));
|
|
76
|
+
const tstdlPrim = benchmark(ITERATIONS_PRIMITIVE, () => tstdlString.parse(validString));
|
|
77
|
+
logResult(`Primitive String Parse (${ITERATIONS_PRIMITIVE} ops)`, tstdlPrim, zodPrim);
|
|
78
|
+
// 2. Simple Object Parsing
|
|
79
|
+
const ITERATIONS_OBJ = 500_000;
|
|
80
|
+
const zodObj = benchmark(ITERATIONS_OBJ, () => zodUser.parse(validUser));
|
|
81
|
+
const tstdlObj = benchmark(ITERATIONS_OBJ, () => tstdlUser.parse(validUser));
|
|
82
|
+
logResult(`Simple Object Parse (${ITERATIONS_OBJ} ops)`, tstdlObj, zodObj);
|
|
83
|
+
// 3. Complex Nested Object Parsing
|
|
84
|
+
const ITERATIONS_COMPLEX = 200_000;
|
|
85
|
+
const zodCmplx = benchmark(ITERATIONS_COMPLEX, () => zodComplex.parse(validComplex));
|
|
86
|
+
const tstdlCmplx = benchmark(ITERATIONS_COMPLEX, () => tstdlComplex.parse(validComplex));
|
|
87
|
+
logResult(`Complex Object Parse (${ITERATIONS_COMPLEX} ops)`, tstdlCmplx, zodCmplx);
|
|
88
|
+
// 4. Large Array Parsing
|
|
89
|
+
const ITERATIONS_ARRAY = 5_000;
|
|
90
|
+
const zodArr = benchmark(ITERATIONS_ARRAY, () => zodArray.parse(largeArrayData));
|
|
91
|
+
const tstdlArr = benchmark(ITERATIONS_ARRAY, () => tstdlArray.parse(largeArrayData));
|
|
92
|
+
logResult(`Large Array (1k items) Parse (${ITERATIONS_ARRAY} ops)`, tstdlArr, zodArr);
|
|
93
|
+
// 5. Error Handling (Exceptions)
|
|
94
|
+
// Note: TSTDL creates stack traces by default, Zod does not.
|
|
95
|
+
const ITERATIONS_ERROR = 100_000;
|
|
96
|
+
const tstdlErr = benchmark(ITERATIONS_ERROR, () => {
|
|
97
|
+
try {
|
|
98
|
+
tstdlUser.parse(invalidUser);
|
|
99
|
+
}
|
|
100
|
+
catch (e) { }
|
|
101
|
+
});
|
|
102
|
+
const zodErr = benchmark(ITERATIONS_ERROR, () => {
|
|
103
|
+
try {
|
|
104
|
+
zodUser.parse(invalidUser);
|
|
105
|
+
}
|
|
106
|
+
catch (e) { }
|
|
107
|
+
});
|
|
108
|
+
logResult(`Error Handling / Throwing (${ITERATIONS_ERROR} ops)`, tstdlErr, zodErr);
|
|
109
|
+
// 6. Error Handling (TSTDL Fast Errors vs Zod)
|
|
110
|
+
// TSTDL has a 'fastErrors' option which might improve performance by skipping stack traces
|
|
111
|
+
const tstdlErrFast = benchmark(ITERATIONS_ERROR, () => {
|
|
112
|
+
try {
|
|
113
|
+
tstdlUser.parse(invalidUser, { fastErrors: true });
|
|
114
|
+
}
|
|
115
|
+
catch (e) { }
|
|
116
|
+
});
|
|
117
|
+
logResult(`Error Handling (TSTDL fastErrors=true) (${ITERATIONS_ERROR} ops)`, tstdlErrFast, zodErr);
|
|
33
118
|
function main() {
|
|
34
|
-
const foo = inject(Foo);
|
|
35
|
-
console.log(foo);
|
|
36
119
|
}
|
|
37
120
|
Application.run('Test', [
|
|
38
121
|
provideModule(main),
|
package/types/types.d.ts
CHANGED
|
@@ -46,7 +46,7 @@ export type UndefinableJsonArray = UndefinableJsonInnerNode[] | readonly Undefin
|
|
|
46
46
|
export type ArrayItem<T extends readonly any[]> = T extends readonly (infer U)[] ? U : never;
|
|
47
47
|
export type Enumeration = EnumerationArray | EnumerationObject;
|
|
48
48
|
export type EnumerationArray = readonly [string | number, ...(string | number)[]];
|
|
49
|
-
export type EnumerationObject<V extends string | number = string | number> = Record<string, V
|
|
49
|
+
export type EnumerationObject<V extends string | number = string | number> = Readonly<Record<string, V>>;
|
|
50
50
|
export type EnumerationKey<T extends EnumerationObject = EnumerationObject> = Extract<keyof T, string>;
|
|
51
51
|
export type EnumerationMap<T extends EnumerationObject = EnumerationObject> = SimplifyObject<{
|
|
52
52
|
[P in EnumerationKey<T>]: (T[P] extends number ? (`${T[P]}` extends `${infer U extends number}` ? U : never) : T[P]) | T[P];
|
package/utils/base64.js
CHANGED
|
@@ -23,8 +23,7 @@ export function encodeBase64(array, bytesOffset, bytesLength) {
|
|
|
23
23
|
}
|
|
24
24
|
export function decodeBase64(base64) {
|
|
25
25
|
if (supportsBuffer) {
|
|
26
|
-
|
|
27
|
-
return buffer;
|
|
26
|
+
return Buffer.from(base64, 'base64');
|
|
28
27
|
}
|
|
29
28
|
return decodeBase64Fallback(base64);
|
|
30
29
|
}
|
package/utils/encoding.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { AssertionError } from '../errors/assertion.error.js';
|
|
2
1
|
import { createArray } from './array/array.js';
|
|
3
2
|
import { toUint8Array } from './binary.js';
|
|
4
|
-
import {
|
|
3
|
+
import { assertDefined } from './type-guards.js';
|
|
5
4
|
const byteToHex = createArray(2 ** 8, (i) => i).map((value) => value.toString(16).padStart(2, '0'));
|
|
6
5
|
const hexToByte = new Map(byteToHex.map((hex, value) => [hex, value]));
|
|
7
6
|
/**
|
|
@@ -61,9 +60,7 @@ export function decodeHex(hex) {
|
|
|
61
60
|
for (let i = 0; i < hex.length; i += 2) {
|
|
62
61
|
const hexPart = hex.substring(i, i + 2);
|
|
63
62
|
const byte = hexToByte.get(hexPart);
|
|
64
|
-
|
|
65
|
-
throw new AssertionError(`Invalid hex string at position ${i}.`);
|
|
66
|
-
}
|
|
63
|
+
assertDefined(byte, `Invalid hex string at position ${i}.`);
|
|
67
64
|
bytes[i / 2] = byte;
|
|
68
65
|
}
|
|
69
66
|
return bytes;
|
package/utils/format-error.js
CHANGED
|
@@ -36,7 +36,7 @@ export function formatError(error, options = {}) {
|
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
// If no message, serialize the whole object as a fallback
|
|
39
|
-
if (isUndefined(message) || String(message).trim().length
|
|
39
|
+
if (isUndefined(message) || (String(message).trim().length == 0)) { // eslint-disable-line @typescript-eslint/no-unnecessary-type-conversion
|
|
40
40
|
// Handle primitives directly
|
|
41
41
|
if (!isObject(actualError)) {
|
|
42
42
|
message = String(actualError);
|
package/utils/object/object.js
CHANGED
|
@@ -147,5 +147,11 @@ export function omit(object, ...keys) {
|
|
|
147
147
|
return filterObject(object, (_, key) => !keys.includes(key));
|
|
148
148
|
}
|
|
149
149
|
export function pick(object, ...keys) {
|
|
150
|
-
|
|
150
|
+
const result = {};
|
|
151
|
+
for (const key of keys) {
|
|
152
|
+
if (hasOwnProperty(object, key)) {
|
|
153
|
+
result[key] = object[key];
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return result;
|
|
151
157
|
}
|
package/utils/z-base32.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { AssertionError } from '../errors/index.js';
|
|
2
1
|
import { Alphabet } from './alphabet.js';
|
|
3
2
|
import { toUint8Array } from './binary.js';
|
|
3
|
+
import { assertDefined } from './type-guards.js';
|
|
4
4
|
const alphabet = Alphabet.ZBase32;
|
|
5
5
|
const charValueMap = new Map(alphabet.split('').map((char, index) => [char, index]));
|
|
6
6
|
export function zBase32Encode(buffer) {
|
|
@@ -30,9 +30,7 @@ export function zBase32Decode(input) {
|
|
|
30
30
|
for (let i = 0; i < input.length; i++) {
|
|
31
31
|
const char = input[i];
|
|
32
32
|
const charValue = charValueMap.get(char);
|
|
33
|
-
|
|
34
|
-
throw new AssertionError(`Invalid character at index ${i}.`);
|
|
35
|
-
}
|
|
33
|
+
assertDefined(charValue, `Invalid zbase32 character at index ${i}.`);
|
|
36
34
|
value = (value << 5) | charValue;
|
|
37
35
|
bits += 5;
|
|
38
36
|
if (bits >= 8) {
|