@chidchanun/bcp 0.1.18 → 0.1.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/docs/README.md CHANGED
@@ -4,7 +4,7 @@ This directory is the documentation source of truth for the BCP Framework docume
4
4
 
5
5
  The recommended documentation website project name is `bcp-docs`.
6
6
 
7
- > Documentation target: BCP Framework `0.1.18`
7
+ > Documentation target: BCP Framework `0.1.19`
8
8
 
9
9
  ## Purpose
10
10
 
@@ -40,6 +40,7 @@ The website should not invent API behavior that is not documented or tested in t
40
40
  | `/docs/route-guards` | `route-guards.md` | Scoped page authorization |
41
41
  | `/docs/form-actions` | `form-actions.md` | Server mutations and forms |
42
42
  | `/docs/server-request-apis` | `server-request-apis.md` | Request, cookie, redirect and response APIs |
43
+ | `/docs/validation` | `validation.md` | Typed form/API validation and field errors |
43
44
 
44
45
  ### Authentication
45
46
 
@@ -72,9 +73,9 @@ Use the files under `docs/releases/` for release pages.
72
73
  Recommended route format:
73
74
 
74
75
  ```text
75
- /releases/0.1.16
76
76
  /releases/0.1.17
77
77
  /releases/0.1.18
78
+ /releases/0.1.19
78
79
  ```
79
80
 
80
81
  The newest release should also be available from `/releases`.
@@ -102,6 +103,9 @@ Recommended feature groups:
102
103
  - Form actions
103
104
  - Request-scoped server APIs
104
105
  - Cookies and redirects
106
+ - `bcp/validation`
107
+ - Typed form/API validation
108
+ - Field and form error output
105
109
 
106
110
  ### Database
107
111
 
@@ -153,7 +157,7 @@ Suggested content, not a required exact copy:
153
157
  ```text
154
158
  BCP Framework
155
159
  A React full-stack framework for building server-rendered applications with routing,
156
- data loading, server mutations, authentication, databases, middleware and production tooling.
160
+ data loading, server mutations, authentication, databases, validation, middleware and production tooling.
157
161
  ```
158
162
 
159
163
  Primary actions:
@@ -183,6 +187,7 @@ Core
183
187
  Server Data Loaders
184
188
  Route Guards
185
189
  Form Actions
190
+ Validation
186
191
 
187
192
  Authentication
188
193
  Authentication Core
@@ -203,9 +208,9 @@ Production
203
208
  Development Logging
204
209
 
205
210
  Releases
211
+ 0.1.19
206
212
  0.1.18
207
213
  0.1.17
