@veloxts/validation 0.6.68 → 0.6.69

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/CHANGELOG.md CHANGED
@@ -1,5 +1,51 @@
1
1
  # @veloxts/validation
2
2
 
3
+ ## 0.6.69
4
+
5
+ ### Patch Changes
6
+
7
+ - implement user feedback improvements across packages
8
+
9
+ ## Summary
10
+
11
+ Addresses 9 user feedback items to improve DX, reduce boilerplate, and eliminate template duplications.
12
+
13
+ ### Phase 1: Validation Helpers (`@veloxts/validation`)
14
+
15
+ - Add `prismaDecimal()`, `prismaDecimalNullable()`, `prismaDecimalOptional()` for Prisma Decimal → number conversion
16
+ - Add `dateToIso`, `dateToIsoNullable`, `dateToIsoOptional` aliases for consistency
17
+
18
+ ### Phase 2: Template Deduplication (`@veloxts/auth`)
19
+
20
+ - Export `createEnhancedTokenStore()` with token revocation and refresh token reuse detection
21
+ - Export `parseUserRoles()` and `DEFAULT_ALLOWED_ROLES`
22
+ - Fix memory leak: track pending timeouts for proper cleanup on `destroy()`
23
+ - Update templates to import from `@veloxts/auth` instead of duplicating code
24
+ - Fix jwtManager singleton pattern in templates
25
+
26
+ ### Phase 3: Router Helpers (`@veloxts/router`)
27
+
28
+ - Add `createRouter()` returning `{ collections, router }` for DRY setup
29
+ - Add `toRouter()` for router-only use cases
30
+ - Update all router templates to use `createRouter()`
31
+
32
+ ### Phase 4: Guard Type Narrowing - Experimental (`@veloxts/auth`, `@veloxts/router`)
33
+
34
+ - Add `NarrowingGuard` interface with phantom `_narrows` type
35
+ - Add `authenticatedNarrow` and `hasRoleNarrow()` guards
36
+ - Add `guardNarrow()` method to `ProcedureBuilder` for context narrowing
37
+ - Enables `ctx.user` to be non-null after guard passes
38
+
39
+ ### Phase 5: Documentation (`@veloxts/router`)
40
+
41
+ - Document `.rest()` override patterns
42
+ - Document `createRouter()` helper usage
43
+ - Document `guardNarrow()` experimental API
44
+ - Add schema browser-safety patterns for RSC apps
45
+
46
+ - Updated dependencies
47
+ - @veloxts/core@0.6.69
48
+
3
49
  ## 0.6.68
4
50
 
5
51
  ### Patch Changes
