@kalutskii/foundation 0.6.5 → 0.6.7

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/README.md CHANGED
@@ -5,11 +5,14 @@ Designed for use in Bun/Node.js and Cloudflare Workers environments.
5
5
 
6
6
  ## What this package provides
7
7
 
8
- - Typed API response contracts.
9
- - Response factories for success and failure payloads.
10
- - Safe async resolvers for contract-based fetch functions.
11
- - A Hono helper for typed JSON success responses.
12
- - Utility types/helpers for query parsing and Date serialization.
8
+ - Typed API response contracts with success/failure factories and safe resolvers.
9
+ - Hono helpers: typed JSON responses, global error handler, request logging middleware.
10
+ - Execution utilities: safe async execution and execution time measurement.
11
+ - Datetime utilities: timezone-aware formatting helpers.
12
+ - Logging utility: unified colored log interface.
13
+ - Random string generation utility.
14
+ - JWT service with optional Zod schema validation.
15
+ - Zod helpers for query param coercion, pagination schemas, and type utilities.
13
16
 
14
17
  ## Installation
15
18
 
@@ -32,8 +35,8 @@ Contract shape:
32
35
 
33
36
  Status code groups are exported as constants and used by types:
34
37
 
35
- - `SUCCESS_STATUS_CODES`
36
- - `EXCEPTION_STATUS_CODES`
38
+ - `SUCCESS_STATUS_CODES` — `[200, 201, 202, 307]`
39
+ - `EXCEPTION_STATUS_CODES` — `[400, 401, 403, 404, 405, 409, 500]`
37
40
 
38
41
  ## Usage
39
42
 
@@ -87,34 +90,132 @@ const app = new Hono();
87
90
  app.get('/health', (c) => {
88
91
  return respond(c, {
89
92
  status: 200,
90
- data: { ok: true, now: new Date() },
93
+ data: { ok: true },
91
94
  });
92
95
  });
93
96
  ```
94
97
 
95
- `respond` returns a typed JSON response contract and includes API error type in route output unions.
98
+ `respond` wraps `c.json` with a typed success payload and includes `APIError` in the route's output union.
96
99
 
97
- ### 4. Parse query params with helpers
100
+ ### 4. Wire up Hono error handler and logging middleware
98
101
 
99
102
  ```ts
100
- import { asQueryBoolean, asQueryNumber } from '@kalutskii/foundation';
103
+ import { honoLoggingHandler, onHandlerError } from '@kalutskii/foundation';
104
+ import { Hono } from 'hono';
105
+
106
+ const app = new Hono();
107
+
108
+ app.use('*', honoLoggingHandler);
109
+ app.onError(onHandlerError);
110
+ ```
111
+
112
+ `onHandlerError` catches all thrown errors, maps `HTTPException` (< 500) to their status codes, and returns a generic 500 with a unique error ID for unexpected errors.
113
+
114
+ `honoLoggingHandler` logs each request with method, status, duration, path, and query params.
115
+ Example: `[12:12:12 (+4 UTC)] hono | POST 200 123ms /api/v1/users (search=term)`
116
+
117
+ ### 5. Execute functions safely and measure performance
118
+
119
+ ```ts
120
+ import { measureExecutionTime, safeExecute } from '@kalutskii/foundation';
121
+
122
+ const result = await safeExecute(
123
+ () => fetchData(),
124
+ (error) => console.error(error)
125
+ );
126
+
127
+ const { result: data, executionTime } = await measureExecutionTime(() => fetchData());
128
+ console.log(`Done in ${executionTime}ms`);
129
+ ```
130
+
131
+ ### 6. Parse query params with `asQuery`
132
+
133
+ ```ts
134
+ import { asQuery } from '@kalutskii/foundation';
135
+ import { z } from 'zod';
136
+
137
+ const schema = z.object({
138
+ page: asQuery(z.number().int().positive()),
139
+ isActive: asQuery(z.boolean()),
140
+ });
141
+
142
+ schema.parse({ page: '2', isActive: 'true' }); // { page: 2, isActive: true }
143
+ ```
144
+
145
+ `asQuery` preprocesses string values from query parameters — coercing `'true'`/`'false'` to booleans and numeric strings to numbers — before passing them to the Zod schema for validation.
146
+
147
+ ### 7. Use pagination schema
148
+
149
+ ```ts
150
+ import { refinePagination, zodPaginationSchema, zodPaginationShape } from '@kalutskii/foundation';
151
+ import { z } from 'zod';
152
+
153
+ // Use as a standalone schema
154
+ const options = zodPaginationSchema.parse({ offset: '0', limit: '20' });
155
+ // { offset: 0, limit: 20 }
156
+
157
+ // Spread shape into a larger query schema
158
+ const querySchema = z.object({
159
+ search: z.string().optional(),
160
+ ...zodPaginationShape,
161
+ });
162
+
163
+ // Strip undefined pagination values before passing to a query builder (e.g. Drizzle)
164
+ await db.query.users.findMany({
165
+ ...refinePagination(options),
166
+ });
167
+ ```
168
+
169
+ ### 8. Work with JWT using `ZodJWTService`
170
+
171
+ ```ts
172
+ import { ZodJWTService } from '@kalutskii/foundation';
101
173
  import { z } from 'zod';
