exisjs 0.7.11 → 0.7.13

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.
Files changed (150) hide show
  1. package/README.md +195 -39
  2. package/dist/cli/commands/dev.d.ts +4 -0
  3. package/dist/cli/commands/dev.d.ts.map +1 -1
  4. package/dist/cli/commands/dev.js +136 -32
  5. package/dist/cli/commands/dev.js.map +1 -1
  6. package/dist/cli/commands/run.d.ts +7 -0
  7. package/dist/cli/commands/run.d.ts.map +1 -0
  8. package/dist/cli/commands/run.js +99 -0
  9. package/dist/cli/commands/run.js.map +1 -0
  10. package/dist/cli/index.js +29 -13
  11. package/dist/cli/index.js.map +1 -1
  12. package/dist/cli/manifest.d.ts.map +1 -1
  13. package/dist/cli/manifest.js +18 -3
  14. package/dist/cli/manifest.js.map +1 -1
  15. package/dist/cli/routes.d.ts +6 -0
  16. package/dist/cli/routes.d.ts.map +1 -1
  17. package/dist/cli/routes.js +224 -135
  18. package/dist/cli/routes.js.map +1 -1
  19. package/dist/config/config.d.ts +2 -1
  20. package/dist/config/config.d.ts.map +1 -1
  21. package/dist/config/config.js.map +1 -1
  22. package/dist/config/types.d.ts +30 -9
  23. package/dist/config/types.d.ts.map +1 -1
  24. package/dist/database/lifecycle.d.ts +6 -1
  25. package/dist/database/lifecycle.d.ts.map +1 -1
  26. package/dist/database/lifecycle.js +15 -4
  27. package/dist/database/lifecycle.js.map +1 -1
  28. package/dist/decorators/params.d.ts +21 -0
  29. package/dist/decorators/params.d.ts.map +1 -1
  30. package/dist/decorators/params.js +21 -1
  31. package/dist/decorators/params.js.map +1 -1
  32. package/dist/di/container.d.ts +15 -6
  33. package/dist/di/container.d.ts.map +1 -1
  34. package/dist/di/container.js +26 -3
  35. package/dist/di/container.js.map +1 -1
  36. package/dist/di/index.d.ts +1 -1
  37. package/dist/di/index.d.ts.map +1 -1
  38. package/dist/di/index.js.map +1 -1
  39. package/dist/di/inject.d.ts +2 -1
  40. package/dist/di/inject.d.ts.map +1 -1
  41. package/dist/di/inject.js +2 -1
  42. package/dist/di/inject.js.map +1 -1
  43. package/dist/docs/BENCHMARKS.md +4 -4
  44. package/dist/docs/content/README.md +43 -44
  45. package/dist/docs/content/boundaries.mdx +141 -48
  46. package/dist/docs/content/cli/commands.mdx +161 -107
  47. package/dist/docs/content/cli/overview.mdx +61 -46
  48. package/dist/docs/content/cli/usage.mdx +107 -30
  49. package/dist/docs/content/config.mdx +145 -147
  50. package/dist/docs/content/controllers.mdx +193 -464
  51. package/dist/docs/content/cron.mdx +183 -69
  52. package/dist/docs/content/decorators.mdx +449 -154
  53. package/dist/docs/content/errors.mdx +202 -305
  54. package/dist/docs/content/exisjs-mcp.mdx +59 -49
  55. package/dist/docs/content/installation.mdx +243 -157
  56. package/dist/docs/content/introduction.mdx +220 -48
  57. package/dist/docs/content/middleware.mdx +171 -497
  58. package/dist/docs/content/modules.mdx +141 -83
  59. package/dist/docs/content/providers.mdx +216 -180
  60. package/dist/docs/content/requests.mdx +168 -488
  61. package/dist/docs/content/responses.mdx +200 -533
  62. package/dist/docs/content/routing.mdx +239 -558
  63. package/dist/docs/content/security.mdx +233 -57
  64. package/dist/docs/content/structure.mdx +151 -240
  65. package/dist/docs/content/swagger.mdx +192 -91
  66. package/dist/docs/content/testing.mdx +205 -74
  67. package/dist/docs/content/validation.mdx +479 -341
  68. package/dist/docs/reference/README.md +34 -2
  69. package/dist/docs/reference/config.mdx +277 -73
  70. package/dist/docs/reference/context.mdx +197 -106
  71. package/dist/docs/reference/cron.mdx +266 -152
  72. package/dist/docs/reference/database.mdx +135 -53
  73. package/dist/docs/reference/decorators.mdx +234 -116
  74. package/dist/docs/reference/di.mdx +146 -112
  75. package/dist/docs/reference/error.mdx +155 -178
  76. package/dist/docs/reference/exis.mdx +141 -70
  77. package/dist/docs/reference/logger.mdx +110 -126
  78. package/dist/docs/reference/middleware.mdx +235 -158
  79. package/dist/docs/reference/plugin.mdx +136 -64
  80. package/dist/docs/reference/router.mdx +277 -251
  81. package/dist/docs/reference/sanitize.mdx +91 -87
  82. package/dist/docs/reference/swagger.mdx +280 -100
  83. package/dist/docs/reference/testing.mdx +223 -89
  84. package/dist/docs/reference/validator.mdx +194 -206
  85. package/dist/lib/inspect-routes.d.ts +2 -0
  86. package/dist/lib/inspect-routes.d.ts.map +1 -0
  87. package/dist/lib/inspect-routes.js +148 -0
  88. package/dist/lib/inspect-routes.js.map +1 -0
  89. package/dist/lib/start-server.js +13 -8
  90. package/dist/lib/start-server.js.map +1 -1
  91. package/dist/module/module.d.ts +6 -3
  92. package/dist/module/module.d.ts.map +1 -1
  93. package/dist/module/module.js +17 -3
  94. package/dist/module/module.js.map +1 -1
  95. package/dist/router/controller-registrar.d.ts.map +1 -1
  96. package/dist/router/controller-registrar.js +26 -4
  97. package/dist/router/controller-registrar.js.map +1 -1
  98. package/dist/router/scanner/cron-mounter.d.ts.map +1 -1
  99. package/dist/router/scanner/cron-mounter.js +9 -2
  100. package/dist/router/scanner/cron-mounter.js.map +1 -1
  101. package/dist/server/app.d.ts +3 -1
  102. package/dist/server/app.d.ts.map +1 -1
  103. package/dist/server/app.js +29 -8
  104. package/dist/server/app.js.map +1 -1
  105. package/dist/server/define.d.ts.map +1 -1
  106. package/dist/server/define.js +5 -0
  107. package/dist/server/define.js.map +1 -1
  108. package/dist/server/request.d.ts +2 -0
  109. package/dist/server/request.d.ts.map +1 -1
  110. package/dist/server/request.js +11 -0
  111. package/dist/server/request.js.map +1 -1
  112. package/dist/swagger/generator.d.ts.map +1 -1
  113. package/dist/swagger/generator.js +130 -10
  114. package/dist/swagger/generator.js.map +1 -1
  115. package/dist/telemetry/index.d.ts +12 -0
  116. package/dist/telemetry/index.d.ts.map +1 -0
  117. package/dist/telemetry/index.js +121 -0
  118. package/dist/telemetry/index.js.map +1 -0
  119. package/dist/testing/client.d.ts +1 -0
  120. package/dist/testing/client.d.ts.map +1 -1
  121. package/dist/testing/client.js +4 -1
  122. package/dist/testing/client.js.map +1 -1
  123. package/dist/testing/index.d.ts.map +1 -1
  124. package/dist/testing/index.js +20 -4
  125. package/dist/testing/index.js.map +1 -1
  126. package/dist/validator/builder.d.ts +26 -1
  127. package/dist/validator/builder.d.ts.map +1 -1
  128. package/dist/validator/builder.js +31 -0
  129. package/dist/validator/builder.js.map +1 -1
  130. package/dist/validator/engine.d.ts +23 -0
  131. package/dist/validator/engine.d.ts.map +1 -1
  132. package/dist/validator/engine.js +228 -31
  133. package/dist/validator/engine.js.map +1 -1
  134. package/dist/validator/index.d.ts +8 -4
  135. package/dist/validator/index.d.ts.map +1 -1
  136. package/dist/validator/index.js +4 -1
  137. package/dist/validator/index.js.map +1 -1
  138. package/dist/validator/tex-types.d.ts +15 -0
  139. package/dist/validator/tex-types.d.ts.map +1 -1
  140. package/dist/validator/tex-types.js +102 -0
  141. package/dist/validator/tex-types.js.map +1 -1
  142. package/dist/validator/tex.d.ts +1 -1
  143. package/dist/validator/tex.d.ts.map +1 -1
  144. package/dist/validator/tex.js +3 -1
  145. package/dist/validator/tex.js.map +1 -1
  146. package/dist/validator/types.d.ts +1 -0
  147. package/dist/validator/types.d.ts.map +1 -1
  148. package/package.json +47 -14
  149. package/dist/docs/content/dataloader.mdx +0 -170
  150. package/dist/docs/reference/dataloader.mdx +0 -91
