@kalutskii/foundation 0.6.22 → 0.6.23
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 +21 -14
- package/dist/index.d.ts +75 -10
- package/dist/index.js +28 -6
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -144,7 +144,7 @@ schema.parse({ page: '2', isActive: 'true' }); // { page: 2, isActive: true }
|
|
|
144
144
|
|
|
145
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
146
|
|
|
147
|
-
###
|
|
147
|
+
### 7. Build flat query schemas from nested objects
|
|
148
148
|
|
|
149
149
|
```typescript
|
|
150
150
|
import { asQuerySchema } from '@kalutskii/foundation';
|
|
@@ -171,26 +171,32 @@ schema.parse({ sort: { field: 'name', order: 'asc' } }); // throws
|
|
|
171
171
|
|
|
172
172
|
`asQuerySchema` flattens nested `z.object(...)` fields to the top level, making it suitable for flat query parameter validation. Nested object fields are lifted, while optionality is preserved: required parents keep their fields required, optional parents make all promoted fields optional. The result is a strict schema (unknown keys are rejected).
|
|
173
173
|
|
|
174
|
-
### 8.
|
|
174
|
+
### 8. Build reusable search schemas
|
|
175
175
|
|
|
176
176
|
```typescript
|
|
177
|
-
import {
|
|
177
|
+
import { zodPaginationSchema, zodSearchSchema } from '@kalutskii/foundation';
|
|
178
178
|
import { z } from 'zod';
|
|
179
179
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
180
|
+
const assetSchema = z.object({
|
|
181
|
+
status: z.enum(['active', 'archived']),
|
|
182
|
+
categoryId: z.number(),
|
|
183
|
+
});
|
|
183
184
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
search: z.string().optional(),
|
|
187
|
-
...zodPaginationShape,
|
|
185
|
+
const assetSearchSchema = zodSearchSchema({
|
|
186
|
+
filters: assetSchema.pick({ status: true, categoryId: true }),
|
|
188
187
|
});
|
|
189
188
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
189
|
+
type AssetSearch = z.infer<typeof assetSearchSchema>;
|
|
190
|
+
|
|
191
|
+
assetSearchSchema.parse({
|
|
192
|
+
where: { status: 'active' },
|
|
193
|
+
query: 'asset name',
|
|
194
|
+
pagination: { offset: 0, limit: 20 },
|
|
193
195
|
});
|
|
196
|
+
|
|
197
|
+
// Pagination remains available as a standalone reusable schema.
|
|
198
|
+
zodPaginationSchema.parse({ offset: '0', limit: '20' });
|
|
199
|
+
// { offset: 0, limit: 20 }
|
|
194
200
|
```
|
|
195
201
|
|
|
196
202
|
### 9. Work with JWT using `ZodJWTService`
|
|
@@ -240,7 +246,8 @@ The package exports all public APIs from a single entrypoint:
|
|
|
240
246
|
- **Hono**: `respond`, `onHandlerError`, `honoLoggingHandler`.
|
|
241
247
|
- **Utilities**: `safeExecute`, `measureExecutionTime`, `generateRandomString`, `log`, `getColoredHTTPStatus`, datetime helpers.
|
|
242
248
|
- **Zod JWT**: `ZodJWTService`.
|
|
243
|
-
- **Zod
|
|
249
|
+
- **Zod Search**: `zodSearchSchema`, `ZodSearchSchemaOptions`.
|
|
250
|
+
- **Zod Pagination**: `zodPaginationSchema`, `zodPaginationShape`, `ZodPaginationOptions`.
|
|
244
251
|
- **Zod Flatten**: `asQuerySchema`, `flattenZodShape`, `isZodObject`, `isZodOptional`, `unwrapOptional`, `FlattenZodShape`.
|
|
245
252
|
- **Zod Validation**: `asQuery`, `AsQuery`, `AtLeastOne`.
|
|
246
253
|
- **Type Utilities**: `Simplify`.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { SQL
|
|
1
|
+
import { SQL } from 'drizzle-orm';
|
|
2
2
|
import { ErrorHandler, MiddlewareHandler, Context, TypedResponse } from 'hono';
|
|
3
3
|
import { Locale } from 'date-fns';
|
|
4
4
|
import { SymmetricAlgorithm } from 'hono/utils/jwt/jwa';
|
|
@@ -274,26 +274,91 @@ declare class ZodJWTService<TPayloadOrSchema> {
|
|
|
274
274
|
verifyOrThrow(token: string, secret: string): Promise<Payload<TPayloadOrSchema>>;
|
|
275
275
|
}
|
|
276
276
|
|
|
277
|
+
/**
|
|
278
|
+
* Validates offset-based pagination shared by search request contracts.
|
|
279
|
+
* Missing fields inside the required object receive predictable defaults.
|
|
280
|
+
*/
|
|
277
281
|
declare const zodPaginationSchema: z.ZodObject<{
|
|
278
282
|
offset: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
279
283
|
limit: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
280
284
|
}, z.core.$strip>;
|
|
281
285
|
/**
|
|
282
|
-
*
|
|
283
|
-
*
|
|
284
|
-
*
|
|
285
|
-
* Spread into another `z.object(...)` when composing a larger query schema,
|
|
286
|
-
* or use `zodPaginationSchema.extend(...)` when building directly from the schema.
|
|
286
|
+
* Exposes pagination fields for composition into other object schemas.
|
|
287
|
+
* The shape stays derived from the schema to prevent contract divergence.
|
|
287
288
|
*/
|
|
288
289
|
declare const zodPaginationShape: {
|
|
289
290
|
offset: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
290
291
|
limit: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
291
292
|
};
|
|
293
|
+
|
|
292
294
|
/**
|
|
293
|
-
* TypeScript
|
|
294
|
-
*
|
|
295
|
+
* TypeScript output produced after successful pagination validation.
|
|
296
|
+
* Defaulted fields are represented as required numeric properties.
|
|
295
297
|
*/
|
|
296
298
|
type ZodPaginationOptions = z.infer<typeof zodPaginationSchema>;
|
|
299
|
+
/**
|
|
300
|
+
* Configuration used to compose a reusable search request schema.
|
|
301
|
+
* Literal feature flags are preserved in the resulting static shape.
|
|
302
|
+
*/
|
|
303
|
+
type ZodSearchSchemaOptions<TShape extends z.ZodRawShape, TQueryEnabled extends boolean = true, TPaginationEnabled extends boolean = true> = {
|
|
304
|
+
/**
|
|
305
|
+
* Object schema whose fields become available inside the `where` object.
|
|
306
|
+
* Every resulting field is optional, but one value must be defined.
|
|
307
|
+
*/
|
|
308
|
+
filters: z.ZodObject<TShape>;
|
|
309
|
+
/**
|
|
310
|
+
* Controls whether an optional non-empty `query` field is generated.
|
|
311
|
+
* The field is enabled when this option is omitted from the call.
|
|
312
|
+
*/
|
|
313
|
+
queryEnabled?: TQueryEnabled;
|
|
314
|
+
/**
|
|
315
|
+
* Controls whether a required `pagination` object is generated.
|
|
316
|
+
* The field is enabled when this option is omitted from the call.
|
|
317
|
+
*/
|
|
318
|
+
paginationEnabled?: TPaginationEnabled;
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Optional text query shared by search request contracts.
|
|
323
|
+
* Provided values are trimmed and must contain visible text.
|
|
324
|
+
*/
|
|
325
|
+
declare const zodSearchQuerySchema: z.ZodOptional<z.ZodString>;
|
|
326
|
+
/**
|
|
327
|
+
* Makes every supplied filter optional while requiring one defined value.
|
|
328
|
+
* The wrapper itself stays optional because searches may omit filtering.
|
|
329
|
+
*/
|
|
330
|
+
declare const createZodSearchWhereSchema: <TShape extends z.ZodRawShape>(filters: z.ZodObject<TShape>) => z.ZodOptional<z.ZodPipe<z.ZodObject<{ -readonly [k in keyof TShape]: z.ZodOptional<TShape[k]>; }, z.core.$strip>, z.ZodTransform<Awaited<AtLeastOne<z.core.$InferObjectOutput<{ -readonly [k in keyof TShape]: z.ZodOptional<TShape[k]>; }, {}>, keyof z.core.$InferObjectOutput<{ -readonly [k in keyof TShape]: z.ZodOptional<TShape[k]>; }, {}>>>, z.core.$InferObjectOutput<{ -readonly [k in keyof TShape]: z.ZodOptional<TShape[k]>; }, {}>>>>;
|
|
331
|
+
/**
|
|
332
|
+
* Empty schema shape used when a search feature is explicitly disabled.
|
|
333
|
+
* Intersections remove disabled fields without widening enabled branches.
|
|
334
|
+
*/
|
|
335
|
+
type ZodDisabledSearchShape = Record<never, never>;
|
|
336
|
+
/**
|
|
337
|
+
* Schema-level shape assembled from filter fields and literal feature flags.
|
|
338
|
+
* Keeping Zod schemas here makes runtime composition the contract source.
|
|
339
|
+
*/
|
|
340
|
+
type ZodSearchShape<TShape extends z.ZodRawShape, TQueryEnabled extends boolean, TPaginationEnabled extends boolean> = {
|
|
341
|
+
where: ReturnType<typeof createZodSearchWhereSchema<TShape>>;
|
|
342
|
+
} & (TQueryEnabled extends false ? ZodDisabledSearchShape : {
|
|
343
|
+
query: typeof zodSearchQuerySchema;
|
|
344
|
+
}) & (TPaginationEnabled extends false ? ZodDisabledSearchShape : {
|
|
345
|
+
pagination: typeof zodPaginationSchema;
|
|
346
|
+
});
|
|
347
|
+
/**
|
|
348
|
+
* Builds a strict schema for reusable search request contracts.
|
|
349
|
+
* The optional `where` object must contain one defined filter.
|
|
350
|
+
*
|
|
351
|
+
* Query is optional and non-empty; pagination is required by default.
|
|
352
|
+
* Literal feature flags update both runtime and inferred static shapes.
|
|
353
|
+
*
|
|
354
|
+
* @example
|
|
355
|
+
* const assetSearchSchema = zodSearchSchema({
|
|
356
|
+
* filters: assetSchema.pick({ status: true }),
|
|
357
|
+
* });
|
|
358
|
+
*
|
|
359
|
+
* type AssetSearch = z.infer<typeof assetSearchSchema>;
|
|
360
|
+
*/
|
|
361
|
+
declare const zodSearchSchema: <const TShape extends z.ZodRawShape, const TQueryEnabled extends boolean = true, const TPaginationEnabled extends boolean = true>({ filters, queryEnabled, paginationEnabled, }: ZodSearchSchemaOptions<TShape, TQueryEnabled, TPaginationEnabled>) => z.ZodObject<ZodSearchShape<TShape, TQueryEnabled, TPaginationEnabled> extends infer T ? { -readonly [P in keyof T]: T[P]; } : never, z.core.$strict>;
|
|
297
362
|
|
|
298
363
|
type QueryPrimitive = string | number | boolean | bigint | Date;
|
|
299
364
|
/**
|
|
@@ -316,7 +381,7 @@ type AsQuery<T> = T extends null | undefined ? T : T extends QueryPrimitive ? st
|
|
|
316
381
|
* // Invalid: {}, { a: undefined, b: undefined, c: undefined }
|
|
317
382
|
* ```
|
|
318
383
|
*/
|
|
319
|
-
type AtLeastOne<T, Keys extends keyof T = keyof T> = Keys extends keyof T ? Simplify
|
|
384
|
+
type AtLeastOne<T, Keys extends keyof T = keyof T> = Keys extends keyof T ? Simplify<Required<Pick<T, Keys>> & Partial<Omit<T, Keys>>> : never;
|
|
320
385
|
|
|
321
386
|
/**
|
|
322
387
|
* Wraps a partial Zod object schema with:
|
|
@@ -356,4 +421,4 @@ declare const asQuery: <T extends z.ZodTypeAny>(schema: T) => z.ZodPreprocess<T>
|
|
|
356
421
|
*/
|
|
357
422
|
declare const isPlainObject: (value: unknown) => value is Record<string, unknown>;
|
|
358
423
|
|
|
359
|
-
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, ZodJWTService, type ZodPaginationOptions, asQuery, createStringEnumRecord, failure, fetchAndThrow, fetchSafely, formatTime, generateRandomString, getColoredHTTPStatus, getFormattedDate, getFormattedTime, getUTCOffset, getZonedTime, honoLoggingHandler, isPlainObject, log, measureExecutionTime, onHandlerError, parseQueryValue, respond, safeExecute, sqlWhere, success, zodAtLeastOne, zodPaginationSchema, zodPaginationShape };
|
|
424
|
+
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, ZodJWTService, type ZodPaginationOptions, type ZodSearchSchemaOptions, asQuery, createStringEnumRecord, failure, fetchAndThrow, fetchSafely, formatTime, generateRandomString, getColoredHTTPStatus, getFormattedDate, getFormattedTime, getUTCOffset, getZonedTime, honoLoggingHandler, isPlainObject, log, measureExecutionTime, onHandlerError, parseQueryValue, respond, safeExecute, sqlWhere, success, zodAtLeastOne, zodPaginationSchema, zodPaginationShape, zodSearchSchema };
|
package/dist/index.js
CHANGED
|
@@ -213,16 +213,25 @@ var ZodJWTService = class {
|
|
|
213
213
|
}
|
|
214
214
|
};
|
|
215
215
|
|
|
216
|
-
// src/zod-
|
|
216
|
+
// src/zod-search/zod-search.pagination.schemas.ts
|
|
217
217
|
import z from "zod";
|
|
218
218
|
var zodPaginationSchema = z.object({
|
|
219
|
-
/**
|
|
219
|
+
/**
|
|
220
|
+
* Zero-based number of records skipped before collecting a result page.
|
|
221
|
+
* String values are coerced to support validation of URL query input.
|
|
222
|
+
*/
|
|
220
223
|
offset: z.coerce.number().int().nonnegative().default(0),
|
|
221
|
-
/**
|
|
224
|
+
/**
|
|
225
|
+
* Positive maximum number of records returned in a single result page.
|
|
226
|
+
* String values are coerced to support validation of URL query input.
|
|
227
|
+
*/
|
|
222
228
|
limit: z.coerce.number().int().positive().default(10)
|
|
223
229
|
});
|
|
224
230
|
var zodPaginationShape = zodPaginationSchema.shape;
|
|
225
231
|
|
|
232
|
+
// src/zod-search/zod-search.schemas.ts
|
|
233
|
+
import z2 from "zod";
|
|
234
|
+
|
|
226
235
|
// src/zod-validation/zod-validation.refiners.ts
|
|
227
236
|
var DEFAULT_ERROR_MESSAGE = "Invalid input. At least one field must be provided.";
|
|
228
237
|
var zodAtLeastOne = (schema) => schema.superRefine((val, ctx) => {
|
|
@@ -231,8 +240,20 @@ var zodAtLeastOne = (schema) => schema.superRefine((val, ctx) => {
|
|
|
231
240
|
ctx.addIssue({ code: "custom", message: DEFAULT_ERROR_MESSAGE });
|
|
232
241
|
}).transform((val) => val);
|
|
233
242
|
|
|
243
|
+
// src/zod-search/zod-search.schemas.ts
|
|
244
|
+
var zodSearchQuerySchema = z2.string().trim().min(1).optional();
|
|
245
|
+
var createZodSearchWhereSchema = (filters) => zodAtLeastOne(filters.partial()).optional();
|
|
246
|
+
var zodSearchSchema = ({ filters, queryEnabled, paginationEnabled }) => {
|
|
247
|
+
const shape = {
|
|
248
|
+
where: createZodSearchWhereSchema(filters),
|
|
249
|
+
...queryEnabled !== false ? { query: zodSearchQuerySchema } : {},
|
|
250
|
+
...paginationEnabled !== false ? { pagination: zodPaginationSchema } : {}
|
|
251
|
+
};
|
|
252
|
+
return z2.object(shape).strict();
|
|
253
|
+
};
|
|
254
|
+
|
|
234
255
|
// src/zod-validation/zod-validation.parsing.ts
|
|
235
|
-
import
|
|
256
|
+
import z3 from "zod";
|
|
236
257
|
|
|
237
258
|
// src/zod-validation/zod-validation.utilities.ts
|
|
238
259
|
var isPlainObject = (value) => {
|
|
@@ -259,7 +280,7 @@ var parseQueryValue = (value) => {
|
|
|
259
280
|
return Number(normalized);
|
|
260
281
|
return value;
|
|
261
282
|
};
|
|
262
|
-
var asQuery = (schema) =>
|
|
283
|
+
var asQuery = (schema) => z3.preprocess(parseQueryValue, schema);
|
|
263
284
|
export {
|
|
264
285
|
EXCEPTION_STATUS_CODES,
|
|
265
286
|
SUCCESS_STATUS_CODES,
|
|
@@ -288,5 +309,6 @@ export {
|
|
|
288
309
|
success,
|
|
289
310
|
zodAtLeastOne,
|
|
290
311
|
zodPaginationSchema,
|
|
291
|
-
zodPaginationShape
|
|
312
|
+
zodPaginationShape,
|
|
313
|
+
zodSearchSchema
|
|
292
314
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kalutskii/foundation",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.23",
|
|
4
4
|
"description": "Typescript collection of most common utilities, schemas and functions among private projects.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
"build": "tsup",
|
|
15
15
|
"lint": "eslint . --ext .ts --fix",
|
|
16
16
|
"format": "prettier --write .",
|
|
17
|
+
"test": "bun test",
|
|
17
18
|
"typecheck": "tsc --noEmit"
|
|
18
19
|
},
|
|
19
20
|
"exports": {
|