@tstdl/base 0.93.47 → 0.93.49

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.
@@ -1,10 +1,10 @@
1
1
  import type { Resolvable, resolveArgumentType } from '../injector/interfaces.js';
2
- import type { AiServiceOptions } from './ai.service.js';
2
+ import { AiModuleOptions } from './module.js';
3
3
  import { type FileContentPart, type FileInput } from './types.js';
4
4
  /**
5
5
  * Options for {@link AiFileService}.
6
6
  */
7
- export type AiFileServiceOptions = Pick<AiServiceOptions, 'apiKey' | 'keyFile' | 'vertex'>;
7
+ export type AiFileServiceOptions = Pick<AiModuleOptions, 'apiKey' | 'keyFile' | 'vertex'>;
8
8
  export type AiFileServiceArgument = AiFileServiceOptions;
9
9
  type File = {
10
10
  id: string;
@@ -21,13 +21,14 @@ import { backoffGenerator } from '../utils/backoff.js';
21
21
  import { formatBytes } from '../utils/format.js';
22
22
  import { readBinaryStream } from '../utils/stream/stream-reader.js';
23
23
  import { assertDefinedPass, isBlob, isDefined, isUndefined } from '../utils/type-guards.js';
24
+ import { AiModuleOptions } from './module.js';
24
25
  import { isPathFileInput } from './types.js';
25
26
  /**
26
27
  * Manages file uploads and state for use with AI models.
27
28
  * Handles both Google Generative AI File API and Google Cloud Storage for Vertex AI.
28
29
  */
29
30
  let AiFileService = class AiFileService {
30
- #options = injectArgument(this);
31
+ #options = injectArgument(this, { optional: true }) ?? inject(AiModuleOptions);
31
32
  #genAI = new GoogleGenAI({
32
33
  vertexai: isDefined(this.#options.vertex?.project),
33
34
  project: this.#options.vertex?.project,
@@ -2,6 +2,7 @@ import type { Resolvable, resolveArgumentType } from '../injector/interfaces.js'
2
2
  import { type SchemaTestable } from '../schema/index.js';
3
3
  import type { Enumeration as EnumerationType, EnumerationValue } from '../types/index.js';
4
4
  import { AiSession } from './ai-session.js';
5
+ import { AiModuleOptions } from './module.js';
5
6
  import { type AiModel, type FileContentPart, type FileInput, type GenerationOptions, type GenerationRequest, type GenerationResult, type SchemaFunctionDeclarations, type SchemaFunctionDeclarationsResult } from './types.js';
6
7
  /**
7
8
  * A generation result that includes a specialized, typed result alongside the raw generation data.
@@ -20,27 +21,7 @@ export type SpecializedGenerationResult<T> = {
20
21
  export type SpecializedGenerationResultGenerator<T> = AsyncGenerator<T> & {
21
22
  raw: Promise<GenerationResult>;
22
23
  };
23
- /**
24
- * Options for configuring the {@link AiService}.
25
- */
26
- export declare class AiServiceOptions {
27
- /** Google AI API key. */
28
- apiKey?: string;
29
- /** Path to the Google Cloud credentials file. */
30
- keyFile?: string;
31
- /** Vertex AI specific options. If provided, the service will use Vertex AI endpoints. */
32
- vertex?: {
33
- project: string;
34
- location: string;
35
- bucket?: string;
36
- };
37
- /**
38
- * The default model to use for generation requests.
39
- * @default 'small'
40
- */
41
- defaultModel?: AiModel;
42
- }
43
- export type AiServiceArgument = AiServiceOptions;
24
+ export type AiServiceArgument = AiModuleOptions;
44
25
  export type ClassificationResult<T extends EnumerationType> = {
45
26
  types: {
46
27
  type: EnumerationValue<T>;
package/ai/ai.service.js CHANGED
@@ -6,7 +6,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
6
6
  };
7
7
  var _a;
8
8
  var AiService_1;
9
- import { FinishReason, FunctionCallingConfigMode as GoogleFunctionCallingMode, GoogleGenAI } from '@google/genai';
9
+ import { ApiError, FinishReason, FunctionCallingConfigMode as GoogleFunctionCallingMode, GoogleGenAI } from '@google/genai';
10
10
  import { match } from 'ts-pattern';
11
11
  import { CancellationSignal } from '../cancellation/index.js';
12
12
  import { NotSupportedError } from '../errors/not-supported.error.js';
@@ -15,7 +15,7 @@ import { inject, injectArgument } from '../injector/inject.js';
15
15
  import { Logger } from '../logger/logger.js';
16
16
  import { DeferredPromise } from '../promise/deferred-promise.js';
17
17
  import { LazyPromise } from '../promise/lazy-promise.js';
18
- import { convertToOpenApiSchema } from '../schema/converters/openapi-converter.js';
18
+ import { convertToOpenApiSchema } from '../schema/converters/open-api-converter.js';
19
19
  import { Schema } from '../schema/index.js';
20
20
  import { toArray } from '../utils/array/array.js';
21
21
  import { mapAsync } from '../utils/async-iterable-helpers/map.js';
@@ -23,28 +23,12 @@ import { toArrayAsync } from '../utils/async-iterable-helpers/to-array.js';
23
23
  import { backoffGenerator } from '../utils/backoff.js';
24
24
  import { lazyObject } from '../utils/object/lazy-property.js';
25
25
  import { hasOwnProperty, objectEntries } from '../utils/object/object.js';
26
- import { assertDefinedPass, isDefined, isError, isNotNull, isNull, isUndefined } from '../utils/type-guards.js';
26
+ import { assertDefinedPass, isDefined, isError, isInstanceOf, isNotNull, isNull, isUndefined } from '../utils/type-guards.js';
27
27
  import { resolveValueOrAsyncProvider } from '../utils/value-or-provider.js';
28
28
  import { AiFileService } from './ai-file.service.js';
29
29
  import { AiSession } from './ai-session.js';
30
+ import { AiModuleOptions } from './module.js';
30
31
  import { isSchemaFunctionDeclarationWithHandler } from './types.js';
31
- /**
32
- * Options for configuring the {@link AiService}.
33
- */
34
- export class AiServiceOptions {
35
- /** Google AI API key. */
36
- apiKey;
37
- /** Path to the Google Cloud credentials file. */
38
- keyFile;
39
- /** Vertex AI specific options. If provided, the service will use Vertex AI endpoints. */
40
- vertex;
41
- /**
42
- * The default model to use for generation requests.
43
- * @default 'small'
44
- */
45
- defaultModel;
46
- }
47
- ;
48
32
  const functionCallingModeMap = {
49
33
  auto: GoogleFunctionCallingMode.AUTO,
50
34
  force: GoogleFunctionCallingMode.ANY,
@@ -58,7 +42,7 @@ let generationCounter = 0;
58
42
  * supporting both standard Google AI and Vertex AI endpoints.
59
43
  */
60
44
  let AiService = AiService_1 = class AiService {
61
- #options = injectArgument(this, { optional: true }) ?? inject(AiServiceOptions);
45
+ #options = injectArgument(this, { optional: true }) ?? inject(AiModuleOptions);
62
46
  #fileService = inject(AiFileService, this.#options);
63
47
  #logger = inject(Logger, AiService_1.name);
64
48
  #cancellationSignal = inject(CancellationSignal);
@@ -73,7 +57,7 @@ let AiService = AiService_1 = class AiService {
73
57
  #backoffOptions = {
74
58
  cancellationSignal: this.#cancellationSignal,
75
59
  strategy: 'exponential',
76
- initialDelay: 2500,
60
+ initialDelay: 5000,
77
61
  increase: 1.5,
78
62
  jitter: 0.2,
79
63
  maximumDelay: 30000,
@@ -81,7 +65,7 @@ let AiService = AiService_1 = class AiService {
81
65
  /**
82
66
  * The default AI model to use for requests if not specified otherwise.
83
67
  */
84
- defaultModel = this.#options.defaultModel ?? 'gemini-flash-lite-latest';
68
+ defaultModel = this.#options.defaultModel ?? 'gemini-2.5-flash-lite';
85
69
  /**
86
70
  * Creates a new {@link AiSession} for managing conversational history.
87
71
  */
@@ -231,7 +215,7 @@ let AiService = AiService_1 = class AiService {
231
215
  }
232
216
  catch (error) {
233
217
  triesLeft -= 1;
234
- if ((triesLeft > 0) && isError(error) && (error.message.includes('429 Too Many Requests') || error.message.includes('503 Service Unavailable'))) {
218
+ if ((triesLeft > 0) && isInstanceOf(error, ApiError) && ((error.status == 429) || (error.status == 503))) {
235
219
  this.#logger.verbose(`Retrying after transient error: ${error.message}`);
236
220
  backoff();
237
221
  continue;
@@ -320,7 +304,7 @@ let AiService = AiService_1 = class AiService {
320
304
  for (const call of generation.functionCalls) {
321
305
  const fn = assertDefinedPass(options.functions[call.name], 'Function in response not declared.');
322
306
  const parametersSchema = await resolveValueOrAsyncProvider(fn.parameters);
323
- const parameters = isDefined(parametersSchema) ? parametersSchema.parse(call.parameters) : call.parameters;
307
+ const parameters = isDefined(parametersSchema) ? parametersSchema.parse(call.parameters, { coerce: true }) : call.parameters;
324
308
  const handlerResult = isSchemaFunctionDeclarationWithHandler(fn) ? await fn.handler(parameters) : undefined;
325
309
  yield {
326
310
  functionName: call.name,
@@ -437,8 +421,8 @@ let AiService = AiService_1 = class AiService {
437
421
  }
438
422
  mapModel(model) {
439
423
  return match(model)
440
- .with('small', () => 'gemini-flash-lite-latest')
441
- .with('medium', () => 'gemini-flash-latest')
424
+ .with('small', () => 'gemini-2.5-flash-lite')
425
+ .with('medium', () => 'gemini-2.5-flash')
442
426
  .with('large', () => 'gemini-2.5-pro')
443
427
  .otherwise(() => model ?? this.defaultModel);
444
428
  }
@@ -486,7 +470,7 @@ export function mergeGenerationStreamItems(items, schema) {
486
470
  catch (error) {
487
471
  throw new Error(`Failed to parse model output as JSON. Raw text: "${this.text}"`, { cause: error });
488
472
  }
489
- return Schema.parse(schema, parsed);
473
+ return Schema.parse(schema, parsed, { coerce: true });
490
474
  },
491
475
  functionCalls() {
492
476
  return parts.filter((part) => hasOwnProperty(part, 'functionCall')).map((part) => part.functionCall);
package/ai/functions.js CHANGED
@@ -1,4 +1,4 @@
1
- import { convertToOpenApiSchema } from '../schema/converters/openapi-converter.js';
1
+ import { convertToOpenApiSchema } from '../schema/converters/open-api-converter.js';
2
2
  import { FunctionSchema, getObjectSchema, object } from '../schema/index.js';
3
3
  import { fromEntries, objectEntries } from '../utils/object/object.js';
4
4
  import { isNotNull, isNull, isString } from '../utils/type-guards.js';
package/ai/module.d.ts CHANGED
@@ -1,11 +1,26 @@
1
- import { AiServiceOptions } from './ai.service.js';
1
+ import type { AiModel } from './types.js';
2
2
  /**
3
- * Options for configuring the AI module.
4
- * @see {@link AiServiceOptions}
3
+ * Options for configuring the AI Module.
5
4
  */
6
- export type ApiModuleOptions = AiServiceOptions;
5
+ export declare class AiModuleOptions {
6
+ /** Google AI API key. */
7
+ apiKey?: string;
8
+ /** Path to the Google Cloud credentials file. */
9
+ keyFile?: string;
10
+ /** Vertex AI specific options. If provided, the service will use Vertex AI endpoints. */
11
+ vertex?: {
12
+ project: string;
13
+ location: string;
14
+ bucket?: string;
15
+ };
16
+ /**
17
+ * The default model to use for generation requests.
18
+ * @default 'small'
19
+ */
20
+ defaultModel?: AiModel;
21
+ }
7
22
  /**
8
23
  * Configures the {@link AiService}.
9
24
  * @param options The configuration options for the AI services.
10
25
  */
11
- export declare function configureAiService(options: ApiModuleOptions): void;
26
+ export declare function configureAiService(options: AiModuleOptions): void;
package/ai/module.js CHANGED
@@ -1,9 +1,25 @@
1
1
  import { Injector } from '../injector/injector.js';
2
- import { AiServiceOptions } from './ai.service.js';
2
+ /**
3
+ * Options for configuring the AI Module.
4
+ */
5
+ export class AiModuleOptions {
6
+ /** Google AI API key. */
7
+ apiKey;
8
+ /** Path to the Google Cloud credentials file. */
9
+ keyFile;
10
+ /** Vertex AI specific options. If provided, the service will use Vertex AI endpoints. */
11
+ vertex;
12
+ /**
13
+ * The default model to use for generation requests.
14
+ * @default 'small'
15
+ */
16
+ defaultModel;
17
+ }
18
+ ;
3
19
  /**
4
20
  * Configures the {@link AiService}.
5
21
  * @param options The configuration options for the AI services.
6
22
  */
7
23
  export function configureAiService(options) {
8
- Injector.register(AiServiceOptions, { useValue: options });
24
+ Injector.register(AiModuleOptions, { useValue: options });
9
25
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tstdl/base",
3
- "version": "0.93.47",
3
+ "version": "0.93.49",
4
4
  "author": "Patrick Hein",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -101,6 +101,8 @@
101
101
  "./rxjs-utils": "./rxjs-utils/index.js",
102
102
  "./schema": "./schema/index.js",
103
103
  "./schema/converters": "./schema/converters/index.js",
104
+ "./schema/converters/open-api": "./schema/converters/open-api-converter.js",
105
+ "./schema/converters/zod": "./schema/converters/zod-converter.js",
104
106
  "./serializer": "./serializer/index.js",
105
107
  "./serializer/handlers": "./serializer/handlers/index.js",
106
108
  "./signals": "./signals/index.js",
@@ -149,12 +151,13 @@
149
151
  "mjml": "^4.17",
150
152
  "nodemailer": "^7.0",
151
153
  "pg": "^8.16",
152
- "playwright": "^1.56",
154
+ "playwright": "^1.57",
153
155
  "preact": "^10.27",
154
156
  "preact-render-to-string": "^6.6",
155
157
  "sharp": "^0.34",
156
158
  "undici": "^7.16",
157
- "urlpattern-polyfill": "^10.1"
159
+ "urlpattern-polyfill": "^10.1",
160
+ "zod": "^4.1"
158
161
  },
159
162
  "peerDependenciesMeta": {
160
163
  "@tstdl/angular": {
@@ -178,7 +181,7 @@
178
181
  "typedoc-plugin-markdown": "4.9",
179
182
  "typedoc-plugin-missing-exports": "4.1",
180
183
  "typescript": "5.9",
181
- "typescript-eslint": "8.47"
184
+ "typescript-eslint": "8.48"
182
185
  },
183
186
  "overrides": {
184
187
  "drizzle-kit": {
@@ -1,17 +1,7 @@
1
1
  import { NotSupportedError } from '../../errors/not-supported.error.js';
2
2
  import { filterUndefinedObjectProperties, fromEntries, hasOwnProperty, objectEntries } from '../../utils/object/object.js';
3
3
  import { isDefined, isNotNull, isNumber, isString } from '../../utils/type-guards.js';
4
- import { ArraySchema } from '../schemas/array.js';
5
- import { BooleanSchema } from '../schemas/boolean.js';
6
- import { DateSchema } from '../schemas/date.js';
7
- import { EnumerationSchema } from '../schemas/enumeration.js';
8
- import { LiteralSchema } from '../schemas/literal.js';
9
- import { nullable, NullableSchema } from '../schemas/nullable.js';
10
- import { NumberSchema } from '../schemas/number.js';
11
- import { ObjectSchema } from '../schemas/object.js';
12
- import { OptionalSchema } from '../schemas/optional.js';
13
- import { StringSchema } from '../schemas/string.js';
14
- import { UnionSchema } from '../schemas/union.js';
4
+ import { ArraySchema, BooleanSchema, DateSchema, DefaultSchema, EnumerationSchema, LiteralSchema, nullable, NullableSchema, NumberSchema, ObjectSchema, OptionalSchema, StringSchema, TransformSchema, Uint8ArraySchema, UnionSchema } from '../schemas/index.js';
15
5
  import { schemaTestableToSchema } from '../testable.js';
16
6
  export function convertToOpenApiSchema(testable) {
17
7
  const schema = schemaTestableToSchema(testable);
@@ -39,12 +29,21 @@ function convertToOpenApiSchemaBase(schema) {
39
29
  maxProperties: isNumber(schema.maximumPropertiesCount) ? schema.maximumPropertiesCount : undefined,
40
30
  });
41
31
  }
32
+ if (schema instanceof DefaultSchema) { // You'd need to import DefaultedSchema
33
+ return {
34
+ ...convertToOpenApiSchema(schema.schema),
35
+ default: schema.defaultValue,
36
+ };
37
+ }
38
+ if (schema instanceof TransformSchema) { // You'd need to import TransformSchema
39
+ return convertToOpenApiSchema(schema.schema);
40
+ }
42
41
  if (schema instanceof StringSchema) {
43
42
  return filterUndefinedObjectProperties({
44
43
  type: 'string',
45
44
  minLength: isNumber(schema.minimumLength) ? schema.minimumLength : undefined,
46
45
  maxLength: isNumber(schema.maximumLength) ? schema.maximumLength : undefined,
47
- pattern: isString(schema.pattern) ? schema.pattern : undefined,
46
+ pattern: isString(schema.pattern) ? schema.pattern : schema.pattern?.source,
48
47
  });
49
48
  }
50
49
  if (schema instanceof DateSchema) {
@@ -79,6 +78,14 @@ function convertToOpenApiSchemaBase(schema) {
79
78
  maxItems: isNumber(schema.maximum) ? schema.maximum : undefined,
80
79
  });
81
80
  }
81
+ if ((schema instanceof Uint8ArraySchema) && (schema.coerceType == 'base64')) {
82
+ return filterUndefinedObjectProperties({
83
+ type: 'string',
84
+ format: 'byte',
85
+ minLength: isNumber(schema.minimumLength) ? schema.minimumLength : undefined,
86
+ maxLength: isNumber(schema.maximumLength) ? schema.maximumLength : undefined,
87
+ });
88
+ }
82
89
  if (schema instanceof EnumerationSchema) {
83
90
  const hasString = schema.allowedValues.some(isString);
84
91
  const hasNumber = schema.allowedValues.some(isNumber);
@@ -0,0 +1,3 @@
1
+ import * as z from 'zod/mini';
2
+ import type { SchemaTestable } from '../schema.js';
3
+ export declare function convertToZodSchema<O, I = unknown>(testable: SchemaTestable<O>): z.ZodMiniType<O, I>;
@@ -0,0 +1,172 @@
1
+ import * as z from 'zod/mini';
2
+ import { NotSupportedError } from '../../errors/not-supported.error.js';
3
+ import { objectEntries, objectKeys } from '../../utils/object/object.js';
4
+ import { isArray, isNotNull, isNumber, isString } from '../../utils/type-guards.js';
5
+ import { any, AnySchema, ArraySchema, BigIntSchema, BooleanSchema, DateSchema, DefaultSchema, DeferredSchema, EnumerationSchema, InstanceSchema, LiteralSchema, NeverSchema, NullableSchema, NumberSchema, ObjectSchema, OneOrManySchema, OptionalSchema, ReadableStreamSchema, RegExpSchema, StringSchema, SymbolSchema, TransformSchema, Uint8ArraySchema, UnionSchema, UnknownSchema } from '../schemas/index.js';
6
+ import { schemaTestableToSchema } from '../testable.js';
7
+ export function convertToZodSchema(testable) {
8
+ const schema = schemaTestableToSchema(testable);
9
+ return convertToZodSchemaBase(schema);
10
+ }
11
+ function convertToZodSchemaBase(schema) {
12
+ if (schema instanceof AnySchema) {
13
+ return z.any();
14
+ }
15
+ if (schema instanceof UnknownSchema) {
16
+ return z.unknown();
17
+ }
18
+ if (schema instanceof NeverSchema) {
19
+ return z.never();
20
+ }
21
+ if (schema instanceof NullableSchema) {
22
+ return z.nullable(convertToZodSchema(schema.schema));
23
+ }
24
+ if (schema instanceof OptionalSchema) {
25
+ return z.optional(convertToZodSchema(schema.schema));
26
+ }
27
+ if (schema instanceof OneOrManySchema) {
28
+ return convertToZodSchema(schema.schema);
29
+ }
30
+ if (schema instanceof StringSchema) {
31
+ const checks = [];
32
+ if (isNumber(schema.minimumLength)) {
33
+ checks.push(z.minLength(schema.minimumLength));
34
+ }
35
+ if (isNumber(schema.maximumLength)) {
36
+ checks.push(z.maxLength(schema.maximumLength));
37
+ }
38
+ if (isNotNull(schema.pattern)) {
39
+ checks.push(z.regex(isString(schema.pattern) ? new RegExp(schema.pattern) : schema.pattern));
40
+ }
41
+ if (schema.lowercase) {
42
+ checks.push(z.toLowerCase());
43
+ }
44
+ const zodSchema = z.string();
45
+ return (checks.length > 0) ? zodSchema.check(...checks) : zodSchema;
46
+ }
47
+ if (schema instanceof NumberSchema) {
48
+ const checks = [];
49
+ if (isNumber(schema.minimum)) {
50
+ checks.push(z.gte(schema.minimum));
51
+ }
52
+ if (isNumber(schema.maximum)) {
53
+ checks.push(z.lte(schema.maximum));
54
+ }
55
+ const zodSchema = (schema.integer) ? z.int() : z.number();
56
+ return (checks.length > 0) ? zodSchema.check(...checks) : zodSchema;
57
+ }
58
+ if (schema instanceof BooleanSchema) {
59
+ return z.boolean();
60
+ }
61
+ if (schema instanceof BigIntSchema) {
62
+ return z.bigint();
63
+ }
64
+ if (schema instanceof SymbolSchema) {
65
+ return z.symbol();
66
+ }
67
+ if (schema instanceof DateSchema) {
68
+ const checks = [];
69
+ if (isNotNull(schema.minimum)) {
70
+ checks.push(z.gte(schema.minimum));
71
+ }
72
+ if (isNotNull(schema.maximum)) {
73
+ checks.push(z.lte(schema.maximum));
74
+ }
75
+ const zodSchema = z.date();
76
+ return (checks.length > 0) ? zodSchema.check(...checks) : zodSchema;
77
+ }
78
+ if (schema instanceof LiteralSchema) {
79
+ return z.literal(schema.value);
80
+ }
81
+ if (schema instanceof ArraySchema) {
82
+ const itemSchema = convertToZodSchema(schema.itemSchema);
83
+ const checks = [];
84
+ if (isNumber(schema.minimum)) {
85
+ checks.push(z.minSize(schema.minimum));
86
+ }
87
+ if (isNumber(schema.maximum)) {
88
+ checks.push(z.maxSize(schema.maximum));
89
+ }
90
+ const zodSchema = z.array(itemSchema);
91
+ return (checks.length > 0) ? zodSchema.check(...checks) : zodSchema;
92
+ }
93
+ if (schema instanceof ObjectSchema) {
94
+ const propertyEntries = objectEntries(schema.properties);
95
+ if ((propertyEntries.length == 0) && (isNotNull(schema.unknownPropertiesKey) || isNotNull(schema.unknownProperties))) {
96
+ return z.record(convertToZodSchema(schema.unknownPropertiesKey ?? any()), convertToZodSchema(schema.unknownProperties ?? any()));
97
+ }
98
+ const shape = {};
99
+ for (const [key, propertySchema] of propertyEntries) {
100
+ shape[key] = convertToZodSchema(propertySchema);
101
+ }
102
+ const loose = isNotNull(schema.unknownPropertiesKey) || isNotNull(schema.unknownProperties);
103
+ const zodSchema = loose
104
+ ? z.looseObject(shape)
105
+ : schema.mask
106
+ ? z.object(shape)
107
+ : z.strictObject(shape);
108
+ if (loose) {
109
+ const propertiesSchema = convertToZodSchema(schema.unknownProperties ?? any());
110
+ const propertiesKeySchema = convertToZodSchema(schema.unknownPropertiesKey ?? any());
111
+ const knownPropertyKeys = new Set(objectKeys(shape));
112
+ return zodSchema.check(z.superRefine((record, context) => {
113
+ for (const [key, value] of objectEntries(record)) {
114
+ if (knownPropertyKeys.has(key)) {
115
+ continue;
116
+ }
117
+ const keyParseResult = propertiesKeySchema.safeParse(key);
118
+ const valueParseResult = propertiesSchema.safeParse(value);
119
+ if (!keyParseResult.success) {
120
+ context.addIssue({
121
+ code: 'invalid_key',
122
+ origin: 'record',
123
+ issues: keyParseResult.error.issues,
124
+ });
125
+ }
126
+ if (!valueParseResult.success) {
127
+ context.addIssue({
128
+ code: 'custom',
129
+ input: value,
130
+ path: [key],
131
+ message: valueParseResult.error.message,
132
+ });
133
+ }
134
+ }
135
+ }));
136
+ }
137
+ return zodSchema;
138
+ }
139
+ if (schema instanceof UnionSchema) {
140
+ const schemas = schema.schemas.map((s) => convertToZodSchema(s));
141
+ return z.union(schemas);
142
+ }
143
+ if (schema instanceof EnumerationSchema) {
144
+ if (isArray(schema.enumeration) && schema.enumeration.some((v) => isNumber(v))) {
145
+ const types = schema.allowedValues.map((value) => z.literal(value));
146
+ return z.union(types);
147
+ }
148
+ return z.enum(schema.enumeration);
149
+ }
150
+ if (schema instanceof DefaultSchema) {
151
+ return z._default(convertToZodSchema(schema.schema), schema.defaultValue);
152
+ }
153
+ if (schema instanceof TransformSchema) {
154
+ return z.transform((input) => schema.transformFn(input));
155
+ }
156
+ if (schema instanceof DeferredSchema) {
157
+ return z.lazy(() => convertToZodSchema(schema.schema));
158
+ }
159
+ if (schema instanceof Uint8ArraySchema) {
160
+ return z.instanceof(Uint8Array);
161
+ }
162
+ if (schema instanceof ReadableStreamSchema) {
163
+ return z.instanceof(ReadableStream);
164
+ }
165
+ if (schema instanceof RegExpSchema) {
166
+ return z.instanceof(RegExp);
167
+ }
168
+ if (schema instanceof InstanceSchema) {
169
+ return z.instanceof(schema.type);
170
+ }
171
+ throw new NotSupportedError(`Schema "${schema.name}" cannot be converted to Zod schema.`);
172
+ }
@@ -27,10 +27,10 @@ export class ArraySchema extends Schema {
27
27
  return { valid: false, error: SchemaError.expectedButGot('array', typeOf(value), path) };
28
28
  }
29
29
  if (isNotNull(this.maximum) && (value.length > this.maximum)) {
30
- throw new Error(`A maximum of ${this.maximum} items are allowed.`);
30
+ return { valid: false, error: new SchemaError(`A maximum of ${this.maximum} items are allowed.`, { path, fast: options.fastErrors }) };
31
31
  }
32
32
  if (isNotNull(this.minimum) && (value.length < this.minimum)) {
33
- throw new Error(`A minimum of ${this.minimum} items are required.`);
33
+ return { valid: false, error: new SchemaError(`A minimum of ${this.minimum} items are required.`, { path, fast: options.fastErrors }) };
34
34
  }
35
35
  const values = [];
36
36
  for (let i = 0; i < value.length; i++) {
@@ -1,10 +1,13 @@
1
- import { type SchemaPropertyDecorator, type SchemaDecoratorOptions } from '../decorators/index.js';
1
+ import { type SchemaDecoratorOptions, type SchemaPropertyDecorator } from '../decorators/index.js';
2
2
  import { SimpleSchema, type SimpleSchemaOptions } from './simple.js';
3
- export type DateSchemaOptions = SimpleSchemaOptions<globalThis.Date> & {
4
- integer?: boolean;
3
+ export type DateSchemaOptions = SimpleSchemaOptions<Date> & {
4
+ minimum?: Date | number | null;
5
+ maximum?: Date | number | null;
5
6
  };
6
- export declare class DateSchema extends SimpleSchema<globalThis.Date> {
7
+ export declare class DateSchema extends SimpleSchema<Date> {
7
8
  readonly name = "date";
9
+ readonly minimum: Date | null;
10
+ readonly maximum: Date | null;
8
11
  constructor(options?: DateSchemaOptions);
9
12
  }
10
13
  export declare function date(options?: DateSchemaOptions): DateSchema;
@@ -1,24 +1,29 @@
1
- import { isValidDate } from '../../utils/type-guards.js';
1
+ import { isNotNullOrUndefined, isValidDate } from '../../utils/type-guards.js';
2
2
  import { PropertySchema } from '../decorators/index.js';
3
3
  import { SchemaError } from '../schema.error.js';
4
4
  import { SimpleSchema } from './simple.js';
5
5
  export class DateSchema extends SimpleSchema {
6
6
  name = 'date';
7
+ minimum;
8
+ maximum;
7
9
  constructor(options) {
8
10
  super('date', isValidDate, options, {
9
11
  coercers: {
10
12
  string: (value, path, options) => {
11
- const result = new globalThis.Date(value);
12
- return globalThis.Number.isNaN(result.getTime())
13
+ const result = new Date(value);
14
+ return Number.isNaN(result.getTime())
13
15
  ? { success: false, error: SchemaError.couldNotCoerce('date', 'string', path, { fast: options.fastErrors }) }
14
16
  : { success: true, value: result, valid: true };
15
17
  },
16
- number: (value) => ({ success: true, value: new globalThis.Date(value), valid: false })
18
+ number: (value) => ({ success: true, value: new Date(value), valid: false }),
17
19
  },
18
20
  constraints: [
19
- (options?.integer == true) ? (value) => globalThis.Number.isInteger(value) ? ({ success: true }) : ({ success: false, error: 'value is not an integer.' }) : null
20
- ]
21
+ isNotNullOrUndefined(options?.minimum) ? (value) => (value >= this.minimum) ? ({ success: true }) : ({ success: false, error: `Value ${value.toDateString()} is less than minimum ${this.minimum.toDateString()}` }) : null,
22
+ isNotNullOrUndefined(options?.maximum) ? (value) => (value <= this.maximum) ? ({ success: true }) : ({ success: false, error: `Value ${value.toDateString()} is greater than maximum ${this.maximum.toDateString()}` }) : null,
23
+ ],
21
24
  });
25
+ this.minimum = isNotNullOrUndefined(options?.minimum) ? new Date(options.minimum) : null;
26
+ this.maximum = isNotNullOrUndefined(options?.maximum) ? new Date(options.maximum) : null;
22
27
  }
23
28
  }
24
29
  export function date(options) {
@@ -27,14 +27,14 @@ export function Method(parameterSchemasOrOptions, returnValueSchema, optionsOrNo
27
27
  if (isArray(parameterSchemasOrOptions) || isNull(parameterSchemasOrOptions)) {
28
28
  return Property({
29
29
  ...optionsOrNothing,
30
- schema: (data) => func(parameterSchemasOrOptions, returnValueSchema, { description: data.description, example: data.example, ...optionsOrNothing })
30
+ schema: (data) => func(parameterSchemasOrOptions, returnValueSchema, { description: data.description, example: data.example, ...optionsOrNothing }),
31
31
  });
32
32
  }
33
33
  return createMethodDecorator({
34
34
  handler: (data, _metdata, originalArguments) => {
35
35
  createSchemaDecorator(parameterSchemasOrOptions)(...originalArguments);
36
36
  Property(getFunctionSchemaFromReflection(data.constructor, data.methodKey), parameterSchemasOrOptions);
37
- }
37
+ },
38
38
  });
39
39
  }
40
40
  export function getFunctionSchemaFromReflection(type, method) {
@@ -1,6 +1,6 @@
1
1
  import type { JsonPath } from '../../json-path/json-path.js';
2
2
  import type { AbstractConstructor } from '../../types/index.js';
3
- import { type SchemaPropertyDecorator, type SchemaDecoratorOptions } from '../decorators/index.js';
3
+ import { type SchemaDecoratorOptions, type SchemaPropertyDecorator } from '../decorators/index.js';
4
4
  import { Schema, type SchemaOptions, type SchemaTestOptions, type SchemaTestResult } from '../schema.js';
5
5
  export type InstanceSchemaOptions<T extends AbstractConstructor> = SchemaOptions<InstanceType<T>>;
6
6
  export declare class InstanceSchema<T extends AbstractConstructor> extends Schema<InstanceType<T>> {
@@ -17,7 +17,7 @@ export class InstanceSchema extends Schema {
17
17
  }
18
18
  return {
19
19
  valid: false,
20
- error: SchemaError.expectedButGot(typeOf(this.type), typeOf(value), path, { fast: options.fastErrors })
20
+ error: SchemaError.expectedButGot(typeOf(this.type), typeOf(value), path, { fast: options.fastErrors }),
21
21
  };
22
22
  }
23
23
  }
@@ -47,6 +47,11 @@ export declare class ObjectSchema<T extends Record = Record> extends Schema<T> {
47
47
  readonly factory: ObjectSchemaFactory<T> | null;
48
48
  constructor(properties: ObjectSchemaProperties<T>, options?: ObjectSchemaOptions<T>);
49
49
  _test(value: any, path: JsonPath, options: SchemaTestOptions): SchemaTestResult<T>;
50
+ pick<const K extends keyof T>(keys: OneOrMany<K>): ObjectSchema<SimplifyObject<Pick<T, K>>>;
51
+ omit<const K extends keyof T>(keys: OneOrMany<K>): ObjectSchema<SimplifyObject<Omit<T, K>>>;
52
+ partial(): ObjectSchema<Partial<T>>;
53
+ partial<const K extends keyof T>(keys: OneOrMany<K>): ObjectSchema<PartialProperty<T, K>>;
54
+ assign<T extends Record>(other: ObjectSchemaOrType<T>): ObjectSchema<Merge<T, T>>;
50
55
  }
51
56
  export declare function object<const K extends PropertyKey, const V>(properties: Record<never>, options: ObjectSchemaOptions<Record<K, V>> & {
52
57
  unknownProperties: SchemaTestable<V>;
@@ -81,6 +81,18 @@ export class ObjectSchema extends Schema {
81
81
  const testResultValue = isFunction(this.factory) ? this.factory(resultValue) : resultValue;
82
82
  return { valid: true, value: testResultValue };
83
83
  }
84
+ pick(keys) {
85
+ return pick(this, keys);
86
+ }
87
+ omit(keys) {
88
+ return omit(this, keys);
89
+ }
90
+ partial(keys) {
91
+ return partial(this, keys);
92
+ }
93
+ assign(other) {
94
+ return assign(this, other);
95
+ }
84
96
  }
85
97
  export function object(properties, options) {
86
98
  return new ObjectSchema(properties, options);
@@ -1,4 +1,4 @@
1
- import { type SchemaPropertyDecorator, type SchemaDecoratorOptions } from '../decorators/index.js';
1
+ import { type SchemaDecoratorOptions, type SchemaPropertyDecorator } from '../decorators/index.js';
2
2
  import { SimpleSchema, type SimpleSchemaOptions } from './simple.js';
3
3
  export type RegExpSchemaOptions = SimpleSchemaOptions<RegExp>;
4
4
  export declare class RegExpSchema extends SimpleSchema<RegExp> {
@@ -15,8 +15,8 @@ export class RegExpSchema extends SimpleSchema {
15
15
  catch (error) {
16
16
  return { success: false, error: SchemaError.couldNotCoerce(globalThis.RegExp, 'string', path, { fast: options.fastErrors, customMessage: error.message }) };
17
17
  }
18
- }
19
- }
18
+ },
19
+ },
20
20
  });
21
21
  }
22
22
  }
@@ -1,15 +1,18 @@
1
- import { type SchemaPropertyDecorator, type SchemaDecoratorOptions } from '../decorators/index.js';
1
+ import { type SchemaDecoratorOptions, type SchemaPropertyDecorator } from '../decorators/index.js';
2
2
  import { SimpleSchema, type SimpleSchemaOptions } from './simple.js';
3
3
  export type Uint8ArraySchemaOptions = SimpleSchemaOptions<Uint8Array> & {
4
4
  /** Minimum byte length */
5
5
  minimumLength?: number;
6
6
  /** Maximum byte length */
7
7
  maximumLength?: number;
8
+ /** Type to coerce from string */
9
+ coerceType?: Uint8ArraySchema['coerceType'];
8
10
  };
9
11
  export declare class Uint8ArraySchema extends SimpleSchema<Uint8Array> {
10
12
  readonly name = "Uint8Array";
11
13
  readonly minimumLength: number | null;
12
14
  readonly maximumLength: number | null;
15
+ readonly coerceType: 'base64' | 'base64url' | 'zbase32' | 'hex' | null;
13
16
  constructor(options?: Uint8ArraySchemaOptions);
14
17
  }
15
18
  export declare function uint8Array(options?: Uint8ArraySchemaOptions): Uint8ArraySchema;
@@ -1,19 +1,48 @@
1
- import { isDefined, isUint8Array } from '../../utils/type-guards.js';
1
+ import { match } from 'ts-pattern';
2
+ import { AssertionError } from '../../errors/index.js';
3
+ import { decodeBase64, decodeBase64Url } from '../../utils/base64.js';
4
+ import { decodeHex } from '../../utils/encoding.js';
5
+ import { isDefined, isInstanceOf, isUint8Array } from '../../utils/type-guards.js';
6
+ import { zBase32Decode } from '../../utils/z-base32.js';
2
7
  import { PropertySchema } from '../decorators/index.js';
8
+ import { SchemaError } from '../schema.error.js';
3
9
  import { SimpleSchema } from './simple.js';
4
10
  export class Uint8ArraySchema extends SimpleSchema {
5
11
  name = 'Uint8Array';
6
12
  minimumLength;
7
13
  maximumLength;
14
+ coerceType;
8
15
  constructor(options) {
9
16
  super('Uint8Array', isUint8Array, options, {
17
+ coercers: {
18
+ string: isDefined(options?.coerceType)
19
+ ? (value, path, options) => {
20
+ 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();
27
+ return { success: true, valid: true, value: coerced };
28
+ }
29
+ catch (error) {
30
+ if (isInstanceOf(error, AssertionError)) {
31
+ return { success: false, error: SchemaError.couldNotCoerce(`${this.coerceType} formatted string`, 'string', path, { fast: options.fastErrors, customMessage: error.message }) };
32
+ }
33
+ throw error;
34
+ }
35
+ }
36
+ : undefined,
37
+ },
10
38
  constraints: [
11
39
  isDefined(options?.minimumLength) ? (value) => (value.byteLength >= this.minimumLength) ? ({ success: true }) : ({ success: false, error: `Size must be at least ${this.minimumLength} bytes.` }) : null,
12
- isDefined(options?.maximumLength) ? (value) => (value.byteLength <= this.maximumLength) ? ({ success: true }) : ({ success: false, error: `Size must be at most ${this.maximumLength} bytes.` }) : null
13
- ]
40
+ isDefined(options?.maximumLength) ? (value) => (value.byteLength <= this.maximumLength) ? ({ success: true }) : ({ success: false, error: `Size must be at most ${this.maximumLength} bytes.` }) : null,
41
+ ],
14
42
  });
15
43
  this.minimumLength = options?.minimumLength ?? null;
16
44
  this.maximumLength = options?.maximumLength ?? null;
45
+ this.coerceType = options?.coerceType ?? null;
17
46
  }
18
47
  }
19
48
  export function uint8Array(options) {
package/utils/encoding.js CHANGED
@@ -62,7 +62,7 @@ export function decodeHex(hex) {
62
62
  const hexPart = hex.substring(i, i + 2);
63
63
  const byte = hexToByte.get(hexPart);
64
64
  if (isUndefined(byte)) {
65
- throw new AssertionError(`invalid hex string at position ${i}`);
65
+ throw new AssertionError(`Invalid hex string at position ${i}.`);
66
66
  }
67
67
  bytes[i / 2] = byte;
68
68
  }
@@ -1,3 +1,3 @@
1
1
  import type { BinaryData } from '../types/index.js';
2
2
  export declare function zBase32Encode(buffer: BinaryData): string;
3
- export declare function zBase32Decode(input: string): Uint8Array;
3
+ export declare function zBase32Decode(input: string): Uint8Array<ArrayBuffer>;
package/utils/z-base32.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { AssertionError } from '../errors/index.js';
1
2
  import { Alphabet } from './alphabet.js';
2
3
  import { toUint8Array } from './binary.js';
3
4
  const alphabet = Alphabet.ZBase32;
@@ -30,7 +31,7 @@ export function zBase32Decode(input) {
30
31
  const char = input[i];
31
32
  const charValue = charValueMap.get(char);
32
33
  if (charValue == undefined) {
33
- throw new Error(`invalid character at index ${i}`);
34
+ throw new AssertionError(`Invalid character at index ${i}.`);
34
35
  }
35
36
  value = (value << 5) | charValue;
36
37
  bits += 5;
@@ -1 +0,0 @@
1
- export * from './openapi-converter.js';
@@ -1 +0,0 @@
1
- export * from './openapi-converter.js';