@tstdl/base 0.93.69 → 0.93.71

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/audit/module.js CHANGED
@@ -28,6 +28,6 @@ export async function migrateAuditSchema() {
28
28
  await migrate(database, {
29
29
  migrationsSchema: 'audit',
30
30
  migrationsTable: '_migrations',
31
- migrationsFolder: import.meta.resolve('./drizzle').replace('file://', ''),
31
+ migrationsFolder: import.meta.resolve('./drizzle'),
32
32
  });
33
33
  }
@@ -50,6 +50,6 @@ export async function migrateAuthenticationSchema() {
50
50
  await migrate(database, {
51
51
  migrationsSchema: 'authentication',
52
52
  migrationsTable: '_migrations',
53
- migrationsFolder: import.meta.resolve('./drizzle').replace('file://', ''),
53
+ migrationsFolder: import.meta.resolve('./drizzle'),
54
54
  });
55
55
  }
@@ -17,6 +17,6 @@ export async function migrateDocumentManagementSchema() {
17
17
  await migrate(database, {
18
18
  migrationsSchema: 'document_management',
19
19
  migrationsTable: '_migrations',
20
- migrationsFolder: import.meta.resolve('./drizzle').replace('file://', ''),
20
+ migrationsFolder: import.meta.resolve('./drizzle'),
21
21
  });
22
22
  }
@@ -1,11 +1,20 @@
1
- export declare let locale: string;
2
- export declare function configureFormats(options: {
1
+ import { DateTime } from 'luxon';
2
+ import type { ValueOrProvider } from '../utils/value-or-provider.js';
3
+ type LocaleOption = {
3
4
  locale?: string;
4
- }): void;
5
+ };
6
+ export type ConfigureLocaleOptions = {
7
+ locale?: ValueOrProvider<string>;
8
+ };
9
+ export type FormatNumberOptions = Intl.NumberFormatOptions & LocaleOption;
10
+ export type FormatDateTimeOptions = Intl.DateTimeFormatOptions & LocaleOption;
11
+ export type FormatNumberValue = number | bigint | Intl.StringNumericLiteral;
12
+ export type FormatDateTimeValue = number | Date | DateTime;
13
+ export declare let getLocale: () => string;
14
+ export declare function configureFormats({ locale }: ConfigureLocaleOptions): void;
5
15
  export declare const integerFormat: Intl.NumberFormatOptions;
6
16
  export declare const decimalFormat: Intl.NumberFormatOptions;
7
17
  export declare const decimal1Format: Intl.NumberFormatOptions;
8
- export declare const yearFormat: Intl.NumberFormatOptions;
9
18
  export declare const dateTimeNumeric: Intl.DateTimeFormatOptions;
10
19
  export declare const dateTimeShort: Intl.DateTimeFormatOptions;
11
20
  export declare const dateTimeLong: Intl.DateTimeFormatOptions;
@@ -16,19 +25,21 @@ export declare const timeShort: Intl.DateTimeFormatOptions;
16
25
  export declare const currencyFormat: Intl.NumberFormatOptions;
17
26
  export declare const currencyFormatWithoutCents: Intl.NumberFormatOptions;
18
27
  export declare const percentFormat: Intl.NumberFormatOptions;