208
- 0.1.16
209
214
  ```
210
215
 
211
216
  ## Suggested documentation page layout
@@ -249,6 +254,11 @@ import {
249
254
  db,
250
255
  } from "bcp/database";
251
256
 
257
+ import {
258
+ v,
259
+ validateFormData,
260
+ } from "bcp/validation";
261
+
252
262
  import {
253
263
  type MiddlewarePipelineHandler,
254
264
  } from "bcp/middleware";
@@ -297,7 +307,7 @@ npx create-bcp-app@latest my-app
297
307
 
298
308
  ## Middleware v2 documentation priority
299
309
 
300
- BCP 0.1.18 introduces a meaningful middleware architecture change, so the `bcp-docs` website should make the distinction between v1 and v2 clear.
310
+ BCP 0.1.18 introduced a meaningful middleware architecture change, so the `bcp-docs` website should make the distinction between v1 and v2 clear.
301
311
 
302
312
  The middleware page should prominently explain:
303
313
 
@@ -310,6 +320,21 @@ Existing v1 code remains supported, so migration documentation should describe v
310
320
 
311
321
  See `middleware.md` for the full API and examples.
312
322
 
323
+ ## Validation documentation priority
324
+
325
+ BCP 0.1.19 introduces the public `bcp/validation` entrypoint. The docs website should show validation in both form-action and API-route examples.
326
+
327
+ Important concepts to surface:
328
+
329
+ - `v.object`, `v.string`, `v.number`, `v.boolean`, `v.array`
330
+ - FormData coercion with `validateFormData()`
331
+ - `success/data` versus `issues/fieldErrors/formErrors`
332
+ - object fields are stripped unless `allowUnknown: true`
333
+ - `refine()` for custom application rules
334
+ - no external validation dependency is required
335
+
336
+ See `validation.md` for the complete API and examples.
337
+
313
338
  ## Release workflow for documentation
314
339
 
315
340
  Before publishing a new BCP release:
@@ -0,0 +1,106 @@
1
+ # BCP Framework 0.1.19
2
+
3
+ BCP Framework 0.1.19 adds the first built-in Validation System.
4
+
5
+ ## Highlights
6
+
7
+ - new `bcp/validation` public entrypoint
8
+ - universal validation API for server and client code
9
+ - `v.object`, `v.string`, `v.number`, `v.boolean`, `v.literal`, `v.enum`, `v.array` and `v.union`
10
+ - `.optional()`, `.nullable()` and `.refine()` composition
11
+ - `safeParse()` and throwing `parse()` modes
12
+ - typed validation output through `InferValidator`
13
+ - `validateFormData()` for BCP form actions
14
+ - repeated FormData keys become arrays
15
+ - string/number/boolean coercion options for HTML forms
16
+ - field errors flattened into dot paths
17
+ - serializable validation failure objects
18
+ - unknown object fields are stripped by default
19
+ - no new external runtime dependency
20
+
21
+ ## Form action example
22
+
23
+ ```ts
24
+ import {
25
+ validateFormData,
26
+ v,
27
+ } from "bcp/validation";
28
+
29
+ const schema =
30
+ v.object({
31
+ email:
32
+ v.string({
33
+ trim: true,
34
+ email: true,
35
+ }),
36
+ age:
37
+ v.number({
38
+ coerce: true,
39
+ min: 18,
40
+ }),
41
+ });
42
+
43
+ export async function saveUser(
44
+ formData: FormData
45
+ ) {
46
+ const result =
47
+ validateFormData(
48
+ schema,
49
+ formData
50
+ );
51
+
52
+ if (!result.success) {
53
+ return result;
54
+ }
55
+
56
+ return {
57
+ success: true,
58
+ user: result.data,
59
+ };
60
+ }
61
+ ```
62
+
63
+ ## API example
64
+
65
+ ```ts
66
+ const result =
67
+ schema.safeParse(
68
+ await request.json()
69
+ );
70
+
71
+ if (!result.success) {
72
+ return Response.json(
73
+ result,
74
+ {
75
+ status: 422,
76
+ }
77
+ );
78
+ }
79
+ ```
80
+
81
+ ## Error shape
82
+
83
+ Validation failures use the same shape in form actions, API routes and direct validation calls:
84
+
85
+ ```ts
86
+ {
87
+ success: false,
88
+ issues: [
89
+ {
90
+ path: ["email"],
91
+ message:
92
+ "Must be a valid email address.",
93
+ code:
94
+ "invalid_email",
95
+ },
96
+ ],
97
+ fieldErrors: {
98
+ email: [
99
+ "Must be a valid email address.",
100
+ ],
101
+ },
102
+ formErrors: [],
103
+ }
104
+ ```
105
+
106
+ See `docs/validation.md` for the full API.
@@ -0,0 +1,342 @@
1
+ # Validation
2
+
3
+ BCP Framework 0.1.19 adds a built-in validation system through `bcp/validation`.
4
+
5
+ The module is universal and can be used in server actions, API routes, loaders and client-side code.
6
+
7
+ ## Basic schema
8
+
9
+ ```ts
10
+ import {
11
+ v,
12
+ } from "bcp/validation";
13
+
14
+ const userSchema =
15
+ v.object({
16
+ name:
17
+ v.string({
18
+ trim: true,
19
+ minLength: 2,
20
+ }),
21
+ email:
22
+ v.string({
23
+ trim: true,
24
+ email: true,
25
+ }),
26
+ age:
27
+ v.number({
28
+ coerce: true,
29
+ integer: true,
30
+ min: 18,
31
+ }),
32
+ role:
33
+ v.enum([
34
+ "admin",
35
+ "user",
36
+ ]),
37
+ });
38
+ ```
39
+
40
+ ## Safe parsing
41
+
42
+ ```ts
43
+ const result =
44
+ userSchema.safeParse(input);
45
+
46
+ if (!result.success) {
47
+ console.log(
48
+ result.fieldErrors
49
+ );
50
+ return;
51
+ }
52
+
53
+ console.log(result.data);
54
+ ```
55
+
56
+ Successful results have:
57
+
58
+ ```ts
59
+ {
60
+ success: true,
61
+ data: value,
62
+ }
63
+ ```
64
+
65
+ Failures have:
66
+
67
+ ```ts
68
+ {
69
+ success: false,
70
+ issues: [...],
71
+ fieldErrors: {
72
+ email: [
73
+ "Must be a valid email address.",
74
+ ],
75
+ },
76
+ formErrors: [],
77
+ }
78
+ ```
79
+
80
+ Nested field paths are flattened with dot notation, for example `profile.email` and `items.0.name`.
81
+
82
+ ## Form actions
83
+
84
+ `validateFormData()` converts repeated FormData keys into arrays and validates the resulting object.
85
+
86
+ ```ts
87
+ import {
88
+ validateFormData,
89
+ v,
90
+ } from "bcp/validation";
91
+
92
+ const schema =
93
+ v.object({
94
+ email:
95
+ v.string({
96
+ trim: true,
97
+ email: true,
98
+ }),
99
+ age:
100
+ v.number({
101
+ coerce: true,
102
+ min: 18,
103
+ }),
104
+ });
105
+
106
+ export async function saveUser(
107
+ formData: FormData
108
+ ) {
109
+ const result =
110
+ validateFormData(
111
+ schema,
112
+ formData
113
+ );
114
+
115
+ if (!result.success) {
116
+ return result;
117
+ }
118
+
119
+ // result.data is typed and validated.
120
+ // await db.execute(...)
121
+
122
+ return {
123
+ success: true,
124
+ user: result.data,
125
+ };
126
+ }
127
+ ```
128
+
129
+ The failure object is serializable and can be returned from a BCP form action directly.
130
+
131
+ ## API routes
132
+
133
+ ```ts
134
+ import {
135
+ v,
136
+ } from "bcp/validation";
137
+
138
+ const schema =
139
+ v.object({
140
+ email:
141
+ v.string({
142
+ email: true,
143
+ }),
144
+ });
145
+
146
+ export async function POST(
147
+ request: Request
148
+ ) {
149
+ const input =
150
+ await request.json();
151
+ const result =
152
+ schema.safeParse(
153
+ input
154
+ );
155
+
156
+ if (!result.success) {
157
+ return Response.json(
158
+ result,
159
+ {
160
+ status: 422,
161
+ }
162
+ );
163
+ }
164
+
165
+ return Response.json({
166
+ user: result.data,
167
+ });
168
+ }
169
+ ```
170
+
171
+ ## Throwing parse
172
+
173
+ Use `parse()` when invalid input should throw a `ValidationError`.
174
+
175
+ ```ts
176
+ import {
177
+ ValidationError,
178
+ parse,
179
+ } from "bcp/validation";
180
+
181
+ try {
182
+ const user =
183
+ parse(
184
+ userSchema,
185
+ input
186
+ );
187
+ } catch (error) {
188
+ if (
189
+ error instanceof
190
+ ValidationError
191
+ ) {
192
+ console.log(
193
+ error.fieldErrors
194
+ );
195
+ }
196
+ }
197
+ ```
198
+
199
+ ## Available validators
200
+
201
+ ```ts
202
+ v.string()
203
+ v.number()
204
+ v.boolean()
205
+ v.literal("active")
206
+ v.enum(["admin", "user"])
207
+ v.array(v.string())
208
+ v.object({ ... })
209
+ v.union([ ... ])
210
+ ```
211
+
212
+ Optional and nullable values:
213
+
214
+ ```ts
215
+ v.string().optional()
216
+ v.string().nullable()
217
+
218
+ v.optional(v.string())
219
+ v.nullable(v.string())
220
+ ```
221
+
222
+ ## String validation
223
+
224
+ ```ts
225
+ v.string({
226
+ trim: true,
227
+ minLength: 2,
228
+ maxLength: 100,
229
+ email: true,
230
+ pattern: /^[A-Z]/,
231
+ })
232
+ ```
233
+
234
+ ## Number validation
235
+
236
+ ```ts
237
+ v.number({
238
+ coerce: true,
239
+ integer: true,
240
+ min: 1,
241
+ max: 100,
242
+ })
243
+ ```
244
+
245
+ `coerce: true` is useful for HTML forms because FormData values are strings.
246
+
247
+ ## Boolean validation
248
+
249
+ ```ts
250
+ v.boolean({
251
+ coerce: true,
252
+ })
253
+ ```
254
+
255
+ Boolean coercion recognizes common form values including `true`, `false`, `1`, `0`, `on`, `off`, `yes` and `no`.
256
+
257
+ ## Custom rules
258
+
259
+ Use `refine()` for application-specific rules.
260
+
261
+ ```ts
262
+ const password =
263
+ v.string({
264
+ minLength: 8,
265
+ }).refine(
266
+ (value) =>
267
+ /\d/.test(value),
268
+ "Password must contain a number.",
269
+ "password_number"
270
+ );
271
+ ```
272
+
273
+ ## Unknown object fields
274
+
275
+ Object validation strips fields that are not declared in the schema by default.
276
+
277
+ This is useful for API and form input because untrusted extra fields do not automatically pass into database writes.
278
+
279
+ To preserve unknown fields explicitly:
280
+
281
+ ```ts
282
+ v.object(
283
+ {
284
+ name: v.string(),
285
+ },
286
+ {
287
+ allowUnknown: true,
288
+ }
289
+ )
290
+ ```
291
+
292
+ ## Validation paths and errors
293
+
294
+ Each issue contains:
295
+
296
+ ```ts
297
+ {
298
+ path: [
299
+ "profile",
300
+ "email",
301
+ ],
302
+ message:
303
+ "Must be a valid email address.",
304
+ code:
305
+ "invalid_email",
306
+ }
307
+ ```
308
+
309
+ Use `getFieldError()` when only the first message for a field is needed.
310
+
311
+ ```ts
312
+ import {
313
+ getFieldError,
314
+ } from "bcp/validation";
315
+
316
+ const emailError =
317
+ getFieldError(
318
+ result,
319
+ "email"
320
+ );
321
+ ```
322
+
323
+ ## Type inference
324
+
325
+ Use `InferValidator` when application code needs the TypeScript output type of a schema.
326
+
327
+ ```ts
328
+ import type {
329
+ InferValidator,
330
+ } from "bcp/validation";
331
+
332
+ type UserInput =
333
+ InferValidator<
334
+ typeof userSchema
335
+ >;
336
+ ```
337
+
338
+ ## External schema libraries
339
+
340
+ BCP 0.1.19 does not require Zod, Valibot or another validation dependency. The built-in API keeps framework validation dependency-free.
341
+
342
+ Adapters for external schema libraries can be added in future releases without changing the validation result model used by actions and API routes.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chidchanun/bcp",
3
- "version": "0.1.18",
3
+ "version": "0.1.19",
4
4
  "description": "BCP Framework - a React full-stack framework with file-based routing, SSR, APIs, middleware, islands, caching and standalone production builds.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -43,6 +43,10 @@
43
43
  "types": "./packages/client/src/config.ts",
44
44
  "default": "./packages/client/src/config.ts"
45
45
  },
46
+ "./validation": {
47
+ "types": "./packages/client/src/validation.ts",
48
+ "default": "./packages/client/src/validation.ts"
49
+ },
46
50
  "./database": {
47
51
  "types": "./packages/client/src/database.ts",
48
52
  "browser": "./packages/client/src/server-only.browser.mjs",