@@ -1,281 +1,269 @@
1
1
  ---
2
- title: "Validation"
3
- description: "API Reference for ExisJS built-in Schema Validator (tex)."
2
+ title: "exisjs/validator"
3
+ description: "API Reference for the Tex validation engine, primitive types, modifier methods, pagination helpers, and schema resolution."
4
+ nextUrl: "/docs/reference/decorators"
5
+ nextTitle: "Decorators Reference"
4
6
  ---
5
7
 
6
- # Validation (`tex`)
8
+ # `exisjs/validator`
7
9
 
8
- ExisJS ships with a efficient, integrated validation library out of the box, exposed as `tex` from `exisjs/validator`. It is powered by a efficient Rust engine, fully isomorphic, and deeply integrated with TypeScript and OpenAPI (Swagger).
10
+ The `exisjs/validator` module provides the `tex` schema builder, Rust-accelerated `TexEngine`, data transformation modifiers, and pagination helpers for request body, query, and parameter validation.
9
11
 
10
12
  ---
11
13
 
12
- ## Core Types
13
-
14
- The `tex` object provides factory functions for every common primitive and data structure:
15
-
16
- - `tex.string(options?)`: Validates a string (supports min, max, trim, sanitizers, masking, SQL injection prevention, defaults).
17
- - `tex.number(options?)`: Validates a numeric value. Automatically coerces numeric strings in query parameters (e.g., `?limit=100` $\to$ `100`) and supports `{ coerce: true, min, max, default }`.
18
- - `tex.boolean(options?)`: Validates a boolean value. Automatically coerces boolean strings in query parameters (e.g., `?active=true` $\to$ `true`, `"1"`/`"0"`, `"yes"`/`"no"`) and supports `{ coerce: true, default }`.
19
- - `tex.email(options?)`: Validates standard email formatting.
20
- - `tex.uuid(options?)`: Validates UUID v1 or v4 strings (`{ version: 1 | 4 }`).
21
- - `tex.cuid(options?)`: Validates CUID identifiers.
22
- - `tex.password(options?)`: Enforces password complexity rules (`requireNumbers`, `requireSymbols`, `requireUppercase`, `requireLowercase`).
23
- - `tex.creditCard(options?)`: Validates credit card number format with optional masking (`{ mask: true }`).
24
- - `tex.date(options?)`: Validates Date instances, or coerces ISO 8601 strings and UNIX timestamps with `minDate` / `maxDate` bounds.
25
- - `tex.enum(values, options?)`: Validates against a specific set of allowed strings.
26
- - `tex.literal(value)`: Validates against an exact literal value (`string | number | boolean`).
27
- - `tex.record(schema)`: Validates a dictionary / map of key-value pairs matching the given value schema.
28
- - `tex.union(schemas)`: Validates that the input matches at least one of the provided schemas.
29
- - `tex.any()`: Allows any arbitrary payload value without constraints.
30
- - `tex.file(options?)`: Validates multipart file uploads (`maxSize`, `mimeTypes`).
31
-
32
- ### Objects & Arrays
33
-
34
- - `tex.object({ ... })`: Validates an object against a strict shape. Supports arbitrarily nested sub-documents (`tex.object({ nested: tex.object(...) })`).
35
- - `tex.array(schema, options?)`: Validates an array of items matching the given schema.
36
- - **Schema Types**: Accepts primitive types (`tex.string()`), nested `tex.object(...)`, or shorthand plain objects (`tex.array({ ... })`).
37
- - **TypeScript Inference**: TypeScript automatically infers the element object shape for both `tex.object(...)` and plain objects without falling back to `never`.
38
- - **Single-Object Coercion**: Automatically coerces single objects (e.g. `{ item: { ... } }` from HTML forms) into single-item arrays `[ { ... } ]` rather than throwing `"Must be an array"`.
39
- - **Stringified JSON**: Automatically parses and deserializes stringified JSON arrays (e.g. from `multipart/form-data`).
40
- - **Options**: Supports `{ min, max, dedupe, optional, nullable, nullish }`.
14
+ ## Module Exports
41
15
 