102
174
 
103
- const pageSchema = asQueryNumber(z.number().int().min(1));
104
- const enabledSchema = asQueryBoolean(z.boolean());
175
+ const payloadSchema = z.object({ userId: z.string() });
176
+ const jwt = new ZodJWTService(payloadSchema, { defaultExpirationSeconds: 3600 });
177
+
178
+ const token = await jwt.sign({ userId: '42' }, 'secret');
179
+ const payload = await jwt.verifyOrThrow(token, 'secret');
180
+ // payload: { userId: '42', exp: ... }
181
+
182
+ // decode without throwing (returns null on invalid token or schema mismatch)
183
+ const decoded = await jwt.decode(token);
184
+ ```
185
+
186
+ ### 9. Datetime helpers
187
+
188
+ ```ts
189
+ import { formatTime, getFormattedDate, getFormattedTime, getZonedTime } from '@kalutskii/foundation';
190
+
191
+ getFormattedTime({ tz: 'Europe/Moscow' }); // '15:30:00 (+3 UTC)'
192
+ getFormattedDate({ tz: 'Europe/Moscow' }); // '22.06.2026 15:30:00 (+3 UTC)'
193
+ getFormattedDate({ tz: 'Europe/Moscow', withTime: false }); // '22.06.2026'
194
+ formatTime(new Date(), { tz: 'Europe/Moscow' }); // '15:30:00, 22 июня 2026 (+3 UTC)'
195
+ ```
196
+
197
+ ### 10. Logging
198
+
199
+ ```ts
200
+ import { log } from '@kalutskii/foundation';
105
201
 