19
- export declare function formatNumber(value: number, format?: Intl.NumberFormatOptions): string;
20
- export declare function formatInteger(value: number): string;
21
- export declare function formatDecimal(value: number, minimumFractionDigits?: number, maximumFractionDigits?: number): string;
22
- export declare function formatYear(value: number): string;
23
- export declare function formatTimeShort(value: number): string;
24
- export declare function formatDateShort(value: Date | number): string;
25
- export declare function formatDate(dateOrTimestamp: number | Date): string;
26
- export declare function formatNumericDate(numericDate: number): string;
27
- export declare function formatCurrency(value: number, currency: string): string;
28
- export declare function formatCurrencyWithoutCents(value: number, currency: string): string;
29
- export declare function formatEuro(value: number): string;
30
- export declare function formatEuroWithoutCents(value: number): string;
31
- export declare function formatPercent(value: number): string;
28
+ export declare function formatNumber(value: FormatNumberValue, format?: FormatNumberOptions): string;
29
+ export declare function formatDateTime(value: number | Date | DateTime, format?: FormatDateTimeOptions): string;
30
+ export declare function formatInteger(value: FormatNumberValue, format?: FormatNumberOptions): string;
31
+ export declare function formatDecimal(value: FormatNumberValue, format?: FormatNumberOptions): string;
32
+ export declare function formatCurrency(value: FormatNumberValue, currency: string, format?: FormatNumberOptions): string;
33
+ export declare function formatCurrencyWithoutCents(value: FormatNumberValue, currency: string, format?: FormatNumberOptions): string;
34
+ export declare function formatEuro(value: FormatNumberValue, format?: FormatNumberOptions): string;
35
+ export declare function formatEuroWithoutCents(value: FormatNumberValue, format?: FormatNumberOptions): string;
36
+ export declare function formatPercent(value: FormatNumberValue, format?: FormatNumberOptions): string;
37
+ export declare function formatTimeShort(value: FormatDateTimeValue, format?: FormatDateTimeOptions): string;
38
+ export declare function formatDateShort(value: FormatDateTimeValue, format?: FormatDateTimeOptions): string;
39
+ export declare function formatDate(dateOrTimestamp: FormatDateTimeValue, format?: FormatDateTimeOptions): string;
40
+ export declare function formatNumericDate(numericDate: number, options?: FormatDateTimeOptions): string;
41
+ export declare function formatNumericDateShort(numericDate: number, options?: FormatDateTimeOptions): string;
42
+ export declare function formatNumericDateLong(numericDate: number, options?: FormatDateTimeOptions): string;
32
43
  export type FormatPersonNameOptions<F = unknown> = {
33
44
  lastNameFirst?: boolean;
34
45
  fallback?: F;
@@ -45,7 +56,4 @@ export declare function formatPersonName(person: {
45
56
  } | null | undefined, options?: FormatPersonNameOptions & {
46
57
  fallback?: undefined;
47
58
  }): string;
48
- /**
49
- * @deprecated use {@link formatPersonName} instead
50
- */
51
- export declare const formatUserName: typeof formatPersonName;
59
+ export {};
@@ -1,9 +1,13 @@
1
- import { numericDateToTimestamp } from '../utils/date-time.js';
2
- import { memoize, memoizeSingle } from '../utils/function/memoize.js';
3
- import { isNullOrUndefined, isUndefined } from '../utils/type-guards.js';
4
- export let locale = 'de-DE';
5
- export function configureFormats(options) {
6
- locale = options.locale ?? locale;
1
+ import { DateTime } from 'luxon';
2
+ import { numericDateToDate } from '../utils/date-time.js';
3
+ import { isDefined, isNullOrUndefined, isString } from '../utils/type-guards.js';
4
+ const numberFormatterMap = new Map();
5
+ const dateTimeFormatterMap = new Map();
6
+ export let getLocale = () => 'de-DE';
7
+ export function configureFormats({ locale }) {
8
+ getLocale = (isString(locale)
9
+ ? () => locale
10
+ : locale) ?? getLocale;
7
11
  }
8
12
  export const integerFormat = {
9
13
  useGrouping: true,
@@ -19,10 +23,6 @@ export const decimal1Format = {
19
23
  minimumFractionDigits: 0,
20
24
  maximumFractionDigits: 1,
21
25
  };
22
- export const yearFormat = {
23
- useGrouping: false,
24
- maximumFractionDigits: 0,
25
- };
26
26
  export const dateTimeNumeric = {
27
27
  year: 'numeric',
28
28
  month: '2-digit',
@@ -77,57 +77,54 @@ export const percentFormat = {
77
77
  minimumFractionDigits: 2,
78
78
  maximumFractionDigits: 2,
79
79
  };
80
- const getDecimalFormatter = memoize(_getDecimalFormatter);
81
- const integerFormatter = memoizeSingle((loc) => Intl.NumberFormat(loc, integerFormat));
82
- const decimalFormatter = memoizeSingle((loc) => Intl.NumberFormat(loc, decimalFormat));
83
- const yearFormatter = memoizeSingle((loc) => Intl.NumberFormat(loc, yearFormat));
84
- const percentFormatter = memoizeSingle((loc) => Intl.NumberFormat(loc, percentFormat));
85
- const dateFormatter = memoizeSingle((loc) => Intl.DateTimeFormat(loc, dateShort));
86
- const currencyFormatter = memoize((currency, loc) => Intl.NumberFormat(loc, { ...currencyFormat, currency }));
87
- const currencyFormatterWithoutCents = memoize((currency, loc) => Intl.NumberFormat(loc, { ...currencyFormatWithoutCents, currency }));
88
- const timeShortFormatter = memoizeSingle((loc) => Intl.DateTimeFormat(loc, timeShort));
89
- const dateShortFormatter = memoizeSingle((loc) => Intl.DateTimeFormat(loc, dateShort));
90
80
  export function formatNumber(value, format) {
91
- return Intl.NumberFormat(locale, format).format(value);
81
+ const locale = format?.locale ?? getLocale();
82
+ return getNumberFormatter(locale, format).format(value);
92
83
  }
93
- export function formatInteger(value) {
94
- return integerFormatter(locale).format(value);
84
+ export function formatDateTime(value, format) {
85
+ const date = DateTime.isDateTime(value) ? value.toJSDate() : value;
86
+ const locale = format?.locale ?? getLocale();
87
+ return getDateTimeFormatter(locale, format).format(date);
95
88
  }
96
- export function formatDecimal(value, minimumFractionDigits, maximumFractionDigits) {
97
- if (isUndefined(minimumFractionDigits) && isUndefined(maximumFractionDigits)) {
98
- return decimalFormatter(locale).format(value);
99
- }
100
- return getDecimalFormatter(locale, minimumFractionDigits ?? 2, maximumFractionDigits ?? 2).format(value);
89
+ export function formatInteger(value, format) {
90
+ return formatNumber(value, { ...integerFormat, ...format });
91
+ }
92
+ export function formatDecimal(value, format) {
93
+ return formatNumber(value, { ...decimalFormat, ...format });
101
94
  }
102
- export function formatYear(value) {
103
- return yearFormatter(locale).format(value);
95
+ export function formatCurrency(value, currency, format) {
96
+ return formatNumber(value, { ...currencyFormat, currency, ...format });
104
97
  }
105
- export function formatTimeShort(value) {
106
- return timeShortFormatter(locale).format(new Date(1970, 1, 1, 0, 0, 0, value));
98
+ export function formatCurrencyWithoutCents(value, currency, format) {
99
+ return formatNumber(value, { ...currencyFormatWithoutCents, currency, ...format });
107
100
  }
108
- export function formatDateShort(value) {
109
- return dateShortFormatter(locale).format(value);
101
+ export function formatEuro(value, format) {
102
+ return formatCurrency(value, 'EUR', format);
110
103
  }
111
- export function formatDate(dateOrTimestamp) {
112
- return dateFormatter(locale).format(dateOrTimestamp);
104
+ export function formatEuroWithoutCents(value, format) {
105
+ return formatCurrencyWithoutCents(value, 'EUR', format);
113
106
  }
114
- export function formatNumericDate(numericDate) {
115
- return formatDate(numericDateToTimestamp(numericDate));
107
+ export function formatPercent(value, format) {
108
+ return formatNumber(value, { ...percentFormat, ...format });
116
109
  }
117
- export function formatCurrency(value, currency) {
118
- return currencyFormatter(currency, locale).format(value);
110
+ export function formatTimeShort(value, format) {
111
+ return formatDateTime(value, { ...timeShort, ...format });
119
112
  }
120
- export function formatCurrencyWithoutCents(value, currency) {
121
- return currencyFormatterWithoutCents(currency, locale).format(value);
113
+ export function formatDateShort(value, format) {
114
+ return formatDateTime(value, { ...dateShort, ...format });
122
115
  }
123
- export function formatEuro(value) {
124
- return currencyFormatter('EUR', locale).format(value);
116
+ export function formatDate(dateOrTimestamp, format) {
117
+ return formatDateTime(dateOrTimestamp, { ...dateShort, ...format });
125
118
  }
126
- export function formatEuroWithoutCents(value) {
127
- return currencyFormatterWithoutCents('EUR', locale).format(value);
119
+ export function formatNumericDate(numericDate, options) {
120
+ const timestamp = numericDateToDate(numericDate);
121
+ return formatDateTime(timestamp, { ...dateShort, ...options });
128
122
  }
129
- export function formatPercent(value) {
130
- return percentFormatter(locale).format(value);
123
+ export function formatNumericDateShort(numericDate, options) {
124
+ return formatNumericDate(numericDate, { ...dateShort, ...options });
125
+ }
126
+ export function formatNumericDateLong(numericDate, options) {
127
+ return formatNumericDate(numericDate, { ...dateLong, ...options });
131
128
  }
132
129
  export function formatPersonName(person, { lastNameFirst = false, fallback } = {}) {
133
130
  if (isNullOrUndefined(person?.firstName) || isNullOrUndefined(person.lastName)) {
@@ -138,14 +135,23 @@ export function formatPersonName(person, { lastNameFirst = false, fallback } = {
138
135
  }
139
136
  return `${person.firstName} ${person.lastName}`;
140
137
  }
141
- /**
142
- * @deprecated use {@link formatPersonName} instead
143
- */
144
- export const formatUserName = formatPersonName;
145
- function _getDecimalFormatter(locale, minimumFractionDigits = 2, maximumFractionDigits = 2) {
146
- return Intl.NumberFormat(locale, {
147
- useGrouping: true,
148
- minimumFractionDigits,
149
- maximumFractionDigits,
150
- });
138
+ function getNumberFormatter(locale, options) {
139
+ const key = JSON.stringify({ locale, options });
140
+ const existing = numberFormatterMap.get(key);
141
+ if (isDefined(existing)) {
142
+ return existing;
143
+ }
144
+ const formatter = new Intl.NumberFormat(locale, options);
145
+ numberFormatterMap.set(key, formatter);
146
+ return formatter;
147
+ }
148
+ function getDateTimeFormatter(locale, options) {
149
+ const key = JSON.stringify({ locale, options });
150
+ const existing = dateTimeFormatterMap.get(key);
151
+ if (isDefined(existing)) {
152
+ return existing;
153
+ }
154
+ const formatter = new Intl.DateTimeFormat(locale, options);
155
+ dateTimeFormatterMap.set(key, formatter);
156
+ return formatter;
151
157
  }
@@ -18,6 +18,6 @@ export async function migratePostgresKeyValueStoreSchema() {
18
18
  await migrate(database, {
19
19
  migrationsSchema: 'key_value_store',
20
20
  migrationsTable: '_migrations',
21
- migrationsFolder: import.meta.resolve('./drizzle').replace('file://', ''),
21
+ migrationsFolder: import.meta.resolve('./drizzle'),
22
22
  });
23
23
  }
@@ -21,6 +21,6 @@ export async function migratePostgresLockSchema() {
21
21
  await migrate(database, {
22
22
  migrationsSchema: 'lock',
23
23
  migrationsTable: '_migrations',
24
- migrationsFolder: import.meta.resolve('./drizzle').replace('file://', ''),
24
+ migrationsFolder: import.meta.resolve('./drizzle'),
25
25
  });
26
26
  }
package/mail/module.js CHANGED
@@ -31,6 +31,6 @@ export async function migrateMailSchema() {
31
31
  await migrate(database, {
32
32
  migrationsSchema: 'mail',
33
33
  migrationsTable: '_migrations',
34
- migrationsFolder: import.meta.resolve('./drizzle').replace('file://', ''),
34
+ migrationsFolder: import.meta.resolve('./drizzle'),
35
35
  });
36
36
  }
@@ -4,7 +4,7 @@
4
4
  * It sets up the dependency injection for the database instance.
5
5
  */
6
6
  import { NodePgDatabase } from 'drizzle-orm/node-postgres';
7
- import { migrate } from 'drizzle-orm/node-postgres/migrator';
7
+ import { migrate as drizzleMigrate } from 'drizzle-orm/node-postgres/migrator';
8
8
  import { type PoolConfig } from 'pg';
9
9
  import type { Resolvable, resolveArgumentType } from '../../injector/interfaces.js';
10
10
  /**
@@ -20,4 +20,6 @@ export type DatabaseArgument = PoolConfig;
20
20
  export declare class Database extends NodePgDatabase<any> implements Resolvable<DatabaseArgument> {
21
21
  readonly [resolveArgumentType]?: DatabaseArgument;
22
22
  }
23
- export { migrate };
23
+ type MigrateParameters = Parameters<typeof drizzleMigrate>;
24
+ export declare function migrate(db: MigrateParameters[0], config: MigrateParameters[1]): Promise<void>;
25
+ export {};
@@ -10,7 +10,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
10
10
  * It sets up the dependency injection for the database instance.
11
11
  */
12
12
  import { drizzle, NodePgDatabase } from 'drizzle-orm/node-postgres';
13
- import { migrate } from 'drizzle-orm/node-postgres/migrator';
13
+ import { migrate as drizzleMigrate } from 'drizzle-orm/node-postgres/migrator';
14
14
  import { Pool } from 'pg';
15
15
  import { inject, Injector, ReplaceClass } from '../../injector/index.js';
16
16
  import { isUndefined } from '../../utils/type-guards.js';
@@ -41,4 +41,9 @@ Injector.registerSingleton(Database, {
41
41
  return drizzle(pool);
42
42
  },
43
43
  });
44
- export { migrate };
44
+ export async function migrate(db, config) {
45
+ const migrationsFolder = config.migrationsFolder
46
+ .replace('file://', '') // drizzle doesn't like it
47
+ .replace(/index.jsx$/, ''); // workaround for weird import.meta.resolve behavior with tsx
48
+ await drizzleMigrate(db, { ...config, migrationsFolder });
49
+ }
@@ -22,7 +22,7 @@ declare const getCurrentTransactionalContext: {
22
22
  export { getCurrentTransactionalContext, isInTransactionalContext, runInTransactionalContext };
23
23
  export declare abstract class Transactional<ContextData = unknown> {
24
24
  #private;
25
- readonly session: Database | PgTransaction;
25
+ readonly session: PgTransaction | Database;
26
26
  readonly isInTransaction: boolean;
27
27
  constructor();
28
28
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tstdl/base",
3
- "version": "0.93.69",
3
+ "version": "0.93.71",
4
4
  "author": "Patrick Hein",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -140,9 +140,9 @@
140
140
  "type-fest": "^5.3"
141
141
  },
142
142
  "peerDependencies": {
143
- "@genkit-ai/google-genai": "^1.25",
143
+ "@genkit-ai/google-genai": "^1.26",
144
144
  "@google-cloud/storage": "^7.18",
145
- "@google/genai": "^1.32",
145
+ "@google/genai": "^1.34",
146
146
  "@toon-format/toon": "^2.1.0",
147
147
  "@tstdl/angular": "^0.93",
148
148
  "@zxcvbn-ts/core": "^3.0",
@@ -151,7 +151,7 @@
151
151
  "@zxcvbn-ts/language-en": "^3.0",
152
152
  "drizzle-orm": "^0.45",
153
153
  "file-type": "^21.1",
154
- "genkit": "^1.25",
154
+ "genkit": "^1.26",
155
155
  "handlebars": "^4.7",
156
156
  "minio": "^8.0",
157
157
  "mjml": "^4.18",
@@ -175,9 +175,9 @@
175
175
  "@types/koa__router": "12.0",
176
176
  "@types/luxon": "3.7",
177
177
  "@types/mjml": "4.7",
178
- "@types/node": "24",
178
+ "@types/node": "25",
179
179
  "@types/nodemailer": "7.0",
180
- "@types/pg": "8.15",
180
+ "@types/pg": "8.16",
181
181
  "concurrently": "9.2",
182
182
  "drizzle-kit": "0.31",
183
183
  "eslint": "9.39",
@@ -24,6 +24,6 @@ export async function migratePostgresQueueSchema() {
24
24
  await migrate(database, {
25
25
  migrationsSchema: 'queue',
26
26
  migrationsTable: '_migrations',
27
- migrationsFolder: import.meta.resolve('./drizzle').replace('file://', ''),
27
+ migrationsFolder: import.meta.resolve('./drizzle'),
28
28
  });
29
29
  }
@@ -20,7 +20,6 @@ type MessagePortDataMessage<T> = MessagePortMessageBase<'data'> & {
20
20
  type MessagePortMessage<T> = MessagePortOpenChannelMessage | MessagePortCloseChannelMessage | MessagePortDataMessage<T>;
21
21
  export declare class MessagePortRpcChannel<Data = any, Req = any, Res = any> extends RpcChannel<Data, Req, Res> {
22
22
  #private;
23
- private readonly _postMessage;
24
23
  private transport;
25
24
  readonly messagePortMessage$: Observable<MessagePortMessage<RpcChannelMessage<Data, Req, Res>>>;
26
25
  channelMessages$: Observable<RpcChannelMessage<Data, Req, Res>>;
@@ -30,9 +29,9 @@ export declare class MessagePortRpcChannel<Data = any, Req = any, Res = any> ext
30
29
  lastSequence: number;
31
30
  constructor(id: string, transport: MessagePortRpcTransport | undefined, endpoint: MessagePortRpcEndpoint);
32
31
  setTransport(transport: MessagePortRpcTransport): void;
33
- postPortMessage(message: MessagePortMessage<RpcChannelMessage<Data, Req, Res>>, transfer?: any[]): void;
32
+ postPortMessage(message: MessagePortMessage<RpcChannelMessage<Data, Req, Res>>, transferOrOptions?: Transferable[] | NodeWorkerThreads.Transferable[] | NodeWorkerThreads.StructuredSerializeOptions | StructuredSerializeOptions): void;
34
33
  close(): void;
35
- postMessage(message: RpcChannelMessage<Data, Req, Res>, transfer?: any[] | undefined): void | Promise<void>;
34
+ postMessage(message: RpcChannelMessage<Data, Req, Res>, transfer?: Transferable[] | StructuredSerializeOptions): void | Promise<void>;
36
35
  }
37
36
  export declare class MessagePortRpcEndpoint extends RpcEndpoint {
38
37
  #private;
@@ -6,7 +6,6 @@ import { RpcChannel, RpcEndpoint } from '../rpc.endpoint.js';
6
6
  export class MessagePortRpcChannel extends RpcChannel {
7
7
  #transportSubject = new ReplaySubject(1);
8
8
  #closeSubject = new Subject();
9
- _postMessage;
10
9
  transport = { postMessage: deferThrow(() => new Error('Rpc transport not yet initialized.')) };
11
10
  messagePortMessage$;
12
11
  channelMessages$;
@@ -22,9 +21,6 @@ export class MessagePortRpcChannel extends RpcChannel {
22
21
  }
23
22
  this.messagePortMessage$ = this.#transportSubject.pipe(startWith(transport), filter(isDefined), switchMap((newTransport) => fromEvent(newTransport, 'message')), takeUntil(this.#closeSubject), map((message) => ((message instanceof MessageEvent) ? message.data : message)), shareReplay({ bufferSize: 0, refCount: true }));
24
23
  this.channelMessages$ = this.messagePortMessage$.pipe(filter((message) => message.type == 'data'), map((message) => message.data));
25
- this._postMessage = isBrowser
26
- ? (data, transfer) => this.transport.postMessage(data, { transfer })
27
- : (data, transfer) => this.transport.postMessage(data, transfer);
28
24
  if (transport instanceof MessagePort) {
29
25
  transport.start();
30
26
  }
@@ -39,8 +35,8 @@ export class MessagePortRpcChannel extends RpcChannel {
39
35
  this.#transportSubject.next(transport);
40
36
  this.#transportSubject.complete();
41
37
  }
42
- postPortMessage(message, transfer) {
43
- this._postMessage(message, transfer);
38
+ postPortMessage(message, transferOrOptions) {
39
+ this.transport.postMessage(message, transferOrOptions);
44
40
  }
45
41
  close() {
46
42
  this.postPortMessage({ seq: this.sequence++, type: 'close-channel' });
package/test/module.js CHANGED
@@ -12,6 +12,6 @@ export async function migrateTestSchema() {
12
12
  await migrate(database, {
13
13
  migrationsSchema: 'test',
14
14
  migrationsTable: '_migrations',
15
- migrationsFolder: import.meta.resolve('./drizzle').replace('file://', ''),
15
+ migrationsFolder: import.meta.resolve('./drizzle'),
16
16
  });
17
17
  }
@@ -0,0 +1 @@
1
+ export {};
package/test-schema.js ADDED
@@ -0,0 +1,124 @@
1
+ import z from 'zod';
2
+ import { Application } from './application/application.js';
3
+ import { provideModule, provideSignalHandler } from './application/providers.js';
4
+ import { PrettyPrintLogFormatter, provideConsoleLogTransport } from './logger/index.js';
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
+ },
64
+ };
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,
70
+ };
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);
118
+ function main() {
119
+ }
120
+ Application.run('Test', [
121
+ provideModule(main),
122
+ provideConsoleLogTransport(PrettyPrintLogFormatter),
123
+ provideSignalHandler(),
124
+ ]);
package/test6.js CHANGED
@@ -1,124 +1,33 @@
1
- import z from 'zod';
2
- import { Application } from './application/application.js';
3
- import { provideModule, provideSignalHandler } from './application/providers.js';
4
- import { PrettyPrintLogFormatter, provideConsoleLogTransport } from './logger/index.js';
5
- import { array, boolean, literal, number, object, optional, string, union } from './schema/index.js';
6
1
  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
- },
64
- };
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,
70
- };
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);
2
+ import { isDefined } from './utils/type-guards.js';
3
+ const numberFormatterMap = new Map();
4
+ function getNumberFormatter1(locale, options) {
5
+ const key = JSON.stringify({ locale, ...options });
6
+ const existing = numberFormatterMap.get(key);
7
+ if (isDefined(existing)) {
8
+ return existing;
105
9
  }
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 });
10
+ const formatter = new Intl.NumberFormat(locale, options);
11
+ numberFormatterMap.set(key, formatter);
12
+ return formatter;
13
+ }
14
+ function getNumberFormatter2(locale, options) {
15
+ const key = JSON.stringify({ locale, options });
16
+ const existing = numberFormatterMap.get(key);
17
+ if (isDefined(existing)) {
18
+ return existing;
114
19
  }
115
- catch (e) { }
116
- });
117
- logResult(`Error Handling (TSTDL fastErrors=true) (${ITERATIONS_ERROR} ops)`, tstdlErrFast, zodErr);
118
- function main() {
20
+ const formatter = new Intl.NumberFormat(locale, options);
21
+ numberFormatterMap.set(key, formatter);
22
+ return formatter;
23
+ }
24
+ function format1(value, locale, format) {
25
+ return getNumberFormatter1(locale, format).format(value);
26
+ }
27
+ function format2(value, locale, format) {
28
+ return getNumberFormatter2(locale, format).format(value);
119
29
  }
120
- Application.run('Test', [
121
- provideModule(main),
122
- provideConsoleLogTransport(PrettyPrintLogFormatter),
123
- provideSignalHandler(),
124
- ]);
30
+ const result1 = benchmark(200000, (run) => format1(run, 'en-US', { maximumFractionDigits: 2 }));
31
+ const result2 = benchmark(200000, (run) => format2(run, 'en-US', { maximumFractionDigits: 2 }));
32
+ console.log(`format: ${result1.operationsPerMillisecond.toFixed(2)} ops/ms`);
33
+ console.log(`formatCached: ${result2.operationsPerMillisecond.toFixed(2)} ops/ms`);