42
16
  ```typescript
43
- const UserProfile = tex.object({
44
- id: tex.string(),
45
- profile: tex.object({
46
- bio: tex.string().nullish(), // allows undefined, null, or ""
47
- skills: tex.array(tex.string()),
48
- }),
49
- // Can use plain object shorthand directly inside tex.array
50
- items: tex.array({
51
- id: tex.string(),
52
- quantity: tex.number({ min: 1 }),
53
- }),
54
- role: tex.enum(['admin', 'user']),
55
- })
17
+ import {
18
+ tex,
19
+ TexBuilder,
20
+ TexEngine,
21
+ TexTransformedEngine,
22
+ TexDiscriminatedUnionEngine,
23
+ TexType,
24
+ ValidatorError,
25
+ paginate,
26
+ getPaginationSkip,
27
+ } from 'exisjs/validator'
28
+
29
+ import type {
30
+ Infer,
31
+ ResolveSchema,
32
+ TexBaseOptions,
33
+ TexStringOptions,
34
+ TexNumberOptions,
35
+ TexBooleanOptions,
36
+ TexArrayOptions,
37
+ TexEnumOptions,
38
+ TexDateOptions,
39
+ TexFileOptions,
40
+ TexPasswordOptions,
41
+ TexObjectOptions,
42
+ ValidationErrorDescriptor,
43
+ PaginationMeta,
44
+ PaginatedResult,
45
+ } from 'exisjs/validator'
56
46
  ```
57
47
 
58
- ### Pagination Primitive (`tex.pagination`)
48
+ ---
59
49
 
