inertia-sails 1.3.3 → 1.4.1
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 +76 -2
- package/index.js +117 -80
- package/lib/handle-bad-request.js +20 -6
- package/lib/helpers/build-page-object.js +117 -12
- package/lib/helpers/inertia-headers.js +4 -1
- package/lib/helpers/is-inertia-partial-request.js +10 -0
- package/lib/helpers/is-inertia-request.js +9 -0
- package/lib/helpers/request-context.js +49 -8
- package/lib/helpers/resolve-asset-version.js +12 -0
- package/lib/helpers/resolve-validation-errors.js +12 -5
- package/lib/location.js +11 -0
- package/lib/middleware/inertia-middleware.js +7 -2
- package/lib/props/always-prop.js +6 -2
- package/lib/props/defer-prop.js +46 -6
- package/lib/props/get-partial-data.js +9 -0
- package/lib/props/merge-prop.js +7 -4
- package/lib/props/merge-targets.js +114 -0
- package/lib/props/mergeable-prop.js +87 -0
- package/lib/props/once-prop.js +9 -1
- package/lib/props/optional-prop.js +12 -4
- package/lib/props/pick-props-to-resolve.js +14 -1
- package/lib/props/resolve-deferred-props.js +17 -1
- package/lib/props/resolve-except-props.js +11 -0
- package/lib/props/resolve-merge-props.js +81 -20
- package/lib/props/resolve-once-props.js +17 -7
- package/lib/props/resolve-only-props.js +10 -3
- package/lib/props/resolve-page-props.js +72 -13
- package/lib/props/resolve-scroll-props.js +17 -3
- package/lib/props/scroll-prop.js +30 -8
- package/lib/render.js +68 -9
- package/lib/responses/server-error.js +23 -6
- package/lib/types.js +68 -0
- package/package.json +3 -2
- package/test.js +53 -12
- package/tests/helpers/build-page-object.test.js +183 -0
- package/tests/helpers/preserve-fragment.test.js +118 -0
- package/tests/props/merge-targets.test.js +74 -0
- package/tests/props/resolve-merge-props.test.js +151 -0
- package/tests/props/resolve-page-props.test.js +99 -0
- package/tests/props/resolve-scroll-props.test.js +51 -0
- package/tests/render.test.js +197 -0
package/README.md
CHANGED
|
@@ -38,7 +38,10 @@ module.exports.inertia = {
|
|
|
38
38
|
<%- shipwright.styles() %>
|
|
39
39
|
</head>
|
|
40
40
|
<body>
|
|
41
|
-
<div id="app"
|
|
41
|
+
<div id="app"></div>
|
|
42
|
+
<script type="application/json" data-page="app">
|
|
43
|
+
<%- JSON.stringify(page).replace(/</g, '\\u003c') %>
|
|
44
|
+
</script>
|
|
42
45
|
<%- shipwright.scripts() %>
|
|
43
46
|
</body>
|
|
44
47
|
</html>
|
|
@@ -87,6 +90,24 @@ Return a URL string to redirect:
|
|
|
87
90
|
return '/dashboard'
|
|
88
91
|
```
|
|
89
92
|
|
|
93
|
+
`inertiaRedirect` performs an Inertia location visit. It does not return a
|
|
94
|
+
page object, so `sails.inertia.preserveFragment()` does not apply to this
|
|
95
|
+
response type. If you already know the fragment you want, include it in the
|
|
96
|
+
returned URL.
|
|
97
|
+
|
|
98
|
+
#### Preserving URL fragments
|
|
99
|
+
|
|
100
|
+
When a standard Inertia redirect should carry the current hash to the next
|
|
101
|
+
page, mark the redirect before returning the URL:
|
|
102
|
+
|
|
103
|
+
```js
|
|
104
|
+
sails.inertia.preserveFragment()
|
|
105
|
+
return '/articles/new-slug'
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
If the user started from `/articles/old-slug#comments`, the Inertia client can
|
|
109
|
+
carry `#comments` to the redirected page.
|
|
110
|
+
|
|
90
111
|
### Sharing Data
|
|
91
112
|
|
|
92
113
|
#### `share(key, value)`
|
|
@@ -191,6 +212,41 @@ return {
|
|
|
191
212
|
}
|
|
192
213
|
```
|
|
193
214
|
|
|
215
|
+
Deferred props can also be rescued when a non-critical callback fails:
|
|
216
|
+
|
|
217
|
+
```js
|
|
218
|
+
return {
|
|
219
|
+
page: 'dashboard',
|
|
220
|
+
props: {
|
|
221
|
+
analytics: sails.inertia
|
|
222
|
+
.defer(async () => {
|
|
223
|
+
return await Analytics.getExpensiveReport()
|
|
224
|
+
})
|
|
225
|
+
.rescue()
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Or pass the rescue option inline:
|
|
231
|
+
|
|
232
|
+
```js
|
|
233
|
+
return {
|
|
234
|
+
page: 'dashboard',
|
|
235
|
+
props: {
|
|
236
|
+
analytics: sails.inertia.defer(
|
|
237
|
+
async () => {
|
|
238
|
+
return await Analytics.getExpensiveReport()
|
|
239
|
+
},
|
|
240
|
+
{ rescue: true }
|
|
241
|
+
)
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
When a rescued deferred prop throws, it is omitted from `props` and its key is
|
|
247
|
+
reported in `rescuedProps`, allowing the client `<Deferred>` component to show
|
|
248
|
+
its rescue slot instead of failing the whole deferred response.
|
|
249
|
+
|
|
194
250
|
### Merge Props
|
|
195
251
|
|
|
196
252
|
Merge with existing client-side data (useful for infinite scroll):
|
|
@@ -199,13 +255,29 @@ Merge with existing client-side data (useful for infinite scroll):
|
|
|
199
255
|
// Shallow merge
|
|
200
256
|
messages: sails.inertia.merge(() => newMessages)
|
|
201
257
|
|
|
258
|
+
// Prepend new items instead of appending
|
|
259
|
+
notifications: sails.inertia.merge(() => newNotifications).prepend()
|
|
260
|
+
|
|
261
|
+
// Merge a nested array inside a paginated object
|
|
262
|
+
users: sails.inertia.merge(() => paginatedUsers).append('data')
|
|
263
|
+
|
|
264
|
+
// Match existing items by ID when merging
|
|
265
|
+
users: sails.inertia
|
|
266
|
+
.merge(() => paginatedUsers)
|
|
267
|
+
.append('data', {
|
|
268
|
+
matchOn: 'id'
|
|
269
|
+
})
|
|
270
|
+
|
|
202
271
|
// Deep merge (nested objects)
|
|
203
272
|
settings: sails.inertia.deepMerge(() => updatedSettings)
|
|
273
|
+
|
|
274
|
+
// Deep merge with item matching
|
|
275
|
+
chat: sails.inertia.deepMerge(() => chatState).matchOn('messages.id')
|
|
204
276
|
```
|
|
205
277
|
|
|
206
278
|
### Infinite Scroll
|
|
207
279
|
|
|
208
|
-
Paginate data with automatic merge behavior. Works with Inertia
|
|
280
|
+
Paginate data with automatic merge behavior. Works with Inertia's `<InfiniteScroll>` component:
|
|
209
281
|
|
|
210
282
|
```js
|
|
211
283
|
// Controller
|
|
@@ -244,6 +316,8 @@ defineProps({ invoices: Object })
|
|
|
244
316
|
</template>
|
|
245
317
|
```
|
|
246
318
|
|
|
319
|
+
`scroll()` targets the wrapped array for merging, such as `invoices.data`, and follows Inertia's infinite-scroll merge intent header so previous-page requests prepend while next-page requests append.
|
|
320
|
+
|
|
247
321
|
### History Encryption
|
|
248
322
|
|
|
249
323
|
Encrypt sensitive data in browser history:
|
package/index.js
CHANGED
|
@@ -9,32 +9,31 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
|
-
* @typedef {import('
|
|
13
|
-
* @typedef {import('
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
* @typedef {
|
|
18
|
-
* @
|
|
19
|
-
* @
|
|
20
|
-
* @
|
|
21
|
-
*
|
|
22
|
-
*/
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* @typedef {Object} InertiaPageProps
|
|
26
|
-
* @property {Object.<string, *>} [props] - Page props to pass to the component
|
|
27
|
-
*/
|
|
28
|
-
|
|
29
|
-
/**
|
|
12
|
+
* @typedef {import('./lib/types').InertiaRequest} Request
|
|
13
|
+
* @typedef {import('./lib/types').InertiaResponse} Response
|
|
14
|
+
* @typedef {import('./lib/types').InertiaProps} InertiaProps
|
|
15
|
+
* @typedef {import('./lib/types').SailsLike} SailsLike
|
|
16
|
+
* @typedef {import('./lib/types').PropCallback} PropCallback
|
|
17
|
+
* @typedef {import('./lib/types').BadRequestData} BadRequestData
|
|
18
|
+
* @typedef {(req: Request, res: Response, next: () => any) => any} Middleware
|
|
19
|
+
* @typedef {Record<string, any>} InertiaHook
|
|
20
|
+
* @typedef {{ message?: string, stack?: string, name?: string }} ErrorLike
|
|
21
|
+
*
|
|
30
22
|
* @typedef {Object} InertiaRenderData
|
|
31
23
|
* @property {string} page - The component name to render
|
|
32
|
-
* @property {
|
|
33
|
-
* @property {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
*
|
|
24
|
+
* @property {InertiaProps} [props] - Props to pass to the component
|
|
25
|
+
* @property {InertiaProps} [locals] - Additional locals for the root EJS template
|
|
26
|
+
*
|
|
27
|
+
* @typedef {Object} DeferOptions
|
|
28
|
+
* @property {boolean} [rescue=false] - Rescue callback failures
|
|
29
|
+
*
|
|
30
|
+
* @typedef {Object} ScrollOptions
|
|
31
|
+
* @property {number} [page=0] - Current page index (0-based)
|
|
32
|
+
* @property {number} [perPage=10] - Items per page
|
|
33
|
+
* @property {number} [total=0] - Total number of items
|
|
34
|
+
* @property {string} [pageName='page'] - Query parameter name for pagination
|
|
35
|
+
* @property {string} [wrapper='data'] - Key to wrap the data in
|
|
36
|
+
* @property {string|null} [matchOn] - Optional field used to match items when merging
|
|
38
37
|
*/
|
|
39
38
|
|
|
40
39
|
const inertia = require('./lib/middleware/inertia-middleware')
|
|
@@ -51,7 +50,12 @@ const ScrollProp = require('./lib/props/scroll-prop')
|
|
|
51
50
|
const handleBadRequest = require('./lib/handle-bad-request')
|
|
52
51
|
const handleServerError = require('./lib/responses/server-error')
|
|
53
52
|
|
|
53
|
+
/**
|
|
54
|
+
* @param {SailsLike} sails
|
|
55
|
+
* @returns {InertiaHook}
|
|
56
|
+
*/
|
|
54
57
|
module.exports = function defineInertiaHook(sails) {
|
|
58
|
+
/** @type {InertiaHook} */
|
|
55
59
|
let hook
|
|
56
60
|
const routesToBindInertiaTo = [
|
|
57
61
|
'GET r|^((?![^?]*\\/[^?\\/]+\\.[^?\\/]+(\\?.*)?).)*$|',
|
|
@@ -66,6 +70,12 @@ module.exports = function defineInertiaHook(sails) {
|
|
|
66
70
|
// Using startup timestamp ensures fresh assets on each server restart
|
|
67
71
|
const startupVersion = Date.now().toString(36)
|
|
68
72
|
|
|
73
|
+
/** @type {Middleware} */
|
|
74
|
+
const runWithRequestContext = (req, res, next) => {
|
|
75
|
+
if (requestContext.getContext()) return next()
|
|
76
|
+
requestContext.run(req, res, next)
|
|
77
|
+
}
|
|
78
|
+
|
|
69
79
|
/**
|
|
70
80
|
* Get asset version from Shipwright manifest.
|
|
71
81
|
* Automatically hashes the manifest content for cache busting.
|
|
@@ -150,9 +160,7 @@ module.exports = function defineInertiaHook(sails) {
|
|
|
150
160
|
}
|
|
151
161
|
}
|
|
152
162
|
if (!mw.inertiaContext) {
|
|
153
|
-
mw.inertiaContext =
|
|
154
|
-
requestContext.run(req, res, next)
|
|
155
|
-
}
|
|
163
|
+
mw.inertiaContext = runWithRequestContext
|
|
156
164
|
}
|
|
157
165
|
}
|
|
158
166
|
},
|
|
@@ -182,28 +190,12 @@ module.exports = function defineInertiaHook(sails) {
|
|
|
182
190
|
before: {
|
|
183
191
|
'GET /*': {
|
|
184
192
|
skipAssets: true,
|
|
185
|
-
fn:
|
|
186
|
-
// Skip if context already set up by HTTP middleware
|
|
187
|
-
if (requestContext.getContext()) return next()
|
|
188
|
-
requestContext.run(req, res, next)
|
|
189
|
-
}
|
|
190
|
-
},
|
|
191
|
-
'POST /*': (req, res, next) => {
|
|
192
|
-
if (requestContext.getContext()) return next()
|
|
193
|
-
requestContext.run(req, res, next)
|
|
194
|
-
},
|
|
195
|
-
'PUT /*': (req, res, next) => {
|
|
196
|
-
if (requestContext.getContext()) return next()
|
|
197
|
-
requestContext.run(req, res, next)
|
|
193
|
+
fn: runWithRequestContext
|
|
198
194
|
},
|
|
199
|
-
'
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
'DELETE /*': (req, res, next) => {
|
|
204
|
-
if (requestContext.getContext()) return next()
|
|
205
|
-
requestContext.run(req, res, next)
|
|
206
|
-
}
|
|
195
|
+
'POST /*': runWithRequestContext,
|
|
196
|
+
'PUT /*': runWithRequestContext,
|
|
197
|
+
'PATCH /*': runWithRequestContext,
|
|
198
|
+
'DELETE /*': runWithRequestContext
|
|
207
199
|
}
|
|
208
200
|
},
|
|
209
201
|
|
|
@@ -325,7 +317,7 @@ module.exports = function defineInertiaHook(sails) {
|
|
|
325
317
|
* Create an optional prop
|
|
326
318
|
* This allows you to define properties that are only evaluated when accessed.
|
|
327
319
|
* @docs https://docs.sailscasts.com/boring-stack/partial-reloads#lazy-data-evaluation
|
|
328
|
-
* @param {
|
|
320
|
+
* @param {PropCallback} callback - The callback function to execute
|
|
329
321
|
* @returns {OptionalProp} - The optional prop
|
|
330
322
|
*/
|
|
331
323
|
optional(callback) {
|
|
@@ -336,7 +328,7 @@ module.exports = function defineInertiaHook(sails) {
|
|
|
336
328
|
* Create a mergeable prop
|
|
337
329
|
* This allows you to merge multiple props together.
|
|
338
330
|
* @docs https://docs.sailscasts.com/boring-stack/merging-props
|
|
339
|
-
* @param {
|
|
331
|
+
* @param {PropCallback} callback - The callback function to execute
|
|
340
332
|
* @returns {MergeProp} - The mergeable prop
|
|
341
333
|
*/
|
|
342
334
|
merge(callback) {
|
|
@@ -347,7 +339,7 @@ module.exports = function defineInertiaHook(sails) {
|
|
|
347
339
|
* Create an always prop
|
|
348
340
|
* Always props are resolved on every request, whether partial or not.
|
|
349
341
|
* @docs https://docs.sailscasts.com/boring-stack/partial-reloads#lazy-data-evaluation
|
|
350
|
-
* @param {
|
|
342
|
+
* @param {PropCallback} callback - The callback function
|
|
351
343
|
* @returns {AlwaysProp} - The always prop
|
|
352
344
|
*/
|
|
353
345
|
always(callback) {
|
|
@@ -357,12 +349,13 @@ module.exports = function defineInertiaHook(sails) {
|
|
|
357
349
|
* Create a deferred prop
|
|
358
350
|
* This allows you to load certain page data after the initial render.
|
|
359
351
|
* @docs https://docs.sailscasts.com/boring-stack/deferred-props
|
|
360
|
-
* @param {
|
|
361
|
-
* @param {string} group - The group name
|
|
352
|
+
* @param {PropCallback} cb - The callback function to execute
|
|
353
|
+
* @param {string|DeferOptions} group - The group name, or options when no group is needed
|
|
354
|
+
* @param {DeferOptions} options - Deferred prop options
|
|
362
355
|
* @returns {DeferProp} - The deferred prop
|
|
363
356
|
*/
|
|
364
|
-
defer(cb, group = 'default') {
|
|
365
|
-
return new DeferProp(cb, group)
|
|
357
|
+
defer(cb, group = 'default', options = {}) {
|
|
358
|
+
return new DeferProp(cb, group, options)
|
|
366
359
|
},
|
|
367
360
|
|
|
368
361
|
/**
|
|
@@ -371,7 +364,7 @@ module.exports = function defineInertiaHook(sails) {
|
|
|
371
364
|
* The client tracks which props it has via X-Inertia-Except-Once-Props header.
|
|
372
365
|
* Useful for expensive computations that don't change often.
|
|
373
366
|
* @docs https://docs.sailscasts.com/boring-stack/once-props
|
|
374
|
-
* @param {
|
|
367
|
+
* @param {PropCallback} callback - The callback function to execute
|
|
375
368
|
* @returns {OnceProp} - The once prop
|
|
376
369
|
* @example
|
|
377
370
|
* // Basic usage
|
|
@@ -394,7 +387,7 @@ module.exports = function defineInertiaHook(sails) {
|
|
|
394
387
|
* Combines share() and once() - the prop is shared and only resolved once.
|
|
395
388
|
* @docs https://docs.sailscasts.com/boring-stack/once-props#share-once
|
|
396
389
|
* @param {string} key - The key of the property
|
|
397
|
-
* @param {
|
|
390
|
+
* @param {PropCallback} callback - The callback function to execute
|
|
398
391
|
* @returns {OnceProp} - The once prop (for chaining)
|
|
399
392
|
* @example
|
|
400
393
|
* // In a policy or middleware
|
|
@@ -445,7 +438,7 @@ module.exports = function defineInertiaHook(sails) {
|
|
|
445
438
|
* This prevents "phantom" toasts/notifications when users navigate back.
|
|
446
439
|
* Flash data is stored in the session so it persists across redirects.
|
|
447
440
|
* @docs https://docs.sailscasts.com/boring-stack/flash
|
|
448
|
-
* @param {string|
|
|
441
|
+
* @param {string|Record<string, any>} key - The key or an object of key-value pairs
|
|
449
442
|
* @param {*} [value] - The value (if key is a string)
|
|
450
443
|
* @returns {Object} - The hook instance for chaining
|
|
451
444
|
* @example
|
|
@@ -469,7 +462,8 @@ module.exports = function defineInertiaHook(sails) {
|
|
|
469
462
|
if (typeof key === 'object' && key !== null) {
|
|
470
463
|
req.session._inertiaFlash = { ...req.session._inertiaFlash, ...key }
|
|
471
464
|
} else {
|
|
472
|
-
|
|
465
|
+
const flashKey = /** @type {string} */ (key)
|
|
466
|
+
req.session._inertiaFlash[flashKey] = value
|
|
473
467
|
}
|
|
474
468
|
return this
|
|
475
469
|
},
|
|
@@ -486,8 +480,8 @@ module.exports = function defineInertiaHook(sails) {
|
|
|
486
480
|
/**
|
|
487
481
|
* Consume and clear flash data from the session.
|
|
488
482
|
* Called internally by build-page-object after adding to response.
|
|
489
|
-
* @param {
|
|
490
|
-
* @returns {
|
|
483
|
+
* @param {Request} req - The request object
|
|
484
|
+
* @returns {InertiaProps} - The flash data that was consumed
|
|
491
485
|
*/
|
|
492
486
|
consumeFlash(req) {
|
|
493
487
|
const flash = req?.session?._inertiaFlash || {}
|
|
@@ -501,7 +495,7 @@ module.exports = function defineInertiaHook(sails) {
|
|
|
501
495
|
* Create a deep merge prop
|
|
502
496
|
* Like merge(), but recursively merges nested objects instead of replacing them.
|
|
503
497
|
* @docs https://docs.sailscasts.com/boring-stack/merging-props#deep-merge
|
|
504
|
-
* @param {
|
|
498
|
+
* @param {PropCallback} callback - The callback function to execute
|
|
505
499
|
* @returns {MergeProp} - The mergeable prop with deep merge enabled
|
|
506
500
|
* @example
|
|
507
501
|
* // Deep merge nested user preferences
|
|
@@ -515,9 +509,9 @@ module.exports = function defineInertiaHook(sails) {
|
|
|
515
509
|
|
|
516
510
|
/**
|
|
517
511
|
* Render the response
|
|
518
|
-
* @param {
|
|
519
|
-
* @param {
|
|
520
|
-
* @param {
|
|
512
|
+
* @param {Request} req - The request object
|
|
513
|
+
* @param {Response} res - The response object
|
|
514
|
+
* @param {InertiaRenderData} data - The data to render
|
|
521
515
|
* @returns {*} - The rendered response
|
|
522
516
|
*/
|
|
523
517
|
render(req, res, data) {
|
|
@@ -527,8 +521,8 @@ module.exports = function defineInertiaHook(sails) {
|
|
|
527
521
|
* Handle Inertia redirects (external URLs or non-Inertia pages)
|
|
528
522
|
* Forces a full page visit instead of an Inertia XHR request.
|
|
529
523
|
* See https://docs.sailscasts.com/boring-stack/redirects
|
|
530
|
-
* @param {
|
|
531
|
-
* @param {
|
|
524
|
+
* @param {Request} req - The request object
|
|
525
|
+
* @param {Response} res - The response object
|
|
532
526
|
* @param {string} url - The URL to redirect to
|
|
533
527
|
* @returns {Object} - The response object with the redirect
|
|
534
528
|
*/
|
|
@@ -585,12 +579,60 @@ module.exports = function defineInertiaHook(sails) {
|
|
|
585
579
|
return requestContext.getClearHistory()
|
|
586
580
|
},
|
|
587
581
|
|
|
582
|
+
/**
|
|
583
|
+
* Preserve the current URL fragment across a standard Inertia redirect.
|
|
584
|
+
* The flag is stored in the session so it survives the redirect request,
|
|
585
|
+
* then it is consumed by the next Inertia page response.
|
|
586
|
+
*
|
|
587
|
+
* @docs https://docs.sailscasts.com/boring-stack/redirects#preserving-fragments
|
|
588
|
+
* @param {boolean} preserve - Whether to preserve the URL fragment
|
|
589
|
+
* @returns {Object} - The hook instance for chaining
|
|
590
|
+
* @example
|
|
591
|
+
* sails.inertia.preserveFragment()
|
|
592
|
+
* return '/article/new-slug'
|
|
593
|
+
*/
|
|
594
|
+
preserveFragment(preserve = true) {
|
|
595
|
+
const context = requestContext.getContext()
|
|
596
|
+
const req = requestContext.getRequest()
|
|
597
|
+
|
|
598
|
+
if (context) {
|
|
599
|
+
requestContext.setPreserveFragment(preserve)
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
if (req?.session) {
|
|
603
|
+
if (preserve) {
|
|
604
|
+
req.session._inertiaPreserveFragment = true
|
|
605
|
+
} else {
|
|
606
|
+
delete req.session._inertiaPreserveFragment
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
return this
|
|
611
|
+
},
|
|
612
|
+
|
|
613
|
+
/**
|
|
614
|
+
* Consume the preserve fragment flag for the current request.
|
|
615
|
+
* @param {Request} req - The request object
|
|
616
|
+
* @returns {boolean} - Whether to preserve the URL fragment
|
|
617
|
+
*/
|
|
618
|
+
consumePreserveFragment(req) {
|
|
619
|
+
const preserve =
|
|
620
|
+
requestContext.getPreserveFragment() ||
|
|
621
|
+
Boolean(req?.session?._inertiaPreserveFragment)
|
|
622
|
+
|
|
623
|
+
if (req?.session) {
|
|
624
|
+
delete req.session._inertiaPreserveFragment
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
return preserve
|
|
628
|
+
},
|
|
629
|
+
|
|
588
630
|
/**
|
|
589
631
|
* Handle bad request responses for Inertia.js
|
|
590
632
|
* For Inertia requests with validation errors, redirects back with errors in session.
|
|
591
|
-
* @param {
|
|
592
|
-
* @param {
|
|
593
|
-
* @param {
|
|
633
|
+
* @param {Request} req - The request object
|
|
634
|
+
* @param {Response} res - The response object
|
|
635
|
+
* @param {BadRequestData|Error|Record<string, any>} [optionalData] - Optional error data or Error object
|
|
594
636
|
* @returns {*} - Response (redirect for Inertia, status code for non-Inertia)
|
|
595
637
|
*/
|
|
596
638
|
handleBadRequest(req, res, optionalData) {
|
|
@@ -602,9 +644,9 @@ module.exports = function defineInertiaHook(sails) {
|
|
|
602
644
|
* For Inertia requests in development, displays a styled error modal with stack trace.
|
|
603
645
|
* In production, redirects back with a flash error message.
|
|
604
646
|
* @docs https://docs.sailscasts.com/boring-stack/error-handling
|
|
605
|
-
* @param {
|
|
606
|
-
* @param {
|
|
607
|
-
* @param {
|
|
647
|
+
* @param {Request} req - The request object
|
|
648
|
+
* @param {Response} res - The response object
|
|
649
|
+
* @param {ErrorLike} [error] - Optional error data or Error object
|
|
608
650
|
* @returns {*} - Response (HTML modal for dev Inertia, redirect for prod)
|
|
609
651
|
*/
|
|
610
652
|
handleServerError(req, res, error) {
|
|
@@ -620,13 +662,8 @@ module.exports = function defineInertiaHook(sails) {
|
|
|
620
662
|
* to 1-based for the Inertia client.
|
|
621
663
|
*
|
|
622
664
|
* @docs https://docs.sailscasts.com/boring-stack/infinite-scroll
|
|
623
|
-
* @param {
|
|
624
|
-
* @param {
|
|
625
|
-
* @param {number} [options.page=0] - Current page index (0-based)
|
|
626
|
-
* @param {number} [options.perPage=10] - Items per page
|
|
627
|
-
* @param {number} [options.total=0] - Total number of items
|
|
628
|
-
* @param {string} [options.pageName='page'] - Query parameter name for pagination
|
|
629
|
-
* @param {string} [options.wrapper='data'] - Key to wrap the data in
|
|
665
|
+
* @param {PropCallback} callback - Callback returning the paginated data array
|
|
666
|
+
* @param {ScrollOptions} [options] - Pagination options
|
|
630
667
|
* @returns {ScrollProp} - The scroll prop
|
|
631
668
|
* @example
|
|
632
669
|
* // Basic usage
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {import('./types').InertiaRequest} InertiaRequest
|
|
3
|
+
* @typedef {import('./types').InertiaResponse} InertiaResponse
|
|
4
|
+
* @typedef {import('./types').BadRequestData} BadRequestData
|
|
5
|
+
*/
|
|
6
|
+
|
|
1
7
|
/**
|
|
2
8
|
* Handle bad request responses for Inertia.js
|
|
3
9
|
*
|
|
@@ -5,9 +11,9 @@
|
|
|
5
11
|
* previous page with errors stored in the session. For non-Inertia requests,
|
|
6
12
|
* it returns a standard 400 response.
|
|
7
13
|
*
|
|
8
|
-
* @param {
|
|
9
|
-
* @param {
|
|
10
|
-
* @param {
|
|
14
|
+
* @param {InertiaRequest} req - Express/Sails request object
|
|
15
|
+
* @param {InertiaResponse} res - Express/Sails response object
|
|
16
|
+
* @param {BadRequestData|Error|Record<string, any>} [optionalData] - Optional error data or Error object
|
|
11
17
|
* @returns {*} - Response (redirect for Inertia, status code for non-Inertia)
|
|
12
18
|
*
|
|
13
19
|
* @example
|
|
@@ -22,13 +28,21 @@ module.exports = function handleBadRequest(req, res, optionalData) {
|
|
|
22
28
|
const statusCodeToSet = 400
|
|
23
29
|
|
|
24
30
|
// Check if it's an Inertia request
|
|
25
|
-
if (req.header('X-Inertia')) {
|
|
26
|
-
if (
|
|
31
|
+
if (req.header?.('X-Inertia')) {
|
|
32
|
+
if (
|
|
33
|
+
optionalData &&
|
|
34
|
+
!(optionalData instanceof Error) &&
|
|
35
|
+
Array.isArray(optionalData.problems)
|
|
36
|
+
) {
|
|
37
|
+
/** @type {Record<string, string[]>} */
|
|
27
38
|
const errors = {}
|
|
28
39
|
optionalData.problems.forEach((problem) => {
|
|
29
40
|
if (typeof problem === 'object') {
|
|
30
41
|
Object.keys(problem).forEach((propertyName) => {
|
|
31
|
-
const sanitizedProblem = problem[propertyName].replace(
|
|
42
|
+
const sanitizedProblem = String(problem[propertyName]).replace(
|
|
43
|
+
/\.$/,
|
|
44
|
+
''
|
|
45
|
+
) // Trim trailing dot
|
|
32
46
|
if (!errors[propertyName]) {
|
|
33
47
|
errors[propertyName] = [sanitizedProblem]
|
|
34
48
|
} else {
|