@kalutskii/foundation 0.5.0 → 0.5.2

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/dist/index.d.ts CHANGED
@@ -1,8 +1,20 @@
1
1
  import { ErrorHandler, MiddlewareHandler, Context, TypedResponse } from 'hono';
2
- import z$1, { z, ZodNumber } from 'zod';
2
+ import z from 'zod';
3
3
  import { Locale } from 'date-fns';
4
4
  import { SymmetricAlgorithm } from 'hono/utils/jwt/jwa';
5
5
 
6
+ /**
7
+ * Global error handler for Hono framework. Catches all exceptions thrown in route handlers and middlewares.
8
+ * Distinguishes between expected HTTPExceptions (mapped to their status codes) and unexpected errors (500).
9
+ */
10
+ declare const onHandlerError: ErrorHandler;
11
+
12
+ /**
13
+ * Request logging middleware for Hono, providing deeply detailed logs for each incoming request.
14
+ * Example log output: [12:12:12 (+4 UTC)] hono | POST 200 123ms /api/v1/users (search=term)
15
+ */
16
+ declare const honoLoggingHandler: MiddlewareHandler;
17
+
6
18
  declare const SUCCESS_STATUS_CODES: readonly [200, 201, 202, 307];
7
19
  declare const EXCEPTION_STATUS_CODES: readonly [400, 401, 403, 404, 405, 409, 500];
8
20
 
@@ -30,6 +42,25 @@ type FetchResult<T> = {
30
42
  data: null;
31
43
  };
32
44
 
45
+ /**
46
+ * Type utility that recursively transforms all Date fields to string, as well as handling arrays and objects.
47
+ * This is necessary for proper typing when working with RPC, as JSON does not support the Date type directly.
48
+ * Example usage: `SerializeDates<{ createdAt: Date; nested: { updatedAt: Date }; tags: Date[] }>` \
49
+ * = `{ createdAt: string; nested: { updatedAt: string }; tags: string[] }`
50
+ */
51
+ type SerializeDates<T> = T extends Date ? string : T extends (infer U)[] ? SerializeDates<U>[] : T extends readonly (infer U)[] ? readonly SerializeDates<U>[] : T extends object ? {
52
+ [K in keyof T]: SerializeDates<T[K]>;
53
+ } : T;
54
+
55
+ /**
56
+ * Wraps c.json with a typed success payload / (or void data) & possible APIError response.
57
+ * When no data is provided, responds with an empty object {} (purely for type consistency).
58
+ */
59
+ declare function respond<T extends object = Record<string, never>, S extends SuccessStatusCode = SuccessStatusCode>(c: Context, options: {
60
+ status: S;
61
+ data?: T;
62
+ }): Response & TypedResponse<APISuccess<SerializeDates<T>> | APIError, S, 'json'>;
63
+
33
64
  /**
34
65
  * Factory for creating successful API responses with serializable data.
35
66
  * Example usage: `success({ status: 200, data: { message: 'Operation successful' } })`
@@ -60,54 +91,43 @@ declare function fetchSafely<TResult extends APIContractResult<unknown>>(fetcher
60
91
  declare function fetchAndThrow<TResult extends APIContractResult<unknown>>(fetcher: () => Promise<TResult>): Promise<APIContractData<TResult>>;
61
92
 
62
93
  /**
63
- * Global error handler for Hono framework. Catches all exceptions thrown in route handlers and middlewares.
64
- * Distinguishes between expected HTTPExceptions (mapped to their status codes) and unexpected errors (500).
65
- */
66
- declare const onHandlerError: ErrorHandler;
67
-
68
- /**
69
- * Request logging middleware for Hono, providing deeply detailed logs for each incoming request.
70
- * Example log output: [12:12:12 (+4 UTC)] hono | POST 200 123ms /api/v1/users (search=term)
71
- */
72
- declare const honoLoggingHandler: MiddlewareHandler;
73
-
74
- /**
75
- * Type utility that recursively transforms all Date fields to string, as well as handling arrays and objects.
76
- * This is necessary for proper typing when working with RPC, as JSON does not support the Date type directly.
77
- * Example usage: `SerializeDates<{ createdAt: Date; nested: { updatedAt: Date }; tags: Date[] }>` \
78
- * = `{ createdAt: string; nested: { updatedAt: string }; tags: string[] }`
79
- */
80
- type SerializeDates<T> = T extends Date ? string : T extends (infer U)[] ? SerializeDates<U>[] : T extends readonly (infer U)[] ? readonly SerializeDates<U>[] : T extends object ? {
81
- [K in keyof T]: SerializeDates<T[K]>;
82
- } : T;
83
- type QueryPrimitive = string | number | boolean | bigint | Date | null | undefined;
84
- /**
85
- * Type utility that recursively transforms all fields to string, as well as handling arrays and objects.
86
- * This is useful for serializing complex data structures into query parameters, which must be strings.
87
- * Example usage: `AsQuery<{ id: number; name: string; tags: string[] }>` = `{ id: string; name: string; tags: string[] }`
88
- */
89
- type AsQuery<T> = T extends QueryPrimitive ? string : T extends readonly (infer Item)[] ? AsQuery<Item>[] : T extends object ? {
90
- [Key in keyof T]: AsQuery<T[Key]>;
91
- } : string;
92
- /**
93
- * Transforms a string to a number (when passing query parameters).
94
- * Example usage: `asQueryNumber(z.number())('123') = 123`
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
+ * ```
95
105
  */