60
- - `tex.pagination(options?)`: Pre-compiled query schema validating `page` (default 1), `limit` (with coercion and clamping), `sort`, and `order` (`asc` | `desc`).
50
+ ## Schema Factory (`tex`)
51
+
52
+ The `tex` instance exposes factory functions for all standard primitives, complex structures, and specialized validators.
53
+
54
+ ### Primitive Validators
55
+
56
+ | Method | Return Type | Options Interface | Description |
57
+ | :--- | :--- | :--- | :--- |
58
+ | `tex.string(options?)` | `TexType<string>` | `TexStringOptions` | String validation with trimming, HTML escaping, min/max bounds, masking, coercion, and injection checks. |
59
+ | `tex.number(options?)` | `TexType<number>` | `TexNumberOptions` | Number validation with optional string coercion (`coerce: true`) and min/max limits. |
60
+ | `tex.boolean(options?)` | `TexType<boolean>` | `TexBooleanOptions` | Boolean validation with string/numeric coercion (`"true"`, `"1"` -> `true`). |
61
+ | `tex.email(options?)` | `TexType<string>` | `TexStringOptions` | Standard RFC 5322 email format validation. |
62
+ | `tex.uuid(options?)` | `TexType<string>` | `{ version?: 1 \| 4 }` | Validates UUID strings (v1 or v4). |
63
+ | `tex.cuid(options?)` | `TexType<string>` | `TexBaseOptions` | Validates CUID identifiers. |
64
+ | `tex.date(options?)` | `TexType<Date>` | `TexDateOptions` | Validates `Date` objects, ISO 8601 strings, or numeric timestamps (`minDate`, `maxDate`). |
65
+ | `tex.password(options?)` | `TexType<string>` | `TexPasswordOptions` | Enforces password complexity (uppercase, lowercase, numbers, symbols). |
66
+ | `tex.creditCard(options?)` | `TexType<string>` | `{ mask?: boolean }` | Luhn-validated credit card numbers with optional masking. |
67
+ | `tex.file(options?)` | `TexType<ExisFile>` | `TexFileOptions` | Multipart file upload validation (`maxSize`, `mimeTypes`). |
68
+ | `tex.enum(values, options?)` | `TexType<T>` | `TexEnumOptions<T>` | Validates against a fixed array or tuple of string literal values. |
69
+ | `tex.literal(value)` | `TexType<T>` | None | Validates an exact primitive literal (`string \| number \| boolean`). |
70
+ | `tex.record(schema)` | `TexType<Record<string, V>>` | None | Validates key-value dictionaries matching value schema `V`. |
71
+ | `tex.union(schemas)` | `TexType<T>` | None | Validates against any one of the provided schemas. |
72
+ | `tex.any()` | `TexType<any>` | None | Allows any arbitrary unconstrained value. |
61
73
 
62
- ```typescript
63
- import { tex, paginate, getPaginationSkip } from 'exisjs/validator'
74
+ ---
64
75
 
65
- const paginationSchema = tex.pagination({
66
- defaultLimit: 20,
67
- maxLimit: 100,
68
- })
76
+ ### Explicit Coercion (`tex.coerce`)
69
77
 
70
- // Database offset calculation helper
71
- const { skip, limit, page } = getPaginationSkip({ page: 2, limit: 20 })
72
- // skip = 20, limit = 20, page = 2
78
+ The `tex.coerce` namespace provides explicit coercion primitives for query parameters, path variables, and form inputs:
73
79
 
74
- // Standardized envelope builder
75
- const paginatedResponse = paginate(items, 150, { page: 2, limit: 20 })
76
- ```
80
+ | Method | Return Type | Description |
81
+ | :--- | :--- | :--- |
82
+ | `tex.coerce.number(options?)` | `TexType<number>` | Coerces string and numeric input to `number`. |
83
+ | `tex.coerce.boolean(options?)` | `TexType<boolean>` | Coerces `"true"`, `"false"`, `"1"`, `"0"` to `boolean`. |
84
+ | `tex.coerce.date(options?)` | `TexType<Date>` | Coerces ISO 8601 strings and timestamps to native `Date`. |
85
+ | `tex.coerce.string(options?)` | `TexType<string>` | Coerces numeric and primitive inputs to `string`. |
77
86
 
78
87
  ---
79
88
 
80
- ## Usage in Routes
89
+ ### Complex Structures
81
90
 
82
- ExisJS tightly integrates the `tex` validator directly into its routing layer, providing immediate TypeScript inference for `ctx.body`, `@Body()`, and other parameters.
91
+ #### `tex.object(shape, options?)`
83
92
 
84
- **Class-Based (OOP)**
85
- ```typescript filename="src/http/users/route.ts"
86
- import { Controller, Post, Body } from 'exisjs/decorators'
87
- import { tex } from 'exisjs/validator'
93
+ Validates an object against a structured field schema.
88
94
 
89
- const CreateUserSchema = tex.object({ email: tex.email() })
90
-
91
- @Controller('/users')
92
- export default class UserController {
93
- @Post('/', { body: CreateUserSchema })
94
- createUser(@Body() body: any) {
95
- return { success: true, email: body.email }
96
- }
97
- }
95
+ ```typescript
96
+ const UserSchema = tex.object({
97
+ id: tex.string(),
98
+ profile: tex.object({
99
+ bio: tex.string().optional(),
100
+ avatarUrl: tex.string().optional(),
101
+ }),
102
+ roles: tex.array(tex.enum(['admin', 'user', 'guest'] as const)),
103
+ })
98
104
  ```
99
105
 
100
- **Functional**
101
- ```typescript filename="src/http/users/route.ts"
102
- import { controller, route } from 'exisjs/router'
103
- import { tex } from 'exisjs/validator'
104
-
105
- const CreateUserSchema = tex.object({ email: tex.email() })
106
-
107
- export default controller({
108
- createUser: route.post('/users', {
109
- body: CreateUserSchema,
110
- async handle(ctx) {
111
- // ctx.body is strictly inferred as { email: string }
112
- return { success: true, email: ctx.body.email }
113
- }
114
- })
115
- })
106
+ #### `tex.discriminatedUnion(discriminator, variants)`
107
+
108
+ Validates polymorphic object variants by inspecting a literal or enum discriminator property:
109
+
110
+ ```typescript
111
+ const ActionSchema = tex.discriminatedUnion('kind', [
112
+ tex.object({ kind: tex.literal('create'), name: tex.string() }),
113
+ tex.object({ kind: tex.literal('delete'), id: tex.uuid() }),
114
+ ])
116
115
  ```