106
- pageSchema.parse('2'); // 2
107
- enabledSchema.parse('yes'); // true
108
- enabledSchema.parse('off'); // false
202
+ log.info('Server started', 'app');
203
+ log.warn('Deprecated call', 'auth');
204
+ log.error('Something failed', 'db', error.stack);
205
+ // [15:30:00 (+3 UTC)] app | Server started
109
206
  ```
110
207
 
111
208
  ## Exports
112
209
 
113
210
  The package exports all public APIs from a single entrypoint:
114
211
 
115
- - HTTP constants, schemas, factories, and resolvers.
116
- - Hono `respond` helper.
117
- - Serialization/query utilities, including `SerializeDates`.
212
+ - **HTTP**: `success`, `failure`, `fetchSafely`, `fetchAndThrow`, constants, schemas.
213
+ - **Hono**: `respond`, `onHandlerError`, `honoLoggingHandler`.
214
+ - **Utilities**: `safeExecute`, `measureExecutionTime`, `generateRandomString`, `log`, `getColoredHTTPStatus`, datetime helpers.
215
+ - **Zod JWT**: `ZodJWTService`.
216
+ - **Zod Pagination**: `zodPaginationSchema`, `zodPaginationShape`, `refinePagination`, `ZodPaginationOptions`.
217
+ - **Zod Validation**: `asQuery`, `AsQuery`, `AtLeastOne`.
218
+ - **Type Utilities**: `Simplify`.
118
219
 
119
220
  ## Development
120
221
 
package/dist/index.d.ts CHANGED
@@ -1,7 +1,24 @@
1
+ import { SQL } from 'drizzle-orm';
1
2
  import { ErrorHandler, MiddlewareHandler, Context, TypedResponse } from 'hono';
2
- import z from 'zod';
3
3
  import { Locale } from 'date-fns';
4
4
  import { SymmetricAlgorithm } from 'hono/utils/jwt/jwa';
5
+ import z from 'zod';
6
+
7
+ /**
8
+ * Builds a Drizzle SQL `where` condition from a plain object.
9
+ *
10
+ * Maps each `where` entry to `eq(table[key], value)` and combines them with `and`.
11
+ * Assumes that `where` was already validated and contains only existing table fields.
12
+ *
13
+ * ```ts
14
+ * await db
15
+ * .update(usersTable)
16
+ * .set(values)
17
+ * .where(sqlWhere(usersTable, { id: 1 }))
18
+ * .returning();
19
+ * ```
20
+ */
21
+ declare function sqlWhere(table: unknown, where: Record<string, unknown>): SQL | undefined;
5
22
 
6
23
  /**
7
24
  * Global error handler for Hono framework. Catches all exceptions thrown in route handlers and middlewares.
@@ -33,7 +50,10 @@ type APIError = {
33
50
  type APIContractResult<TData = void> = APISuccess<TData> | APIError;
34
51
  type APIContractData<TResult extends APIContractResult<unknown>> = TResult extends APISuccess<infer TData> ? TData : never;
35
52
  type APIContractError<TResult extends APIContractResult<unknown>> = Extract<TResult, APIError>;
36
- /** Discriminated union result of a fetch operation. If `error` is set, `data` is null and vice versa. */
53
+ /**
54
+ * Discriminated union result of a fetch operation.
55
+ * If `error` is set, `data` is null and vice versa.
56
+ */
37
57
  type FetchResult<T> = {
38
58
  error: null;
39
59
  data: T;
@@ -42,15 +62,36 @@ type FetchResult<T> = {
42
62
  data: null;
43
63
  };
44
64
 
65
+ /**
66
+ * Utility type that simplifies a given type T by flattening its structure.
67
+ * This is particularly useful for improving the readability of complex types.
68
+ */
69
+ type Simplify<T> = {
70
+ [K in keyof T]: T[K];
71
+ } & {};
72
+
45
73
  type QueryPrimitive = string | number | boolean | bigint | Date | null | undefined;
46
74
  /**
47
75
  * Type utility that recursively transforms all fields to string, as well as handling arrays and objects.
48
76
  * This is useful for serializing complex data structures into query parameters, which must be strings.
49
- * Example usage: `AsQuery<{ id: number; name: string; tags: string[] }>` = `{ id: string; name: string; tags: string[] }`
50
77
  */
51
78
  type AsQuery<T> = T extends QueryPrimitive ? string : T extends readonly (infer Item)[] ? AsQuery<Item>[] : T extends object ? {
52
79
  [Key in keyof T]: AsQuery<T[Key]>;
53
80
  } : string;
81
+ /**
82
+ * Utility type that ensures at least one property from the specified
83
+ * keys of a given type T is required, while the rest remain optional.
84
+ *
85
+ * This is useful for scenarios where you want to enforce that at least one
86
+ * of several optional (nullable) properties must be provided in an object.
87
+ *
88
+ * ```ts
89
+ * type Example = AtLeastOne<{ a?: string; b?: number; c?: boolean }>;
90
+ * // Valid: { a: "hello" }, { b: 42 }, { c: true }, { a: "hello", b: 42 }
91
+ * // Invalid: {}, { a: undefined, b: undefined, c: undefined }
92
+ * ```
93
+ */
94
+ type AtLeastOne<T, Keys extends keyof T = keyof T> = Keys extends keyof T ? Simplify<Required<Pick<T, Keys>> & Partial<Omit<T, Keys>>> : never;
54
95
 
55
96
  /**
56
97
  * Wraps c.json with a typed success payload / (or void data) & possible APIError response.
@@ -80,86 +121,65 @@ declare function failure({ status, error }: {
80
121
 
81
122
  /**
82
123
  * Resolves an APIContractResult fetcher into a FetchResult discriminated union.
83
- * Does not throw errors, instead returning them in the error property of the FetchResult.
124
+ * Does not throw errors, instead returning them as property of the FetchResult.
84
125
  * Example usage: const result = await fetchSafely(() => api.fetchUser(userId));
85
126
  */
86
127
  declare function fetchSafely<TResult extends APIContractResult<unknown>>(fetcher: () => Promise<TResult>): Promise<FetchResult<APIContractData<TResult>>>;
87
128
  /**
88
- * Resolves an APIContractResult fetcher, throwing an error if the result is an APIError.
129
+ * Resolves an APIContractResult, throwing an error if the result is an APIError.
89
130
  * Example usage: const data = await fetchAndThrow(() => api.fetchUser(userId));
90
131
  */
91
132
  declare function fetchAndThrow<TResult extends APIContractResult<unknown>>(fetcher: () => Promise<TResult>): Promise<APIContractData<TResult>>;
92
133
 
93
134
  /**
94
- * A Zod schema for validating pagination options, specifically the `offset` and `limit` parameters.
95
- * - `offset`: An optional non-negative integer representing the starting point for pagination.
96
- * - `limit`: An optional positive integer representing the maximum number of items to return.
97
- *
98
- * This schema can be used to inject in other Zod schemas for validating pagination options:
99
- *
100
- * ```ts
101
- * const mySchema = paginationSchema.extend({
102
- * // other fields here
103
- * });
104
- * ```
105
- */
106
- declare const paginationSchema: z.ZodObject<{
107
- offset: z.ZodOptional<z.ZodPreprocess<z.ZodNumber>>;
108
- limit: z.ZodOptional<z.ZodPreprocess<z.ZodNumber>>;
109
- }, z.core.$strip>;
110
- /**
111
- * A TypeScript type representing the pagination options validated by the `paginationSchema`.
112
- * Read documentation for `paginationSchema` for more details on the structure and usage of this type.
113
- */
114
- type PaginationOptions = z.infer<typeof paginationSchema>;
115
-
116
- /**
117
- * A utility function that extracts pagination options from the provided input.
118
- * It returns an object containing the `offset` and `limit` properties if they are defined.
119
- * If the input is undefined or does not contain these properties, it returns an empty object.
135
+ * Returns the current date and time shifted to the specified timezone.
120
136
  *
121
- * ```ts
122
- * await db.query.someTable.findMany({
123
- * ...refinePagination(options),
124
- * });
125
- * ```
126
- */
127
- declare const refinePagination: (options?: PaginationOptions) => {
128
- offset?: number | undefined;
129
- limit?: number | undefined;
130
- };
131
-
132
- /**
133
- * Returns the current time in the specified timezone.
134
- * Example usage: `getZonedTime({ tz: 'America/New_York' }) = new Date('2026-03-15T12:00:00Z')`
137
+ * This is useful when the application needs to display "now" from another
138
+ * region's point of view instead of relying on the server's local timezone.
135
139
  */
136
140
  declare const getZonedTime: ({ tz }?: {
137
141
  tz?: string;
138
142
  }) => Date;
139
143
  /**
140
- * Returns the timezone offset in the format (+4 UTC) / (-5 UTC).
141
- * Example usage: `getUTCOffset(new Date('2026-03-15T12:00:00Z'), 'America/New_York') = '(-4 UTC)'`
144
+ * Returns the UTC offset for the specified timezone at the given date.
145
+ *
146
+ * The offset is formatted for display next to dates and times, for example
147
+ * `(+4 UTC)`, `(0 UTC)`, or `(-5 UTC)`. The date is required because timezone
148
+ * offsets may change depending on daylight saving time or historical rules.
142
149
  */
143
150
  declare const getUTCOffset: (date: Date, tz: string) => string;
144
151
  /**
145
- * Returns the current time in the specified timezone, formatted as HH:mm:ss (+X UTC).
146
- * Example usage: `getFormattedTime({ tz: 'America/New_York' }) = '12:00:00 (-4 UTC)'`
152
+ * Returns the current time formatted in the specified timezone.
153
+ *
154
+ * The result includes both the local time and its UTC offset, making it suitable
155
+ * for UI labels, logs, bot messages, and other places where the user should see
156
+ * not only the time itself, but also the timezone context behind that value.
157
+ *
158
+ * Format: `HH:mm:ss (+X UTC)`.
147
159
  */
148
160
  declare const getFormattedTime: ({ tz }?: {
149
161
  tz?: string;
150
162
  }) => string;
151
163
  /**
152
- * Returns the current date in the specified timezone, formatted as dd.MM.yyyy.
153
- * By default, it also includes time (HH:mm:ss) and timezone offset.
154
- * Example usage: `getFormattedDate({ tz: 'America/New_York' }) = '03.15.2026 12:00:00 (-4 UTC)'`
164
+ * Returns the current date formatted in the specified timezone.
165
+ *
166
+ * By default, the result includes date, time, and UTC offset. This is useful for
167
+ * complete timestamps displayed in UI, bot messages, reports, or diagnostics.
168
+ *
169
+ * Format with time: `dd.MM.yyyy HH:mm:ss (+X UTC)`.
170
+ * Format without time: `dd.MM.yyyy`.
155
171
  */
156
172
  declare const getFormattedDate: ({ tz, withTime, }?: {
157
173
  tz?: string;
158
174
  withTime?: boolean;
159
175
  }) => string;
160
176
  /**
161
- * Formats the given time in the specified timezone, using the provided locale for date formatting.
162
- * Example usage: `formatTime(new Date('2026-03-15T12:00:00Z'), { tz: 'America/New_York' }) = '12:00:00, March 15, 2026 (-4 UTC)'`
177
+ * Formats the provided date and time in the specified timezone.
178
+ *
179
+ * Unlike helpers that always use the current moment, this function accepts an
180
+ * explicit Date value and formats that exact point in time for another timezone.
181
+ *
182
+ * Format: `HH:mm:ss, d MMMM yyyy (+X UTC)`.
163
183
  */
164
184
  declare const formatTime: (time: Date, { locale, tz }?: {
165
185
  locale?: Locale;
@@ -211,55 +231,6 @@ declare const log: {
211
231
  error: (message: string, service?: string, stack?: string) => void;
212
232
  };
213
233
 
214
- /**
215
- * Utility type that simplifies a given type T by flattening its structure.
216
- * This is particularly useful for improving the readability of complex types.
217
- */
218
- type Simplify<T> = {
219
- [K in keyof T]: T[K];
220
- } & {};
221
-
222
- /**
223
- * Transforms a string to a number (when passing query parameters).
224
- * Example usage: `asQueryNumber(z.number())('123') = 123`
225
- */
226
- declare const asQueryNumber: <T extends z.ZodTypeAny>(schema: T) => z.ZodPreprocess<T>;
227
- /**
228
- * Transforms various string representations of boolean values into actual booleans.
229
- * See POSITIVE_VALUES and NEGATIVE_VALUES arrays below for supported inputs.
230
- */
231
- declare const asQueryBoolean: <T extends z.ZodTypeAny>(schema: T) => z.ZodPreprocess<T>;
232
-
233
- /**
234
- * A utility type that represents a value that can be of type T or null.
235
- * Commonly used for optional fields in data models or function return types.
236
- *
237
- * ```ts
238
- * export type ObjectSelect = XOR<
239
- * Pick<Object, 'id'>,
240
- * Pick<Object, 'anotherUniqueField'>
241
- * >;
242
- */
243
- type XOR<T, U> = Simplify<T & {
244
- [K in keyof U]?: never;
245
- }> | Simplify<U & {
246
- [K in keyof T]?: never;
247
- }>;
248
- /**
249
- * Utility type that ensures at least one property from the specified
250
- * keys of a given type T is required, while the rest remain optional.
251
- *
252
- * This is useful for scenarios where you want to enforce that at least one
253
- * of several optional (nullable) properties must be provided in an object.
254
- *
255
- * ```ts
256
- * type Example = AtLeastOne<{ a?: string; b?: number; c?: boolean }>;
257
- * // Valid: { a: "hello" }, { b: 42 }, { c: true }, { a: "hello", b: 42 }
258
- * // Invalid: {}, { a: undefined, b: undefined, c: undefined }
259
- * ```
260
- */
261
- type AtLeastOne<T, Keys extends keyof T = keyof T> = Keys extends keyof T ? Simplify<Required<Pick<T, Keys>> & Partial<Omit<T, Keys>>> : never;
262
-
263
234
  /** Options for configuring the JWT service. */
