@sot1986/appsync-precognition 0.5.2 → 0.5.3
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 +309 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,3 +1,311 @@
|
|
|
1
1
|
# appsync-precognition
|
|
2
2
|
|
|
3
|
-
Lean validation library for
|
|
3
|
+
Lean validation library for AppSync JS runtime, implementing Precognition protocol for real-time form validation.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Installation](#installation)
|
|
8
|
+
- [Basic Usage](#basic-usage)
|
|
9
|
+
- [Simple Validation](#simple-validation)
|
|
10
|
+
- [Precognitive Validation (Real-time)](#precognitive-validation-real-time)
|
|
11
|
+
- [Validation Rules](#validation-rules)
|
|
12
|
+
- [Basic Rules](#basic-rules)
|
|
13
|
+
- [Type Rules](#type-rules)
|
|
14
|
+
- [String Validation](#string-validation)
|
|
15
|
+
- [Date/Time Validation](#datetime-validation)
|
|
16
|
+
- [Numeric Validation](#numeric-validation)
|
|
17
|
+
- [Size Constraints](#size-constraints)
|
|
18
|
+
- [Pattern Matching](#pattern-matching)
|
|
19
|
+
- [Date Comparisons](#date-comparisons)
|
|
20
|
+
- [Nested Object Validation](#nested-object-validation)
|
|
21
|
+
- [Array Validation](#array-validation)
|
|
22
|
+
- [Custom Error Messages](#custom-error-messages)
|
|
23
|
+
- [Custom Attribute Names](#custom-attribute-names)
|
|
24
|
+
- [Validation Options](#validation-options)
|
|
25
|
+
- [Precognitive Validation Features](#precognitive-validation-features)
|
|
26
|
+
- [Client Integration](#client-integration)
|
|
27
|
+
- [Internationalization](#internationalization)
|
|
28
|
+
- [Error Handling](#error-handling)
|
|
29
|
+
- [Advanced Usage](#advanced-usage)
|
|
30
|
+
- [Assert Validated Data](#assert-validated-data)
|
|
31
|
+
- [Check Localization](#check-localization)
|
|
32
|
+
|
|
33
|
+
## Installation
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npm install appsync-precognition
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Basic Usage
|
|
40
|
+
|
|
41
|
+
### Simple Validation
|
|
42
|
+
Any valid object can be validated, using simplified validation rules.
|
|
43
|
+
|
|
44
|
+
```javascript
|
|
45
|
+
import { validate } from 'appsync-precognition'
|
|
46
|
+
|
|
47
|
+
export function request(ctx) {
|
|
48
|
+
const validatedArgs = validate({
|
|
49
|
+
name: 'Marco',
|
|
50
|
+
age: 15,
|
|
51
|
+
email: 'marco@email.it',
|
|
52
|
+
}, {
|
|
53
|
+
name: ['required', ['min', 3]],
|
|
54
|
+
age: ['required', ['min', 18]],
|
|
55
|
+
email: ['required', 'email'],
|
|
56
|
+
phone: ['sometimes', 'phone']
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
operation: 'PutItem',
|
|
61
|
+
key: util.dynamodb.toMapValues({ id: util.autoId() }),
|
|
62
|
+
attributeValues: util.dynamodb.toMapValues(validatedArgs)
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
This validation will throw an Error of type `ValidationError`.
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
util.error(
|
|
70
|
+
'age min value is 18',
|
|
71
|
+
'ValidationError',
|
|
72
|
+
null,
|
|
73
|
+
{
|
|
74
|
+
path: 'age',
|
|
75
|
+
value: 15
|
|
76
|
+
}
|
|
77
|
+
)
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Precognitive Validation (Real-time)
|
|
81
|
+
If your frontend application supports precognitive validation like [Nuxt precognition](https://nuxt.com/modules/precognition), everything will be handled automatically.
|
|
82
|
+
|
|
83
|
+
```javascript
|
|
84
|
+
import { precognitiveValidation } from 'appsync-precognition'
|
|
85
|
+
|
|
86
|
+
export function request(ctx) {
|
|
87
|
+
const validatedArgs = precognitiveValidation(ctx, {
|
|
88
|
+
name: ['required', ['min', 3]],
|
|
89
|
+
age: ['required', ['min', 18]],
|
|
90
|
+
email: ['required', 'email'],
|
|
91
|
+
phone: ['nullable', 'phone']
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
operation: 'PutItem',
|
|
96
|
+
key: util.dynamodb.toMapValues({ id: util.autoId() }),
|
|
97
|
+
attributeValues: util.dynamodb.toMapValues(validatedArgs)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
1. Module checks for precognition headers and/or keys.
|
|
102
|
+
2. Validates the request payload accordingly
|
|
103
|
+
3. In case of success and precognitive requests, it will immediately return with `{ data: null, errors: undefined }`
|
|
104
|
+
|
|
105
|
+
## Validation Rules
|
|
106
|
+
|
|
107
|
+
### Basic Rules
|
|
108
|
+
- `required` - Field must have a value
|
|
109
|
+
- `nullable` - Field can be null but validates if present
|
|
110
|
+
- `sometimes` - Field is optional but validates if present
|
|
111
|
+
|
|
112
|
+
### Type Rules
|
|
113
|
+
- `string` - Must be a string
|
|
114
|
+
- `number` - Must be a number
|
|
115
|
+
- `boolean` - Must be a boolean
|
|
116
|
+
- `array` - Must be an array
|
|
117
|
+
- `object` - Must be an object
|
|
118
|
+
|
|
119
|
+
### String Validation
|
|
120
|
+
- `email` - Valid email format
|
|
121
|
+
- `phone` - Valid phone number (+123...)
|
|
122
|
+
- `url` - Valid URL format
|
|
123
|
+
- `uuid` - Valid UUID format
|
|
124
|
+
- `ulid` - Valid ULID format
|
|
125
|
+
|
|
126
|
+
### Date/Time Validation
|
|
127
|
+
- `date` - Valid date (YYYY-MM-DD)
|
|
128
|
+
- `time` - Valid time (HH:MM:SS)
|
|
129
|
+
- `datetime` - Valid ISO datetime
|
|
130
|
+
|
|
131
|
+
### Numeric Validation
|
|
132
|
+
- `integer` - Valid integer
|
|
133
|
+
- `numeric` - Valid number format
|
|
134
|
+
|
|
135
|
+
### Size Constraints
|
|
136
|
+
- `['min', number]` - Minimum value/length
|
|
137
|
+
- `['max', number]` - Maximum value/length
|
|
138
|
+
- `['between', min, max]` - Between values (inclusive)
|
|
139
|
+
- `['bigger', number]` - Strictly greater than
|
|
140
|
+
- `['lower', number]` - Strictly less than
|
|
141
|
+
- `['within', min, max]` - Strictly between values
|
|
142
|
+
|
|
143
|
+
### Pattern Matching
|
|
144
|
+
- `['regex', pattern]` - Match regex pattern
|
|
145
|
+
- `['in', ...values]` - Value must be in list
|
|
146
|
+
- `['notIn', ...values]` - Value must not be in list
|
|
147
|
+
|
|
148
|
+
### Date Comparisons
|
|
149
|
+
- `['before', date]` - Before specified date
|
|
150
|
+
- `['after', date]` - After specified date
|
|
151
|
+
- `['beforeOrEqual', date]` - Before or equal to date
|
|
152
|
+
- `['afterOrEqual', date]` - After or equal to date
|
|
153
|
+
|
|
154
|
+
## Nested Object Validation
|
|
155
|
+
|
|
156
|
+
```javascript
|
|
157
|
+
const validatedArgs = validate(ctx.args, {
|
|
158
|
+
'user.name': ['required', ['min', 3]],
|
|
159
|
+
'user.email': ['required', 'email'],
|
|
160
|
+
'address.street': ['required'],
|
|
161
|
+
'address.zipCode': ['required', ['between', 5, 10]]
|
|
162
|
+
})
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Array Validation
|
|
166
|
+
|
|
167
|
+
To simplify error rules definition, the shortcut `*` is supported.
|
|
168
|
+
|
|
169
|
+
```javascript
|
|
170
|
+
const validatedArgs = validate(ctx.args, {
|
|
171
|
+
'hobbies': ['required', 'array', ['min', 1]],
|
|
172
|
+
'hobbies.*': ['required', 'string', ['max', 50]], // Validates each array item
|
|
173
|
+
'tags': ['sometimes', 'array'],
|
|
174
|
+
'tags.*.value': ['string']
|
|
175
|
+
})
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## Custom Error Messages
|
|
179
|
+
|
|
180
|
+
```javascript
|
|
181
|
+
const validatedArgs = validate(ctx.args, {
|
|
182
|
+
email: [{ rule: 'email', msg: 'Please enter a valid email address' }],
|
|
183
|
+
age: [{ rule: ['min', 18], msg: 'You must be at least 18 years old' }]
|
|
184
|
+
})
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## Custom Attribute Names
|
|
188
|
+
Attribute names can be customized by adding a third parameter to the `validate` function.
|
|
189
|
+
|
|
190
|
+
```javascript
|
|
191
|
+
const validatedArgs = validate(ctx.args, {
|
|
192
|
+
'email': ['required', 'email'],
|
|
193
|
+
'phone': ['required', 'phone'],
|
|
194
|
+
'hobbies.*': ['required', ['min', 2]] // each hobbies should have at least 2 chars
|
|
195
|
+
}, {
|
|
196
|
+
attributes: {
|
|
197
|
+
':email': 'Email Address',
|
|
198
|
+
':phone': 'Phone Number',
|
|
199
|
+
':hobbies.*': 'Hobby' // 'Hobby must have at least 2 characters'
|
|
200
|
+
}
|
|
201
|
+
})
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## Validation Options
|
|
205
|
+
|
|
206
|
+
```javascript
|
|
207
|
+
const validatedArgs = validate(ctx.args, rules, {
|
|
208
|
+
trim: true, // Trim string values (default: true)
|
|
209
|
+
allowEmptyString: false, // Allow empty strings (default: false)
|
|
210
|
+
errors: {
|
|
211
|
+
required: ':attr is mandatory',
|
|
212
|
+
email: ':attr must be a valid email'
|
|
213
|
+
},
|
|
214
|
+
attributes: {
|
|
215
|
+
':email': 'Email Address'
|
|
216
|
+
}
|
|
217
|
+
})
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## Precognitive Validation Features
|
|
221
|
+
|
|
222
|
+
The `precognitiveValidation` function automatically handles:
|
|
223
|
+
- **Full validation** when `precognition` header is not present
|
|
224
|
+
- **Selective validation** when `precognition-validate-only` header specifies fields
|
|
225
|
+
- **Early return** for precognitive requests with proper headers
|
|
226
|
+
- **Response headers** for client-side precognition handling
|
|
227
|
+
|
|
228
|
+
### Client Integration
|
|
229
|
+
|
|
230
|
+
Your frontend should send these headers for precognitive validation:
|
|
231
|
+
```json
|
|
232
|
+
{
|
|
233
|
+
"Precognition": "true",
|
|
234
|
+
"Precognition-Validate-Only": "email,name"
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
In addition, these headers must be enabled in the CORS policy of AppSync.
|
|
239
|
+
|
|
240
|
+
## Internationalization
|
|
241
|
+
|
|
242
|
+
```javascript
|
|
243
|
+
import { localize } from 'appsync-precognition/i18n'
|
|
244
|
+
|
|
245
|
+
export function request(ctx) {
|
|
246
|
+
// Set up localization based on Accept-Language header
|
|
247
|
+
localize(ctx, {
|
|
248
|
+
errors: {
|
|
249
|
+
en: { required: ':attr is required' },
|
|
250
|
+
es: { required: ':attr es requerido' },
|
|
251
|
+
it: { required: 'il campo :attr è obbligatorio' }
|
|
252
|
+
},
|
|
253
|
+
attributes: {
|
|
254
|
+
en: { ':name': 'Name', ':email': 'Email' },
|
|
255
|
+
es: { ':name': 'Nombre', ':email': 'Correo' },
|
|
256
|
+
it: { ':name': 'Nome', ':email': 'Email' }
|
|
257
|
+
}
|
|
258
|
+
}) // locale details will be added in stash object
|
|
259
|
+
|
|
260
|
+
const validatedArgs = precognitiveValidation(ctx, {
|
|
261
|
+
name: ['required'],
|
|
262
|
+
email: ['required', 'email']
|
|
263
|
+
})
|
|
264
|
+
|
|
265
|
+
return { /* your operation */ }
|
|
266
|
+
}
|
|
267
|
+
```
|
|
268
|
+
If needed, it can be useful to split the localization and validation in separate resolvers of the same pipeline.
|
|
269
|
+
|
|
270
|
+
## Error Handling
|
|
271
|
+
|
|
272
|
+
Validation errors are thrown as AppSync errors with detailed information:
|
|
273
|
+
|
|
274
|
+
```javascript
|
|
275
|
+
// When validation fails, the error contains:
|
|
276
|
+
// - message: Human-readable error message
|
|
277
|
+
// - errorType: 'ValidationError'
|
|
278
|
+
// - errorInfo: { path: 'field.name', value: invalidValue }
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
## Advanced Usage
|
|
282
|
+
|
|
283
|
+
### Assert Validated Data
|
|
284
|
+
|
|
285
|
+
```javascript
|
|
286
|
+
import { assertValidated } from 'appsync-precognition'
|
|
287
|
+
|
|
288
|
+
export function request(ctx) {
|
|
289
|
+
precognitiveValidation(ctx, rules)
|
|
290
|
+
return { /* operation */ }
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
export function response(ctx) {
|
|
294
|
+
assertValidated(ctx) // Ensures validation was performed
|
|
295
|
+
return ctx.stash.__validated
|
|
296
|
+
}
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
### Check Localization
|
|
300
|
+
|
|
301
|
+
```javascript
|
|
302
|
+
import { assertLocalized, isLocalized } from 'appsync-precognition'
|
|
303
|
+
|
|
304
|
+
export function request(ctx) {
|
|
305
|
+
if (isLocalized(ctx, 'es')) {
|
|
306
|
+
// Handle Spanish localization
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
assertLocalized(ctx) // Throws if not localized
|
|
310
|
+
}
|
|
311
|
+
```
|