117
116
 
118
- ---
117
+ #### `tex.array(schema, options?)`
119
118
 
120
- ## Type Options
121
-
122
- All validators accept an options object that allows you to configure validation constraints and behaviors.
123
-
124
- - `optional` / `.optional()`: Marks the field as optional (allows `undefined`).
125
- - `nullable` / `.nullable()`: Marks the field as nullable (allows `null`).
126
- - `nullish` / `.nullish()`: Marks the field as nullish (allows both `null` and `undefined`).
127
- - `default`: Accepts a static literal value (`string | number | boolean | ...`) or a lazy factory function (`() => value`). Automatically marks the field as optional in schemas while inferring a non-optional concrete TypeScript return type.
128
- - `coerce`: Instructs the Rust engine to automatically convert types when possible before validating. For example, converting `"123"` into a number, or `3000` into a string.
129
-
130
- > [!TIP]
131
- > **Static vs. Dynamic Defaults**:
132
- > ```typescript
133
- > const ConfigSchema = tex.object({
134
- > // Static defaults
135
- > PORT: tex.number({ coerce: true, default: 3000 }),
136
- > THEME: tex.enum(['light', 'dark'] as const, { default: 'light' }),
137
- > TAGS: tex.array(tex.string(), { default: ['exis', 'framework'] }),
138
- >
139
- > // Dynamic defaults (evaluated per parse call)
140
- > createdAt: tex.date({ default: () => new Date() }),
141
- > requestId: tex.string({ default: () => crypto.randomUUID() }),
142
- > })
143
- > ```
144
-
145
- > [!NOTE]
146
- > **Empty String Interop**: If an incoming string is empty (`""`), and the field is configured as `optional`, `nullable`, or `nullish`, ExisJS automatically converts it to `undefined` or `null` before running type checks. This cleanly handles form submissions and query strings without requiring ad-hoc pre-processors.
119
+ Validates an array of items matching a schema or object shape shorthand.
147
120
 
148
121
  ```typescript
149
- // Type Conversion Examples using { coerce: true }
150
- const EnvSchema = tex.object({
151
- // Converts string "3000" from process.env to number 3000
152
- PORT: tex.number({ coerce: true }),
153
-
154
- // Converts boolean 'false' or number '0' to string "false" / "0"
155
- ID_AS_STRING: tex.string({ coerce: true }),
156
-
157
- // Converts string "true" or number '1' to boolean true
158
- ENABLE_FEATURE: tex.boolean({ coerce: true })
122
+ const CartSchema = tex.object({
123
+ items: tex.array({
124
+ sku: tex.string(),
125
+ quantity: tex.number({ min: 1 }),
126
+ }, { min: 1, max: 50 }),
159
127
  })
160
128
  ```
161
129
 
162
- ### Primitive Options Summary
163
-
164
- | Primitive | Available Options |
165
- | :--- | :--- |
166
- | `tex.string(opts?)` | `min`, `max`, `trim`, `collapseWhitespace`, `toLowerCase`, `toUpperCase`, `escapeHtml`, `stripHtml`, `slugify`, `mask`, `preventSql`, `preventTraversal`, `default`, `optional`, `nullable`, `nullish` |
167
- | `tex.number(opts?)` | `min`, `max`, `coerce`, `default`, `optional`, `nullable`, `nullish` |
168
- | `tex.boolean(opts?)` | `coerce`, `default`, `optional`, `nullable`, `nullish` |
169
- | `tex.date(opts?)` | `coerce`, `minDate`, `maxDate`, `default`, `optional`, `nullable`, `nullish` |
170
- | `tex.array(schema, opts?)` | `min`, `max`, `dedupe`, `coerce`, `default`, `optional`, `nullable`, `nullish` |
171
- | `tex.enum(values, opts?)` | `default`, `optional`, `nullable`, `nullish` |
172
- | `tex.uuid(opts?)` | `version: 1 \| 4`, `default`, `optional`, `nullable`, `nullish` |
173
- | `tex.password(opts?)` | `min`, `max`, `requireNumbers`, `requireSymbols`, `requireUppercase`, `requireLowercase`, `optional`, `nullable`, `nullish` |
174
- | `tex.creditCard(opts?)` | `mask: boolean`, `optional`, `nullable`, `nullish` |
175
- | `tex.file(opts?)` | `maxSize`, `mimeTypes: string[]`, `optional`, `nullable`, `nullish` |
130
+ #### `tex.env(shape)`
131
+
132
+ Constructs a boot-time environment parser that automatically enables coercion for all numeric, boolean, date, and array fields.
176
133
 
177
134
  ```typescript
178
- const User = tex.object({
179
- id: tex.string(),
180
- email: tex.email(),
181
- password: tex.string({ min: 8, max: 100 }),
182
- age: tex.number({ min: 13, optional: true }),
183
- role: tex.enum(['admin', 'member'], { default: 'member' })
184
- })
135
+ export const env = tex.env({
136
+ PORT: tex.number({ default: 3000 }),
137
+ HOST: tex.string({ default: '0.0.0.0' }),
138
+ DATABASE_URL: tex.string(),
139
+ CORS_ORIGINS: tex.array(tex.string()),
140
+ }).parse(process.env)
185
141
  ```