264
235
  type JWTServiceOptions = {
265
236
  /** The algorithm to use for signing and verifying JWTs. Defaults to 'HS256'. */
@@ -272,9 +243,13 @@ type JWTSignOptions = {
272
243
  /** The number of seconds until the JWT expires. */
273
244
  expiresInSeconds?: number;
274
245
  };
275
- /** Utility types for inferring payload types from Zod schemas. */
246
+ /**
247
+ * Utility types for inferring payload types from Zod schemas.
248
+ */
276
249
  type Payload<T> = T extends z.ZodType ? z.infer<T> : T;
277
- /** Utility type to extract the payload schema type from a Zod schema. */
250
+ /**
251
+ * Utility type to extract the payload schema type from a Zod schema.
252
+ * */
278
253
  type PayloadSchema<T> = T extends z.ZodType ? T : never;
279
254
 
280
255
  /**
@@ -308,4 +283,59 @@ declare class ZodJWTService<TPayloadOrSchema> {
308
283
  verifyOrThrow(token: string, secret: string): Promise<Payload<TPayloadOrSchema>>;
309
284
  }
310
285
 
311
- 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 PaginationOptions, type Payload, type PayloadSchema, SUCCESS_STATUS_CODES, type Simplify, type SuccessStatusCode, type XOR, ZodJWTService, asQueryBoolean, asQueryNumber, failure, fetchAndThrow, fetchSafely, formatTime, generateRandomString, getColoredHTTPStatus, getFormattedDate, getFormattedTime, getUTCOffset, getZonedTime, honoLoggingHandler, log, measureExecutionTime, onHandlerError, paginationSchema, refinePagination, respond, safeExecute, success };
286
+ declare const zodPaginationSchema: z.ZodObject<{
287
+ offset: z.ZodOptional<z.ZodPreprocess<z.ZodNumber>>;
288
+ limit: z.ZodOptional<z.ZodPreprocess<z.ZodNumber>>;
289
+ }, z.core.$strip>;
290
+ /**
291
+ * Reusable pagination fields for flat query schemas, allowing for
292
+ * consistent validation of pagination parameters across different endpoints.
293
+ *
294
+ * Spread into another `z.object(...)` when composing a larger query schema,
295
+ * or use `zodPaginationSchema.extend(...)` when building directly from the schema.
296
+ */
297
+ declare const zodPaginationShape: {
298
+ offset: z.ZodOptional<z.ZodPreprocess<z.ZodNumber>>;
299
+ limit: z.ZodOptional<z.ZodPreprocess<z.ZodNumber>>;
300
+ };
301
+ /**
302
+ * TypeScript type representing the pagination options validated by the `zodPaginationSchema`.
303
+ * Read documentation for `zodPaginationSchema` for more details on the structure and usage of this type.
304
+ */
305
+ type ZodPaginationOptions = z.infer<typeof zodPaginationSchema>;
306
+
307
+ /**
308
+ * Extracts defined pagination options from a query object.
309
+ *
310
+ * This keeps query builder calls clean by omitting undefined values while
311
+ * preserving valid pagination values like `0`, useful when spreading pagination
312
+ * options into APIs that expect only explicitly provided fields (e.g., Drizzle).
313
+ *
314
+ * ```ts
315
+ * await db.query.someTable.findMany({
316
+ * ...refinePagination(options),
317
+ * });
318
+ * ```
319
+ */
320
+ declare const refinePagination: (options?: ZodPaginationOptions) => ZodPaginationOptions;
321
+
322
+ /**
323
+ * Query parameters always arrive as strings, even when they semantically represent numbers
324
+ * or booleans, so we preprocess them to convert to the appropriate types before validation.
325
+ *
326
+ * ```ts
327
+ * const schema = z.object({
328
+ * page: z.number().int().positive(),
329
+ * isActive: z.boolean(),
330
+ * });
331
+ *
332
+ * const queryParams = { page: '2', isActive: 'true' };
333
+ * const result = schema.parse(queryParams); // { page: 2, isActive: true }
334
+ * ```
335
+ *
336
+ * @param schema - The Zod schema to validate the query parameters against.
337
+ * @returns A new Zod schema that preprocesses query parameter values before validation.
338
+ */
339
+ declare const asQuery: <T extends z.ZodTypeAny>(schema: T) => z.ZodPreprocess<T>;
340
+
341
+ 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, SUCCESS_STATUS_CODES, type Simplify, type SuccessStatusCode, ZodJWTService, type ZodPaginationOptions, asQuery, failure, fetchAndThrow, fetchSafely, formatTime, generateRandomString, getColoredHTTPStatus, getFormattedDate, getFormattedTime, getUTCOffset, getZonedTime, honoLoggingHandler, log, measureExecutionTime, onHandlerError, refinePagination, respond, safeExecute, sqlWhere, success, zodPaginationSchema, zodPaginationShape };
package/dist/index.js CHANGED
@@ -1,3 +1,9 @@
1
+ // src/drizzle/drizzle.refiners.ts
2
+ import { and, eq } from "drizzle-orm";
3
+ function sqlWhere(table, where) {
4
+ return and(...Object.entries(where).map(([key, value]) => eq(table[key], value)));
5
+ }
6
+
1
7
  // src/hono/hono.execution.ts