96
- declare const asQueryNumber: <T extends ZodNumber>(schema: T) => z.ZodPreprocess<T>;
106
+ declare const paginationSchema: z.ZodObject<{
107
+ offset: z.ZodOptional<z.ZodNumber>;
108
+ limit: z.ZodOptional<z.ZodNumber>;
109
+ }, z.core.$strip>;
97
110
  /**
98
- * Transforms various string representations of boolean values into actual booleans.
99
- * See POSITIVE_VALUES and NEGATIVE_VALUES arrays below for supported inputs.
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.
100
113
  */
101
- declare const asQueryBoolean: <T extends z.ZodTypeAny>(schema: T) => z.ZodPreprocess<T>;
114
+ type PaginationOptions = z.infer<typeof paginationSchema>;
102
115
 
103
116
  /**
104
- * Wraps c.json with a typed success payload / (or void data) & possible APIError response.
105
- * When no data is provided, responds with an empty object {} (purely for type consistency).
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.
120
+ *
121
+ * ```ts
122
+ * await db.query.someTable.findMany({
123
+ * ...getPagination(options),
124
+ * });
125
+ * ```
106
126
  */
107
- declare function respond<T extends object = Record<string, never>, S extends SuccessStatusCode = SuccessStatusCode>(c: Context, options: {
108
- status: S;
109
- data?: T;
110
- }): Response & TypedResponse<APISuccess<SerializeDates<T>> | APIError, S, 'json'>;
127
+ declare const getPagination: (options?: PaginationOptions) => {
128
+ offset?: number | undefined;
129
+ limit?: number | undefined;
130
+ };
111
131
 
112
132
  /**
113
133
  * Returns the current time in the specified timezone.
@@ -228,6 +248,45 @@ type XOR<T, U> = Simplify<T & {
228
248
  */
229
249
  type AtLeastOne<T, Keys extends keyof T = keyof T> = Keys extends keyof T ? Simplify<Required<Pick<T, Keys>> & Partial<Omit<T, Keys>>> : never;
230
250
 
251
+ /**
252
+ * A Zod refinement function that ensures at least one of the specified keys in a Zod object schema is defined.
253
+ * This is useful for validating input where at least one of several optional fields must be provided.
254
+ *
255
+ * ```ts
256
+ * const mySchema = atLeastOneDefined(
257
+ * z.object({
258
+ * name: z.string().optional(),
259
+ * email: z.string().email().optional(),
260
+ * phone: z.string().optional(),
261
+ * })
262
+ * );
263
+ *
264
+ * // Valid: { name: "Alice" }, { email: "alice@example.com" }, { phone: "123-456-7890" }
265
+ * // Invalid: {}, { name: undefined, email: undefined, phone: undefined }
266
+ * ```
267
+ */
268
+ declare const atLeastOneDefined: <T extends z.ZodRawShape>(schema: z.ZodObject<T>) => z.ZodObject<T, z.core.$strip>;
269
+ /**
270
+ * Transforms a string to a number (when passing query parameters).
271
+ * Example usage: `asQueryNumber(z.number())('123') = 123`
272
+ */
273
+ declare const asQueryNumber: <T extends z.ZodNumber>(schema: T) => z.ZodPreprocess<T>;
274
+ /**
275
+ * Transforms various string representations of boolean values into actual booleans.
276
+ * See POSITIVE_VALUES and NEGATIVE_VALUES arrays below for supported inputs.
277
+ */
278
+ declare const asQueryBoolean: <T extends z.ZodTypeAny>(schema: T) => z.ZodPreprocess<T>;
279
+
280
+ type QueryPrimitive = string | number | boolean | bigint | Date | null | undefined;
281
+ /**
282
+ * Type utility that recursively transforms all fields to string, as well as handling arrays and objects.
283
+ * This is useful for serializing complex data structures into query parameters, which must be strings.
284
+ * Example usage: `AsQuery<{ id: number; name: string; tags: string[] }>` = `{ id: string; name: string; tags: string[] }`
285
+ */
286
+ type AsQuery<T> = T extends QueryPrimitive ? string : T extends readonly (infer Item)[] ? AsQuery<Item>[] : T extends object ? {
287
+ [Key in keyof T]: AsQuery<T[Key]>;
288
+ } : string;
289
+
231
290
  /** Options for configuring the JWT service. */