186
142
 
187
143
  ---
188
144
 
189
- ## Custom Data Manipulation
145
+ ## Modifier Methods & Schema Chaining
190
146
 
191
- ### `.sanitize(...fns)`
192
- Runs one or more synchronous sanitization functions over the data **before** the Rust validation executes. Use this for complex object transformations or string formatting. ExisJS provides a rich suite of built-in sanitizers through the `exisjs/sanitize` module.
147
+ All `TexType` instances provide fluent modifier and documentation methods:
193
148
 
194
- ```typescript
195
- import { sanitize } from 'exisjs/sanitize'
149
+ | Method | Signature | Description |
150
+ | :--- | :--- | :--- |
151
+ | `.optional()` | `() => TexType<T \| undefined>` | Marks the field as optional (permits `undefined`). |
152
+ | `.nullable()` | `() => TexType<T \| null>` | Marks the field as nullable (permits `null`). |
153
+ | `.nullish()` | `() => TexType<T \| null \| undefined>` | Permits both `null` and `undefined`. |
154
+ | `.default(val)` | `(val: T \| (() => T)) => TexType<T>` | Sets a static value or dynamic factory returned when input is omitted. |
155
+ | `.describe(text)` | `(description: string) => this` | Sets documentation description metadata for OpenAPI 3.1 generation. |
156
+ | `.example(val)` | `(example: any) => this` | Sets example value metadata for OpenAPI 3.1 generation. |
157
+ | `.sanitize(...fns)` | `(...fns: ((val: T) => T)[]) => this` | Executes synchronous transformation functions before validation. |
158
+ | `.refine(fn, message)` | `(fn: (val: T) => boolean, msg: string) => this` | Adds custom validation logic evaluated after native validation. |
159
+ | `.refineAsync(fn, message)` | `(fn: (val: T) => Promise<boolean>, msg: string) => this` | Adds asynchronous validation logic (e.g. database lookups). |
160
+ | `.transform(fn)` | `<Out>(fn: (val: T) => Out) => TexTransformed<Out>` | Applies a synchronous transformation to validated field data. |
161
+ | `.toOpenApi()` | `() => Record<string, any>` | Converts the type definition into an OpenAPI 3.1 schema object. |
196
162
 
197
- // Built-in sanitization
198
- const CleanString = tex.string().sanitize(sanitize.trim, sanitize.toLowerCase)
163
+ ---
199
164
 
200
- // Custom data transformation
201
- const WhiteListIPs = tex.array(tex.string())
202
- .sanitize((val) => typeof val === 'string' ? val.split(',') : val)
203
- ```
165
+ ## `TexEngine` Schema Object Methods
204
166
 
205
- ### `.refine(fn, message)`
206
- Runs custom validation logic **after** the Rust validation executes. Useful for complex business logic that cannot be expressed purely with basic schema types.
167
+ `TexEngine` instances constructed via `tex.object()`, `tex.env()`, or derived builders provide composition and execution methods:
207
168
 
208
- ```typescript
209
- const PasswordConfirm = tex.object({
210
- password: tex.string(),
211
- confirm: tex.string()
212
- }).refine((data) => data.password === data.confirm, "Passwords must match")
213
- ```
169
+ | Method | Signature | Description |
170
+ | :--- | :--- | :--- |
171
+ | `partial()` | `() => TexEngine<Partial<T>>` | Returns a copy of the schema with all fields marked optional. |
172
+ | `pick(keys)` | `<K extends keyof T>(keys: K[]) => TexEngine<Pick<T, K>>` | Creates a new schema containing only the specified keys. |
173
+ | `omit(keys)` | `<K extends keyof T>(keys: K[]) => TexEngine<Omit<T, K>>` | Creates a new schema omitting the specified keys. |
174
+ | `extend(shape)` | `<E>(extension: E) => TexEngine<T & ResolveSchema<E>>` | Merges additional or overridden fields into the existing schema. |
175
+ | `merge(other)` | `<U>(other: TexEngine<U>) => TexEngine<T & U>` | Deeply merges two `TexEngine` schemas into a combined validator. |
176
+ | `transform(fn)` | `<Out>(fn: (data: T) => Out) => TexTransformedEngine<T, Out>` | Applies post-validation data reshaping to the entire schema output. |
177
+ | `parse(data)` | `(data: unknown) => T` | Synchronously parses and validates data, throwing `ValidatorError` on failure. |
178
+ | `parseAsync(data)` | `(data: unknown) => Promise<T>` | Asynchronously parses and validates data, evaluating async refinements. |
179
+ | `safeParse(data)` | `(data: unknown) => { success: true, data: T } \| { success: false, error: ValidatorError }` | Validates data without throwing exceptions. |
180
+ | `toOpenApi()` | `() => Record<string, any>` | Translates the schema into an OpenAPI 3.1 JSON schema representation. |
214
181
 
215
182
  ---
216
183
 
217
- ## Type Inference
184
+ ## TypeScript Type Extraction
218
185
 
219
- You can extract the raw TypeScript interface directly from any schema using the `ResolveSchema` utility.
186
+ Extract static TypeScript types from any schema using `Infer<T>` or `ResolveSchema<T>`:
220
187
 
