@freshpointcz/fresh-core 0.0.17 → 0.0.19

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.mts CHANGED
@@ -132,6 +132,236 @@ declare class StatusDto {
132
132
  constructor(status: Status, details?: string, timestamp?: string);
133
133
  }
134
134
 
135
+ /**
136
+ * Pagination parameters used to describe the current page slice of a query.
137
+ *
138
+ * Construct this object manually or via {@link getPaginationParams} to ensure
139
+ * defaults are applied consistently. Pass it into {@link constructTypeormPagination}
140
+ * when building a TypeORM `findAndCount` call.
141
+ *
142
+ * @example
143
+ * const pagination: PaginationParams = {
144
+ * page: 2,
145
+ * limit: 25,
146
+ * skip: 25, // (page - 1) * limit
147
+ * };
148
+ */
149
+ interface PaginationParams {
150
+ /** 1-based page number. */
151
+ page: number;
152
+ /**
153
+ * Maximum number of items to return in a single page.
154
+ *
155
+ * @remarks Do NOT exceed 1000 — large limits can cause memory pressure and
156
+ * slow queries. Use {@link listAll} for bulk data retrieval instead.
157
+ */
158
+ limit: number;
159
+ /**
160
+ * Number of records to skip before returning results.
161
+ *
162
+ * @remarks Always derived as `(page - 1) * limit`. Do not set this
163
+ * independently; keep it in sync with `page` and `limit`.
164
+ */
165
+ skip: number;
166
+ }
167
+ /**
168
+ * Merges caller-supplied pagination overrides with {@link DEFAULT_PAGINATION_PARAMS}.
169
+ *
170
+ * Use this at the DAO or service layer to normalize an optional `PaginationParams`
171
+ * argument before passing it to {@link constructTypeormPagination}.
172
+ *
173
+ * @param params - Partial pagination parameters to merge. Any omitted fields
174
+ * fall back to the defaults defined in {@link DEFAULT_PAGINATION_PARAMS}.
175
+ * @returns A complete {@link PaginationParams} object with all fields populated.
176
+ *
177
+ * @example
178
+ * // No args — returns the default params
179
+ * getPaginationParams();
180
+ * // → { page: 1, limit: 1000, skip: 0 }
181
+ *
182
+ * // Partial override — only limit is customized
183
+ * getPaginationParams({ page: 3, limit: 50, skip: 100 });
184
+ * // → { page: 3, limit: 50, skip: 100 }
185
+ */
186
+ declare function getPaginationParams(params?: Partial<PaginationParams>): PaginationParams;
187
+ /**
188
+ * Default pagination parameters applied when no overrides are provided.
189
+ *
190
+ * - `page`: `1` (first page)
191
+ * - `limit`: `1000` (fetch-all default; suited for internal batch operations)
192
+ * - `skip`: `0`
193
+ *
194
+ * @remarks
195
+ * This high default limit is intentional for internal/service-layer usage.
196
+ * Controller layer endpoints should always supply explicit, lower limits
197
+ * (e.g. 20–100) to avoid returning unbounded result sets to API consumers.
198
+ *
199
+ * @see {@link getPaginationParams} for applying these defaults with optional overrides.
200
+ */
201
+ declare const DEFAULT_PAGINATION_PARAMS: PaginationParams;
202
+
203
+ /**
204
+ * Converts {@link PaginationParams} into the `take`/`skip` shape expected by
205
+ * TypeORM's `find`, `findAndCount`, and `findOne` options.
206
+ *
207
+ * @param params - A fully-populated {@link PaginationParams} object (use
208
+ * {@link getPaginationParams} to guarantee all fields are set).
209
+ * @returns An object with `take` and `skip` keys ready to spread into a TypeORM
210
+ * `FindManyOptions`.
211
+ *
212
+ * @example
213
+ * const pagination = getPaginationParams({ page: 2, limit: 25, skip: 25 });
214
+ *
215
+ * const [data, total] = await repo.findAndCount({
216
+ * where: { isActive: true },
217
+ * ...constructTypeormPagination(pagination),
218
+ * });
219
+ *
220
+ * @see {@link https://typeorm.io/find-options | TypeORM Find Options}
221
+ */
222
+ declare function constructTypeormPagination(params: PaginationParams): {
223
+ take: number;
224
+ skip: number;
225
+ };
226
+ /**
227
+ * Parses pagination parameters from a {@link URLSearchParams} instance.
228
+ *
229
+ * Intended for use in contexts where query parameters arrive as a raw URL
230
+ * string (e.g. middleware, edge functions, or fetch-based handlers) rather
231
+ * than through a framework that deserialises them automatically.
232
+ *
233
+ * **Clamping rules applied automatically:**
234
+ * - `page` is clamped to a minimum of `1`.
235
+ * - `limit` is clamped between `1` and `100` (inclusive).
236
+ * - `skip` is derived as `(page - 1) * limit`.
237
+ *
238
+ * @param searchParams - A `URLSearchParams` instance, typically obtained from
239
+ * `new URL(request.url).searchParams`.
240
+ * @returns A fully-populated {@link PaginationParams} object.
241
+ *
242
+ * @example
243
+ * const url = new URL("https://api.example.com/items?page=3&limit=50");
244
+ * const pagination = parsePaginationFromURL(url.searchParams);
245
+ * // → { page: 3, limit: 50, skip: 100 }
246
+ *
247
+ * @example
248
+ * // Missing params fall back to sensible defaults
249
+ * const url = new URL("https://api.example.com/items");
250
+ * const pagination = parsePaginationFromURL(url.searchParams);
251
+ * // → { page: 1, limit: 20, skip: 0 }
252
+ *
253
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams | MDN: URLSearchParams}
254
+ */
255
+ declare function parsePaginationFromURL(searchParams: URLSearchParams): PaginationParams;
256
+
257
+ /**
258
+ * Metadata returned alongside a paginated result set.
259
+ *
260
+ * Populated automatically by {@link getPaginationMeta}.
261
+ *
262
+ * @example
263
+ * // Example meta for page 2 of 3, 25 items per page, 70 total items:
264
+ * const meta: PaginationMeta = {
265
+ * total: 70,
266
+ * page: 2,
267
+ * limit: 25,
268
+ * totalPages: 3,
269
+ * };
270
+ */
271
+ interface PaginationMeta {
272
+ /** Total number of records across all pages. */
273
+ total: number;
274
+ /** Current 1-based page number. */
275
+ page: number;
276
+ /** Maximum number of items per page that was requested. */
277
+ limit: number;
278
+ /**
279
+ * Total number of pages available given the current `limit`.
280
+ *
281
+ * Computed as `Math.ceil(total / limit)`. Returns `0` when `total` is `0`.
282
+ */
283
+ totalPages: number;
284
+ }
285
+ /**
286
+ * Constructs a {@link PaginationMeta} object from raw query result data.
287
+ *
288
+ * Call this after a `findAndCount` (or equivalent) to build the `meta` field
289
+ * of a {@link PaginatedList} response.
290
+ *
291
+ * @param total - Total number of records matching the query (the second element
292
+ * returned by TypeORM's `findAndCount`).
293
+ * @param page - The 1-based page number that was requested.
294
+ * @param limit - The page size that was requested.
295
+ * @returns A {@link PaginationMeta} object ready to include in a {@link PaginatedList}.
296
+ *
297
+ * @example
298
+ * const [data, total] = await repo.findAndCount({ take: 25, skip: 25 });
299
+ *
300
+ * return {
301
+ * data,
302
+ * meta: getPaginationMeta(total, 2, 25),
303
+ * // → { total: 70, page: 2, limit: 25, totalPages: 3 }
304
+ * };
305
+ */
306
+ declare function getPaginationMeta(total: number, page: number, limit: number): PaginationMeta;
307
+
308
+ /**
309
+ * A generic wrapper for a single page of query results combined with its
310
+ * pagination metadata.
311
+ *
312
+ * Returned by DAO `findMany` methods and exposed directly from API endpoints.
313
+ *
314
+ * @typeParam T - The entity or DTO type contained in the `data` array.
315
+ *
316
+ * @example
317
+ * const result: PaginatedList<UserEntity> = {
318
+ * data: [{ id: 1, name: "Alice" }, { id: 2, name: "Bob" }],
319
+ * meta: { total: 42, page: 1, limit: 20, totalPages: 3 },
320
+ * };
321
+ */
322
+ interface PaginatedList<T> {
323
+ /** The records for the current page. */
324
+ data: T[];
325
+ /** Pagination metadata describing the full result set. */
326
+ meta: PaginationMeta;
327
+ }
328
+
329
+ /**
330
+ * Exhaustively fetches all records by iterating through pages until the last
331
+ * page is reached.
332
+ *
333
+ * Use this utility when you need the full result set for an operation that
334
+ * does not support streaming (e.g. bulk exports, aggregations, seeding).
335
+ * For large datasets, prefer a streaming or cursor-based approach where possible.
336
+ *
337
+ * @typeParam T - The entity or DTO type returned by `fetchPage`.
338
+ *
339
+ * @param fetchPage - An async function that accepts a {@link PaginationParams}
340
+ * and returns a {@link PaginatedList}. Typically, a bound DAO method.
341
+ * @param batchSize - Number of records to fetch per page. Defaults to `1000`.
342
+ * Do not exceed `1000` to avoid memory pressure.
343
+ * @returns A promise resolving to a flat array of all matching records.
344
+ *
345
+ * @remarks
346
+ * - Iteration stops when `page >= meta.totalPages`.
347
+ * - If the underlying data changes during iteration, results may be inconsistent.
348
+ * For consistency guarantees, wrap the call in a database transaction.
349
+ *
350
+ * @example
351
+ * // Fetch all active users without worrying about pagination
352
+ * const allUsers = await listAll(
353
+ * (pagination) => userDao.findMany({ where: { isActive: true }, pagination }),
354
+ * );
355
+ *
356
+ * @example
357
+ * // Use a smaller batch size to reduce memory footprint
358
+ * const allOrders = await listAll(
359
+ * (pagination) => orderDao.findMany({ pagination }),
360
+ * 200,
361
+ * );
362
+ */
363
+ declare function listAll<T>(fetchPage: (pagination: PaginationParams) => Promise<PaginatedList<T>>, batchSize?: number): Promise<T[]>;
364
+
135
365
  declare const AMOUNT_UNIT: {
136
366
  readonly 1: {
137
367
  readonly symbol: "g";
@@ -404,6 +634,137 @@ declare class Product extends FreshEntity {
404
634
  declare class Subcategory extends FreshEntity {
405
635
  }
406
636
 
637
+ /**
638
+ * Formats a number to a database-safe decimal string.
639
+ * @param num - The number to format
640
+ * @param precision - Total number of significant digits allowed (e.g., 9)
641
+ * @param scale - Number of digits after the decimal point (e.g., 2)
642
+ * @returns string formatted for database insertion of decimal type
643
+ */
644
+ declare function toDecimal(num: number, precision?: number, scale?: number): string;
645
+ /**
646
+ * Options for {@link isDecimal}.
647
+ */
648
+ interface IsDecimalOptions {
649
+ /**
650
+ * Restrict accepted JavaScript type.
651
+ *
652
+ * - `"number"` — only JS `number` values pass (strings are rejected).
653
+ * - `"string"` — only JS `string` values pass (numbers are rejected).
654
+ * - omitted — both `number` and `string` are accepted.
655
+ *
656
+ * @example
657
+ * isDecimal("3.14", { allowedType: "number" }); // false — wrong JS type
658
+ * isDecimal(3.14, { allowedType: "number" }); // true
659
+ * isDecimal(3.14, { allowedType: "string" }); // false — wrong JS type
660
+ * isDecimal("3.14", { allowedType: "string" }); // true
661
+ */
662
+ allowedType?: "number" | "string";
663
+ /**
664
+ * The decimal separator character to accept for string values.
665
+ * Has no effect on `number` inputs (JS numbers always use `.` internally).
666
+ *
667
+ * Defaults to `"."`.
668
+ *
669
+ * @example
670
+ * isDecimal("3,14", { decimalPoint: "," }); // true
671
+ * isDecimal("3.14", { decimalPoint: "," }); // false — wrong separator
672
+ */
673
+ decimalPoint?: "." | ",";
674
+ /**
675
+ * Maximum total number of significant digits (integer digits + fractional digits combined).
676
+ *
677
+ * @example
678
+ * isDecimal("12345.67", { precision: 7 }); // true — 5 + 2 = 7 digits
679
+ * isDecimal("12345.67", { precision: 6 }); // false — 5 + 2 = 7 > 6
680
+ */
681
+ precision?: number;
682
+ /**
683
+ * Maximum number of digits allowed after the decimal point.
684
+ *
685
+ * @example
686
+ * isDecimal("3.14", { scale: 2 }); // true
687
+ * isDecimal("3.145", { scale: 2 }); // false — 3 fractional digits > 2
688
+ * isDecimal("3", { scale: 2 }); // true — 0 fractional digits ≤ 2
689
+ */
690
+ scale?: number;
691
+ }
692
+ /**
693
+ * Narrows `v` to `number` when `allowedType` is `"number"`.
694
+ * @param v - Value to test.
695
+ * @param options - Must include `allowedType: "number"`.
696
+ */
697
+ declare function isDecimal(v: unknown, options: IsDecimalOptions & {
698
+ allowedType: "number";
699
+ }): v is number;
700
+ /**
701
+ * Narrows `v` to `string` when `allowedType` is `"string"`.
702
+ * @param v - Value to test.
703
+ * @param options - Must include `allowedType: "string"`.
704
+ */
705
+ declare function isDecimal(v: unknown, options: IsDecimalOptions & {
706
+ allowedType: "string";
707
+ }): v is string;
708
+ /**
709
+ * Returns `true` when `v` is a valid decimal value (number or string).
710
+ *
711
+ * Validates that the input represents a finite decimal number. Accepts both
712
+ * JS `number` primitives and decimal strings. Optionally restricts the accepted
713
+ * JS type, the decimal separator character, and enforces precision/scale bounds.
714
+ *
715
+ * **Type narrowing via overloads:**
716
+ * When `allowedType` is specified the return type is narrowed accordingly —
717
+ * `v is number` for `"number"` and `v is string` for `"string"`.
718
+ *
719
+ * **Behaviour per input type:**
720
+ * - `number` — must be finite and non-NaN. Scientific notation (e.g. `1e20`) is
721
+ * rejected because it has no standard decimal representation.
722
+ * - `string` — must match `^-?\d+([.,]\d+)?$` using the configured separator.
723
+ * Leading/trailing whitespace is not trimmed; pass `.trim()` yourself if needed.
724
+ *
725
+ * @param v - Value to test. Anything other than `number` or `string` returns `false`.
726
+ * @param options - Optional validation constraints.
727
+ * @returns `true` when `v` is a valid decimal within the specified constraints.
728
+ *
729
+ * @example
730
+ * // Basic usage — accepts both number and string
731
+ * isDecimal(3.14); // true
732
+ * isDecimal("3.14"); // true
733
+ * isDecimal("abc"); // false
734
+ * isDecimal(Infinity); // false
735
+ * isDecimal(NaN); // false
736
+ * isDecimal(null); // false
737
+ *
738
+ * @example
739
+ * // Restrict to a single JS type
740
+ * isDecimal(3.14, { allowedType: "number" }); // true
741
+ * isDecimal("3.14", { allowedType: "number" }); // false — string rejected
742
+ * isDecimal("3.14", { allowedType: "string" }); // true
743
+ * isDecimal(3.14, { allowedType: "string" }); // false — number rejected
744
+ *
745
+ * @example
746
+ * // Comma as decimal separator (e.g. European locale strings)
747
+ * isDecimal("3,14", { decimalPoint: "," }); // true
748
+ * isDecimal("3.14", { decimalPoint: "," }); // false
749
+ *
750
+ * @example
751
+ * // Precision and scale constraints (matching SQL decimal(9, 2))
752
+ * isDecimal("1234567.89", { precision: 9, scale: 2 }); // true — 7+2=9 digits
753
+ * isDecimal("12345678.9", { precision: 9, scale: 2 }); // false — 8+1=9 but scale violated? no — true, 1≤2
754
+ * isDecimal("3.145", { scale: 2 }); // false — 3 fractional digits
755
+ * isDecimal("3", { scale: 2 }); // true — 0 fractional digits
756
+ *
757
+ * @example
758
+ * // Type narrowing in practice
759
+ * function process(raw: unknown) {
760
+ * if (isDecimal(raw, { allowedType: "string" })) {
761
+ * raw; // narrowed to string here
762
+ * console.log(raw.toUpperCase());
763
+ * }
764
+ * }
765
+ */
766
+ declare function isDecimal(v: unknown, options?: IsDecimalOptions): v is number | string;
767
+
407
768
  /**
408
769
  * Runtime validation for enum values.
409
770
  * If a runtime enum object is provided, the value must match one of its values
@@ -474,6 +835,46 @@ declare function isNumberInRange(v: unknown, min: number, max: number, options?:
474
835
  includeMin?: boolean;
475
836
  includeMax?: boolean;
476
837
  }): v is number;
838
+ /**
839
+ * Numeric boolean flag type — either 0 or 1
840
+ */
841
+ type BinaryFlag = 0 | 1;
842
+ /**
843
+ * Converts a number, boolean, null, or undefined to a BinaryFlag (0 or 1)
844
+ *
845
+ * @example
846
+ * TO_BINARY_FLAG(true) // 1
847
+ * TO_BINARY_FLAG(1) // 1
848
+ * TO_BINARY_FLAG(false) // 0
849
+ * TO_BINARY_FLAG(null) // 0
850
+ */
851
+ declare const TO_BINARY_FLAG: (v: number | boolean | null | undefined) => BinaryFlag;
852
+
853
+ /**
854
+ * Runs async worker functions over items with a concurrency limit.
855
+ *
856
+ * @param items - Array of items to process
857
+ * @param concurrency - Maximum number of concurrent workers
858
+ * @param worker - Async function to run for each item
859
+ *
860
+ * @example
861
+ * await runWithConcurrency(productIds, 5, async (id) => {
862
+ * await processProduct(id);
863
+ * });
864
+ */
865
+ declare function runWithConcurrency<T>(items: T[], concurrency: number, worker: (item: T) => Promise<void>): Promise<void>;
866
+
867
+ type TransformMap<TIn, TOut, K extends keyof TIn & keyof TOut> = Partial<{
868
+ [P in K]: (val: TIn[P]) => TOut[P];
869
+ }>;
870
+ /**
871
+ * Builds a partial patch object from an input DTO, only including keys that are defined.
872
+ * Optionally applies transform functions to specific keys.
873
+ *
874
+ * @example
875
+ * const patch = buildPatch(data, ["name", "active"], { active: (v) => v ? 1 : 0 });
876
+ */
877
+ declare function buildPatch<TOut extends Record<string, any>, TIn extends Record<string, any>, K extends keyof TIn & keyof TOut = keyof TIn & keyof TOut>(data: Partial<TIn>, keys: readonly K[], transforms?: TransformMap<TIn, TOut, K>): Partial<Pick<TOut, K>>;
477
878
 
478
879
  type Maybe<T> = T | null;
479
880
  /**
@@ -605,6 +1006,18 @@ declare class ApiError extends Error {
605
1006
  constructor(statusCode: number, status: Status, detail?: string);
606
1007
  }
607
1008
 
1009
+ /**
1010
+ * Represents a non-fatal business rule violation that should be
1011
+ * communicated to the caller without being treated as a system error.
1012
+ *
1013
+ * @example
1014
+ * throw new BusinessWarning("ORDER_ALREADY_SENT", "Tato objednávka již byla odeslána.");
1015
+ */
1016
+ declare class BusinessWarning extends Error {
1017
+ readonly code: string;
1018
+ constructor(code: string, message: string);
1019
+ }
1020
+
608
1021
  declare const FRESH_ESLINT_CONFIG: {
609
1022
  files: string[];
610
1023
  ignores: string[];
@@ -682,4 +1095,4 @@ interface HealthCheckResult {
682
1095
  };
683
1096
  }
684
1097
 
685
- export { AMOUNT_UNIT, ActionCommandCode, ApiError, BaseEntityChangeSubscriber, type CardNumber, Category, DataHelper, DateUtils, type Deferred, DepotPoolStatus, Device, type EntityChangeEvent, FreshDao, FreshEntity, FreshHyperEntity, FreshJob, FreshTranslationBase, type HealthCheckResult, HttpStatus, LanguageCode, Manufacturer, type Maybe, PaymentMethod, PG_DATA_SOURCE_OPTIONS as PgDataSourceOptions, Product, SinglePromiseWaiter, Singleton, type Status, StatusDto, Subcategory, TimestampColumn, TransactionType, createDeferred, FRESH_ESLINT_CONFIG as freshEslintConfig, hasOwn, isEnumValue, isFlag01, isMaybe, isNumber, isNumberInRange, isObject, isString, isValidCron };
1098
+ export { AMOUNT_UNIT, ActionCommandCode, ApiError, BaseEntityChangeSubscriber, type BinaryFlag, BusinessWarning, type CardNumber, Category, DEFAULT_PAGINATION_PARAMS, DataHelper, DateUtils, type Deferred, DepotPoolStatus, Device, type EntityChangeEvent, FreshDao, FreshEntity, FreshHyperEntity, FreshJob, FreshTranslationBase, type HealthCheckResult, HttpStatus, type IsDecimalOptions, LanguageCode, Manufacturer, type Maybe, type PaginatedList, type PaginationMeta, type PaginationParams, PaymentMethod, PG_DATA_SOURCE_OPTIONS as PgDataSourceOptions, Product, SinglePromiseWaiter, Singleton, type Status, StatusDto, Subcategory, TO_BINARY_FLAG, TimestampColumn, TransactionType, buildPatch, constructTypeormPagination, createDeferred, FRESH_ESLINT_CONFIG as freshEslintConfig, getPaginationMeta, getPaginationParams, hasOwn, isDecimal, isEnumValue, isFlag01, isMaybe, isNumber, isNumberInRange, isObject, isString, isValidCron, listAll, parsePaginationFromURL, runWithConcurrency, toDecimal };