2
8
  import { HTTPException } from "hono/http-exception";
3
9
  import { red as red2 } from "kleur/colors";
@@ -130,53 +136,6 @@ async function fetchAndThrow(fetcher) {
130
136
  return response.data;
131
137
  }
132
138
 
133
- // src/pagination/pagination.schemas.ts
134
- import z2 from "zod";
135
-
136
- // src/validation/validation.refiners.ts
137
- import z from "zod";
138
- var asQueryNumber = (schema) => z.preprocess((value) => {
139
- if (typeof value !== "string")
140
- return value;
141
- if (value.trim() === "")
142
- return value;
143
- return Number(value);
144
- }, schema);
145
- var asQueryBoolean = (schema) => {
146
- const POSITIVE_VALUES = ["1", "true", "yes", "y", "on"];
147
- const NEGATIVE_VALUES = ["0", "false", "no", "n", "off"];
148
- return z.preprocess((value) => {
149
- if (typeof value === "boolean")
150
- return value;
151
- if (typeof value === "string") {
152
- const normalized = value.trim().toLowerCase();
153
- if (POSITIVE_VALUES.includes(normalized))
154
- return true;
155
- if (NEGATIVE_VALUES.includes(normalized))
156
- return false;
157
- }
158
- return value;
159
- }, schema);
160
- };
161
-
162
- // src/pagination/pagination.schemas.ts
163
- var paginationSchema = z2.object({
164
- offset: asQueryNumber(z2.number().int().nonnegative()).optional(),
165
- limit: asQueryNumber(z2.number().int().positive()).optional()
166
- });
167
-
168
- // src/pagination/pagination.refiners.ts
169
- var refinePagination = (options) => {
170
- const pagination = {};
171
- if (options?.offset !== void 0) {
172
- pagination.offset = options.offset;
173
- }
174
- if (options?.limit !== void 0) {
175
- pagination.limit = options.limit;
176
- }
177
- return pagination;
178
- };
179
-
180
139
  // src/utilities/execution.utilities.ts
