@kalutskii/foundation 0.7.7 → 0.7.9
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/LICENSE +21 -0
- package/README.md +21 -16
- package/dist/index.d.ts +382 -123
- package/dist/index.js +191 -53
- package/package.json +5 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ilya Kalutskii
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# @kalutskii/foundation
|
|
2
2
|
|
|
3
3
|
Shared TypeScript foundation for contracts, schemas, framework adapters, and reusable utilities.
|
|
4
|
-
The package is designed for Bun, Node.js, Hono applications
|
|
4
|
+
The package is designed for Bun, Node.js, and Hono applications.
|
|
5
5
|
|
|
6
6
|
This repository is not intended to document every exported function through standalone usage snippets.
|
|
7
7
|
Public JSDoc, generated declarations, colocated specifications, and editor inference are the API reference.
|
|
@@ -24,6 +24,7 @@ The package contains several deliberately isolated areas:
|
|
|
24
24
|
| ---------------- | ----------------------------------------------------------------------------------------- |
|
|
25
25
|
| `utilities` | Framework-independent datetime, enum, execution, generation, logging, and type utilities. |
|
|
26
26
|
| `http` | Shared HTTP result contracts, factories, status constants, and result resolvers. |
|
|
27
|
+
| `upload` | Shared file-format metadata, upload validation, and reusable Zod file schemas. |
|
|
27
28
|
| `zod-validation` | Generic Zod parsing, validation, refinement, and related type utilities. |
|
|
28
29
|
| `zod-search` | Reusable search and pagination contracts composed from lower-level Zod primitives. |
|
|
29
30
|
| `zod-bulk` | Include/exclude selection contracts shared by frontend and backend bulk operations. |
|
|
@@ -46,7 +47,7 @@ Framework adapters
|
|
|
46
47
|
│
|
|
47
48
|
▼
|
|
48
49
|
Contract composition
|
|
49
|
-
zod-search / zod-bulk
|
|
50
|
+
zod-search / zod-bulk / upload
|
|
50
51
|
│
|
|
51
52
|
▼
|
|
52
53
|
Contract primitives
|
|
@@ -88,6 +89,7 @@ src/
|
|
|
88
89
|
├── drizzle/
|
|
89
90
|
├── hono/
|
|
90
91
|
├── http/
|
|
92
|
+
├── upload/
|
|
91
93
|
├── utilities/
|
|
92
94
|
├── zod-bulk/
|
|
93
95
|
├── zod-jwt/
|
|
@@ -111,20 +113,23 @@ single public API boundary and should export only symbols intentionally supporte
|
|
|
111
113
|
|
|
112
114
|
## File responsibilities
|
|
113
115
|
|
|
114
|
-
| Suffix
|
|
115
|
-
|
|
|
116
|
-
| `*.constants.ts`
|
|
117
|
-
| `*.
|
|
118
|
-
| `*.
|
|
119
|
-
| `*.
|
|
120
|
-
| `*.
|
|
121
|
-
| `*.
|
|
122
|
-
| `*.
|
|
123
|
-
| `*.
|
|
124
|
-
| `*.
|
|
125
|
-
| `*.
|
|
126
|
-
| `*.
|
|
127
|
-
| `*.
|
|
116
|
+
| Suffix | Expected content |
|
|
117
|
+
| ----------------- | ------------------------------------------------------------------------- |
|
|
118
|
+
| `*.constants.ts` | Immutable configuration values and metadata without behavior. |
|
|
119
|
+
| `*.enums.ts` | Literal collections, derived unions, ergonomic records, and aliases. |
|
|
120
|
+
| `*.schemas.ts` | Runtime Zod schemas and factories whose result is a schema. |
|
|
121
|
+
| `*.types.ts` | Type aliases, generic contracts, and schema-derived output types. |
|
|
122
|
+
| `*.services.ts` | Stateful service classes that coordinate one external capability. |
|
|
123
|
+
| `*.validation.ts` | Ordered validation behavior returning stable domain error keys. |
|
|
124
|
+
| `*.factory.ts` | Functions whose primary responsibility is constructing non-schema values. |
|
|
125
|
+
| `*.resolvers.ts` | Functions that unwrap, normalize, or translate an existing result. |
|
|
126
|
+
| `*.utilities.ts` | Stateless reusable behavior that has no narrower architectural owner. |
|
|
127
|
+
| `*.parsing.ts` | Input parsing and preprocessing before domain validation. |
|
|
128
|
+
| `*.refiners.ts` | Refinement logic that narrows or safely composes an existing value. |
|
|
129
|
+
| `*.execution.ts` | Framework lifecycle execution and error-boundary behavior. |
|
|
130
|
+
| `*.logging.ts` | Logging formatters, sinks, or middleware behavior. |
|
|
131
|
+
| `*.respond.ts` | Framework response construction and response-specific contracts. |
|
|
132
|
+
| `*.spec.ts` | The single colocated runtime and compile-time specification for a module. |
|
|
128
133
|
|
|
129
134
|
Do not place TypeScript-only contracts in a schema file when they can be separated without creating a circular
|
|
130
135
|
responsibility. Do not split tiny files mechanically either: separation must communicate ownership, not line count.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,30 +1,27 @@
|
|
|
1
1
|
import { SQL } from 'drizzle-orm';
|
|
2
2
|
import { ErrorHandler, MiddlewareHandler, Context, TypedResponse } from 'hono';
|
|
3
|
-
import { Locale } from 'date-fns';
|
|
4
3
|
import z$1, { z, ZodObject, ZodRawShape } from 'zod';
|
|
4
|
+
import { Locale } from 'date-fns';
|
|
5
5
|
import { SymmetricAlgorithm } from 'hono/utils/jwt/jwa';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
* Builds a Drizzle
|
|
8
|
+
* Builds a Drizzle `WHERE` clause by combining defined object entries with `and`.
|
|
9
|
+
* Expects the supplied keys to be validated against the table beforehand.
|
|
9
10
|
*
|
|
10
|
-
*
|
|
11
|
-
* Assumes that `where` was already validated and contains only existing table fields.
|
|
12
|
-
*
|
|
13
|
-
* ```typescript
|
|
11
|
+
* @example
|
|
14
12
|
* await db.update(usersTable).set(values).where(sqlWhere(usersTable, { id: 1 })).returning();
|
|
15
|
-
* ```
|
|
16
13
|
*/
|
|
17
14
|
declare function sqlWhere(table: unknown, where: Record<string, unknown>): SQL;
|
|
18
15
|
|
|
19
16
|
/**
|
|
20
|
-
*
|
|
21
|
-
*
|
|
17
|
+
* Converts expected `HTTPException` values into shared API error envelopes.
|
|
18
|
+
* Unexpected failures are logged and represented by a traceable generic response.
|
|
22
19
|
*/
|
|
23
20
|
declare const onHandlerError: ErrorHandler;
|
|
24
21
|
|
|
25
22
|
/**
|
|
26
|
-
*
|
|
27
|
-
*
|
|
23
|
+
* Logs request method, response status, duration, URL details, and a body preview.
|
|
24
|
+
* Multipart bodies remain unread to avoid buffering uploaded file content into logs.
|
|
28
25
|
*/
|
|
29
26
|
declare const honoLoggingHandler: MiddlewareHandler;
|
|
30
27
|
|
|
@@ -33,10 +30,10 @@ declare const EXCEPTION_STATUS_CODES: readonly [400, 401, 403, 404, 405, 409, 50
|
|
|
33
30
|
|
|
34
31
|
type SuccessStatusCode = (typeof SUCCESS_STATUS_CODES)[number];
|
|
35
32
|
type ExceptionStatusCode = (typeof EXCEPTION_STATUS_CODES)[number];
|
|
36
|
-
type APISuccess<
|
|
33
|
+
type APISuccess<TData = void> = {
|
|
37
34
|
kind: 'data';
|
|
38
35
|
status: SuccessStatusCode;
|
|
39
|
-
data:
|
|
36
|
+
data: TData;
|
|
40
37
|
};
|
|
41
38
|
type APIError = {
|
|
42
39
|
kind: 'error';
|
|
@@ -46,48 +43,55 @@ type APIError = {
|
|
|
46
43
|
type APIContractResult<TData = void> = APISuccess<TData> | APIError;
|
|
47
44
|
type APIContractData<TResult extends APIContractResult<unknown>> = TResult extends APISuccess<infer TData> ? TData : never;
|
|
48
45
|
type APIContractError<TResult extends APIContractResult<unknown>> = Extract<TResult, APIError>;
|
|
49
|
-
|
|
50
|
-
* Discriminated union result of a fetch operation.
|
|
51
|
-
* If `error` is set, `data` is null and vice versa.
|
|
52
|
-
*/
|
|
53
|
-
type FetchResult<T> = {
|
|
46
|
+
type FetchResult<TData> = {
|
|
54
47
|
error: null;
|
|
55
|
-
data:
|
|
48
|
+
data: TData;
|
|
56
49
|
} | {
|
|
57
50
|
error: string;
|
|
58
51
|
data: null;
|
|
59
52
|
};
|
|
60
53
|
|
|
61
54
|
/**
|
|
62
|
-
*
|
|
63
|
-
*
|
|
55
|
+
* Options for a typed JSON response wrapped in the shared API success envelope.
|
|
56
|
+
* The status generic preserves the literal code inferred by the route contract.
|
|
64
57
|
*/
|
|
65
|
-
|
|
66
|
-
status:
|
|
67
|
-
data?:
|
|
68
|
-
}
|
|
58
|
+
type HonoRespondOptions<TData extends object, TStatus extends SuccessStatusCode> = {
|
|
59
|
+
status: TStatus;
|
|
60
|
+
data?: TData;
|
|
61
|
+
};
|
|
69
62
|
/**
|
|
70
|
-
*
|
|
71
|
-
*
|
|
63
|
+
* Options for a downloadable binary response with attachment metadata.
|
|
64
|
+
* The content type remains optional and falls back to a generic binary type.
|
|
72
65
|
*/
|
|
73
|
-
|
|
74
|
-
status:
|
|
66
|
+
type HonoFileRespondOptions<TStatus extends SuccessStatusCode> = {
|
|
67
|
+
status: TStatus;
|
|
75
68
|
content: Uint8Array<ArrayBuffer>;
|
|
76
69
|
filename: string;
|
|
77
70
|
contentType?: string;
|
|
78
|
-
}
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Wraps `c.json` in the shared success envelope while preserving its literal status.
|
|
75
|
+
* Missing response data is represented by an empty object for contract consistency.
|
|
76
|
+
*/
|
|
77
|
+
declare function respond<T extends object = Record<string, never>, S extends SuccessStatusCode = SuccessStatusCode>(c: Context, options: HonoRespondOptions<T, S>): Response & TypedResponse<APISuccess<T> | APIError, S, 'json'>;
|
|
78
|
+
/**
|
|
79
|
+
* Responds with downloadable binary content and its attachment headers.
|
|
80
|
+
* Unknown/undefined content types default to `application/octet-stream`.
|
|
81
|
+
*/
|
|
82
|
+
declare function fileRespond<S extends SuccessStatusCode>(c: Context, options: HonoFileRespondOptions<S>): Response;
|
|
79
83
|
|
|
80
84
|
/**
|
|
81
|
-
*
|
|
82
|
-
*
|
|
85
|
+
* Creates a successful API envelope without cloning its data.
|
|
86
|
+
* Generic inference preserves the exact supplied payload type.
|
|
83
87
|
*/
|
|
84
88
|
declare function success<T = unknown>({ status, data }: {
|
|
85
89
|
status: SuccessStatusCode;
|
|
86
90
|
data: T;
|
|
87
91
|
}): APISuccess<T>;
|
|
88
92
|
/**
|
|
89
|
-
*
|
|
90
|
-
*
|
|
93
|
+
* Creates a failed API envelope with one supported exception status.
|
|
94
|
+
* The supplied message remains unchanged for downstream presentation.
|
|
91
95
|
*/
|
|
92
96
|
declare function failure({ status, error }: {
|
|
93
97
|
status: ExceptionStatusCode;
|
|
@@ -95,132 +99,369 @@ declare function failure({ status, error }: {
|
|
|
95
99
|
}): APIError;
|
|
96
100
|
|
|
97
101
|
/**
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
* Example usage: const result = await fetchSafely(() => api.fetchUser(userId));
|
|
102
|
+
* Converts an API contract envelope into a mutually exclusive safe result.
|
|
103
|
+
* Only `APIError` values are normalized; rejected fetchers still reject.
|
|
101
104
|
*/
|
|
102
105
|
declare function fetchSafely<TResult extends APIContractResult<unknown>>(fetcher: () => Promise<TResult>): Promise<FetchResult<APIContractData<TResult>>>;
|
|
103
106
|
/**
|
|
104
|
-
*
|
|
105
|
-
*
|
|
107
|
+
* Returns successful API data and throws for an `APIError` envelope.
|
|
108
|
+
* Rejected fetchers propagate their original error without replacement.
|
|
106
109
|
*/
|
|
107
110
|
declare function fetchAndThrow<TResult extends APIContractResult<unknown>>(fetcher: () => Promise<TResult>): Promise<APIContractData<TResult>>;
|
|
108
111
|
|
|
109
112
|
/**
|
|
110
|
-
*
|
|
113
|
+
* Canonical metadata shared by upload controls and runtime validation.
|
|
114
|
+
* Every supported format defines display, MIME, and extension values.
|
|
115
|
+
*/
|
|
116
|
+
declare const fileFormatsConfig: {
|
|
117
|
+
readonly png: {
|
|
118
|
+
readonly name: "PNG";
|
|
119
|
+
readonly mimeTypes: readonly ["image/png"];
|
|
120
|
+
readonly extensions: readonly [".png"];
|
|
121
|
+
};
|
|
122
|
+
readonly jpg: {
|
|
123
|
+
readonly name: "JPG";
|
|
124
|
+
readonly mimeTypes: readonly ["image/jpeg"];
|
|
125
|
+
readonly extensions: readonly [".jpg", ".jpeg"];
|
|
126
|
+
};
|
|
127
|
+
readonly webp: {
|
|
128
|
+
readonly name: "WEBP";
|
|
129
|
+
readonly mimeTypes: readonly ["image/webp"];
|
|
130
|
+
readonly extensions: readonly [".webp"];
|
|
131
|
+
};
|
|
132
|
+
readonly avif: {
|
|
133
|
+
readonly name: "AVIF";
|
|
134
|
+
readonly mimeTypes: readonly ["image/avif"];
|
|
135
|
+
readonly extensions: readonly [".avif"];
|
|
136
|
+
};
|
|
137
|
+
readonly heic: {
|
|
138
|
+
readonly name: "HEIC";
|
|
139
|
+
readonly mimeTypes: readonly ["image/heic", "image/heif"];
|
|
140
|
+
readonly extensions: readonly [".heic", ".heif"];
|
|
141
|
+
};
|
|
142
|
+
readonly pdf: {
|
|
143
|
+
readonly name: "PDF";
|
|
144
|
+
readonly mimeTypes: readonly ["application/pdf"];
|
|
145
|
+
readonly extensions: readonly [".pdf"];
|
|
146
|
+
};
|
|
147
|
+
readonly rtf: {
|
|
148
|
+
readonly name: "RTF";
|
|
149
|
+
readonly mimeTypes: readonly ["application/rtf"];
|
|
150
|
+
readonly extensions: readonly [".rtf"];
|
|
151
|
+
};
|
|
152
|
+
readonly txt: {
|
|
153
|
+
readonly name: "TXT";
|
|
154
|
+
readonly mimeTypes: readonly ["text/plain"];
|
|
155
|
+
readonly extensions: readonly [".txt"];
|
|
156
|
+
};
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
declare const fileFormatsArray: readonly ["png", "jpg", "webp", "avif", "heic", "pdf", "rtf", "txt"];
|
|
160
|
+
type FileFormat = (typeof fileFormatsArray)[number];
|
|
161
|
+
declare const fileFormatsRecord: Readonly<{
|
|
162
|
+
AVIF: "avif";
|
|
163
|
+
JPG: "jpg";
|
|
164
|
+
PDF: "pdf";
|
|
165
|
+
PNG: "png";
|
|
166
|
+
RTF: "rtf";
|
|
167
|
+
TXT: "txt";
|
|
168
|
+
WEBP: "webp";
|
|
169
|
+
HEIC: "heic";
|
|
170
|
+
}>;
|
|
171
|
+
declare const fileFormat: Readonly<{
|
|
172
|
+
AVIF: "avif";
|
|
173
|
+
JPG: "jpg";
|
|
174
|
+
PDF: "pdf";
|
|
175
|
+
PNG: "png";
|
|
176
|
+
RTF: "rtf";
|
|
177
|
+
TXT: "txt";
|
|
178
|
+
WEBP: "webp";
|
|
179
|
+
HEIC: "heic";
|
|
180
|
+
}>;
|
|
181
|
+
declare const uploadValidationErrorsArray: readonly ["empty_file", "unsupported_file_format", "file_size_exceeded", "files_count_exceeded"];
|
|
182
|
+
type UploadValidationError = (typeof uploadValidationErrorsArray)[number];
|
|
183
|
+
declare const uploadValidationErrorsRecord: Readonly<{
|
|
184
|
+
EMPTY_FILE: "empty_file";
|
|
185
|
+
UNSUPPORTED_FILE_FORMAT: "unsupported_file_format";
|
|
186
|
+
FILE_SIZE_EXCEEDED: "file_size_exceeded";
|
|
187
|
+
FILES_COUNT_EXCEEDED: "files_count_exceeded";
|
|
188
|
+
}>;
|
|
189
|
+
declare const uploadValidationError: Readonly<{
|
|
190
|
+
EMPTY_FILE: "empty_file";
|
|
191
|
+
UNSUPPORTED_FILE_FORMAT: "unsupported_file_format";
|
|
192
|
+
FILE_SIZE_EXCEEDED: "file_size_exceeded";
|
|
193
|
+
FILES_COUNT_EXCEEDED: "files_count_exceeded";
|
|
194
|
+
}>;
|
|
195
|
+
|
|
196
|
+
type FileFormatConfig = (typeof fileFormatsConfig)[FileFormat];
|
|
197
|
+
/**
|
|
198
|
+
* Constraints used to validate an incoming collection of browser files.
|
|
199
|
+
* Existing and incoming counts are combined when enforcing capacity.
|
|
200
|
+
*/
|
|
201
|
+
type UploadFilesValidationOptions = Readonly<{
|
|
202
|
+
/**
|
|
203
|
+
* Number of files already retained before the incoming batch is validated.
|
|
204
|
+
* Existing entries reduce the remaining capacity without being revalidated.
|
|
205
|
+
*/
|
|
206
|
+
currentFilesCount: number;
|
|
207
|
+
/**
|
|
208
|
+
* Supported formats used to validate every file in the incoming batch.
|
|
209
|
+
* MIME and extension metadata are resolved through the canonical catalog.
|
|
210
|
+
*/
|
|
211
|
+
formats: readonly FileFormat[];
|
|
212
|
+
/**
|
|
213
|
+
* Maximum accepted size of one uploaded file measured in bytes.
|
|
214
|
+
* Files exceeding this boundary receive the stable size error key.
|
|
215
|
+
*/
|
|
216
|
+
maxFileSize: number;
|
|
217
|
+
/**
|
|
218
|
+
* Optional maximum number of retained files after accepting the batch.
|
|
219
|
+
* Omitting this value leaves collection capacity unrestricted.
|
|
220
|
+
*/
|
|
221
|
+
maxFilesCount?: number;
|
|
222
|
+
}>;
|
|
223
|
+
/**
|
|
224
|
+
* Accepted files and the final rejection encountered in one batch.
|
|
225
|
+
* Valid entries remain available when another entry fails validation.
|
|
226
|
+
*/
|
|
227
|
+
type UploadFilesValidationResult = Readonly<{
|
|
228
|
+
/**
|
|
229
|
+
* Valid incoming files that fit the remaining collection capacity.
|
|
230
|
+
* Accepted file objects preserve their original identity and ordering.
|
|
231
|
+
*/
|
|
232
|
+
acceptedFiles: File[];
|
|
233
|
+
/**
|
|
234
|
+
* Final stable rejection encountered while processing the incoming batch.
|
|
235
|
+
* The field remains absent when every supplied file is accepted.
|
|
236
|
+
*/
|
|
237
|
+
validationError?: UploadValidationError;
|
|
238
|
+
}>;
|
|
239
|
+
/**
|
|
240
|
+
* Options used to construct one reusable Zod file schema.
|
|
241
|
+
* Extension fallback is disabled by default to preserve strict MIME checks.
|
|
242
|
+
*/
|
|
243
|
+
type ZodUploadFileSchemaOptions = Readonly<{
|
|
244
|
+
/**
|
|
245
|
+
* Supported formats accepted by the generated file schema.
|
|
246
|
+
* Strict MIME validation uses metadata from the canonical format catalog.
|
|
247
|
+
*/
|
|
248
|
+
formats: readonly FileFormat[];
|
|
249
|
+
/**
|
|
250
|
+
* Maximum accepted file size measured in bytes for the generated file schema.
|
|
251
|
+
* Empty files remain invalid independently of this configured boundary.
|
|
252
|
+
*/
|
|
253
|
+
maxFileSize: number;
|
|
254
|
+
/**
|
|
255
|
+
* Allows a supported extension to compensate for missing MIME metadata.
|
|
256
|
+
* The fallback remains disabled by default to preserve strict validation.
|
|
257
|
+
*/
|
|
258
|
+
extensionFallback?: boolean;
|
|
259
|
+
}>;
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Builds a reusable Zod schema for one uploaded file with format and size.
|
|
263
|
+
* MIME matching is strict unless extension fallback is explicitly enabled.
|
|
264
|
+
*/
|
|
265
|
+
declare function zodUploadFileSchema(options: ZodUploadFileSchemaOptions): z.ZodFile;
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Extracts a normalized trailing extension from a complete file name.
|
|
269
|
+
* Returns an empty string when no valid dot-delimited suffix exists.
|
|
270
|
+
*/
|
|
271
|
+
declare function getFileExtension(fileName: string): string;
|
|
272
|
+
/**
|
|
273
|
+
* Normalizes a configured extension to a lowercase dot-prefixed value.
|
|
274
|
+
* Existing prefixes remain intact so repeated normalization is stable.
|
|
275
|
+
*/
|
|
276
|
+
declare function normalizeFileExtension(extension: string): string;
|
|
277
|
+
/**
|
|
278
|
+
* Compares a concrete MIME type with an exact or wildcard configuration.
|
|
279
|
+
* Wildcards match every subtype belonging to the configured media group.
|
|
280
|
+
*/
|
|
281
|
+
declare function matchesMimeType(fileMimeType: string, configuredMimeType: string): boolean;
|
|
282
|
+
/**
|
|
283
|
+
* Checks whether a file MIME type belongs to one selected format.
|
|
284
|
+
* File names and extensions cannot make an unsupported MIME type valid.
|
|
285
|
+
*/
|
|
286
|
+
declare function isFileMimeTypeSupported(file: File, formats: readonly FileFormat[]): boolean;
|
|
287
|
+
/**
|
|
288
|
+
* Checks whether a file extension belongs to one selected format.
|
|
289
|
+
* The comparison is case-insensitive and requires a complete suffix.
|
|
290
|
+
*/
|
|
291
|
+
declare function isFileExtensionSupported(file: File, formats: readonly FileFormat[]): boolean;
|
|
292
|
+
/**
|
|
293
|
+
* Checks whether a file matches one configured MIME type or extension.
|
|
294
|
+
* This permissive predicate supports clients where MIME metadata is absent.
|
|
295
|
+
*/
|
|
296
|
+
declare function isFileFormatSupported(file: File, formats: readonly FileFormat[]): boolean;
|
|
297
|
+
/**
|
|
298
|
+
* Builds a native file-picker hint from configured MIME types and extensions.
|
|
299
|
+
* Duplicate values are removed while their configuration order is retained.
|
|
300
|
+
*/
|
|
301
|
+
declare function createUploadAccept(formats: readonly FileFormat[]): string;
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Validates one file against configured format and size constraints.
|
|
305
|
+
* Returns the first stable error key or nothing for a valid file.
|
|
306
|
+
*/
|
|
307
|
+
declare function validateUploadFile(file: File, formats: readonly FileFormat[], maxFileSize: number): UploadValidationError | undefined;
|
|
308
|
+
/**
|
|
309
|
+
* Validates an incoming collection while preserving every accepted file.
|
|
310
|
+
* Returns accepted entries and the final rejection key from the batch.
|
|
311
|
+
*/
|
|
312
|
+
declare function validateUploadFiles(incomingFiles: readonly File[], options: UploadFilesValidationOptions): UploadFilesValidationResult;
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Projects the current instant onto the wall-clock fields of another timezone.
|
|
316
|
+
* The timezone defaults to `Europe/London` when no option is provided.
|
|
111
317
|
*
|
|
112
|
-
*
|
|
113
|
-
*
|
|
318
|
+
* @example
|
|
319
|
+
* const londonTime = getZonedTime({ tz: 'Europe/London' });
|
|
114
320
|
*/
|
|
115
|
-
declare
|
|
321
|
+
declare function getZonedTime({ tz }?: {
|
|
116
322
|
tz?: string;
|
|
117
|
-
})
|
|
323
|
+
}): Date;
|
|
118
324
|
/**
|
|
119
|
-
*
|
|
325
|
+
* Formats the UTC offset of a timezone at the supplied date.
|
|
326
|
+
* An explicit date preserves daylight-saving and historical offset rules.
|
|
120
327
|
*
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
* offsets may change depending on daylight saving time or historical rules.
|
|
328
|
+
* @example
|
|
329
|
+
* getUTCOffset(date, 'Europe/Moscow'); // `(+3 UTC)`
|
|
124
330
|
*/
|
|
125
|
-
declare
|
|
331
|
+
declare function getUTCOffset(date: Date, tz: string): string;
|
|
126
332
|
/**
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
* The result includes both the local time and its UTC offset, making it suitable
|
|
130
|
-
* for UI labels, logs, bot messages, and other places where the user should see
|
|
131
|
-
* not only the time itself, but also the timezone context behind that value.
|
|
333
|
+
* Formats the current time and UTC offset in the selected timezone.
|
|
334
|
+
* The timezone defaults to `Europe/London` when no option is provided.
|
|
132
335
|
*
|
|
133
|
-
*
|
|
336
|
+
* @example
|
|
337
|
+
* getFormattedTime({ tz: 'Europe/Moscow' }); // `03:04:05 (+3 UTC)`
|
|
134
338
|
*/
|
|
135
|
-
declare
|
|
339
|
+
declare function getFormattedTime({ tz }?: {
|
|
136
340
|
tz?: string;
|
|
137
|
-
})
|
|
341
|
+
}): string;
|
|
138
342
|
/**
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
* By default, the result includes date, time, and UTC offset. This is useful for
|
|
142
|
-
* complete timestamps displayed in UI, bot messages, reports, or diagnostics.
|
|
343
|
+
* Formats the current date with optional time and UTC offset components.
|
|
344
|
+
* Time is included by default and uses the selected timezone for display.
|
|
143
345
|
*
|
|
144
|
-
*
|
|
145
|
-
*
|
|
346
|
+
* @example
|
|
347
|
+
* getFormattedDate({ tz: 'UTC', withTime: false }); // `02.01.2024`
|
|
146
348
|
*/
|
|
147
|
-
declare
|
|
349
|
+
declare function getFormattedDate({ tz, withTime, }?: {
|
|
148
350
|
tz?: string;
|
|
149
351
|
withTime?: boolean;
|
|
150
|
-
})
|
|
352
|
+
}): string;
|
|
151
353
|
/**
|
|
152
|
-
* Formats
|
|
153
|
-
*
|
|
154
|
-
* Unlike helpers that always use the current moment, this function accepts an
|
|
155
|
-
* explicit Date value and formats that exact point in time for another timezone.
|
|
354
|
+
* Formats an explicit instant in another timezone using the selected locale.
|
|
355
|
+
* The result includes localized date text, wall-clock time, and UTC offset.
|
|
156
356
|
*
|
|
157
|
-
*
|
|
357
|
+
* @example
|
|
358
|
+
* formatTime(date, { locale: ru, tz: 'Europe/Moscow' });
|
|
158
359
|
*/
|
|
159
|
-
declare
|
|
360
|
+
declare function formatTime(time: Date, { locale, tz }?: {
|
|
160
361
|
locale?: Locale;
|
|
161
362
|
tz?: string;
|
|
162
|
-
})
|
|
363
|
+
}): string;
|
|
163
364
|
|
|
164
365
|
/**
|
|
165
|
-
* Recursively replaces dots in a string with underscores
|
|
166
|
-
*
|
|
366
|
+
* Recursively replaces dots in a string literal with underscores.
|
|
367
|
+
* Every other character remains unchanged in the resulting type.
|
|
368
|
+
*
|
|
369
|
+
* @example
|
|
370
|
+
* type Key = ReplaceDotsWithUnderscores<'foo.bar.baz'>; // `foo_bar_baz`
|
|
167
371
|
*/
|
|
168
|
-
type ReplaceDotsWithUnderscores<
|
|
372
|
+
type ReplaceDotsWithUnderscores<TValue extends string> = TValue extends `${infer Head}.${infer Tail}` ? `${Head}_${ReplaceDotsWithUnderscores<Tail>}` : TValue;
|
|
169
373
|
/**
|
|
170
|
-
*
|
|
171
|
-
*
|
|
374
|
+
* Maps string literals to immutable uppercase enum-like keys.
|
|
375
|
+
* Dots become underscores while every value preserves its original literal.
|
|
376
|
+
*
|
|
377
|
+
* @example
|
|
378
|
+
* type Statuses = StringEnumRecord<readonly ['review.pending', 'published']>;
|
|
172
379
|
*/
|
|
173
|
-
type StringEnumRecord<
|
|
174
|
-
[Value in
|
|
380
|
+
type StringEnumRecord<TValues extends readonly string[]> = Readonly<{
|
|
381
|
+
[Value in TValues[number] as Uppercase<ReplaceDotsWithUnderscores<Value>>]: Value;
|
|
175
382
|
}>;
|
|
176
383
|
/**
|
|
177
384
|
* Creates an immutable enum-like record from a readonly string array.
|
|
178
|
-
*
|
|
385
|
+
* Keys are uppercased and every dot is replaced with an underscore.
|
|
386
|
+
*
|
|
387
|
+
* @example
|
|
388
|
+
* createStringEnumRecord(['foo.s', 'baz'] as const); // `{ FOO_S: 'foo.s', BAZ: 'baz' }`
|
|
179
389
|
*/
|
|
180
390
|
declare function createStringEnumRecord<const T extends readonly string[]>(values: T): StringEnumRecord<T>;
|
|
181
391
|
|
|
182
392
|
/**
|
|
183
|
-
*
|
|
184
|
-
*
|
|
393
|
+
* Resolves synchronous and asynchronous executions through one promise-based contract.
|
|
394
|
+
* Failures use the supplied fallback or propagate unchanged when none is available.
|
|
395
|
+
*
|
|
396
|
+
* @example
|
|
397
|
+
* await safeExecute(() => fetchData(), (error) => console.error(error));
|
|
398
|
+
*/
|
|
399
|
+
declare function safeExecute<T, E = never>(fn: () => Promise<T> | T, onError?: (error: unknown) => E | Promise<E>): Promise<T | E>;
|
|
400
|
+
/**
|
|
401
|
+
* Result returned after measuring one successful asynchronous execution.
|
|
402
|
+
* The original value is preserved beside its rounded millisecond duration.
|
|
185
403
|
*/
|
|
186
|
-
declare function safeExecute<T, E = never>(fn: () => Promise<T> | T, onError?: (error?: unknown) => E | Promise<E>): Promise<T | E>;
|
|
187
404
|
type MeasuredExecution<T> = {
|
|
188
|
-
/**
|
|
405
|
+
/**
|
|
406
|
+
* Value resolved by the measured execution without cloning or transformation.
|
|
407
|
+
* Its generic type remains identical to the original asynchronous result.
|
|
408
|
+
*/
|
|
189
409
|
result: T;
|
|
190
|
-
/**
|
|
410
|
+
/**
|
|
411
|
+
* Rounded wall-clock duration of the measured execution in milliseconds.
|
|
412
|
+
* The value is always collected after the supplied promise resolves.
|
|
413
|
+
*/
|
|
191
414
|
executionTime: number;
|
|
192
415
|
};
|
|
193
416
|
/**
|
|
194
|
-
*
|
|
195
|
-
*
|
|
417
|
+
* Measures an asynchronous execution while preserving its resolved result.
|
|
418
|
+
* Rejected executions propagate unchanged and do not produce a measurement.
|
|
196
419
|
*
|
|
197
|
-
*
|
|
420
|
+
* @example
|
|
198
421
|
* const { result, executionTime } = await measureExecutionTime(async () => {
|
|
199
422
|
* return await fetchData();
|
|
200
423
|
* });
|
|
201
424
|
* console.log(`Execution time: ${executionTime}ms`);
|
|
202
|
-
* ```
|
|
203
425
|
*/
|
|
204
426
|
declare function measureExecutionTime<T>(execution: () => Promise<T>): Promise<MeasuredExecution<T>>;
|
|
205
427
|
|
|
206
428
|
/**
|
|
207
|
-
* Creates a
|
|
208
|
-
*
|
|
429
|
+
* Creates a cryptographically sourced string from the alphanumeric set.
|
|
430
|
+
* Requested length defaults to `10` characters when omitted.
|
|
431
|
+
*
|
|
432
|
+
* @example
|
|
433
|
+
* generateRandomString(5); // `aZ3fG`
|
|
434
|
+
* generateRandomString(); // `G5kLm2P9sQ`
|
|
209
435
|
*/
|
|
210
436
|
declare function generateRandomString(length?: number): string;
|
|
211
437
|
|
|
212
438
|
/**
|
|
213
|
-
*
|
|
214
|
-
*
|
|
439
|
+
* Selects a terminal color function for one HTTP response status.
|
|
440
|
+
* Successes are green, client errors yellow, and server errors red.
|
|
441
|
+
*
|
|
442
|
+
* @example
|
|
443
|
+
* getColoredHTTPStatus(404)('404');
|
|
215
444
|
*/
|
|
216
445
|
declare function getColoredHTTPStatus(status: number): (text: string) => string;
|
|
217
446
|
/**
|
|
218
|
-
*
|
|
219
|
-
*
|
|
447
|
+
* Shared logging interface for informational, warning, and error messages.
|
|
448
|
+
* Every level supports a service label while errors may include a stack trace.
|
|
220
449
|
*/
|
|
221
450
|
declare const log: {
|
|
451
|
+
/**
|
|
452
|
+
* Writes an informational message with an optional service label.
|
|
453
|
+
* Missing service names use the shared `log` fallback label.
|
|
454
|
+
*/
|
|
222
455
|
info: (message: string, service?: string) => void;
|
|
456
|
+
/**
|
|
457
|
+
* Writes a warning message with an optional service label.
|
|
458
|
+
* Missing service names use the shared `log` fallback label.
|
|
459
|
+
*/
|
|
223
460
|
warn: (message: string, service?: string) => void;
|
|
461
|
+
/**
|
|
462
|
+
* Writes an error message with optional service and stack trace context.
|
|
463
|
+
* Stack traces are rendered beneath the primary error message when supplied.
|
|
464
|
+
*/
|
|
224
465
|
error: (message: string, service?: string, stack?: string) => void;
|
|
225
466
|
};
|
|
226
467
|
|
|
@@ -244,13 +485,13 @@ type Simplify<T> = {
|
|
|
244
485
|
* identifierSchema: z.string().min(1),
|
|
245
486
|
* });
|
|
246
487
|
*/
|
|
247
|
-
declare
|
|
488
|
+
declare function zodBulkSelectionSchema<const TIdentifierSchema extends z.ZodType<string | number>>({ identifierSchema, }: {
|
|
248
489
|
/**
|
|
249
490
|
* Schema used to validate every included or excluded entity identifier.
|
|
250
491
|
* Its transforms and exact inferred output are preserved in both branches.
|
|
251
492
|
*/
|
|
252
493
|
identifierSchema: TIdentifierSchema;
|
|
253
|
-
})
|
|
494
|
+
}): z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
254
495
|
mode: z.ZodLiteral<"include">;
|
|
255
496
|
identifiers: z.ZodArray<TIdentifierSchema>;
|
|
256
497
|
}, z.core.$strict>, z.ZodObject<{
|
|
@@ -278,30 +519,47 @@ type ZodBulkExcludeSelection<TIdentifier extends string | number = string> = Ext
|
|
|
278
519
|
mode: 'exclude';
|
|
279
520
|
}>;
|
|
280
521
|
|
|
281
|
-
/**
|
|
522
|
+
/**
|
|
523
|
+
* Configures the algorithm and default expiration used by `ZodJWTService`.
|
|
524
|
+
* Omitted values fall back to `HS256` and fifteen minutes respectively.
|
|
525
|
+
*/
|
|
282
526
|
type JWTServiceOptions = {
|
|
283
|
-
/**
|
|
527
|
+
/**
|
|
528
|
+
* Symmetric algorithm used to sign and verify every token handled by the service.
|
|
529
|
+
* Defaults to `HS256` when the configuration does not provide another value.
|
|
530
|
+
*/
|
|
284
531
|
algorithm?: SymmetricAlgorithm;
|
|
285
|
-
/**
|
|
532
|
+
/**
|
|
533
|
+
* Default lifetime assigned to signed tokens, expressed in seconds.
|
|
534
|
+
* Defaults to `900` seconds, which is equivalent to fifteen minutes.
|
|
535
|
+
*/
|
|
286
536
|
defaultExpirationSeconds?: number;
|
|
287
537
|
};
|
|
288
|
-
/**
|
|
538
|
+
/**
|
|
539
|
+
* Configures one JWT signing operation without changing service defaults.
|
|
540
|
+
* A supplied expiration takes precedence over `defaultExpirationSeconds`.
|
|
541
|
+
*/
|
|
289
542
|
type JWTSignOptions = {
|
|
290
|
-
/**
|
|
543
|
+
/**
|
|
544
|
+
* Lifetime assigned to the token created by this signing operation.
|
|
545
|
+
* Overrides the service default and remains expressed in seconds.
|
|
546
|
+
*/
|
|
291
547
|
expiresInSeconds?: number;
|
|
292
548
|
};
|
|
293
549
|
/**
|
|
294
|
-
*
|
|
550
|
+
* Resolves the parsed payload of a Zod schema or preserves a direct payload type.
|
|
551
|
+
* Schema transforms are reflected in the resulting inferred payload.
|
|
295
552
|
*/
|
|
296
553
|
type Payload<T> = T extends z$1.ZodType ? z$1.infer<T> : T;
|
|
297
554
|
/**
|
|
298
|
-
*
|
|
299
|
-
*
|
|
555
|
+
* Preserves a Zod payload schema and rejects direct payload types with `never`.
|
|
556
|
+
* The result controls whether runtime payload validation is available.
|
|
557
|
+
*/
|
|
300
558
|
type PayloadSchema<T> = T extends z$1.ZodType ? T : never;
|
|
301
559
|
|
|
302
560
|
/**
|
|
303
|
-
*
|
|
304
|
-
*
|
|
561
|
+
* Signs, decodes, and verifies JWTs with optional Zod payload validation.
|
|
562
|
+
* A supplied schema parses payloads returned by decoding and verification.
|
|
305
563
|
*/
|
|
306
564
|
declare class ZodJWTService<TPayloadOrSchema> {
|
|
307
565
|
readonly payloadSchema: PayloadSchema<TPayloadOrSchema> | undefined;
|
|
@@ -309,23 +567,27 @@ declare class ZodJWTService<TPayloadOrSchema> {
|
|
|
309
567
|
protected readonly defaultExpirationSeconds: number;
|
|
310
568
|
constructor(payloadSchema?: PayloadSchema<TPayloadOrSchema>, options?: JWTServiceOptions);
|
|
311
569
|
/**
|
|
312
|
-
* Signs a
|
|
313
|
-
*
|
|
570
|
+
* Signs a payload using the configured algorithm and expiration settings.
|
|
571
|
+
* Per-call expiration overrides the default configured by the service.
|
|
572
|
+
*
|
|
573
|
+
* @example
|
|
574
|
+
* const token = await jwtService.sign({ userId: '123' }, secret, { expiresInSeconds: 300 });
|
|
314
575
|
*/
|
|
315
576
|
sign(payload: Payload<TPayloadOrSchema>, secret: string, options?: JWTSignOptions): Promise<string>;
|
|
316
577
|
/**
|
|
317
|
-
* Decodes
|
|
318
|
-
*
|
|
578
|
+
* Decodes without authenticating it and parses its payload when a schema exists.
|
|
579
|
+
* Schema validation failures return `null`, while malformed tokens still reject.
|
|
319
580
|
*
|
|
320
|
-
*
|
|
581
|
+
* @example
|
|
582
|
+
* const payload = await jwtService.decode(token);
|
|
321
583
|
*/
|
|
322
584
|
decode(token: string): Promise<Payload<TPayloadOrSchema> | null>;
|
|
323
585
|
/**
|
|
324
|
-
* Verifies
|
|
325
|
-
*
|
|
586
|
+
* Verifies a token using the configured algorithm and parses its payload.
|
|
587
|
+
* Signature, expiration, and schema validation failures reject the operation.
|
|
326
588
|
*
|
|
327
|
-
*
|
|
328
|
-
*
|
|
589
|
+
* @example
|
|
590
|
+
* const payload = await jwtService.verifyOrThrow(token, secret);
|
|
329
591
|
*/
|
|
330
592
|
verifyOrThrow(token: string, secret: string): Promise<Payload<TPayloadOrSchema>>;
|
|
331
593
|
}
|
|
@@ -462,11 +724,10 @@ type AsQuery<T> = T extends null | undefined ? T : T extends QueryPrimitive ? st
|
|
|
462
724
|
* This is useful for scenarios where you want to enforce that at least one
|
|
463
725
|
* of several optional (nullable) properties must be provided in an object.
|
|
464
726
|
*
|
|
465
|
-
*
|
|
727
|
+
* @example
|
|
466
728
|
* type Example = AtLeastOne<{ a?: string; b?: number; c?: boolean }>;
|
|
467
729
|
* // Valid: { a: "hello" }, { b: 42 }, { c: true }, { a: "hello", b: 42 }
|
|
468
730
|
* // Invalid: {}, { a: undefined, b: undefined, c: undefined }
|
|
469
|
-
* ```
|
|
470
731
|
*/
|
|
471
732
|
type AtLeastOne<T, Keys extends keyof T = keyof T> = Keys extends keyof T ? Simplify<Required<Pick<T, Keys>> & Partial<Omit<T, Keys>>> : never;
|
|
472
733
|
|
|
@@ -476,9 +737,8 @@ type AtLeastOne<T, Keys extends keyof T = keyof T> = Keys extends keyof T ? Simp
|
|
|
476
737
|
* 2. A `.transform()` that narrows the output type to `AtLeastOne<T>`,
|
|
477
738
|
* making it directly assignable to domain `*Select` and `*Update` types without casting.
|
|
478
739
|
*
|
|
479
|
-
*
|
|
740
|
+
* @example
|
|
480
741
|
* zQuery(zodAtLeastOne(userSelectSchema)) // result: UserSelect ✓
|
|
481
|
-
* ```
|
|
482
742
|
*/
|
|
483
743
|
declare const zodAtLeastOne: <T extends ZodObject<ZodRawShape>>(schema: T) => z.ZodPipe<T, z.ZodTransform<Awaited<AtLeastOne<z.core.output<T>>>, z.core.output<T>>>;
|
|
484
744
|
|
|
@@ -490,7 +750,7 @@ declare const parseQueryValue: (value: unknown) => unknown;
|
|
|
490
750
|
* numbers or booleans. This helper converts only clear primitive values, allowing
|
|
491
751
|
* regular schemas like `z.number()` and `z.boolean()` to validate query input directly.
|
|
492
752
|
*
|
|
493
|
-
*
|
|
753
|
+
* @example
|
|
494
754
|
* const schema = asQuery(z.object({
|
|
495
755
|
* page: z.number().int().positive(),
|
|
496
756
|
* isActive: z.boolean(),
|
|
@@ -498,7 +758,6 @@ declare const parseQueryValue: (value: unknown) => unknown;
|
|
|
498
758
|
*
|
|
499
759
|
* schema.parse({ page: '2', isActive: 'true' });
|
|
500
760
|
* // { page: 2, isActive: true }
|
|
501
|
-
* ```
|
|
502
761
|
*/
|
|
503
762
|
declare const asQuery: <T extends z.ZodTypeAny>(schema: T) => z.ZodPreprocess<T>;
|
|
504
763
|
|
|
@@ -508,4 +767,4 @@ declare const asQuery: <T extends z.ZodTypeAny>(schema: T) => z.ZodPreprocess<T>
|
|
|
508
767
|
*/
|
|
509
768
|
declare const isPlainObject: (value: unknown) => value is Record<string, unknown>;
|
|
510
769
|
|
|
511
|
-
export { type APIContractData, type APIContractError, type APIContractResult, type APIError, type APISuccess, type AsQuery, type AtLeastOne, EXCEPTION_STATUS_CODES, type ExceptionStatusCode, type FetchResult, type JWTServiceOptions, type JWTSignOptions, type MeasuredExecution, type Payload, type PayloadSchema, type ReplaceDotsWithUnderscores, SUCCESS_STATUS_CODES, type Simplify, type StringEnumRecord, type SuccessStatusCode, type ZodBulkExcludeSelection, type ZodBulkIncludeSelection, type ZodBulkSelection, ZodJWTService, type ZodPaginationOptions, type ZodSearchSchemaOptions, asQuery, createStringEnumRecord, failure, fetchAndThrow, fetchSafely, fileRespond, formatTime, generateRandomString, getColoredHTTPStatus, getFormattedDate, getFormattedTime, getUTCOffset, getZonedTime, honoLoggingHandler, isPlainObject, log, measureExecutionTime, onHandlerError, parseQueryValue, respond, safeExecute, sqlWhere, success, zodAtLeastOne, zodBulkSelectionSchema, zodPaginationSchema, zodPaginationShape, zodSearchSchema };
|
|
770
|
+
export { type APIContractData, type APIContractError, type APIContractResult, type APIError, type APISuccess, type AsQuery, type AtLeastOne, EXCEPTION_STATUS_CODES, type ExceptionStatusCode, type FetchResult, type FileFormat, type FileFormatConfig, type HonoFileRespondOptions, type HonoRespondOptions, type JWTServiceOptions, type JWTSignOptions, type MeasuredExecution, type Payload, type PayloadSchema, type ReplaceDotsWithUnderscores, SUCCESS_STATUS_CODES, type Simplify, type StringEnumRecord, type SuccessStatusCode, type UploadFilesValidationOptions, type UploadFilesValidationResult, type UploadValidationError, type ZodBulkExcludeSelection, type ZodBulkIncludeSelection, type ZodBulkSelection, ZodJWTService, type ZodPaginationOptions, type ZodSearchSchemaOptions, type ZodUploadFileSchemaOptions, asQuery, createStringEnumRecord, createUploadAccept, createZodSearchWhereSchema, failure, fetchAndThrow, fetchSafely, fileFormat, fileFormatsArray, fileFormatsConfig, fileFormatsRecord, fileRespond, formatTime, generateRandomString, getColoredHTTPStatus, getFileExtension, getFormattedDate, getFormattedTime, getUTCOffset, getZonedTime, honoLoggingHandler, isFileExtensionSupported, isFileFormatSupported, isFileMimeTypeSupported, isPlainObject, log, matchesMimeType, measureExecutionTime, normalizeFileExtension, onHandlerError, parseQueryValue, respond, safeExecute, sqlWhere, success, uploadValidationError, uploadValidationErrorsArray, uploadValidationErrorsRecord, validateUploadFile, validateUploadFiles, zodAtLeastOne, zodBulkSelectionSchema, zodPaginationSchema, zodPaginationShape, zodSearchQuerySchema, zodSearchSchema, zodUploadFileSchema };
|
package/dist/index.js
CHANGED
|
@@ -40,50 +40,64 @@ import { blue, dim, green, red, white, yellow } from "kleur/colors";
|
|
|
40
40
|
import { format } from "date-fns";
|
|
41
41
|
import { getTimezoneOffset, toZonedTime } from "date-fns-tz";
|
|
42
42
|
import { ru } from "date-fns/locale";
|
|
43
|
-
|
|
43
|
+
function getZonedTime({ tz = "Europe/London" } = {}) {
|
|
44
44
|
return toZonedTime(/* @__PURE__ */ new Date(), tz);
|
|
45
|
-
}
|
|
46
|
-
|
|
45
|
+
}
|
|
46
|
+
function getUTCOffset(date, tz) {
|
|
47
47
|
const offset = getTimezoneOffset(tz, date) / (60 * 60 * 1e3);
|
|
48
48
|
return `(${offset >= 0 ? "+" : ""}${offset} UTC)`;
|
|
49
|
-
}
|
|
50
|
-
|
|
49
|
+
}
|
|
50
|
+
function getFormattedTime({ tz = "Europe/London" } = {}) {
|
|
51
51
|
const zonedTime = getZonedTime({ tz });
|
|
52
52
|
return `${format(zonedTime, "HH:mm:ss")} ${getUTCOffset(zonedTime, tz)}`;
|
|
53
|
-
}
|
|
54
|
-
|
|
53
|
+
}
|
|
54
|
+
function getFormattedDate({ tz = "Europe/London", withTime = true } = {}) {
|
|
55
55
|
const zonedTime = getZonedTime({ tz });
|
|
56
56
|
const pattern = withTime ? "dd.MM.yyyy HH:mm:ss" : "dd.MM.yyyy";
|
|
57
57
|
const formatted = format(zonedTime, pattern);
|
|
58
58
|
return `${formatted}${withTime ? ` ${getUTCOffset(zonedTime, tz)}` : ""}`;
|
|
59
|
-
}
|
|
60
|
-
|
|
59
|
+
}
|
|
60
|
+
function formatTime(time, { locale = ru, tz = "Europe/London" } = {}) {
|
|
61
61
|
const zonedTime = toZonedTime(time, tz);
|
|
62
62
|
return `${format(zonedTime, "HH:mm:ss, d MMMM yyyy", { locale })} ${getUTCOffset(zonedTime, tz)}`;
|
|
63
|
-
}
|
|
63
|
+
}
|
|
64
64
|
|
|
65
65
|
// src/utilities/logging.utilities.ts
|
|
66
|
+
var HTTP_STATUS_COLORS = [
|
|
67
|
+
{ range: [200, 299], color: green },
|
|
68
|
+
{ range: [400, 499], color: yellow },
|
|
69
|
+
{ range: [500, 599], color: red }
|
|
70
|
+
];
|
|
71
|
+
var LOG_LEVEL_COLORS = { info: blue, warn: yellow, error: red };
|
|
66
72
|
function getColoredHTTPStatus(status) {
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
];
|
|
72
|
-
const statusColor = colorMap.find(({ range: [min, max] }) => status >= min && status <= max);
|
|
73
|
-
return statusColor ? statusColor.colorFn : (text) => text;
|
|
73
|
+
const statusColor = HTTP_STATUS_COLORS.find(({ range: [minimum, maximum] }) => {
|
|
74
|
+
return status >= minimum && status <= maximum;
|
|
75
|
+
});
|
|
76
|
+
return statusColor ? statusColor.color : (text) => text;
|
|
74
77
|
}
|
|
75
78
|
function writeLog(message, level, service = "log", stack) {
|
|
76
|
-
const logColorMap = { info: blue, warn: yellow, error: red };
|
|
77
79
|
const timestamp = dim(getFormattedTime());
|
|
78
|
-
const serviceName =
|
|
80
|
+
const serviceName = LOG_LEVEL_COLORS[level](service.padEnd(12));
|
|
79
81
|
const formattedMessage = white(message);
|
|
80
82
|
console.log(`[${timestamp}] ${serviceName} | ${formattedMessage}`);
|
|
81
83
|
if (stack)
|
|
82
84
|
console.log(`[${timestamp}] ${red("\u21B3 trace").padEnd(18)} | ${dim(stack)}`);
|
|
83
85
|
}
|
|
84
86
|
var log = {
|
|
87
|
+
/**
|
|
88
|
+
* Writes an informational message with an optional service label.
|
|
89
|
+
* Missing service names use the shared `log` fallback label.
|
|
90
|
+
*/
|
|
85
91
|
info: (message, service) => writeLog(message, "info", service),
|
|
92
|
+
/**
|
|
93
|
+
* Writes a warning message with an optional service label.
|
|
94
|
+
* Missing service names use the shared `log` fallback label.
|
|
95
|
+
*/
|
|
86
96
|
warn: (message, service) => writeLog(message, "warn", service),
|
|
97
|
+
/**
|
|
98
|
+
* Writes an error message with optional service and stack trace context.
|
|
99
|
+
* Stack traces are rendered beneath the primary error message when supplied.
|
|
100
|
+
*/
|
|
87
101
|
error: (message, service, stack) => writeLog(message, "error", service, stack)
|
|
88
102
|
};
|
|
89
103
|
|
|
@@ -116,14 +130,14 @@ var honoLoggingHandler = async (c, next) => {
|
|
|
116
130
|
// Otherwise, we read the request body as text and normalize whitespace.
|
|
117
131
|
(await c.req.raw.clone().text()).replaceAll(/\s+/g, " ").trim()
|
|
118
132
|
);
|
|
119
|
-
const bodyPreview = body.length >
|
|
133
|
+
const bodyPreview = body.length > 80 ? `${body.slice(0, 40)}\u2026${body.slice(-40)}` : body;
|
|
120
134
|
const startTime = performance.now();
|
|
121
135
|
await next();
|
|
122
136
|
const duration = Math.round(performance.now() - startTime);
|
|
123
137
|
const coloredMethod = bold(blue2(c.req.method.padEnd(4)));
|
|
124
138
|
const coloredStatus = getColoredHTTPStatus(c.res.status)(String(c.res.status).padEnd(4));
|
|
125
139
|
const coloredTime = dim2(`${duration}ms`.padStart(6));
|
|
126
|
-
const coloredPath = white2(c.req.path.padEnd(
|
|
140
|
+
const coloredPath = white2(c.req.path.padEnd(32));
|
|
127
141
|
const coloredSearchParams = searchParams ? dim2(` (${searchParams})`) : "";
|
|
128
142
|
const coloredBody = bodyPreview ? dim2(` ${bodyPreview}`) : "";
|
|
129
143
|
log.info(`${coloredMethod} ${coloredStatus} ${coloredTime} ${coloredPath}${coloredSearchParams}${coloredBody}`, "hono");
|
|
@@ -162,14 +176,115 @@ function createStringEnumRecord(values) {
|
|
|
162
176
|
return Object.freeze(Object.fromEntries(values.map((value) => [value.replace(/\./g, "_").toUpperCase(), value])));
|
|
163
177
|
}
|
|
164
178
|
|
|
179
|
+
// src/upload/upload.enums.ts
|
|
180
|
+
var fileFormatsArray = [
|
|
181
|
+
...["png", "jpg", "webp", "avif", "heic"],
|
|
182
|
+
// Image formats.
|
|
183
|
+
...["pdf", "rtf", "txt"]
|
|
184
|
+
// Text and document formats.
|
|
185
|
+
];
|
|
186
|
+
var fileFormatsRecord = createStringEnumRecord(fileFormatsArray);
|
|
187
|
+
var fileFormat = fileFormatsRecord;
|
|
188
|
+
var uploadValidationErrorsArray = [
|
|
189
|
+
"empty_file",
|
|
190
|
+
"unsupported_file_format",
|
|
191
|
+
"file_size_exceeded",
|
|
192
|
+
"files_count_exceeded"
|
|
193
|
+
];
|
|
194
|
+
var uploadValidationErrorsRecord = createStringEnumRecord(uploadValidationErrorsArray);
|
|
195
|
+
var uploadValidationError = uploadValidationErrorsRecord;
|
|
196
|
+
|
|
197
|
+
// src/upload/upload.constants.ts
|
|
198
|
+
var fileFormatsConfig = {
|
|
199
|
+
// Image formats.
|
|
200
|
+
[fileFormatsRecord.PNG]: { name: "PNG", mimeTypes: ["image/png"], extensions: [".png"] },
|
|
201
|
+
[fileFormatsRecord.JPG]: { name: "JPG", mimeTypes: ["image/jpeg"], extensions: [".jpg", ".jpeg"] },
|
|
202
|
+
[fileFormatsRecord.WEBP]: { name: "WEBP", mimeTypes: ["image/webp"], extensions: [".webp"] },
|
|
203
|
+
[fileFormatsRecord.AVIF]: { name: "AVIF", mimeTypes: ["image/avif"], extensions: [".avif"] },
|
|
204
|
+
[fileFormatsRecord.HEIC]: { name: "HEIC", mimeTypes: ["image/heic", "image/heif"], extensions: [".heic", ".heif"] },
|
|
205
|
+
// Text and document formats.
|
|
206
|
+
[fileFormatsRecord.PDF]: { name: "PDF", mimeTypes: ["application/pdf"], extensions: [".pdf"] },
|
|
207
|
+
[fileFormatsRecord.RTF]: { name: "RTF", mimeTypes: ["application/rtf"], extensions: [".rtf"] },
|
|
208
|
+
[fileFormatsRecord.TXT]: { name: "TXT", mimeTypes: ["text/plain"], extensions: [".txt"] }
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
// src/upload/upload.schemas.ts
|
|
212
|
+
import { z } from "zod";
|
|
213
|
+
|
|
214
|
+
// src/upload/upload.utilities.ts
|
|
215
|
+
function getFileExtension(fileName) {
|
|
216
|
+
const extensionIndex = fileName.lastIndexOf(".");
|
|
217
|
+
return extensionIndex > 0 && extensionIndex < fileName.length - 1 ? fileName.slice(extensionIndex).toLowerCase() : "";
|
|
218
|
+
}
|
|
219
|
+
function normalizeFileExtension(extension) {
|
|
220
|
+
const normalizedExtension = extension.toLowerCase();
|
|
221
|
+
return normalizedExtension.startsWith(".") ? normalizedExtension : `.${normalizedExtension}`;
|
|
222
|
+
}
|
|
223
|
+
function matchesMimeType(fileMimeType, configuredMimeType) {
|
|
224
|
+
if (configuredMimeType.endsWith("/*"))
|
|
225
|
+
return fileMimeType.startsWith(configuredMimeType.slice(0, -1));
|
|
226
|
+
return fileMimeType === configuredMimeType;
|
|
227
|
+
}
|
|
228
|
+
function isFileMimeTypeSupported(file, formats) {
|
|
229
|
+
return formats.some((format2) => fileFormatsConfig[format2].mimeTypes.some((mimeType) => matchesMimeType(file.type, mimeType)));
|
|
230
|
+
}
|
|
231
|
+
function isFileExtensionSupported(file, formats) {
|
|
232
|
+
const fileExtension = getFileExtension(file.name);
|
|
233
|
+
return formats.some((format2) => fileFormatsConfig[format2].extensions.some((extension) => normalizeFileExtension(extension) === fileExtension));
|
|
234
|
+
}
|
|
235
|
+
function isFileFormatSupported(file, formats) {
|
|
236
|
+
return isFileMimeTypeSupported(file, formats) || isFileExtensionSupported(file, formats);
|
|
237
|
+
}
|
|
238
|
+
function createUploadAccept(formats) {
|
|
239
|
+
const acceptedValues = formats.flatMap((format2) => [
|
|
240
|
+
...fileFormatsConfig[format2].mimeTypes,
|
|
241
|
+
...fileFormatsConfig[format2].extensions
|
|
242
|
+
]);
|
|
243
|
+
return [...new Set(acceptedValues)].join(",");
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// src/upload/upload.schemas.ts
|
|
247
|
+
function zodUploadFileSchema(options) {
|
|
248
|
+
const { formats, maxFileSize, extensionFallback = false } = options;
|
|
249
|
+
return z.file().min(1, uploadValidationError.EMPTY_FILE).max(maxFileSize, uploadValidationError.FILE_SIZE_EXCEEDED).refine((file) => extensionFallback ? isFileFormatSupported(file, formats) : isFileMimeTypeSupported(file, formats), uploadValidationError.UNSUPPORTED_FILE_FORMAT);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// src/upload/upload.validation.ts
|
|
253
|
+
function validateUploadFile(file, formats, maxFileSize) {
|
|
254
|
+
if (file.size === 0)
|
|
255
|
+
return uploadValidationError.EMPTY_FILE;
|
|
256
|
+
if (isFileFormatSupported(file, formats) === false)
|
|
257
|
+
return uploadValidationError.UNSUPPORTED_FILE_FORMAT;
|
|
258
|
+
if (file.size > maxFileSize)
|
|
259
|
+
return uploadValidationError.FILE_SIZE_EXCEEDED;
|
|
260
|
+
return void 0;
|
|
261
|
+
}
|
|
262
|
+
function validateUploadFiles(incomingFiles, options) {
|
|
263
|
+
const { currentFilesCount, formats, maxFileSize, maxFilesCount } = options;
|
|
264
|
+
const availableFilesCount = maxFilesCount === void 0 ? Infinity : maxFilesCount - currentFilesCount;
|
|
265
|
+
const acceptedFiles = [];
|
|
266
|
+
let validationError;
|
|
267
|
+
for (const file of incomingFiles) {
|
|
268
|
+
const fileValidationError = validateUploadFile(file, formats, maxFileSize) ?? // Collection limits apply only after intrinsic file validation succeeds.
|
|
269
|
+
// This preserves remaining slots for supported files from the same batch.
|
|
270
|
+
(acceptedFiles.length >= availableFilesCount ? uploadValidationError.FILES_COUNT_EXCEEDED : void 0);
|
|
271
|
+
if (fileValidationError) {
|
|
272
|
+
validationError = fileValidationError;
|
|
273
|
+
continue;
|
|
274
|
+
}
|
|
275
|
+
acceptedFiles.push(file);
|
|
276
|
+
}
|
|
277
|
+
return { acceptedFiles, validationError };
|
|
278
|
+
}
|
|
279
|
+
|
|
165
280
|
// src/utilities/execution.utilities.ts
|
|
166
281
|
async function safeExecute(fn, onError) {
|
|
167
282
|
try {
|
|
168
283
|
return await fn();
|
|
169
|
-
} catch (
|
|
284
|
+
} catch (error) {
|
|
170
285
|
if (onError)
|
|
171
|
-
return await onError(
|
|
172
|
-
throw
|
|
286
|
+
return await onError(error);
|
|
287
|
+
throw error;
|
|
173
288
|
}
|
|
174
289
|
}
|
|
175
290
|
async function measureExecutionTime(execution) {
|
|
@@ -180,19 +295,19 @@ async function measureExecutionTime(execution) {
|
|
|
180
295
|
}
|
|
181
296
|
|
|
182
297
|
// src/zod-bulk/zod-bulk.schemas.ts
|
|
183
|
-
import { z } from "zod";
|
|
298
|
+
import { z as z2 } from "zod";
|
|
184
299
|
var DEFAULT_ERROR_MESSAGE = "Selection identifiers must be provided.";
|
|
185
|
-
|
|
186
|
-
const identifiersSchema =
|
|
300
|
+
function zodBulkSelectionSchema({ identifierSchema }) {
|
|
301
|
+
const identifiersSchema = z2.array(identifierSchema).refine((identifiers) => new Set(identifiers).size === identifiers.length, {
|
|
187
302
|
message: DEFAULT_ERROR_MESSAGE
|
|
188
303
|
});
|
|
189
|
-
return
|
|
190
|
-
|
|
191
|
-
|
|
304
|
+
return z2.discriminatedUnion("mode", [
|
|
305
|
+
z2.object({ mode: z2.literal("include"), identifiers: identifiersSchema }).strict(),
|
|
306
|
+
z2.object({ mode: z2.literal("exclude"), excludedIdentifiers: identifiersSchema }).strict()
|
|
192
307
|
]);
|
|
193
|
-
}
|
|
308
|
+
}
|
|
194
309
|
|
|
195
|
-
// src/zod-jwt/zod-jwt.
|
|
310
|
+
// src/zod-jwt/zod-jwt.services.ts
|
|
196
311
|
import { decode as decodeJWT, sign as signJWT, verify as verifyJWT } from "hono/jwt";
|
|
197
312
|
var ZodJWTService = class {
|
|
198
313
|
payloadSchema;
|
|
@@ -204,37 +319,41 @@ var ZodJWTService = class {
|
|
|
204
319
|
this.defaultExpirationSeconds = options?.defaultExpirationSeconds ?? 60 * 15;
|
|
205
320
|
}
|
|
206
321
|
/**
|
|
207
|
-
* Signs a
|
|
208
|
-
*
|
|
322
|
+
* Signs a payload using the configured algorithm and expiration settings.
|
|
323
|
+
* Per-call expiration overrides the default configured by the service.
|
|
324
|
+
*
|
|
325
|
+
* @example
|
|
326
|
+
* const token = await jwtService.sign({ userId: '123' }, secret, { expiresInSeconds: 300 });
|
|
209
327
|
*/
|
|
210
328
|
async sign(payload, secret, options) {
|
|
211
329
|
const exp = Math.floor(Date.now() / 1e3) + (options?.expiresInSeconds ?? this.defaultExpirationSeconds);
|
|
212
330
|
return signJWT({ ...payload, exp }, secret, this.algorithm);
|
|
213
331
|
}
|
|
214
332
|
/**
|
|
215
|
-
* Decodes
|
|
216
|
-
*
|
|
333
|
+
* Decodes without authenticating it and parses its payload when a schema exists.
|
|
334
|
+
* Schema validation failures return `null`, while malformed tokens still reject.
|
|
217
335
|
*
|
|
218
|
-
*
|
|
336
|
+
* @example
|
|
337
|
+
* const payload = await jwtService.decode(token);
|
|
219
338
|
*/
|
|
220
339
|
async decode(token) {
|
|
221
340
|
const { payload } = decodeJWT(token);
|
|
222
|
-
if (
|
|
341
|
+
if (this.payloadSchema === void 0) {
|
|
223
342
|
return payload;
|
|
224
343
|
}
|
|
225
344
|
const { success: success2, data } = await this.payloadSchema.safeParseAsync(payload);
|
|
226
345
|
return success2 ? data : null;
|
|
227
346
|
}
|
|
228
347
|
/**
|
|
229
|
-
* Verifies
|
|
230
|
-
*
|
|
348
|
+
* Verifies a token using the configured algorithm and parses its payload.
|
|
349
|
+
* Signature, expiration, and schema validation failures reject the operation.
|
|
231
350
|
*
|
|
232
|
-
*
|
|
233
|
-
*
|
|
351
|
+
* @example
|
|
352
|
+
* const payload = await jwtService.verifyOrThrow(token, secret);
|
|
234
353
|
*/
|
|
235
354
|
async verifyOrThrow(token, secret) {
|
|
236
355
|
const payload = await verifyJWT(token, secret, this.algorithm);
|
|
237
|
-
if (
|
|
356
|
+
if (this.payloadSchema === void 0) {
|
|
238
357
|
return payload;
|
|
239
358
|
}
|
|
240
359
|
return this.payloadSchema.parseAsync(payload);
|
|
@@ -242,23 +361,23 @@ var ZodJWTService = class {
|
|
|
242
361
|
};
|
|
243
362
|
|
|
244
363
|
// src/zod-search/zod-search.pagination.schemas.ts
|
|
245
|
-
import { z as
|
|
246
|
-
var zodPaginationSchema =
|
|
364
|
+
import { z as z3 } from "zod";
|
|
365
|
+
var zodPaginationSchema = z3.object({
|
|
247
366
|
/**
|
|
248
367
|
* Zero-based number of records skipped before collecting a result page.
|
|
249
368
|
* String values are coerced to support validation of URL query input.
|
|
250
369
|
*/
|
|
251
|
-
offset:
|
|
370
|
+
offset: z3.coerce.number().int().nonnegative().default(0),
|
|
252
371
|
/**
|
|
253
372
|
* Positive maximum number of records returned in a single result page.
|
|
254
373
|
* String values are coerced to support validation of URL query input.
|
|
255
374
|
*/
|
|
256
|
-
limit:
|
|
375
|
+
limit: z3.coerce.number().int().positive().default(10)
|
|
257
376
|
});
|
|
258
377
|
var zodPaginationShape = zodPaginationSchema.shape;
|
|
259
378
|
|
|
260
379
|
// src/zod-search/zod-search.schemas.ts
|
|
261
|
-
import { z as
|
|
380
|
+
import { z as z4 } from "zod";
|
|
262
381
|
|
|
263
382
|
// src/zod-validation/zod-validation.refiners.ts
|
|
264
383
|
var DEFAULT_ERROR_MESSAGE2 = "Invalid input. At least one field must be provided.";
|
|
@@ -269,7 +388,7 @@ var zodAtLeastOne = (schema) => schema.superRefine((val, ctx) => {
|
|
|
269
388
|
}).transform((val) => val);
|
|
270
389
|
|
|
271
390
|
// src/zod-search/zod-search.schemas.ts
|
|
272
|
-
var zodSearchQuerySchema =
|
|
391
|
+
var zodSearchQuerySchema = z4.string().trim().min(1).optional();
|
|
273
392
|
var createZodSearchWhereSchema = (filters) => zodAtLeastOne(filters.partial());
|
|
274
393
|
var zodSearchSchema = (options) => {
|
|
275
394
|
const whereSchema = options.filters !== void 0 ? createZodSearchWhereSchema(options.filters) : options.whereSchema;
|
|
@@ -278,11 +397,11 @@ var zodSearchSchema = (options) => {
|
|
|
278
397
|
...options.queryEnabled !== false ? { query: zodSearchQuerySchema } : {},
|
|
279
398
|
...options.paginationEnabled !== false ? { pagination: zodPaginationSchema } : {}
|
|
280
399
|
};
|
|
281
|
-
return
|
|
400
|
+
return z4.object(shape).strict();
|
|
282
401
|
};
|
|
283
402
|
|
|
284
403
|
// src/zod-validation/zod-validation.parsing.ts
|
|
285
|
-
import { z as
|
|
404
|
+
import { z as z5 } from "zod";
|
|
286
405
|
|
|
287
406
|
// src/zod-validation/zod-validation.utilities.ts
|
|
288
407
|
var isPlainObject = (value) => {
|
|
@@ -309,37 +428,56 @@ var parseQueryValue = (value) => {
|
|
|
309
428
|
return Number(normalized);
|
|
310
429
|
return value;
|
|
311
430
|
};
|
|
312
|
-
var asQuery = (schema) =>
|
|
431
|
+
var asQuery = (schema) => z5.preprocess(parseQueryValue, schema);
|
|
313
432
|
export {
|
|
314
433
|
EXCEPTION_STATUS_CODES,
|
|
315
434
|
SUCCESS_STATUS_CODES,
|
|
316
435
|
ZodJWTService,
|
|
317
436
|
asQuery,
|
|
318
437
|
createStringEnumRecord,
|
|
438
|
+
createUploadAccept,
|
|
439
|
+
createZodSearchWhereSchema,
|
|
319
440
|
failure,
|
|
320
441
|
fetchAndThrow,
|
|
321
442
|
fetchSafely,
|
|
443
|
+
fileFormat,
|
|
444
|
+
fileFormatsArray,
|
|
445
|
+
fileFormatsConfig,
|
|
446
|
+
fileFormatsRecord,
|
|
322
447
|
fileRespond,
|
|
323
448
|
formatTime,
|
|
324
449
|
generateRandomString,
|
|
325
450
|
getColoredHTTPStatus,
|
|
451
|
+
getFileExtension,
|
|
326
452
|
getFormattedDate,
|
|
327
453
|
getFormattedTime,
|
|
328
454
|
getUTCOffset,
|
|
329
455
|
getZonedTime,
|
|
330
456
|
honoLoggingHandler,
|
|
457
|
+
isFileExtensionSupported,
|
|
458
|
+
isFileFormatSupported,
|
|
459
|
+
isFileMimeTypeSupported,
|
|
331
460
|
isPlainObject,
|
|
332
461
|
log,
|
|
462
|
+
matchesMimeType,
|
|
333
463
|
measureExecutionTime,
|
|
464
|
+
normalizeFileExtension,
|
|
334
465
|
onHandlerError,
|
|
335
466
|
parseQueryValue,
|
|
336
467
|
respond,
|
|
337
468
|
safeExecute,
|
|
338
469
|
sqlWhere,
|
|
339
470
|
success,
|
|
471
|
+
uploadValidationError,
|
|
472
|
+
uploadValidationErrorsArray,
|
|
473
|
+
uploadValidationErrorsRecord,
|
|
474
|
+
validateUploadFile,
|
|
475
|
+
validateUploadFiles,
|
|
340
476
|
zodAtLeastOne,
|
|
341
477
|
zodBulkSelectionSchema,
|
|
342
478
|
zodPaginationSchema,
|
|
343
479
|
zodPaginationShape,
|
|
344
|
-
|
|
480
|
+
zodSearchQuerySchema,
|
|
481
|
+
zodSearchSchema,
|
|
482
|
+
zodUploadFileSchema
|
|
345
483
|
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kalutskii/foundation",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.9",
|
|
4
4
|
"description": "Typescript collection of most common utilities, schemas and functions among private projects.",
|
|
5
|
+
"license": "MIT",
|
|
5
6
|
"type": "module",
|
|
6
7
|
"repository": {
|
|
7
8
|
"type": "git",
|
|
@@ -12,10 +13,12 @@
|
|
|
12
13
|
},
|
|
13
14
|
"scripts": {
|
|
14
15
|
"build": "tsup",
|
|
16
|
+
"test": "bun test",
|
|
15
17
|
"typecheck": "tsc --noEmit",
|
|
16
18
|
"lint": "eslint . --ext .ts --fix",
|
|
19
|
+
"lint:check": "eslint . --ext .ts",
|
|
17
20
|
"format": "prettier --write .",
|
|
18
|
-
"
|
|
21
|
+
"format:check": "prettier --check ."
|
|
19
22
|
},
|
|
20
23
|
"exports": {
|
|
21
24
|
".": {
|
|
@@ -27,7 +30,6 @@
|
|
|
27
30
|
"dist"
|
|
28
31
|
],
|
|
29
32
|
"peerDependencies": {
|
|
30
|
-
"@hono/zod-validator": "^0.8.0",
|
|
31
33
|
"date-fns": "^4.1.0",
|
|
32
34
|
"date-fns-tz": "^3.2.0",
|
|
33
35
|
"drizzle-orm": "^1.0.0-beta.22",
|