232
291
  type JWTServiceOptions = {
233
292
  /** The algorithm to use for signing and verifying JWTs. Defaults to 'HS256'. */
@@ -241,9 +300,9 @@ type JWTSignOptions = {
241
300
  expiresInSeconds?: number;
242
301
  };
243
302
  /** Utility types for inferring payload types from Zod schemas. */
244
- type Payload<T> = T extends z$1.ZodType ? z$1.infer<T> : T;
303
+ type Payload<T> = T extends z.ZodType ? z.infer<T> : T;
245
304
  /** Utility type to extract the payload schema type from a Zod schema. */
246
- type PayloadSchema<T> = T extends z$1.ZodType ? T : never;
305
+ type PayloadSchema<T> = T extends z.ZodType ? T : never;
247
306
 
248
307
  /**
249
308
  * A service class for handling JWT operations with optional Zod schema validation for the payload.
@@ -276,4 +335,4 @@ declare class ZodJWTService<TPayloadOrSchema> {
276
335
  verifyOrThrow(token: string, secret: string): Promise<Payload<TPayloadOrSchema>>;
277
336
  }
278
337
 
279
- 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 SerializeDates, type Simplify, type SuccessStatusCode, type XOR, ZodJWTService, asQueryBoolean, asQueryNumber, failure, fetchAndThrow, fetchSafely, formatTime, generateRandomString, getColoredHTTPStatus, getFormattedDate, getFormattedTime, getUTCOffset, getZonedTime, honoLoggingHandler, log, measureExecutionTime, onHandlerError, respond, safeExecute, success };
338
+ 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 SerializeDates, type Simplify, type SuccessStatusCode, type XOR, ZodJWTService, asQueryBoolean, asQueryNumber, atLeastOneDefined, failure, fetchAndThrow, fetchSafely, formatTime, generateRandomString, getColoredHTTPStatus, getFormattedDate, getFormattedTime, getPagination, getUTCOffset, getZonedTime, honoLoggingHandler, log, measureExecutionTime, onHandlerError, paginationSchema, respond, safeExecute, success };
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
- // src/http/http.constants.ts
2
- var SUCCESS_STATUS_CODES = [200, 201, 202, 307];
3
- var EXCEPTION_STATUS_CODES = [400, 401, 403, 404, 405, 409, 500];
1
+ // src/hono/hono.execution.ts
2
+ import { HTTPException } from "hono/http-exception";
3
+ import { red as red2 } from "kleur/colors";
4
4
 
5
5
  // src/http/http.factory.ts
6
6
  function success({ status, data }) {
@@ -10,24 +10,6 @@ function failure({ status, error }) {
10
10
  return { kind: "error", status, error };
11
11
  }
12
12
 
13
- // src/http/http.resolvers.ts
14
- async function fetchSafely(fetcher) {
15
- const response = await fetcher();
16
- if (response.kind === "error")
17
- return { error: response.error, data: null };
18
- return { error: null, data: response.data };
19
- }
20
- async function fetchAndThrow(fetcher) {
21
- const response = await fetcher();
22
- if (response.kind === "error")
23
- throw new Error(response.error);
24
- return response.data;
25
- }
26
-
27
- // src/hono/hono.execution.ts
28
- import { HTTPException } from "hono/http-exception";
29
- import { red as red2 } from "kleur/colors";
30
-
31
13
  // src/utilities/generation.utilities.ts
32
14
  var DEFAULT_CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
33
15
  function generateRandomString(length = 10) {
@@ -130,6 +112,43 @@ function respond(c, options) {
130
112
  return c.json(success({ status: options.status, data: options.data ?? {} }), options.status);
131
113
  }
132
114
 
115
+ // src/http/http.constants.ts
116
+ var SUCCESS_STATUS_CODES = [200, 201, 202, 307];
117
+ var EXCEPTION_STATUS_CODES = [400, 401, 403, 404, 405, 409, 500];
118
+
119
+ // src/http/http.resolvers.ts
120
+ async function fetchSafely(fetcher) {
121
+ const response = await fetcher();
122
+ if (response.kind === "error")
123
+ return { error: response.error, data: null };
124
+ return { error: null, data: response.data };
125
+ }
126
+ async function fetchAndThrow(fetcher) {
127
+ const response = await fetcher();
128
+ if (response.kind === "error")
129
+ throw new Error(response.error);
130
+ return response.data;
131
+ }
132
+
133
+ // src/pagination/pagination.schemas.ts
134
+ import z from "zod";
135
+ var paginationSchema = z.object({
136
+ offset: z.number().int().nonnegative().optional(),
137
+ limit: z.number().int().positive().optional()
138
+ });
139
+
140
+ // src/pagination/pagination.utilities.ts
141
+ var getPagination = (options) => {
142
+ const pagination = {};
143
+ if (options?.offset !== void 0) {
144
+ pagination.offset = options.offset;
145
+ }
146
+ if (options?.limit !== void 0) {
147
+ pagination.limit = options.limit;
148
+ }
149
+ return pagination;
150
+ };
151
+
133
152
  // src/utilities/execution.utilities.ts
134
153
  async function safeExecute(fn, onError) {
135
154
  try {
@@ -147,13 +166,18 @@ async function measureExecutionTime(execution) {
147
166
  return { result, executionTime: Math.round(executionTime) };
148
167
  }
149
168
 
150
- // src/utilities/serialization.utilities.ts
151
- import { z } from "zod";
152
- var asQueryNumber = (schema) => z.preprocess((v) => typeof v === "string" ? Number(v) : v, schema);
169
+ // src/validation/validation.refiners.ts
170
+ import z2 from "zod";
171
+ var AT_LEAST_ONE_DEFINED_ERROR = (keys) => `At least one of the specified keys must be defined: ${keys.join(", ")}`;
172
+ var atLeastOneDefined = (schema) => {
173
+ const keys = Object.keys(schema.shape);
174
+ return schema.refine((value) => keys.some((key) => value[key] !== void 0 && value[key] !== null), { message: AT_LEAST_ONE_DEFINED_ERROR(keys) });
175
+ };
176
+ var asQueryNumber = (schema) => z2.preprocess((v) => typeof v === "string" ? Number(v) : v, schema);
153
177
  var asQueryBoolean = (schema) => {
154
178
  const POSITIVE_VALUES = ["1", "true", "yes", "y", "on"];
155
179
  const NEGATIVE_VALUES = ["0", "false", "no", "n", "off"];
156
- return z.preprocess((value) => {
180
+ return z2.preprocess((value) => {
157
181
  if (typeof value === "boolean")
158
182
  return value;
159
183
  if (typeof value === "string") {
@@ -221,6 +245,7 @@ export {
221
245
  ZodJWTService,
222
246
  asQueryBoolean,
223
247
  asQueryNumber,
248
+ atLeastOneDefined,
224
249
  failure,
225
250
  fetchAndThrow,
226
251
  fetchSafely,
@@ -229,12 +254,14 @@ export {
229
254
  getColoredHTTPStatus,
230
255
  getFormattedDate,
231
256
  getFormattedTime,
257
+ getPagination,
232
258
  getUTCOffset,
233
259
  getZonedTime,
234
260
  honoLoggingHandler,
235
261
  log,
236
262
  measureExecutionTime,
237
263
  onHandlerError,
264
+ paginationSchema,
238
265
  respond,
239
266
  safeExecute,
240
267
  success
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kalutskii/foundation",
3
- "version": "0.5.0",
3
+ "version": "0.5.2",
4
4
  "description": "Typescript collection of most common utilities, schemas and functions among private projects.",
5
5
  "type": "module",
6
6
  "repository": {
@@ -40,6 +40,7 @@
40
40
  "esbuild-plugin-tsconfig-paths": "^1.0.2",
41
41
  "eslint": "^10.2.1",
42
42
  "eslint-config-prettier": "^10.1.8",
43
+ "jiti": "^2.7.0",
43
44
  "prettier": "^3.8.1",
44
45
  "tsup": "^8.5.1",
45
46
  "typescript-eslint": "^8.58.2"