181
140
  async function safeExecute(fn, onError) {
182
141
  try {
@@ -242,12 +201,49 @@ var ZodJWTService = class {
242
201
  return this.payloadSchema.parseAsync(payload);
243
202
  }
244
203
  };
204
+
205
+ // src/zod-pagination/zod-pagination.refiners.ts
206
+ var refinePagination = (options) => ({
207
+ ...options?.offset !== void 0 ? { offset: options.offset } : {},
208
+ ...options?.limit !== void 0 ? { limit: options.limit } : {}
209
+ });
210
+
211
+ // src/zod-pagination/zod-pagination.schemas.ts
212
+ import z2 from "zod";
213
+
214
+ // src/zod-validation/zod-validation.refiners.ts
215
+ import z from "zod";
216
+ var parseQueryValue = (value) => {
217
+ if (Array.isArray(value))
218
+ return value.map(parseQueryValue);
219
+ if (typeof value !== "string")
220
+ return value;
221
+ const normalized = value.trim().toLowerCase();
222
+ if (normalized === "")
223
+ return value;
224
+ if (normalized === "true")
225
+ return true;
226
+ if (normalized === "false")
227
+ return false;
228
+ if (/^[+-]?(?:\d+\.?\d*|\.\d+)(?:e[+-]?\d+)?$/i.test(normalized))
229
+ return Number(normalized);
230
+ return value;
231
+ };
232
+ var asQuery = (schema) => z.preprocess(parseQueryValue, schema);
233
+
234
+ // src/zod-pagination/zod-pagination.schemas.ts
235
+ var zodPaginationSchema = z2.object({
236
+ /** Integer representing the starting point for pagination. */
237
+ offset: asQuery(z2.number().int().nonnegative()).optional(),
238
+ /** Integer representing the maximum number of items to return. */
239
+ limit: asQuery(z2.number().int().positive()).optional()
240
+ });
241
+ var zodPaginationShape = zodPaginationSchema.shape;
245
242
  export {
246
243
  EXCEPTION_STATUS_CODES,
247
244
  SUCCESS_STATUS_CODES,
248
245
  ZodJWTService,
249
- asQueryBoolean,
250
- asQueryNumber,
246
+ asQuery,
251
247
  failure,
252
248
  fetchAndThrow,
253
249
  fetchSafely,
@@ -262,9 +258,11 @@ export {
262
258
  log,
263
259
  measureExecutionTime,
264
260
  onHandlerError,
265
- paginationSchema,
266
261
  refinePagination,
267
262
  respond,
268
263
  safeExecute,
269
- success
264
+ sqlWhere,
265
+ success,
266
+ zodPaginationSchema,
267
+ zodPaginationShape
270
268
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kalutskii/foundation",
3
- "version": "0.6.5",
3
+ "version": "0.6.7",
4
4
  "description": "Typescript collection of most common utilities, schemas and functions among private projects.",
5
5
  "type": "module",
6
6
  "repository": {
@@ -12,7 +12,8 @@
12
12
  },
13
13
  "scripts": {
14
14
  "build": "tsup",
15
- "lint": "eslint .",
15
+ "lint": "eslint . --ext .ts",
16
+ "format": "prettier --write .",
16
17
  "typecheck": "tsc --noEmit"
17
18
  },
18
19
  "exports": {
@@ -28,6 +29,7 @@
28
29
  "@hono/zod-validator": "^0.8.0",
29
30
  "date-fns": "^4.1.0",
30
31
  "date-fns-tz": "^3.2.0",
32
+ "drizzle-orm": "^1.0.0-beta.22",
31
33
  "hono": "^4.12.18",
32
34
  "kleur": "^4.1.5",
33
35
  "typescript": "^5",