package/dist/index.d.ts CHANGED
@@ -34,4 +34,4 @@ export { baseEntitySchema, booleanStringSchema, createIdSchema, datetimeSchema,
34
34
  export type { CursorPaginatedResponse, CursorPaginationInput, CursorPaginationMeta, PaginatedResponse, PaginationInput, PaginationMeta, } from './schemas/pagination.js';
35
35
  export { calculateOffset, calculatePaginationMeta, createCursorPaginatedResponseSchema, createPaginatedResponse, createPaginatedResponseSchema, createPaginationSchema, cursorPaginationSchema, PAGINATION_DEFAULTS, paginationInputSchema, } from './schemas/pagination.js';
36
36
  export type { InferWithTimestamps, OmitTimestamps, SerializedDates, WithOptional, } from './schemas/serialization.js';
37
- export { dateToISOString, dateToISOStringNullable, dateToISOStringOptional, timestamps, timestampsWithSoftDelete, withTimestamps, } from './schemas/serialization.js';
37
+ export { dateToISOString, dateToISOStringNullable, dateToISOStringOptional, dateToIso, dateToIsoNullable, dateToIsoOptional, prismaDecimal, prismaDecimalNullable, prismaDecimalOptional, timestamps, timestampsWithSoftDelete, withTimestamps, } from './schemas/serialization.js';
package/dist/index.js CHANGED
@@ -35,4 +35,12 @@ export { isSchema, isZodSchema, wrapSchema } from './types.js';
35
35
  export { assertSchema, createTypeGuard, createValidator, formatZodErrors, parse, parseAll, safeParse, zodErrorToValidationError, } from './middleware.js';
36
36
  export { baseEntitySchema, booleanStringSchema, createIdSchema, datetimeSchema, emailSchema, idParamSchema, integerStringSchema, makePartial, nonEmptyStringSchema, numberStringSchema, omitFields, partialExcept, pickFields, timestampFieldsSchema, urlSchema, uuidSchema, } from './schemas/common.js';
37
37
  export { calculateOffset, calculatePaginationMeta, createCursorPaginatedResponseSchema, createPaginatedResponse, createPaginatedResponseSchema, createPaginationSchema, cursorPaginationSchema, PAGINATION_DEFAULTS, paginationInputSchema, } from './schemas/pagination.js';
38
- export { dateToISOString, dateToISOStringNullable, dateToISOStringOptional, timestamps, timestampsWithSoftDelete, withTimestamps, } from './schemas/serialization.js';
38
+ export {
39
+ // Date serialization
40
+ dateToISOString, dateToISOStringNullable, dateToISOStringOptional,
41
+ // Date aliases (shorter names)
42
+ dateToIso, dateToIsoNullable, dateToIsoOptional,
43
+ // Prisma Decimal helpers
44
+ prismaDecimal, prismaDecimalNullable, prismaDecimalOptional,
45
+ // Timestamp presets
46
+ timestamps, timestampsWithSoftDelete, withTimestamps, } from './schemas/serialization.js';
@@ -35,6 +35,34 @@ export type WithOptional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>
35
35
  * Omits timestamp fields from a type (useful for create/update inputs)
36
36
  */
37
37
  export type OmitTimestamps<T> = Omit<T, 'createdAt' | 'updatedAt' | 'deletedAt'>;
38
+ /**
39
+ * Creates a field that accepts Prisma Decimal and transforms to number
40
+ *
41
+ * Use in OUTPUT schemas where Prisma returns Decimal objects.
42
+ * Handles Prisma Decimal, regular numbers, and string representations.
43
+ *
44
+ * @example
45
+ * ```typescript
46
+ * const ProductSchema = z.object({
47
+ * id: z.string(),
48
+ * price: prismaDecimal(), // Decimal -> number
49
+ * discount: prismaDecimalNullable() // Decimal | null -> number | null
50
+ * });
51
+ * ```
52
+ */
53
+ export declare function prismaDecimal(): z.ZodEffects<z.ZodAny, number, any>;
54
+ /**
55
+ * Nullable version of prismaDecimal
56
+ *
57
+ * Returns null for null/undefined inputs, otherwise converts to number.
58
+ */
59
+ export declare function prismaDecimalNullable(): z.ZodEffects<z.ZodAny, number | null, any>;
60
+ /**
61
+ * Optional version of prismaDecimal
62
+ *
63
+ * Returns undefined for null/undefined inputs, otherwise converts to number.
64
+ */
65
+ export declare function prismaDecimalOptional(): z.ZodEffects<z.ZodAny, number | undefined, any>;
38
66
  /**
39
67
  * Creates a date field that serializes to ISO string
40
68
  *
@@ -59,6 +87,21 @@ export declare function dateToISOStringNullable(): z.ZodEffects<z.ZodNullable<z.
59
87
  * Creates an optional date field that serializes to ISO string or undefined
60
88
  */
61
89
  export declare function dateToISOStringOptional(): z.ZodEffects<z.ZodOptional<z.ZodDate>, string | undefined, Date | undefined>;
90
+ /**
91
+ * Alias for dateToISOString for naming consistency
92
+ * @alias dateToISOString
93
+ */
94
+ export declare const dateToIso: typeof dateToISOString;
95
+ /**
96
+ * Alias for dateToISOStringNullable for naming consistency
97
+ * @alias dateToISOStringNullable
98
+ */
99
+ export declare const dateToIsoNullable: typeof dateToISOStringNullable;
100
+ /**
101
+ * Alias for dateToISOStringOptional for naming consistency
102
+ * @alias dateToISOStringOptional
103
+ */
104
+ export declare const dateToIsoOptional: typeof dateToISOStringOptional;
62
105
  /**
63
106
  * Standard timestamp fields for entities
64
107
  *
@@ -8,6 +8,100 @@
8
8
  */
9
9
  import { z } from 'zod';
10
10
  // ============================================================================
11
+ // Prisma Decimal Helpers
12
+ // ============================================================================
13
+ /**
14
+ * Creates a field that accepts Prisma Decimal and transforms to number
15
+ *
16
+ * Use in OUTPUT schemas where Prisma returns Decimal objects.
17
+ * Handles Prisma Decimal, regular numbers, and string representations.
18
+ *
19
+ * @example
20
+ * ```typescript
21
+ * const ProductSchema = z.object({
22
+ * id: z.string(),
23
+ * price: prismaDecimal(), // Decimal -> number
24
+ * discount: prismaDecimalNullable() // Decimal | null -> number | null
25
+ * });
26
+ * ```
27
+ */
28
+ export function prismaDecimal() {
29
+ return z.any().transform((val) => {
30
+ if (val === null || val === undefined) {
31
+ throw new Error('Expected Decimal, got null/undefined');
32
+ }
33
+ // Prisma Decimal has toNumber() method
34
+ if (typeof val === 'object' && 'toNumber' in val && typeof val.toNumber === 'function') {
35
+ return val.toNumber();
36
+ }
37
+ // Already a number
38
+ if (typeof val === 'number') {
39
+ return val;
40
+ }
41
+ // String (from JSON)
42
+ if (typeof val === 'string') {
43
+ const num = Number.parseFloat(val);
44
+ if (Number.isNaN(num)) {
45
+ throw new Error(`Cannot convert "${val}" to number`);
46
+ }
47
+ return num;
48
+ }
49
+ throw new Error(`Cannot convert ${typeof val} to number`);
50
+ });
51
+ }
52
+ /**
53
+ * Nullable version of prismaDecimal
54
+ *
55
+ * Returns null for null/undefined inputs, otherwise converts to number.
56
+ */
57
+ export function prismaDecimalNullable() {
58
+ return z.any().transform((val) => {
59
+ if (val === null || val === undefined) {
60
+ return null;
61
+ }
62
+ if (typeof val === 'object' && 'toNumber' in val && typeof val.toNumber === 'function') {
63
+ return val.toNumber();
64
+ }
65
+ if (typeof val === 'number') {
66
+ return val;
67
+ }
68
+ if (typeof val === 'string') {
69
+ const num = Number.parseFloat(val);
70
+ if (Number.isNaN(num)) {
71
+ throw new Error(`Cannot convert "${val}" to number`);
72
+ }
73
+ return num;
74
+ }
75
+ throw new Error(`Cannot convert ${typeof val} to number`);
76
+ });
77
+ }
78
+ /**
79
+ * Optional version of prismaDecimal
80
+ *
81
+ * Returns undefined for null/undefined inputs, otherwise converts to number.
82
+ */
83
+ export function prismaDecimalOptional() {
84
+ return z.any().transform((val) => {
85
+ if (val === null || val === undefined) {
86
+ return undefined;
87
+ }
88
+ if (typeof val === 'object' && 'toNumber' in val && typeof val.toNumber === 'function') {
89
+ return val.toNumber();
90
+ }
91
+ if (typeof val === 'number') {
92
+ return val;
93
+ }
94
+ if (typeof val === 'string') {
95
+ const num = Number.parseFloat(val);
96
+ if (Number.isNaN(num)) {
97
+ throw new Error(`Cannot convert "${val}" to number`);
98
+ }
99
+ return num;
100
+ }
101
+ throw new Error(`Cannot convert ${typeof val} to number`);
102
+ });
103
+ }
104
+ // ============================================================================
11
105
  // Date Field Helpers
12
106
  // ============================================================================
13
107
  /**
@@ -46,6 +140,21 @@ export function dateToISOStringOptional() {
46
140
  .optional()
47
141
  .transform((date) => (date ? date.toISOString() : undefined));
48
142
  }
143
+ /**
144
+ * Alias for dateToISOString for naming consistency
145
+ * @alias dateToISOString
146
+ */
147
+ export const dateToIso = dateToISOString;
148
+ /**
149
+ * Alias for dateToISOStringNullable for naming consistency
150
+ * @alias dateToISOStringNullable
151
+ */
152
+ export const dateToIsoNullable = dateToISOStringNullable;
153
+ /**
154
+ * Alias for dateToISOStringOptional for naming consistency
155
+ * @alias dateToISOStringOptional
156
+ */
157
+ export const dateToIsoOptional = dateToISOStringOptional;
49
158
  // ============================================================================
50
159
  // Timestamp Schema Presets
51
160
  // ============================================================================
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@veloxts/validation",
3
- "version": "0.6.68",
3
+ "version": "0.6.69",
4
4
  "description": "Zod integration and validation middleware for VeloxTS framework",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -23,7 +23,7 @@
23
23
  },
24
24
  "dependencies": {
25
25
  "zod": "3.25.76",
26
- "@veloxts/core": "0.6.68"
26
+ "@veloxts/core": "0.6.69"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@vitest/coverage-v8": "4.0.16",