@tstdl/base 0.93.51 → 0.93.54

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 (44) hide show
  1. package/browser/utils.js +2 -2
  2. package/object-storage/google/google.object-storage-provider.d.ts +44 -0
  3. package/object-storage/google/google.object-storage-provider.js +81 -0
  4. package/object-storage/google/google.object-storage.d.ts +32 -0
  5. package/object-storage/google/google.object-storage.js +220 -0
  6. package/object-storage/google/google.object.d.ts +16 -0
  7. package/object-storage/google/google.object.js +69 -0
  8. package/object-storage/google/index.d.ts +3 -0
  9. package/object-storage/google/index.js +3 -0
  10. package/object-storage/object-storage.d.ts +7 -4
  11. package/object-storage/s3/s3.object-storage-provider.js +1 -1
  12. package/object-storage/s3/s3.object-storage.d.ts +3 -3
  13. package/object-storage/s3/s3.object-storage.js +21 -11
  14. package/object-storage/s3/s3.object.d.ts +1 -1
  15. package/object-storage/s3/s3.object.js +1 -1
  16. package/orm/sqls.js +3 -3
  17. package/package.json +2 -2
  18. package/schema/converters/zod-converter.d.ts +8 -2
  19. package/schema/converters/zod-converter.js +182 -136
  20. package/schema/schemas/bigint.js +2 -2
  21. package/schema/schemas/boolean.d.ts +1 -0
  22. package/schema/schemas/boolean.js +16 -16
  23. package/schema/schemas/constraint.d.ts +19 -0
  24. package/schema/schemas/constraint.js +36 -0
  25. package/schema/schemas/index.d.ts +1 -0
  26. package/schema/schemas/index.js +1 -0
  27. package/schema/schemas/object.d.ts +3 -1
  28. package/schema/schemas/object.js +57 -28
  29. package/schema/schemas/simple.d.ts +6 -1
  30. package/schema/schemas/simple.js +11 -2
  31. package/schema/schemas/string.d.ts +4 -2
  32. package/schema/schemas/string.js +10 -5
  33. package/schema/schemas/transform.d.ts +4 -0
  34. package/schema/schemas/transform.js +4 -0
  35. package/schema/schemas/uint8-array.d.ts +1 -0
  36. package/schema/schemas/uint8-array.js +10 -7
  37. package/templates/template.model.js +2 -2
  38. package/test6.js +112 -29
  39. package/types/types.d.ts +1 -1
  40. package/utils/base64.js +1 -2
  41. package/utils/encoding.js +2 -5
  42. package/utils/format-error.js +1 -1
  43. package/utils/object/object.js +7 -1
  44. package/utils/z-base32.js +2 -4
@@ -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, objectKeys } from '../../utils/object/object.js';
6
- import { assert, isArray, isDefined, isFunction, isLiteralObject, isNotNull, isNotNullOrUndefined, isNull, isObject, isUndefined } from '../../utils/type-guards.js';
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
- propertyKeys;
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.propertyKeys = new Set(objectKeys(properties));
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,66 @@ 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 = isLiteralObject(this.factory) ? new this.factory.type() : {};
45
- const keys = objectKeys(value);
46
- if (isNotNull(this.minimumPropertiesCount) && (keys.length < this.minimumPropertiesCount)) {
47
- return { valid: false, error: new SchemaError(`Object has too few properties (minimum ${this.minimumPropertiesCount}, got ${keys.length}).`, { path, fast: options.fastErrors }) };
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); // manual check of __proto__ below
54
+ inputKeyCount = inputKeys.length;
48
55
  }
49
- if (isNotNull(this.maximumPropertiesCount) && (keys.length > this.maximumPropertiesCount)) {
50
- return { valid: false, error: new SchemaError(`Object has too many properties (maximum ${this.maximumPropertiesCount}, got ${keys.length}).`, { path, fast: options.fastErrors }) };
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
- const valueKeys = new Set(keys);
53
- const unknownValuePropertyKeys = valueKeys.difference(this.propertyKeys);
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
- for (const key of this.propertyKeys) {
58
- const propertyResult = this.properties[key]._test(value[key], path.add(key), options);
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 (this.allowUnknownProperties) {
65
- for (const key of unknownValuePropertyKeys) {
66
- const propertyPath = path.add(key);
67
- const keyResult = this.unknownPropertiesKey?._test(key, propertyPath, options) ?? { valid: true };
68
- if (!keyResult.valid) {
69
- if (mask) {
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 (key == '__proto__') {
86
+ return { valid: false, error: new SchemaError('Property key "__proto__" is not allowed.', { path: path.add(key), fast: options.fastErrors }) };
87
+ }
88
+ if (this.knownPropertyKeys.has(key)) {
70
89
  continue;
71
90
  }
72
- return { valid: false, error: new SchemaError('Invalid property key.', { path: propertyPath, inner: keyResult.error, fast: options.fastErrors }) };
73
- }
74
- const innerValueResult = this.unknownProperties?._test(value[key], propertyPath, options) ?? { valid: true, value: value[key] };
75
- if (!innerValueResult.valid) {
76
- return innerValueResult;
91
+ const propertyPath = path.add(key);
92
+ if (!this.allowUnknownProperties) {
93
+ return { valid: false, error: new SchemaError('Unexpected property', { path: propertyPath, fast: options.fastErrors }) };
94
+ }
95
+ const keyResult = this.unknownPropertiesKey?._test(key, propertyPath, options) ?? { valid: true };
96
+ if (!keyResult.valid) {
97
+ if (mask) {
98
+ continue;
99
+ }
100
+ return { valid: false, error: new SchemaError('Invalid property key.', { path: propertyPath, inner: keyResult.error, fast: options.fastErrors }) };
101
+ }
102
+ const innerValueResult = this.unknownProperties?._test(value[key], propertyPath, options) ?? { valid: true, value: value[key] };
103
+ if (!innerValueResult.valid) {
104
+ return innerValueResult;
105
+ }
106
+ resultValue[key] = innerValueResult.value;
77
107
  }
78
- resultValue[key] = innerValueResult.value;
79
108
  }
80
109
  }
81
110
  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
  }
@@ -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 ?? ((value) => value);
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
- pattern?: RegExp | string;
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 pattern: RegExp | null;
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;
@@ -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
- pattern;
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 = match(this.coerceType)
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);
@@ -53,8 +53,8 @@ export function simpleTemplate(name, template, options) {
53
53
  return {
54
54
  name,
55
55
  fields: {
56
- template
56
+ template,
57
57
  },
58
- options
58
+ options,
59
59
  };
60
60
  }
package/test6.js CHANGED
@@ -1,38 +1,121 @@
1
- var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
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 { _throw } from './utils/throw.js';
15
- const MY_INTEGER = injectionToken('MyInteger');
16
- // current method
17
- let Foo = class Foo {
18
- bar = inject(Bar);
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
- Foo = __decorate([
21
- Singleton({
22
- providers: [
23
- { provide: MY_INTEGER, useFactory: () => _throw(new NotImplementedError()) },
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
- Bar = __decorate([
31
- Singleton()
32
- ], Bar);
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
- const buffer = Buffer.from(base64, 'base64');
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 { isUndefined } from './type-guards.js';
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
- if (isUndefined(byte)) {
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;
@@ -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 === 0) { // eslint-disable-line @typescript-eslint/no-unnecessary-type-conversion
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);
@@ -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
- return filterObject(object, (_, key) => keys.includes(key));
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
- if (charValue == undefined) {
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) {