221
188
  ```typescript
222
- import { tex, type ResolveSchema } from 'exisjs/validator'
189
+ import { tex, type Infer, type ResolveSchema } from 'exisjs/validator'
223
190
 
224
- const ProfileSchema = tex.object({
225
- name: tex.string(),
226
- age: tex.number({ optional: true })
191
+ const CreateProductSchema = tex.object({
192
+ title: tex.string(),
193
+ price: tex.number({ min: 0 }),
194
+ tags: tex.array(tex.string()).optional(),
227
195
  })
228
196
 
229
- // Resolves to: { name: string; age?: number | undefined }
230
- type Profile = ResolveSchema<typeof ProfileSchema>
197
+ // Using Infer:
198
+ type CreateProductInput = Infer<typeof CreateProductSchema>
199
+
200
+ // Using ResolveSchema:
201
+ type ProductDto = ResolveSchema<typeof CreateProductSchema>
231
202
  ```
232
203
 
233
204
  ---
234
205
 
235
- ## OpenAPI Generation
206
+ ## Pagination Utilities
236
207
 
237
- The `TexEngine` automatically converts validation schemas into strictly compliant OpenAPI/Swagger JSON schemas when attached to routes. This powers zero-config Swagger documentation in `exisjs/swagger`.
208
+ ### `tex.pagination(options?)`
238
209
 
239
- ---
210
+ Creates a pre-configured query string schema for paginated endpoints:
240
211
 
241
- ## Environment Validation (`tex.env`)
212
+ ```typescript
213
+ const paginationQuery = tex.pagination({
214
+ defaultLimit: 20,
215
+ maxLimit: 100,
216
+ })
217
+ ```
242
218
 
243
- Because `process.env` always supplies raw string values (e.g. `"4000"`, `"true"`, `"1"`), ExisJS provides a dedicated `tex.env()` schema parser that automatically applies type coercion (`number`, `boolean`, `date`, `array`) and parses string representations without requiring verbose `{ coerce: true }` flags on every field.
219
+ ### `getPaginationSkip(query)`
244
220
 
245
- ```typescript filename="src/config/env.ts"
246
- import { tex } from 'exisjs/validator'
221
+ Calculates database offset values (`skip`, `limit`, `page`) from query inputs.
247
222
 
248
- // Validates process.env synchronously with automatic type coercion on boot.
249
- export const env = tex.env({
250
- PORT: tex.number({ default: 3000 }), // Automatically coerced from "3000" -> 3000
251
- DEBUG: tex.boolean({ default: false }), // Parsed from "true" | "1" | "false" | "0"
252
- DATABASE_URL: tex.string(),
253
- JWT_SECRET: tex.string({ min: 32 }),
254
- ALLOWED_ORIGINS: tex.array(tex.string()), // Parsed from "http://localhost:3000,https://app.com"
255
- NODE_ENV: tex.enum(['development', 'production', 'test'] as const, {
256
- default: 'development',
257
- }),
258
- }).parse(process.env)
223
+ ```typescript
224
+ const { skip, limit, page } = getPaginationSkip({ page: 3, limit: 25 })
225
+ // Result: skip = 50, limit = 25, page = 3
259
226
  ```
260
227
 
261
- > [!TIP]
262
- > `tex.env` automatically applies defaults and lazy factories (`default: () => ...`) when environment variables are omitted, while throwing structured `ValidatorError` diagnostics on boot if required keys are missing.
228
+ ### `paginate(items, total, options)`
229
+
230
+ Wraps an array of items into a standardized paginated response envelope:
231
+
232
+ ```typescript
233
+ const result = paginate(items, 250, { page: 1, limit: 20 })
234
+ ```
235
+
236
+ #### Envelope Output Structure
237
+
238
+ ```typescript
239
+ export interface PaginatedResult<T> {
240
+ data: T[]
241
+ pagination: {
242
+ page: number
243
+ limit: number
244
+ total: number
245
+ totalPages: number
246
+ hasNextPage: boolean
247
+ hasPrevPage: boolean
248
+ hasNext: boolean
249
+ hasPrev: boolean
250
+ }
251
+ }
252
+ ```
263
253
 
264
254
  ---
265
255
 
266
- ## Validation Error Descriptors (`ValidationErrorDescriptor`)
256
+ ## Error Handling & Diagnostics
267
257
 
268
- When validation fails, ExisJS emits structured error descriptors passed across the FFI boundary:
258
+ When validation fails, `TexEngine` raises a `ValidatorError` containing an array of structured issue descriptors:
269
259
 
270
260
  ```typescript
271
261
  export interface ValidationErrorDescriptor {
272
- path: string // e.g. "email" or "items[0].sku"
273
- message: string // Human-readable failure description
274
- expected: string // Expected constraint or type rule
275
- received: string // Received value representation
276
- code: string // Machine-readable code (e.g. MISSING_REQUIRED_FIELD, INVALID_TYPE, VALUE_TOO_SMALL)
262
+ path: string // Property key or nested path (e.g. "user.email", "items[0].sku")
263
+ message: string // Descriptive validation error message
264
+ expected?: string // Expected constraint or format
265
+ received?: string // Received value type representation
266
+ code?: string // Machine-readable error code (e.g. MISSING_REQUIRED_FIELD)
277
267
  }
278
268
  ```
279
269
 
280
- In HTTP 400 responses, the envelope contains both a legacy `errors: Record<string, string>` dictionary for backward compatibility and a structured `details: ValidationErrorDescriptor[]` array for modern form libraries.
281
-
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=inspect-routes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inspect-routes.d.ts","sourceRoot":"","sources":["../../src/lib/inspect-routes.ts"],"names":[],"mappings":""}
@@ -0,0 +1,148 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ const node_url_1 = require("node:url");
40
+ const node_path_1 = __importDefault(require("node:path"));
41
+ const node_fs_1 = __importDefault(require("node:fs"));
42
+ async function inspect() {
43
+ const entryFile = process.env.EXIS_ENTRY_FILE;
44
+ if (!entryFile) {
45
+ console.error('EXIS_ENTRY_FILE environment variable is missing.');
46
+ process.exit(1);
47
+ }
48
+ const cwd = process.cwd();
49
+ const dynamicImport = new Function('specifier', 'return import(specifier)');
50
+ // 1. Load .env
51
+ try {
52
+ const { loadEnv } = await Promise.resolve().then(() => __importStar(require('../config/env.js')));
53
+ loadEnv(cwd);
54
+ }
55
+ catch {
56
+ /* ignore */
57
+ }
58
+ // 2. Register path alias loader
59
+ try {
60
+ const { registerPathAliasLoader } = await Promise.resolve().then(() => __importStar(require('../cli/resolve-aliases.js')));
61
+ registerPathAliasLoader(cwd);
62
+ }
63
+ catch {
64
+ /* ignore */
65
+ }
66
+ // 3. Auto-load Environment variables validation file if it exists
67
+ const envFiles = [
68
+ node_path_1.default.join(cwd, 'src', 'config', 'env.ts'),
69
+ node_path_1.default.join(cwd, 'src', 'config', 'env.js'),
70
+ ];
71
+ for (const envFile of envFiles) {
72
+ if (node_fs_1.default.existsSync(envFile)) {
73
+ await dynamicImport((0, node_url_1.pathToFileURL)(envFile).href).catch(() => {
74
+ /* ignore */
75
+ });
76
+ break;
77
+ }
78
+ }
79
+ try {
80
+ const url = (0, node_url_1.pathToFileURL)(entryFile).href;
81
+ const mod = await dynamicImport(url);
82
+ const isApp = (obj) => !!(obj &&
83
+ typeof obj === 'object' &&
84
+ 'getRoutes' in obj &&
85
+ typeof obj.getRoutes === 'function');
86
+ let rawApp = mod.default || mod.app;
87
+ if (rawApp &&
88
+ !rawApp._isExisAppDefinition &&
89
+ typeof rawApp.create !== 'function' &&
90
+ rawApp.default) {
91
+ rawApp = rawApp.default;
92
+ }
93
+ let app;
94
+ if (rawApp && rawApp._isExisAppDefinition) {
95
+ const { App, setActiveAppInstance } = await Promise.resolve().then(() => __importStar(require('../server/app.js')));
96
+ app = new App(rawApp.options);
97
+ setActiveAppInstance(app);
98
+ }
99
+ else if (rawApp &&
100
+ typeof rawApp === 'function' &&
101
+ rawApp.prototype &&
102
+ rawApp.prototype[Symbol.for('exisjs:server_config')]) {
103
+ const serverConfig = rawApp.prototype[Symbol.for('exisjs:server_config')];
104
+ const { App, setActiveAppInstance } = await Promise.resolve().then(() => __importStar(require('../server/app.js')));
105
+ app = new App({ plugins: serverConfig.plugins });
106
+ setActiveAppInstance(app);
107
+ }
108
+ else {
109
+ const appExport = Object.values(mod).find(isApp);
110
+ app = isApp(rawApp) ? rawApp : appExport;
111
+ }
112
+ if (!app) {
113
+ console.error('\x1b[31m[Exis CLI Error]\x1b[0m Your entry file must export an instance of Exis App.');
114
+ process.exit(1);
115
+ }
116
+ // Boot the app to register file-system routes
117
+ try {
118
+ await app.create();
119
+ if (app.routeScanner?.loadAllRoutes) {
120
+ await app.routeScanner.loadAllRoutes();
121
+ }
122
+ }
123
+ catch {
124
+ /* ignore if already created */
125
+ }
126
+ const routes = app.getRoutes().map((r) => ({
127
+ method: r.method,
128
+ path: r.path,
129
+ handlersCount: r.handlers ? r.handlers.length : 1,
130
+ sourceFile: r.sourceFile || null,
131
+ }));
132
+ // Send the extracted routes via IPC if available, otherwise print serialized payload marker
133
+ if (process.send) {
134
+ process.send({ type: 'exis:routes', routes });
135
+ process.exit(0);
136
+ }
137
+ else {
138
+ console.log(`__EXIS_ROUTES_PAYLOAD__:${JSON.stringify(routes)}`);
139
+ process.exit(0);
140
+ }
141
+ }
142
+ catch (err) {
143
+ console.error('\x1b[31m[Exis CLI Error]\x1b[0m Failed to import app:', err?.message || err);
144
+ process.exit(1);
145
+ }
146
+ }
147
+ inspect();
148
+ //# sourceMappingURL=inspect-routes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inspect-routes.js","sourceRoot":"","sources":["../../src/lib/inspect-routes.ts"],"names":[],"mappings":"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"}